Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Dictionary broken or should GetHashCode() only base on immutable members?

When an object is added to the .NET System.Collections.Generic.Dictionary class the hashcode of the key is stored internally and used for later comparisons. When the hashcode changes after its initial insertion into the dictionary it often becomes "inaccessible" and may surprise its users when an existence check, even using the same reference, returns false (sample code below).

The GetHashCode documentation says:

The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method.

So, according to the GetHashCode docs, the hashcode may change whenever equality-determining state is changed, yet the Dictionary implementation does not support this.

Is the current .NET dictionary implementation broken in that it incorrectly ignore the hashcode allowances? Should GetHashCode() only be based on immutable members? Or, is there something else to break a possible false dichotomy?

class Hashable
{
    public int PK { get; set; }

    public override int GetHashCode()
    {
        if (PK != 0) return PK.GetHashCode();
        return base.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Hashable);
    }

    public virtual bool Equals(Hashable other)
    {
        if (other == null) return false;
        else if (ReferenceEquals(this, other)) return true;
        else if (PK != 0 && other.PK != 0) return Equals(PK, other.PK);
        return false;
    }

    public override string ToString()
    {
        return string.Format("Hashable {0}", PK);
    }
}

class Test
{
    static void Main(string[] args)
    {
        var dict = new Dictionary<Hashable, bool>();
        var h = new Hashable();
        dict.Add(h, true);

        h.PK = 42;
        if (!dict.ContainsKey(h)) // returns false, despite same reference
            dict.Add(h, false);
    }
}
like image 306
Kaleb Pederson Avatar asked Feb 01 '11 22:02

Kaleb Pederson


People also ask

Should I override GetHashCode?

Why is it important to override GetHashCode ? It s important to implement both equals and gethashcode, due to collisions, in particular while using dictionaries. if two object returns same hashcode, they are inserted in the dictionary with chaining. While accessing the item equals method is used.

Why do we need GetHashCode C#?

GetHashCode returns a value based on the current instance that is suited for hashing algorithms and data structures such as a hash table. Two objects that are the same type and are equal must return the same hash code to ensure that instances of System.

What is GetHashCode in Iequalitycomparer?

Every object in . NET has an Equals method and a GetHashCode method. The Equals method is used to compare one object with another object - to see if the two objects are equivalent. The GetHashCode method generates a 32-bit integer representation of the object.

How does GetHashCode work in C#?

GetHashCode method of the base class, which computes a hash code based on an object's reference; for more information, see RuntimeHelpers. GetHashCode. In other words, two objects for which the ReferenceEquals method returns true have identical hash codes. If value types do not override GetHashCode, the ValueType.


1 Answers

No, you just shouldn't mutate a key (in a material way) after inserting it into a dictionary. This is by design, and the way that every hash table I've ever used works. The docs even specify this:

As long as an object is used as a key in the Dictionary<TKey, TValue>, it must not change in any way that affects its hash value. Every key in a Dictionary<TKey, TValue> must be unique according to the dictionary's equality comparer. A key cannot be null, but a value can be, if the value type TValue is a reference type.

So it's only going to surprise users who don't read documentation :)

like image 85
Jon Skeet Avatar answered Oct 02 '22 16:10

Jon Skeet