EqualityComparer<;T>;.默认不';t返回派生的EqualityComparer
本文关键字:EqualityComparer 返回 派生 默认 lt gt | 更新日期: 2023-09-27 18:24:40
我有一个类Person,并创建了一个从EqualityComparer<个人>。然而,默认的EqualityComparer不会调用我的Equals比较器的Equales函数
根据MSDN EqualityComparer<T>.默认属性:
Default属性检查类型T是否实现了System.IEquatable接口,如果实现了,则返回使用该实现的EqualityComparer。否则,它返回一个EqualityComparer,它使用T.提供的Object.Equals和Object.GetHashCode的重写
在下面的(简化)示例中,类Person没有实现System.IEquatable<个人>。所以我希望PersonComparer.Default会返回PersonComparer的一个实例。
然而,没有调用PersonComparer.Equals。没有调试输出,返回的值为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存储在某个东西中,它可能是数据库,但也可能是File或MemoryStream。返回后,我得到一个标识符,在数据库的情况下,它当然是主键。有了这个键,我就可以检索到具有相同值的对象。
我想在单元测试中测试这一点:如果你在其中放了一些东西,你应该得到一个可以用来检索项目的密钥。遗憾的是,数据库并没有返回相同的Person,而是一个派生类Person(至少在使用EF6时是这样)。所以我不能使用普通的IEquatable,如果对象不是同一类型,它应该返回false。这就是为什么我想使用一个特殊的比较器,它声明两个Person相等,如果它们的属性值相同,即使它们都是来自Person的不同派生类。作为一个字符串比较器,它接受O和O,并且ó等于
让我们重新阅读您添加的报价:
Default属性检查类型T是否实现
System.IEquatable
接口,如果实现,则返回EqualityComparer使用该实现
因此,Default
属性查找IEqutable<T>
的实现,而Person
没有提供该实现。
如果对象没有实现IEquatable<T>
,则:
否则,它返回一个EqualityComparer,使用由T.提供的Object.Equals和Object.GetHashCode的重写
这向您展示了object.Equals
和object.GetHashCode
被调用的确切原因。您有两种选择,要么使用new PersonComparer().Equals()
,要么在您的类型上实现IEquatable<Person>
(如果可以实现这样的单个实现)。
您混淆了一些事情,但这并不奇怪,因为每个人都必须承认,.NET Framework有太多的平等比较可能性。
只有当您想为特定情况指定特殊的比较逻辑时,才应该实现IEqualityComparer<T>
,例如对Dictionary<TKey, TValue>
。
EqualityComparer<T>
在.NET中是一个令人困惑的可重写类型;但是,并不打算重写它,这样做也没有用。它为泛型类型提供了一个默认比较器,如果您在List<T>
(Contains
、IndexOf
等)中使用T
,或者当T
是字典的键并且没有向字典传递任何自定义IEqualityComparer
时,它将调用您的IEquatable<T>
实现。
这是因为EqualityComparer<T>
的Default
属性不返回PersonComparer
,而是返回ObjectEqualityComparer<T>
。正如您参考文档所述,ObjectEqualityComparer<T>
使用Person
上的Equals
进行比较。
请参阅实际来源。在第89行,它返回CCD_。
实际上,您的代码没有任何问题,正如您在PersonComparer
:实例上实际运行代码时所看到的那样
bool b1 = new PersonComparer().Equals(x, y);