我有一个班级Foo
,其中有两个字段,其中Equals
和GetHashCode
方法已被覆盖:
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; }
}
如果我创建一个Foo
:s数组并计算此数组的Distinct
值的数量:
var array = new[] { new Foo(1, 1), new Foo(1, 2), new Foo(2, 2), new Foo(3, 2) };
Console.WriteLine(array.Distinct().Count());
不同值的数量被识别为:
2
如果我现在使用以下实现使我的课程Foo
实现IEquatable<Foo>
:
public bool Equals(Foo other) { return _y == other._y; }
不同值的数量仍为:
2
但是如果我将实现更改为:
public bool Equals(Foo other) { return _x == other._x; }
计算出的不同Foo
:s的数量既不是3(即不同_x
的数量)也不是2(不同_y
的数量),但是:
4
如果我注释掉Equals
和GetHashCode
覆盖但保留IEquatable<Foo>
实施,那么答案也是4
。
根据MSDN documentation,此Distinct
重载应使用静态属性EqualityComparer.Default来定义相等性比较,并且:
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.
但是看看上面的实验,这个说法似乎并不成立。最好的情况是IEquatable<Foo>
实现支持已提供的Equals
和GetHashCode
覆盖,最糟糕的是它会完全破坏相等比较。
我的问题:
IEquatable<T>
的独立实施会破坏平等比较? Equals
和GetHashCode
覆盖而发挥作用? EqualityComparer<T>.Default
首先要查找此实现?答案 0 :(得分:4)
GetHashCode
方法仅取决于y
。这意味着如果您的Equals
方法不依赖于y
,那么您就违反了平等合同......它们是不一致的。
Distinct()
将期望相等的元素具有相同的哈希码。在您的情况下,x
值的唯一相等元素具有不同的哈希码,因此Equals
甚至不会被调用。
来自IEquatable<T>.Equals
的文档:
如果实现
Equals
,则还应覆盖Object.Equals(Object)
和GetHashCode
的基类实现,以使其行为与IEquatable<T>.Equals
方法的行为一致。 / p>
Equals(Foo)
的实施与 Equals(object)
或 GetHashCode
不一致。
EqualityComparer<T>.Default
仍然会委托您使用GetHashCode
方法 - 它会优先使用您的Equals(T)
方法,而不是Equals(object)
方法。
所以按顺序回答你的问题:
- 为什么独立实施
IEquatable<T>
会破坏平等比较?
因为您引入了一个不一致的实现。 意味着意味着在行为方面是独立的。这只是为了通过避免类型检查(和拳击,值类型)来提高效率。
- 它是否可以独立于
Equals
和GetHashCode
覆盖而发挥作用?
为了理智,它应该与Equals(object)
一致,为了正确起见,必须与GetHashCode
保持一致。
如果没有,为什么
EqualityComparer<T>.Default
首先要查找此实现?
主要是为了避免运行时类型检查和装箱/拆箱。