简体   繁体   English

为什么要实现 IEqualityComparer<T> 在一个单独的班级

[英]Why implement IEqualityComparer<T> in a separate class

When I was looking up the generic IEqualityComparer interface on msdn I noticed the interface was implemented in a separate 'comparer' class, opposed to IEquatable<T> which is implemented in the class itself.当我在msdn上查找通用IEqualityComparer接口时,我注意到该接口是在单独的“比较器”类中实现的,而不是在类本身中实现的IEquatable<T> When I searched for some more examples, every single one was using a separate class and that got me wondering: why not implement it on the class itself?当我搜索更多示例时,每个示例都在使用一个单独的类,这让我想知道:为什么不在类本身上实现它?

I can imagine overriding object.Equals and object.GetHashCode isn't considered good practice because it's used in a lot of different situations, but even msdn says (emphasis mine):我能想象重写object.Equalsobject.GetHashCode ,因为它在很多不同的情况下使用不被认为是很好的做法,但即使MSDN说(重点煤矿):

This interface allows the implementation of customized equality comparison for collections .此接口允许为集合实现自定义的相等性比较。

so its uses are pretty much limited to Linq.所以它的用途几乎仅限于 Linq。 There's only 2 reasons I can think of why to define a separate comparer class:我能想到为什么要定义一个单独的比较器类的原因只有两个:

  1. Different methods on a collection of the class require a different comparer.类集合上的不同方法需要不同的比较器。
  2. The class is big and instantiating another object of it isn't desired (although if that's really the issue, why isn't having a whole collection of it not bad?).这个类很大,不希望实例化它的另一个对象(尽管如果这真的是问题,为什么不拥有一个完整的集合还不错?)。

So my question is:所以我的问题是:

Is there any particular reason that I overlooked which causes everyone to define another comparerclass just for comparing instead of just implementing the interface on the class itself (which would not be worse in my opinion to say the least)?是否有任何我忽略的特殊原因导致每个人定义另一个比较器类只是为了比较而不是仅仅在类本身上实现接口(至少在我看来这不会更糟)?

A small example:一个小例子:

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(); }
}

why not implement it on the class itself?为什么不在类本身上实现它?

Because it makes no sense.因为没有意义。 The whole purpose of the IEqualityComparer<T> is to be implemented outside the type T because it targets the "reason 1" from your post. IEqualityComparer<T>的全部目的是在类型T之外实现,因为它针对您帖子中的“原因 1”

If you want the class itself to implement the equality logic, then you are expected to implement IEquatable<T> which is provided specifically for such scenario, and EqualityComparer<T>.Default will provide the necessary bridge to your implementation anytime IEqualityComparer<T> is needed and not specified explicitly.如果您希望类本身实现相等逻辑,那么您应该实现专门为此类场景提供的IEquatable<T> ,并且EqualityComparer<T>.Default将随时为您的实现提供必要的桥梁IEqualityComparer<T>是需要的,没有明确指定。

Since the class can provide only one hardcoded logic without any dynamic behavior and/or options, it's considered to be the default equality logic, hence the name of the static EqualityProvider<T> property providing access to it.由于该类只能提供一个没有任何动态行为和/或选项的硬编码逻辑,因此它被认为是默认的相等逻辑,因此静态EqualityProvider<T>属性的名称提供了对它的访问。

IComparer<T> as well as IEqualityComparer<T> work with two instances of T so they have no need to be implemented as a part of T class; IComparer<T>IEqualityComparer<T>IEqualityComparer<T>两个实例一起工作,因此它们不需要作为T类的一部分来实现; however, implementing IEqualityComparer<T> within the T is a good practice, the scheme can be然而,在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; }
    ...
  }

In this case you just use the comparer you want:在这种情况下,您只需使用所需的比较器:

int distinctCountComparerClass = testList.Distinct(Test.MyTestComparer).Count();

Simply put, this way you can use different ways of comparing objects from the same class depending on the context.简而言之,通过这种方式,您可以根据上下文使用不同的方法来比较来自同一类的对象。

It's basically inversion of control: it is not for the class itself to decide how another class might want to compare its instances.它基本上是控制反转:不是由类本身决定另一个类可能想要如何比较它的实例。

It is a good practice to implement IEqualityComparer<T> rather than IEquatable<T> , because when a class implements the IEquatable<T> interface, it enters a contract in which it states "I know how to compare two instances of type T or any type derived from T for equality".实现IEqualityComparer<T>而不是IEquatable<T>是一个很好的做法,因为当一个类实现IEquatable<T>接口时,它会进入一个契约,其中声明“我知道如何比较两个T类型的实例或从T派生的任何类型的相等性”。 However if that class is derived, it is very unlikely that the base class will know how to make a meaningful comparison.但是,如果该类是派生的,则基类不太可能知道如何进行有意义的比较。 Therefore, that implicit contract is now broken.因此,该隐性合同现在已被打破。

Alternatively, the class whose instances we want to be comparable , could be made sealed , but for the reasons already stated in this conversation thread, the other approach is much more elegant.或者,我们希望将其实例进行comparable的类可以被sealed ,但由于本对话线程中已经说明的原因,另一种方法要优雅得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM