0

I have a Node class :

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

And I have EqualityComparer for this Node class like this :

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

I create my HashSet by passing the NodeEqualityComparer.

I have 4 Node instances :

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

When I add n1,n2,n3,n4 to my hashset , n4 get ignored.

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

BUT after I use this changing :

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

there will be 2 elements that are equal together (value=1) based on NodeEqualityComparer. those are n1 and n3.

Debbuger

WHY the hashset does not prevent updating node or remove it ?

Parsa
  • 7,995
  • 2
  • 27
  • 37
  • 2
    Can you please give full code? – Aldert Dec 07 '18 at 17:50
  • 3
    Possible duplicate of [Is Dictionary broken or should GetHashCode() only base on immutable members?](https://stackoverflow.com/questions/4868932/is-dictionary-broken-or-should-gethashcode-only-base-on-immutable-members) – Etienne de Martel Dec 07 '18 at 17:52
  • @Aldert I added the full code – Parsa Dec 07 '18 at 18:02
  • 1
    Consider using `T` instead of `object` to avoid boxing and provide type safety. – Yeldar Kurmangaliyev Dec 07 '18 at 18:12
  • 1
    How are you expecting `HashSet` to prevent a consumer from mutating an element in the set? Can you write your own collection that doesn't allow items in it to be mutated? – Servy Dec 07 '18 at 18:14
  • 1
    How would a HashSet ever know that some of your code alters a field/property of an object that is stored in the HashSet? The HashSet is just a collection, it is not some kind of "overlord" that watches over what your code is doing anywhere anytime... –  Dec 07 '18 at 18:18
  • @elgonzo so what should I do ? say the developer "please do not mutate the hashset otherwise the program breaks" ? – Parsa Dec 07 '18 at 18:29
  • 1
    Depends on your use case. You need to work out the following first by yourself before asking anybody else what you should do: Do you want to allow to mutate the objects (nodes) in the collection? If not, make the node type immutable. If yes, what _meaningful thing precisely_ should happen if a node changes in a way that it conflicts/collide with another node in the collection? –  Dec 07 '18 at 18:35
  • 1
    @Parsa It's important that you not change the object *in a way that changes its identity* while it's in the hashset. It's perfectly fine to change it in ways that don't affect how it defines its own equality. – Servy Dec 07 '18 at 18:54
  • 1
    @Parsa, you might be looking for a SortedSet, SortedList or even an ImmutableSortedSet. – Aldert Dec 08 '18 at 05:58

1 Answers1

0

This is by design: hashing collections (whether it's dictionaries or hash sets or any other) assume that an object's hash code does not change after it's been inserted in the collection. And since two objects that are considered equal must also have the same hash code, then it also means that whatever its Equals implementation returns must not change for the same parameter.

This applies to what is hashed: in dictionaries, that's the key. In sets, that's the entire object.

The .NET documentation states:

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.

In your Node class, you use a mutable property (Value) to compute the hash code. That's usually a bad idea, and it's actually something that ReSharper will warn against. Then again, overriding Equals and GetHashCode normally means that you're treating the type as a "value" rather than an "entity", and values should be treated as immutable when possible.

If you can't make your object immutable, don't store it in a hash collection.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111