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 ofobj
is a reference type andobj
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.