EqualityComparer<T>。默认不 return 派生的 EqualityComparer

EqualityComparer<T>.Default doesn't return the derived EqualityComparer

我有一个 class 人,并创建了一个从 EqualityComparer 派生的平等计算器 class。然而默认的 EqualityComparer 不调用我的相等比较器的 Equals 函数

根据 MSDN EqualityComparer < T > .Default property

The Default property checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T.

在下面的(简化)示例中,class Person 未实现 implement System.IEquatable 。所以我希望 PersonComparer.Default 会 return PersonComparer 的一个实例。

但是 PersonComparer.Equals 没有被调用。没有调试输出,returned 值为 false。

public class Person
{
    public string Name { get; set; }
}

public class PersonComparer : EqualityComparer<Person>
{
    public override bool Equals(Person x, Person y)
    {
        Debug.WriteLine("PersonComparer.Equals called");
        return true;
    }

    public override int GetHashCode(Person obj)
    {
        Debug.WriteLine("PersonComparer.GetHasCode called");
        return obj.Name.GetHashCode();
    }
}

public static void Main()
{
    Person x = new Person() { Name = "x" };
    Person y = new Person() { Name = "x" };
    bool b1 = PersonComparer.Default.Equals(x, y);
}

问题:我做错了什么?

如果您可能想知道为什么我不想实现 IEquatable

我的问题类似于字符串的比较。有时您希望两个字符串相等,如果它们是完全相同的字符串,有时您希望忽略大小写,有时您希望将字符视为 óò 等,就好像它们是字符 o.

在我的例子中:我将一个 Person 存储在某个东西中,它可能是一个数据库,但它也可能是一个文件或一个 MemoryStream。 return 我得到一个标识符,在数据库的情况下,它当然是主键。使用此键,我可以检索具有相同值的对象。

我想在单元测试中对此进行测试:我将一些东西放入其中,你应该得到一个可用于检索该项目的密钥。 las,数据库不是 return 同一个人,而是派生的 class 个人(至少在使用 EF 6 时)。所以我不能使用普通的 IEquatable,如果对象不是同一类型,它应该 return false。这就是为什么我想使用一个特殊的比较器,如果两个 Person 具有相同的属性值,它会声明两个 Person 相等,即使它们都是从 Person 派生的不同 classes。作为接受 O 和 o 和 ó 相等的字符串比较器非常具有可比性

让我们重新阅读您添加的报价:

The Default property checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation.

因此,Default 属性 寻找 IEqutable<T> 的实现,而您的 Person 没有提供。

如果对象没有实现 IEquatable<T>,那么:

Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T.

这向您展示了为什么调用 object.Equalsobject.GetHashCode。你有两个选择,要么使用 new PersonComparer().Equals(),要么在你的类型上实现 IEquatable<Person>(如果这样的单一实现是可能的)。

那是因为EqualityComparer<T>Default属性不是return一个PersonComparer,而是一个ObjectEqualityComparer<T>ObjectEqualityComparer<T>,正如您引用的文档,使用 Person 上的 Equals 进行比较。

参见 the actual source。在第 89 行,它 return 是 ObjectEqualityComparer<T>.

您的代码实际上没有任何问题,正如您在 PersonComparer:

的实例上实际尝试 运行 您的代码时所看到的
bool b1 = new PersonComparer().Equals(x, y);

你把一些东西弄混了,但这并不奇怪,因为每个人都必须承认.NET Framework 有 用于相等比较。

仅当您想为特定情况指定特殊比较逻辑时才应实施 IEqualityComparer<T>,例如 Dictionary<TKey, TValue>

EqualityComparer<T> 在 .NET 中是一个令人困惑的可重写类型;但是,您无意覆盖它,这样做也没有用。它为泛型类型提供默认比较器,如果您在 List<T>ContainsIndexOf 等)中使用 T,它将调用您的 IEquatable<T> 实现或者当 T 是字典的键并且您没有将任何自定义 IEqualityComparer 传递给字典时。