Элемент HashSet может быть изменен на тот же элемент в наборе - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть Node class:

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

И у меня есть EqualityComparer для этого класса Node:

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();
        }
    }

Я создаю свой HashSet , передавая NodeEqualityComparer.

У меня есть 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);

НО после того, как я использую это изменение:

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

будет 2 элемента, которые равны друг другу (значение = 1) на основена NodeEqualityComparer.это n1 и n3.

Debbuger

ПОЧЕМУ хэш-сет не не запрещает обновление узла или удаление его

1 Ответ

0 голосов
/ 07 декабря 2018

Это сделано специально: хэшированные коллекции (будь то словари, хэш-наборы или любые другие) предполагают, что хеш-код объекта не изменяется после его вставки в коллекцию.И поскольку два объекта, которые считаются равными, также должны иметь одинаковый хеш-код, то это также означает, что независимо от того, что возвращает его реализация Equals, не должны изменяться для одного и того же параметра.

Это относится к тому, что хэшируется: всловари, это ключ.В наборах это весь объект.

Документация .NET гласит:

В общем случае для изменяемых ссылочных типов следует переопределять только GetHashCode ()if:

  • Вы можете вычислить хеш-код из полей, которые не являются изменяемыми;или

  • Вы можете гарантировать, что хеш-код изменяемого объекта не изменится, пока объект содержится в коллекции, которая использует свой хеш-код.

В вашем классе Node вы используете изменяемое свойство (Value) для вычисления хеш-кода.Обычно это плохая идея, и на самом деле это то, против чего ReSharper будет предупреждать.С другой стороны, переопределение Equals и GetHashCode обычно означает, что вы рассматриваете тип как «значение», а не как «сущность», и значения следует рассматривать как неизменяемые, когда это возможно.

Если выне можете сделать ваш объект неизменным, не храните его в коллекции хешей.

...