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;
}
有帮助吗?

解决方案

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top