2

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 Equals and GetHashCode overrides?
  • If not, why does EqualityComparer<T>.Default look for this implementation first?
4

1 回答 1

6

您的GetHashCode方法取决于y. 这意味着如果您的Equals方法依赖于y,那么您就违反了平等契约……它们是不一致的。

Distinct()将期望相等的元素具有相同的哈希码。在您的情况下,唯一按x值相等的元素具有不同的哈希码,因此Equals甚至不会被调用。

来自以下文档IEquatable<T>.Equals

如果您实现Equals,您还应该覆盖 和 的基类实现,Object.Equals(Object)以便GetHashCode它们的行为与IEquatable<T>.Equals方法的行为一致。

您的实现orEquals(Foo)不一致。 Equals(object) GetHashCode

EqualityComparer<T>.Default仍将委托给您的GetHashCode方法 - 它只会使用您的Equals(T)方法而不是您的Equals(object)方法。

因此,按顺序回答您的问题:

  • 为什么独立执行会IEquatable<T>破坏相等比较?

因为您引入了不一致的实现。它并不意味着在行为方面是独立的。它只是为了通过避免类型检查(和值类型的装箱)来提高效率。

  • Equals它可以独立于和GetHashCode覆盖发挥作用吗?

Equals(object)为了理智起见,它应该是一致的GetHashCode,为了正确起见,它必须是一致的。

如果不是,为什么EqualityComparer<T>.Default要先寻找这个实现?

主要是为了避免运行时类型检查和装箱/拆箱。

于 2013-05-26T19:38:27.040 回答