IEqualityComparer < T > 和 IEquable < T > 的区别是什么?

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

33809 次浏览

IEqualityComparer<T>是对两个类型为 T的对象执行比较的对象的接口。

IEquatable<T>用于 T类型的对象,这样它就可以将自己与同一类型的另一个对象进行比较。

一个比较两个 T。另一个可以与其他 T进行比较。通常,您只需要一次使用一个,而不是两个都使用。

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

IEqutable 是为对象本身(为了相等而进行比较的对象)实现的。

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

至于 T1可以混淆在第一。从定义中可以清楚地看出,因此 IEquatable<T>(在类 T中定义)应该是表示其对象/实例唯一性的行业标准。HashSet<T>Dictionary<T, U>(考虑到 GetHashCode也被覆盖了)、 List<T>上的 Contains等都利用了这一点。在 T上实施 IEqualityComparer<T>对上述一般情况没有帮助。随后,在除 T之外的任何其他类上实现 IEquatable<T>几乎没有什么价值。这个:

class MyClass : IEquatable<T>

很少说得通。

另一方面

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


public bool Equals(T other)
{
//....
}
}

就是应该怎么做。

当您需要相等性的自定义验证时,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;
}
}

试试看

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

同样,T上的 IEqualityComparer<T>也没有意义。

class Person : IEqualityComparer<Person>

这确实有效,但是看起来不太好,而且违反逻辑。

通常你需要的是 IEquatable<T>。另外,理想情况下,您可以只有一个 IEquatable<T>,而多个 IEqualityComparer<T>是可能的基于不同的标准。

IEqualityComparer<T>IEquatable<T>Comparer<T>IComparable<T>完全类似,Comparer<T>IComparable<T>用于比较目的,而不是等同; 这是一个很好的线程 给你,我在其中写了相同的答案:)

在决定使用 IEquatable<T>还是 IEqualityComparer<T>时, 人们可能会问:

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

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

  • 另一方面,如果有几个同样合理的方法来比较两个 T是否相等,那么 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; }
}

为什么这些类型中只有一种继承 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;
}
…
}

请注意,我链接到(上面)的页面明确指出,这个接近相等的测试有一些弱点。因为这是一个 IEqualityComparer<T>实现,所以如果它对于您的目的来说不够好,您可以简单地将它替换掉。