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

时间:2018-12-07 17:39:54

标签: c# hashset equality iequalitycomparer

我有一个 Node 类:

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

对于这个Node类,我有 EqualityComparer

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实例:

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);
在使用此更改后,

但是

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

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

Debbuger

为什么,哈希集不会阻止更新节点或将其删除

1 个答案:

答案 0 :(得分:0)

这是设计使然:哈希集合(无论是字典还是哈希集或任何其他)都假定将对象的哈希码插入集合后不发生变化。而且,由于被认为相等的两个对象也必须具有相同的哈希码,所以这也意味着对于相同的参数,无论其Equals实现的返回结果如何,都不得更改。

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

.NET documentation指出:

  

通常,对于可变引用类型,仅在以下情况下才应覆盖GetHashCode():

     
      
  • 您可以从不可变的字段中计算哈希码;或

  •   
  • 您可以确保当可变对象的哈希代码包含在依赖于其哈希代码的集合中时,该对象的哈希代码不会更改。

  •   

Node类中,您使用可变属性(Value)计算哈希码。这通常是一个坏主意,实际上这是ReSharper会警告的。再一次,覆盖EqualsGetHashCode通常意味着您将类型视为“值”而不是“实体”,并且值应尽可能视为不可变的。

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