【问题标题】:HashSet item can be changed into same item in SetHashSet 项可以更改为 Set 中的相同项
【发布时间】:2018-12-07 17:39:54
【问题描述】:

我有一个 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 n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(1);

当我将 n1,n2,n3,n4 添加到我的 hashset 时,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。

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

【问题讨论】:

  • 能否给出完整代码?
  • 考虑使用T 而不是object 以避免装箱并提供类型安全。
  • 您希望HashSet 如何防止消费者改变集合中的元素?你能编写自己的集合,不允许其中的项目发生变异吗?
  • HashSet 怎么会知道你的某些代码改变了存储在 HashSet 中的对象的字段/属性? HashSet 只是一个集合,它不是某种“霸王”,随时随地监视您的代码在做什么......

标签: c# hashset equality iequalitycomparer


【解决方案1】:

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

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

.NET documentation 声明:

一般来说,对于可变引用类型,只有在以下情况下才应该覆盖 GetHashCode():

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 2018-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    相关资源
    最近更新 更多