Search code examples
c#overridinghashcode

Why is it important to override GetHashCode when Equals method is overridden?


Given the following class

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null) 
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

I have overridden the Equals method because Foo represent a row for the Foos table. Which is the preferred method for overriding the GetHashCode?

Why is it important to override GetHashCode?


Solution

  • Yes, it is important if your item will be used as a key in a dictionary, or HashSet<T>, etc - since this is used (in the absence of a custom IEqualityComparer<T>) to group items into buckets. If the hash-code for two items does not match, they may never be considered equal (Equals will simply never be called).

    The GetHashCode() method should reflect the Equals logic; the rules are:

    • if two things are equal (Equals(...) == true) then they must return the same value for GetHashCode()
    • if the GetHashCode() is equal, it is not necessary for them to be the same; this is a collision, and Equals will be called to see if it is a real equality or not.

    In this case, it looks like "return FooId;" is a suitable GetHashCode() implementation. If you are testing multiple properties, it is common to combine them using code like below, to reduce diagonal collisions (i.e. so that new Foo(3,5) has a different hash-code to new Foo(5,3)):

    In modern frameworks, the HashCode type has methods to help you create a hashcode from multiple values; on older frameworks, you'd need to go without, so something like:

    unchecked // only needed if you're compiling with arithmetic checks enabled
    { // (the default compiler behaviour is *disabled*, so most folks won't need this)
        int hash = 13;
        hash = (hash * 7) + field1.GetHashCode();
        hash = (hash * 7) + field2.GetHashCode();
        ...
        return hash;
    }
    

    Oh - for convenience, you might also consider providing == and != operators when overriding Equals and GetHashCode.


    A demonstration of what happens when you get this wrong is here.