I have a class Foo with two fields where the Equals and GetHashCode methods have been overridden:
public class Foo
{
private readonly int _x;
private readonly int _y;
public Foo(int x, int y) { _x = x; _y = y; }
public override bool Equals(object obj) {
Foo other = obj as Foo;
return other != null && _y == other._y;
}
public override int GetHashCode() { return _y; }
}
If I create an array of Foo:s and count the number of Distinct values of this array:
var array = new[] { new Foo(1, 1), new Foo(1, 2), new Foo(2, 2), new Foo(3, 2) };
Console.WriteLine(array.Distinct().Count());
The number of distinct values is recognized as:
2
If I now make my class Foo implement IEquatable<Foo> using the following implementation:
public bool Equals(Foo other) { return _y == other._y; }
The number of distinct values is still:
2
But if I change the implementation to this:
public bool Equals(Foo other) { return _x == other._x; }
The computed number of distinct Foo:s is neither 3 (i.e. the number of distinct _x) nor 2 (number of distinct _y), but:
4
And if I comment out the Equals and GetHashCode overrides but keep the IEquatable<Foo> implementation, the answer is also 4.
According to MSDN documentation, this Distinct overload should use the static property EqualityComparer.Default to define the equality comparison, and:
The Default property checks whether type T implements the System.IEquatable<T>
interface and, if so, returns an EqualityComparer<T> that uses that
implementation. Otherwise, it returns an EqualityComparer<T> that uses the
overrides of Object.Equals and Object.GetHashCode provided by T.
But looking at the experiment above, this statement does not seem to hold. At best, the IEquatable<Foo> implementation supports the already provided Equals and GetHashCode overrides, and at worst it completely corrupts the equality comparison.
My questions:
- Why does the independent implementation of
IEquatable<T>corrupt the equality comparison? - Can it play a role independent of the
EqualsandGetHashCodeoverrides? - If not, why does
EqualityComparer<T>.Defaultlook for this implementation first?