HashSet 项可以更改为 Set 中的相同项

HashSet item can be changed into same item in Set

我有一个 节点 class :

public class Node : INode
    {  
        public object Value { get; set; }
    }

我有 EqualityComparer 这个节点 class 像这样:

public class INodeEqualityComparer : EqualityComparer<INode>
    {
        private INodeEqualityComparer()
        {

        }

        private static readonly INodeEqualityComparer _instance = 
        new INodeEqualityComparer();

        public static INodeEqualityComparer Instance
        {
            get { return _instance; }
        }

        public override bool Equals(INode x, INode y)
        {
            return (int)(x.Value) == (int)(y.Value);
        }

        public override int GetHashCode(INode obj)
        {
            return ((int)(obj.Value)).GetHashCode();
        }
    }

我通过传递 NodeEqualityComparer 创建我的 HashSet

我有 4 个节点实例:

Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(1);

当我将 n1,n2,n3,n4 添加到我的哈希集时,n4 会被忽略。

HashSet<INode> nodes = new HashSet<INode>(INodeEqualityComparer.Instance);
nodes.Add(n1);
nodes.Add(n2);
nodes.Add(n3);
nodes.Add(n4);

BUT 在我使用此更改后:

nodes.Where(n => (int)(n.Value) == 3).FirstOrDefault().Value = 1;

根据 NodeEqualityComparer,将有 2 个元素(值=1)相等。那些是 n1 和 n3。

为什么哈希集不会阻止更新节点或删除它?

这是设计使然:散列集合(无论是字典、散列集还是任何其他)假设对象的散列码在插入集合后不会改变。并且由于两个被认为相等的对象也必须具有相同的哈希码,因此这也意味着无论其 Equals 实现如何 returns 都不能因相同的参数而改变。

这适用于散列的内容:在字典中,这是关键。在集合中,这是整个对象。

.NET documentation 状态:

In general, for mutable reference types, you should override GetHashCode() only if:

  • You can compute the hash code from fields that are not mutable; or

  • You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.

在您的 Node class 中,您使用可变的 属性 (Value) 来计算哈希码。这通常是个坏主意,实际上 ReSharper 会发出警告。再者,覆盖 EqualsGetHashCode 通常意味着您将类型视为 "value" 而不是 "entity",并且值应尽可能视为不可变的。

如果不能使对象不可变,请不要将其存储在哈希集合中。