我想了解何时应该使用IEqualityComparer<T>
和IEquatable<T>
。两者的MSDN文档看起来非常相似。
IEqualityComparer<T>
是一个接口,用于比较两个类型为T
的对象。
IEquatable<T>
是用于类型为T
的对象,以便它可以将自己与另一个相同类型的对象进行比较。
IEqualityComparer<T>
是一个接口,用于提供在 T
上操作的比较函数的对象(通常是与 T
不同的轻量级类)。 - rwong当决定使用 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<>
,而另一种没有?
理论上,比较这两种类型的实例只有一种明智的方式:如果两个实例中的 X
和 Y
属性相等,则它们相等。根据这种思考方式,这两种类型都应该实现 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>
的实现,如果不足以满足您的需求,您可以简单地将其替换掉。IEqualityComparer<T>
实现都 必须 实现等价关系,这意味着在所有情况下,如果两个对象与第三个对象相等,则它们 必须 相互相等。任何以与上述规则相反的方式实现 IEquatable<T>
或 IEqualityComparer<T>
的类都是有问题的。 - supercatIEqualityComparer<String>
认为"hello"等于"Hello"和"hElLo"必须同时认为"Hello"和"hElLo"相互相等,但对于大多数比较方法而言,这不会成为问题。 - supercatGetHashCode
应该允许你快速确定两个值是否不同。大致规则如下:**(1)** 对于相同的值,GetHashCode
必须始终生成相同的哈希码。**(2)** GetHashCode
应该很快(比 Equals
更快)。**(3)** GetHashCode
不必精确(不像 Equals
那样精确)。这意味着它可能为不同的值产生相同的哈希码。您能使其更精确就越好,但保持其快速可能更重要。 - stakx - no longer contributingT
上实现IEquatable<T>
,则类型为T
的对象的Equals
方法将告诉您该对象本身(正在测试相等性的对象)是否等于同一类型T
的另一个实例。而IEqualityComparer<T>
用于测试任意两个T
实例的相等性,通常超出了T
实例的范围。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>
完全类似;我在这里写了同样的答案 :)
public int GetHashCode(Person obj)
should return obj.GetHashCode()
- hyankovobj.Age.GetHashCode
。将进行编辑。 - nawfalperson.GetHashCode
.... 为什么要重写它? - 我们重写它是因为IEqualityComparer
的整个意义在于根据我们真正熟悉的规则拥有不同的比较实现 - 规则。 - nawfalAge
属性进行比较,因此我调用Age.GetHashCode
。Age的类型是int
,但无论类型如何,在.NET中哈希码合同是“不同的对象可以具有相等的哈希码,但相等的对象永远不能具有不同的哈希码”。这是一个合同,我们可以盲目地相信。当然,我们自定义的比较器也应遵守这个规则。如果调用someObject.GetHashCode
会出问题,那么问题在于someObject
的类型实现者,而不是我们的问题。 - nawfalIEqualityComparer用于在两个对象的相等性是外部实现时使用,例如,如果您想要为两种类型定义一个比较器,但您没有源代码,或者在两个东西之间的相等性只有在某些有限的情况下才有意义。
IEquatable用于对象本身(被比较相等性的对象)进行实现。
一个用于比较两个T
的对象。另一个可以将自己与其他T
进行比较。通常,您只需要一次使用一个对象,而不是两个对象。
EqualityComparer<T>
而不是实现接口,因为EqualityComparer<T>
使用IEquatable<T>
测试相等性。” - radarbobIEquatable<T>
的T
创建自定义集合。否则,像List<T>
这样的集合会有某种微妙的错误吗? - radarbob