我有一个 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。
为什么,哈希集不会阻止更新节点或将其删除?
答案 0 :(得分:0)
这是设计使然:哈希集合(无论是字典还是哈希集或任何其他)都假定将对象的哈希码插入集合后不发生变化。而且,由于被认为相等的两个对象也必须具有相同的哈希码,所以这也意味着对于相同的参数,无论其Equals
实现的返回结果如何,都不得更改。
这适用于散列的内容:在字典中,这就是关键。在集合中,这就是整个对象。
通常,对于可变引用类型,仅在以下情况下才应覆盖GetHashCode():
您可以从不可变的字段中计算哈希码;或
您可以确保当可变对象的哈希代码包含在依赖于其哈希代码的集合中时,该对象的哈希代码不会更改。
在Node
类中,您使用可变属性(Value
)计算哈希码。这通常是一个坏主意,实际上这是ReSharper会警告的。再一次,覆盖Equals
和GetHashCode
通常意味着您将类型视为“值”而不是“实体”,并且值应尽可能视为不可变的。
如果您不能使对象不可变,请不要将其存储在哈希集合中。