IEqualityComparer 实现中 GetHashCode 和 Equals 之间的关系是啥? [复制]
Posted
技术标签:
【中文标题】IEqualityComparer 实现中 GetHashCode 和 Equals 之间的关系是啥? [复制]【英文标题】:What's the relation between GetHashCode and Equals in a IEqualityComparer implementation? [duplicate]IEqualityComparer 实现中 GetHashCode 和 Equals 之间的关系是什么? [复制] 【发布时间】:2014-04-08 07:48:14 【问题描述】:我有一个继承自 B 类并实现 IEqualityComparer<A>
的类 A。
这意味着 A 类提供了自己的 Equals 和 GetHashCode 方法的实现。到目前为止,一切都很好。
问题是我不明白为什么代码的行为方式如下:
只有当 A 的 GetHashCode 实现返回时,调试器才会到达 A 的 Equals 实现断点
this.GetHashCode()
而不是
obj.GetHashCode()
,其中“obj”是 GetHashCode 的签名定义的参数(在我的例子中是 A 类型的变量)。
直觉上,我认为我应该返回收到的对象的哈希码,但这样做会使编译器忽略实例的 Equals 实现。
为什么会这样?
代码演示:
public class A : B, IEqualityComparer<A>
public bool Equals(A x, A y)
//my implementation...
public int GetHashCode(A obj)
//return obj.GetHashCode(); -> this makes my Equals implementation above be ignored! Why?
return this.GetHashCode(); -> my Equals implementation is used
【问题讨论】:
你能贴一些代码来解释这个问题吗,很难猜对 【参考方案1】:听起来您使用了错误的界面。 IEqualityComparer<>
通常用于比较其他类型实例的类。
你的类型应该简单地实现IEquatable<A>
和覆盖Equals(object)
和GetHashCode()
。注意签名。
像这样:
public class A : B, IEquatable<A>
public bool Equals(A other)
if (other == null || GetType() != other.GetType())
return false;
//your implementation
public override bool Equals(object obj)
return Equals(obj as A);
public override int GetHashCode()
//your implementation
然后您可以执行someEnumerableOfA.Distinct()
之类的操作,Linq 方法将使用您的实现。
另一种选择是:
public class A : B // no interfaces
public class AEqualComparer : IEqualityComparer<A>
public bool Equals(A x, A y)
//your implementation
public int GetHashCode(A x)
//your implementation
使用此其他选项,您需要someEnumerableOfA.Distinct(new AEqualComparer ())
。
【讨论】:
我选择 IEqualityComparer 的原因是因为我的解决方案依赖于调用 IEnumerable.Distinct(IEqualityComparer)。换句话说,如果我想返回我的集合的不同元素,为什么我不能在这里使用 Distinct 并因此需要提供 IEqualityComparer 实现? OP 实现了正确的接口,但方式错误。有关更多信息,请参阅链接的重复线程。 @Veverke,如果您覆盖Equals
和 GetHashCode
,则无需实现或提供 IEqualityComparer<T>
。
@SriramSakthivel 不同意。你不应该有一个类A
实现IEqualityComparer<A>
。那是滥用。看看String
类。它实现了IEquatable<String>
。但是,还有其他类,例如实现 IEqualityComparer<String>
的 StringComparer
(但不 IEqualityComparer<StringComparer>
)。
@Jodrell:我对 Distinct 的 IEnumerable 调用将如何? variable.Distinct(this) - 当前类在哪里覆盖 Equals 和 GetHashCode?span>
【参考方案2】:
实现IEqualityComparer<T>
不是override
的基本实现GetHashCode
和Equals
。
实现IEqualityComparer<T>
允许您提供实现者的实例作为T
的相等比较器。这是几个 linq 扩展和通用集合构造函数的通用参数。
覆盖Equals
和GetHashCode
会影响对类实例进行相等性测试的方式。利用调用 Equals
和 GetHashCode
的其他实现,例如基本 =
和 !=
运算符和 linq 扩展和通用集合构造函数,您不提供替代 IEqualityComparer<T>
。
这些概念相似,但用途不同,不能部分互换。
让我用一个例子来扩展,
public class A
public string Value1 get; set;
public int Value2 get; set;
public override int GetHashCode()
unchecked
int hash = 17;
hash = (hash * 23) +
StringComparer.Ordinal.GetHashCode(this.Value1);
hash = (hash * 23) + this.Value2;
return hash;
public override bool Equals(object obj)
var a = obj as A;
if (a == null)
return false;
if (a.Value2 != this.Value2)
return false;
return StringComparer.Ordinal.Equals(
a.Value1,
this.Value1);
A
的这个实现正确地覆盖了Equals
和GetHashCode
,这个改变足以保证在调用linq扩展之后
var distinct = aSequneceOfA.Distinct();
distinct
不会包含具有相同Value2
和通常可比较Value1
的任何实例。无需其他接口实现即可实现此目的。
现在,假设在某些情况下我对Value1
的这个序数比较不满意,也许我需要不区分大小写。我可能会实现一个新的相等比较器。
public class AComparerInsensitive : IEqualityComparer<A>
public bool Equals(A x, A y)
if (x == null)
return y == null;
if (y == null)
return false;
if (x.Value2 != y.Value2)
return false;
return StringComparer.CurrentCultureIgnoreCase.Equals(
x.Value1,
y.Value1)
public int GetHashCode(A a)
if (a == null)
return 0;
unchecked
int hash = 17;
hash = (hash * 23) +
StringComparer.CurrentCultureIgnoreCase.GetHashCode(
a.Value1);
hash = (hash * 23) + a.Value2;
return hash;
这将允许我调用 Distinct
的替代重载,
var insensitivelyDistinct = aSequneceOfA.Distinct(
new AComparerInsensitive());
这种重载的不同忽略 A
s 被覆盖 Equals
和 GetHashCode
并使用 AComparerInsensitive
执行比较。
【讨论】:
@Veverke 我希望我的扩展答案能让事情更清楚。以上是关于IEqualityComparer 实现中 GetHashCode 和 Equals 之间的关系是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何实现 IEqualityComparer 以返回不同的值?
使用带有容差的 IEqualityComparer GetHashCode
通用 IEqualityComparer 使用反射和属性来标记我想要比较的内容