为什么要在单独的类中实现IEqualityComparer
本文关键字:IEqualityComparer 实现 单独 为什么 | 更新日期: 2023-09-27 18:34:05
当我在msdn上查找通用IEqualityComparer
接口时,我注意到该接口是在单独的"比较器"类中实现的,而不是在类本身中实现的IEquatable<T>
。当我搜索更多示例时,每个示例都使用一个单独的类,这让我想知道:为什么不在类本身上实现它?
我可以想象覆盖object.Equals
和object.GetHashCode
不被认为是好的做法,因为它在许多不同的情况下使用,但即使是 msdn 也说(强调我的(:
此接口允许为集合实现自定义相等比较。
所以它的用途几乎仅限于 Linq。我能想到为什么要定义一个单独的比较器类的原因只有 2 个:
- 类集合上的不同方法需要不同的比较器。
- 该类很大,并且不需要实例化它的另一个对象(尽管如果这真的是问题所在,为什么没有它的整个集合还不错?
所以我的问题是:
我忽略了什么特殊的原因,导致每个人都定义另一个比较类只是为了比较,而不仅仅是在类本身上实现接口(在我看来至少可以说不会更糟(?
一个小例子:
public static void Main(string[] args)
{
Test t1 = new Test { id = 1, date = default(DateTime) };
Test t2 = new Test { id = 1, date = default(DateTime) };
Test t3 = new Test { id = 0, date = default(DateTime) };
List<Test> testList = new List<Test>{ t1, t2, t3 };
//Same result
int distinctCountClass = testList.Distinct(new Test()).Count();
int distinctCountComparerClass = testList.Distinct(new TestComparer()).Count();
}
public partial class Test
{
public int id { get; set; }
public DateTime date { get; set; }
}
public partial class Test : IEqualityComparer<Test>
{
public bool Equals(Test x, Test y) { return x.id == y.id && x.date == y.date; }
public int GetHashCode(Test obj) { return obj.id.GetHashCode(); }
}
public class TestComparer : IEqualityComparer<Test>
{
public bool Equals(Test x, Test y) { return x.id == y.id && x.date == y.date; }
public int GetHashCode(Test obj) { return obj.id.GetHashCode(); }
}
为什么不在类本身上实现它呢?
因为这毫无意义。IEqualityComparer<T>
的全部目的是在类型T
之外实现,因为它针对您帖子中的"原因 1"。
如果您希望类本身实现相等逻辑,则需要实现专门为此类场景提供的IEquatable<T>
,并且EqualityComparer<T>.Default
将在需要且未显式指定IEqualityComparer<T>
时为您的实现提供必要的桥梁。
由于该类只能提供一个没有任何动态行为和/或选项的硬编码逻辑,因此它被认为是默认的相等逻辑,因此是提供对它的访问的静态EqualityProvider<T>
属性的名称。
IComparer<T>
以及IEqualityComparer<T>
使用两个 T 实例,因此它们不需要作为类的一部分实现T
;但是,在T
内实现IEqualityComparer<T>
是一种很好的做法,该方案可以是
public partial class Test {
private class TestComparer : IEqualityComparer<Test> {
public bool Equals(Test x, Test y) {
return x.id == y.id && x.date == y.date;
}
public int GetHashCode(Test obj) {
return obj.id.GetHashCode();
}
}
// Please, note "static"
public static IEqualityComparer<Test> MyTestComparer {get;} = new TestComparer();
public int id { get; set; }
public DateTime date { get; set; }
...
}
在这种情况下,您只需使用所需的比较器:
int distinctCountComparerClass = testList.Distinct(Test.MyTestComparer).Count();
简而言之,这样您就可以根据上下文使用不同的方法来比较来自同一类的对象。
它基本上是控制反转:它不是由类本身决定另一个类可能想要如何比较其实例。
实现IEqualityComparer<T>
而不是IEquatable<T>
是一个很好的做法,因为当一个类实现IEquatable<T>
接口时,它会进入一个合约,其中它声明"我知道如何比较两个类型 T
或从 T
派生的任何类型的实例以获得相等"。但是,如果派生了该类,则基类不太可能知道如何进行有意义的比较。因此,该隐式契约现在被打破了。
或者,我们想要comparable
实例的类 ,可以sealed
,但由于此对话线程中已经说明的原因,另一种方法要优雅得多。