Question

In some C# code, I use linq GroupBy<TSource, TKey>() method with a custom IEqualityComparer<T>.

GroupBy(x => x.SomeField, new FooComparer());

The field i use as a grouping key can be null. As a consequence, i had to add some null checks in Equals() method :

public bool Equals(Foo x, Foo y)
{
    if (x == null && y == null)
       return true;
    else if (x == null && y != null)
       return false;
    else if (x != null && y == null)
       return false;
    else 
       return x.Id == y.Id;
}

The question is : should I do the same in GetHashCode() function ?

public int GetHashCode(Foo obj)
{
    if (obj == null)          //is this really needed ?
       return default(int);   //
    else
       return obj.Id;
}

Something i do not understand : even with null keys provided in GroupBy() method, GetHashCode() is never called with a null object in obj parameter. Can somebody explain me why ? (is it just "pure chance" because the way GroupBy() is implemented and order of the elements i give to it ?)


EDIT :

as caerolus pointed it out, there is some special checks made in GroupBy() implementation.

I checked in ILSpy and GroupBy() is implemented with a Lookup<TKey, TElement>

Here is the revelant function :

internal int InternalGetHashCode(TKey key)
{
    if (key != null)
    {
        return this.comparer.GetHashCode(key) & 2147483647;
    }
    return 0;
}
Was it helpful?

Solution

According to the documentation of IEqualityComparer<T>.GetHashCode:

ArgumentNullException
The type of obj is a reference type and obj is null.

So this is part of the contract of that interface, and as such you should care. Implement it by throwing ArgumentNullException if obj is null.

You should always adhere to an interface, even if you suspect or can prove that the code will never touch the parts you don't care about. Changes later might introduce code that relies on that behaviour.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top