Search code examples
c#equalitygethashcode

GetHashCode for a type holding string fields


I have this class, where I have Object Equals overriden:

public class Foo
{
    public string string1 { get; set; }

    public string string2 { get; set; }

    public string string3 { get; set; }

    public override bool Equals(object other)
    {
        if (!(other is Foo)) return false;
        Foo otherFoo = (other as Foo);

        return otherFoo.string1 == string1 && otherFoo.string2 == string2 && otherFoo.string3 == string3;
    }
}

I get a warning "overrides object.equals but not object.gethashcode" of which I understand the need of overriding the GetHashCode, for my type to behave according to a hashable type.

As far as I researched, for this code to be unique usually the XOR operator is used, or prime number multiplications are involved. So according to my sources, source1 and source2 I was considering this two options for my GesHashCode override method.

1:

public override int GetHashCode() {
        return string1.GetHashCode() ^ string2.GetHashCode() ^ string3.GetHashCode();
}

2:

public override int GetHashCode() {
        return (string1 + string2 + string3).GetHashCode();
}

I am not sure either if this approaches ensures the purpose of the GetHashCode override in my case, that is to eliminate the compilation warning, and by the way ensure that the type can be handeled in collections properly, which I believe thas is that if the values they hold are equal to be considered equal, but if equal values on diferent instances take place in a collection, each needs to be found accordingly.

In the case of both of the approaches being valid, I wonder which one might be better and why.


Solution

  • There's a fairly simple but effective way to do this:

    public override int GetHashCode()
    {
        unchecked // Hash code calculation can overflow.
        {
            int hash = 17;
    
            hash = hash * 23 + firstItem.GetHashCode();
            hash = hash * 23 + secondItem.GetHashCode();
    
            // ...and so on for each item.
    
            return hash;
        }
    }
    

    Where firstItem, secondItem and so on are the items which contribute to the hash code. (Larger primes can also be used instead of 17 and 23, but it really doesn't make much difference.)

    However note that if you're using .Net Core 3.1, you can do this instead:

    public override int GetHashCode() => HashCode.Combine(firstItem, secondItem, ...etc);
    

    Incidentally, if anyone wants to look at the implementation of HashCode.Combine(), it's here.

    It's a lot more sophisticated than the code I posted. :)