IEqualityComparer <T>和IEquatable <T>之间有什么区别?

Til*_*lak 139 .net c# equals iequatable iequalitycomparer

我想了解应该使用IEqualityComparer<T>IEquatable<T>应该使用的场景.两者的MSDN文档看起来非常相似.

Jus*_*ner 105

IEqualityComparer<T>是一个对象的接口,它对该类型的两个对象执行比较T.

IEquatable<T>用于类型的对象,T以便它可以将自己与另一个对象进行比较.

  • +1 _IEquatable用于一个对象,以便它可以将自己与同一类型的另一个**进行比较** (42认同)
  • 同样的区别是 IComparable / IComparer (2认同)
  • 上尉队长 (2认同)
  • (已编辑)“IEqualityComparer&lt;T&gt;”是对象**(通常是与“T”不同的轻量级类)**的接口,提供对“T”进行操作的比较函数 (2认同)

sta*_*ica 60

在决定是否使用IEquatable<T>或时IEqualityComparer<T>,可以问:

是否有一种测试两个T相等实例的首选方法,还是有几种同样有效的方法?

  • 如果只有一种方法可以测试两个T相等的实例,或者如果首选几种方法中的一种,那么IEquatable<T>这将是正确的选择:这个接口应该只由T它自己实现,所以一个实例T具有内部知识如何将自己与另一个实例进行比较T.

  • 另一方面,如果有几种同样合理的方法来比较两个Ts的相等性,IEqualityComparer<T>那么看起来更合适:这个接口不是由T它自己实现的,而是由其他"外部"类实现的.因此,在测试两个T相等的实例时,由于T没有内部对相等性的理解,因此您必须明确选择IEqualityComparer<T>根据您的特定要求执行测试的实例.

例:

让我们考虑这两种类型(它们应该具有值语义):

interface IIntPoint : IEquatable<IIntPoint>
{
    int X { get; }
    int Y { get; }
}

interface IDoublePoint  // does not inherit IEquatable<IDoublePoint>; see below.
{
    double X { get; }
    double Y { get; }
}
Run Code Online (Sandbox Code Playgroud)

为什么只有这些类型中的一个继承IEquatable<>而不是另一个?

从理论上讲,只有一种合理的方法可以比较两种类型的两个实例:如果两个实例中的XY属性相等,它们是相等的.根据这种想法,两种类型都应该实现IEquatable<>,因为似乎没有其他有意义的方法来进行相等性测试.

这里的问题是,由于分钟舍入错误,比较浮点数的相等性可能无法按预期工作.有不同的方法来比较浮点数的近似相等,每个方法都有特定的优点和权衡,你可能希望能够选择适合自己的方法.

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
    public DoublePointNearEqualityComparerByTolerance(double tolerance) { … }
    …
    public bool Equals(IDoublePoint a, IDoublePoint b)
    {
        return Math.Abs(a.X - b.X) <= tolerance  &&  Math.Abs(a.Y - b.Y) <= tolerance;
    }
    …
}
Run Code Online (Sandbox Code Playgroud)

请注意,我链接到(上面)的页面明确指出这个近似相等的测试有一些缺点.由于这是一个IEqualityComparer<T>实现,如果它不适合您的目的,您可以简单地将其交换出来.

  • 用于测试双点相等性的建议比较方法被打破,因为任何合法的`IEqualityComparer <T>`实现*必须*实现等价关系,这意味着在*all*两个对象比较等于三分之一的情况下,它们*必须*相互比较相等.任何以与上述相反的方式实现`IEquatable <T>`或`IEqualityComparer <T>`的类都被破坏了. (6认同)
  • 一个更好的例子是字符串之间的比较.有一个字符串比较方法,只有当字符串包含相同的字节序列时,它才有意义,但是还有其他有用的比较方法*也构成了等价关系*,例如不区分大小写的比较.注意,认为"hello"等于"Hello"和"hElLo"的`IEqualityComparer <String>`必须考虑"Hello"和"hElLo"彼此相等,但是对于大多数比较方法来说都不是问题. (5认同)

naw*_*fal 26

您已经掌握了它们的基本定义.简而言之,如果IEquatable<T>在类上实现T,Equals对类型对象的方法会T告诉您对象本身(被测试的相等性)是否等于同一类型的另一个实例T.然而,IEqualityComparer<T>用于测试任何两个实例的相等性T,通常在实例的范围之外T.

至于它们的用途可能会让人感到困惑.从定义中可以清楚地看出,因此IEquatable<T>(在类T本身中定义)应该是表示其对象/实例的唯一性的事实标准.HashSet<T>,Dictionary<T, U>(考虑到GetHashCode被重写为好),ContainsList<T>等利用这一点.实施IEqualityComparer<T>T上述一般情况没有帮助.随后,除了IEquatable<T>以外的任何其他类的实现几乎没有价值T.这个:

class MyClass : IEquatable<T>
Run Code Online (Sandbox Code Playgroud)

很少有道理.

另一方面

class T : IEquatable<T>
{
    //override ==, !=, GetHashCode and non generic Equals as well

    public bool Equals(T other)
    {
        //....
    }
}
Run Code Online (Sandbox Code Playgroud)

应该怎么做.

IEqualityComparer<T>当您需要自定义的相等验证时,它可能很有用,但不是一般规则.例如,在Person某个阶段,您可能需要根据年龄来测试两个人的平等.在这种情况下,你可以这样做:

class Person
{
    public int Age;
}

class AgeEqualityTester : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Age.GetHashCode;
    }
}
Run Code Online (Sandbox Code Playgroud)

要测试它们,请尝试

var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };

print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true
Run Code Online (Sandbox Code Playgroud)

同样IEqualityComparer<T>T没有任何意义.

class Person : IEqualityComparer<Person>
Run Code Online (Sandbox Code Playgroud)

这是真的有效,但对眼睛看起来并不好看并且打败了逻辑.

通常你需要的是IEquatable<T>.理想情况下,您可以只有一个,IEquatable<T>同时IEqualityComparer<T>根据不同的标准可以多个.

IEqualityComparer<T>IEquatable<T>是完全类似Comparer<T>IComparable<T>其用于比较目的,而不是等同; 这里有一个好的帖子,我写了相同的答案:)


Chr*_*ain 11

IEqualityComparer用于在外部实现两个对象的相等性时使用,例如,如果您想为两个类型定义比较器,而这两个类型没有源,或者两个事物之间的相等只在某些有限的上下文中有意义的情况.

IEquatable用于实现对象本身(要进行相等的比较).


Mat*_*all 5

一个比较两个Ts。另一个可以将自己与其他Ts 进行比较。通常,您一次只需使用一个,而不是同时使用两者。