为什么要在单独的类中实现IEqualityComparer

本文关键字:IEqualityComparer 实现 单独 为什么 | 更新日期: 2023-09-27 18:34:05

当我在msdn上查找通用IEqualityComparer接口时,我注意到该接口是在单独的"比较器"类中实现的,而不是在类本身中实现的IEquatable<T>。当我搜索更多示例时,每个示例都使用一个单独的类,这让我想知道:为什么不在类本身上实现它?

我可以想象覆盖object.Equalsobject.GetHashCode不被认为是好的做法,因为它在许多不同的情况下使用,但即使是 msdn 也说(强调我的(:

此接口允许为集合实现自定义相等比较。

所以它的用途几乎仅限于 Linq。我能想到为什么要定义一个单独的比较器类的原因只有 2 个:

  1. 类集合上的不同方法需要不同的比较器。
  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>

为什么不在类本身上实现它呢?

因为这毫无意义。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,但由于此对话线程中已经说明的原因,另一种方法要优雅得多。

相关文章:
  • 没有找到相关文章