【问题标题】:LINQ Distinct with EqualityComparer<T>.Default: IEquatable<T> implementation ignored?LINQ Distinct with EqualityComparer<T>.Default: IEquatable<T> 实现被忽略?
【发布时间】:2013-05-26 19:36:42
【问题描述】:

我有一个类 Foo 有两个字段,其中 EqualsGetHashCode 方法已被覆盖:

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&lt;Foo&gt;

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

如果我注释掉 EqualsGetHashCode 覆盖但保留 IEquatable&lt;Foo&gt; 实现,答案也是 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&lt;Foo&gt; 实现支持已经提供的EqualsGetHashCode 覆盖,最坏的情况是它完全破坏了相等比较。

我的问题:

  • 为什么IEquatable&lt;T&gt;的独立实现会破坏相等比较?
  • 它能否独立于EqualsGetHashCode 覆盖发挥作用?
  • 如果不是,为什么EqualityComparer&lt;T&gt;.Default 会先寻找这个实现?

【问题讨论】:

    标签: c# linq iequatable


    【解决方案1】:

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

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

    来自IEquatable&lt;T&gt;.Equals的文档:

    如果你实现了Equals,你还应该重写Object.Equals(Object)GetHashCode的基类实现,使它们的行为与IEquatable&lt;T&gt;.Equals方法的行为一致。

    您对Equals(Foo) 的实现与任一 Equals(object) GetHashCode 不一致。

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

    所以按顺序回答你的问题:

    • 为什么IEquatable&lt;T&gt;的独立实现会破坏相等比较?

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

    • 它能否发挥独立于EqualsGetHashCode 覆盖的作用?

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

    如果不是,为什么EqualityComparer&lt;T&gt;.Default 会先寻找这个实现?

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

    【讨论】:

    • 非常感谢,乔恩,非常有用的回答。我意识到我应该更彻底地阅读IEquatable&lt;T&gt;.Equals 文档。关于类型检查的要点,很明显现在这是主要好处:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 2011-02-13
    相关资源
    最近更新 更多