【问题标题】:What is the difference between IEqualityComparer<T> and IEquatable<T>?IEqualityComparer<T> 和 IEquatable<T> 有什么区别?
【发布时间】:2012-02-16 18:27:50
【问题描述】:

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

【问题讨论】:

  • MSDN sez:"这个接口允许实现自定义的相等比较集合。"这当然是在所选答案中推断出来的。 MSDN 还建议继承EqualityComparer&lt;T&gt; 而不是实现接口“因为EqualityComparer&lt;T&gt; 使用IEquatable&lt;T&gt; 测试相等性
  • ... 以上建议我应该为任何实现IEquatable&lt;T&gt;T 创建一个自定义集合。否则,像List&lt;T&gt; 这样的集合会不会有某种微妙的错误?
  • @RamilShavaleev 链接已损坏

标签: c# .net equals iequalitycomparer iequatable


【解决方案1】:

IEqualityComparer&lt;T&gt; 是一个对象接口,用于对T 类型的两个对象进行比较。

IEquatable&lt;T&gt; 用于T 类型的对象,以便它可以将自己与另一个相同类型的对象进行比较。

【讨论】:

  • IComparable / IComparer 之间的区别相同
  • (已编辑)IEqualityComparer&lt;T&gt; 是对象的接口(通常是不同于T 的轻量级类),它提供了对T 进行操作的比较函数
【解决方案2】:

在决定是使用IEquatable&lt;T&gt;还是IEqualityComparer&lt;T&gt;时, 有人可能会问:

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

    1234563通过T 本身,因此T 的一个实例具有如何将自己与T 的另一个实例进行比较的内部知识。 1234563但通过其他“外部”类。因此,在测试T 的两个实例是否相等时,由于T 对相等没有内部理解,因此您必须根据您的具体要求明确选择执行测试的IEqualityComparer&lt;T&gt; 实例。

示例:

让我们考虑这两种类型(应该有value semantics):

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&lt;&gt;,而不是另一种?

理论上,比较任一类型的两个实例只有一种明智的方法:如果两个实例中的XY 属性相等,则它们相等。根据这种想法,这两种类型都应该实现IEquatable&lt;&gt;,因为似乎不太可能有其他有意义的方法来进行相等性测试。

这里的问题是comparing floating-point numbers for equality might not work as expected, due to minute rounding errors。比较近似相等的浮点数有不同的方法,每种方法都有特定的优点和权衡,您可能希望能够自己选择哪种方法合适。

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&lt;T&gt; 实现,如果它对您的目的不够好,您可以简单地换掉它。

【讨论】:

  • 测试双点相等性的建议比较方法被破坏,因为任何合法的IEqualityComparer&lt;T&gt;实现必须实现等价关系,这意味着在all 在两个对象比较等于第三个的情况下,它们必须彼此比较相等。任何以与上述相反的方式实现IEquatable&lt;T&gt;IEqualityComparer&lt;T&gt; 的类都会被破坏。
  • 一个更好的例子是字符串之间的比较。只有当字符串包含相同的字节序列时才将字符串视为相等的字符串比较方法是有意义的,但是还有其他有用的比较方法也构成等价关系,例如不区分大小写的比较.请注意,IEqualityComparer&lt;String&gt; 认为 "hello" 等于 "Hello" 和 "hElLo" 必须认为 "Hello" 和 "hElLo" 彼此相等,但对于大多数比较方法来说这不会有问题。跨度>
  • @supercat,感谢您的宝贵反馈。在接下来的几天里,我可能不会抽空修改我的答案,所以如果您愿意,请随时根据您的需要进行编辑。
  • 这正是我需要的,我正在做的。但是需要覆盖 GetHashCode,一旦值不同,它将返回 false。您如何处理您的 GetHashCode?​​span>
  • @teapeng:不确定您在谈论什么具体的用例。一般来说,GetHashCode 应该可以让您快速确定两个值是否不同。规则大致如下: (1) GetHashCode 必须始终为相同的值生成相同的哈希码。 (2) GetHashCode 应该快(比Equals 快)。 (3) GetHashCode 不必精确(不如Equals 精确)。这意味着它可能为不同的值生成相同的哈希码。您可以做得越精确越好,但保持快速可能更重要。
【解决方案3】:

您已经了解了它们是什么的基本定义。简而言之,如果您在类T 上实现IEquatable&lt;T&gt;,则Equals 类型对象上的Equals 方法会告诉您对象本身(正在测试相等性的对象)是否等于相同的另一个实例输入T。而IEqualityComparer&lt;T&gt; 用于测试T 的任何两个实例的相等性,通常在T 的实例范围之外。

至于它们的用途,起初可能会令人困惑。从定义中应该很清楚,因此IEquatable&lt;T&gt;(在类T 本身中定义)应该是表示其对象/实例的唯一性的事实标准。 HashSet&lt;T&gt;Dictionary&lt;T, U&gt;(考虑到GetHashCode 也被覆盖)、Contains 上的List&lt;T&gt; 等都利用了这个。在T 上实现IEqualityComparer&lt;T&gt; 对上述一般情况没有帮助。随后,在T 以外的任何其他类上实现IEquatable&lt;T&gt; 几乎没有价值。这个:

class MyClass : IEquatable<T>

很少有意义。

另一方面

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

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

是应该怎么做的。

IEqualityComparer&lt;T&gt; 在您需要自定义相等性验证时很有用,但不是一般规则。例如,在 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&lt;T&gt; 同样没有意义。

class Person : IEqualityComparer<Person>

的确,这可行,但看起来不太好,并且不符合逻辑。

通常你需要的是IEquatable&lt;T&gt;。理想情况下,您只能拥有一个IEquatable&lt;T&gt;,而多个IEqualityComparer&lt;T&gt; 可以根据不同的标准。

IEqualityComparer&lt;T&gt;IEquatable&lt;T&gt;Comparer&lt;T&gt;IComparable&lt;T&gt; 完全相同,它们用于比较而不是等同;一个很好的线程here 我写了相同的答案:)

【讨论】:

  • public int GetHashCode(Person obj) 应该返回 obj.GetHashCode()
  • @HristoYankov 应该返回 obj.Age.GetHashCode。将编辑。
  • 请解释为什么比较器必须对它比较的对象的哈希值做出这样的假设?你的比较器不知道 Person 是如何计算它的 Hash 的,你为什么要覆盖它?
  • @HristoYankov 您的比较器不知道 Person 是如何计算其哈希值的 - 当然,这就是为什么我们没有在任何地方直接调用 person.GetHashCode.... 为什么你会覆盖它吗? - 我们覆盖它是因为IEqualityComparer 的全部意义在于根据我们的规则(我们真正熟悉的规则)进行不同的比较实现。
  • 在我的示例中,我需要根据Age 属性进行比较,因此我调用Age.GetHashCode。年龄的类型是int,但无论是什么类型的.NET 中的哈希码合约都是different objects can have equal hash but equal objects cant ever have different hashes。这是我们可以盲目相信的合同。当然,我们的自定义比较器也应遵守此规则。如果调用someObject.GetHashCode 会破坏事情,那么这是someObject 类型的实现者的问题,这不是我们的问题。
【解决方案4】:

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

IEquatable 用于实现对象本身(被比较是否相等的对象)。

【讨论】:

  • 您是唯一真正提出“插入平等”(外部使用)问题的人。加 1。
【解决方案5】:

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

【讨论】:

    猜你喜欢
    • 2012-03-10
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 2019-10-30
    相关资源
    最近更新 更多