And*_*son 2 c# linq iequatable
我有一个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; }
}
Run Code Online (Sandbox Code Playgroud)
如果我创建一个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());
Run Code Online (Sandbox Code Playgroud)
不同值的数量被识别为:
2
Run Code Online (Sandbox Code Playgroud)
如果我现在让我的类Foo实现IEquatable<Foo>使用下面的实现:
public bool Equals(Foo other) { return _y == other._y; }
Run Code Online (Sandbox Code Playgroud)
不同值的数量仍然是:
2
Run Code Online (Sandbox Code Playgroud)
但是,如果我将实现更改为:
public bool Equals(Foo other) { return _x == other._x; }
Run Code Online (Sandbox Code Playgroud)
计算出的 distinct Foo:s 数既不是 3(即 distinct 的数量_x)也不是 2(distinct 的数量_y),而是:
4
Run Code Online (Sandbox Code Playgroud)
如果我注释掉Equals和GetHashCode覆盖但保留IEquatable<Foo>实现,答案也是4.
根据MSDN 文档,此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.
Run Code Online (Sandbox Code Playgroud)
但是看上面的实验,这个说法似乎不成立。最好的情况是,IEquatable<Foo>实现支持已经提供的Equals和GetHashCode覆盖,最坏的情况是它完全破坏了相等比较。
我的问题:
IEquatable<T>破坏相等比较?Equals和GetHashCode覆盖的作用吗?EqualityComparer<T>.Default要先寻找这个实现?您的GetHashCode方法仅取决于y. 这意味着如果您的Equals方法不依赖于y,那么您就违反了平等契约……它们是不一致的。
Distinct()将期望相等的元素具有相同的哈希码。在您的情况下,按x值唯一相等的元素具有不同的哈希码,因此Equals甚至不会被调用。
来自以下文档IEquatable<T>.Equals:
如果实现了
Equals,你也应该重写的基类的实现Object.Equals(Object),并GetHashCode让他们的行为与该一致的IEquatable<T>.Equals方法。
你的实现Equals(Foo)是不相符或者 Equals(object) 或 GetHashCode。
EqualityComparer<T>.Default仍将委托给您的GetHashCode方法 - 它只会Equals(T)优先使用您的方法而不是您的Equals(object)方法。
因此,按顺序回答您的问题:
- 为什么独立执行会
IEquatable<T>破坏相等比较?
因为您引入了不一致的实现。它并不意味着在行为方面是独立的。它只是为了通过避免类型检查(和装箱,对于值类型)来提高效率。
- 它可以发挥独立于
Equals和GetHashCode覆盖的作用吗?
它应该是一致Equals(object)的理智的缘故,而且它必须是一致GetHashCode的正确性的缘故。
如果没有,为什么
EqualityComparer<T>.Default要先寻找这个实现?
主要是为了避免运行时类型检查和装箱/拆箱。