如何在不可变的泛型 Pair 结构上实现 IEqualityComparer?
Posted
技术标签:
【中文标题】如何在不可变的泛型 Pair 结构上实现 IEqualityComparer?【英文标题】:How do I implement IEqualityComparer on an immutable generic Pair struct? 【发布时间】:2010-09-12 08:33:21 【问题描述】:目前我有这个(阅读建议后编辑):
struct Pair<T, K> : IEqualityComparer<Pair<T, K>>
readonly private T _first;
readonly private K _second;
public Pair(T first, K second)
_first = first;
_second = second;
public T First get return _first;
public K Second get return _second;
#region IEqualityComparer<Pair<T,K>> Members
public bool Equals(Pair<T, K> x, Pair<T, K> y)
return x.GetHashCode(x) == y.GetHashCode(y);
public int GetHashCode(Pair<T, K> obj)
int hashCode = obj.First == null ? 0 : obj._first.GetHashCode();
hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode();
return hashCode;
#endregion
public override int GetHashCode()
return this.GetHashCode(this);
public override bool Equals(object obj)
return (obj != null) &&
(obj is Pair<T, K>) &&
this.Equals(this, (Pair<T, K>) obj);
问题是 First 和 Second 可能不是引用类型(VS 实际上警告我这一点),但代码仍然可以编译。我应该在比较它们之前将它们(第一和第二)转换为对象,还是有更好的方法来做到这一点?
编辑: 请注意,我希望此结构支持值和引用类型(换句话说,按类约束不是有效的解决方案)
编辑 2: 至于我想要实现的目标,我希望它可以在字典中工作。其次,SRP 现在对我来说并不重要,因为这并不是这个问题的真正本质——它总是可以在以后重构。第三,与 default(T) 比较不能代替与 null 比较 - 试试吧。
【问题讨论】:
【参考方案1】:您的 IEqualityComparer 实现应该是一个不同的类(并且绝对不是一个结构,因为您想重用引用)。
此外,您的哈希码永远不应被缓存,因为结构的默认 GetHashcode 实现(您不会覆盖)将考虑该成员。
【讨论】:
【参考方案2】:看来您需要 IEquatable:
internal struct Pair<T, K> : IEquatable<Pair<T, K>>
private readonly T _first;
private readonly K _second;
public Pair(T first, K second)
_first = first;
_second = second;
public T First
get return _first;
public K Second
get return _second;
public bool Equals(Pair<T, K> obj)
return Equals(obj._first, _first) && Equals(obj._second, _second);
public override bool Equals(object obj)
return obj is Pair<T, K> && Equals((Pair<T, K>) obj);
public override int GetHashCode()
unchecked
return (_first != null ? _first.GetHashCode() * 397 : 0) ^ (_second != null ? _second.GetHashCode() : 0);
【讨论】:
【参考方案3】:如果您在比较方法中使用哈希码,如果哈希码相同,则应检查“真实值”。
bool result = ( x._hashCode == y._hashCode );
if ( result ) result = ( x._first == y._first && x._second == y._second );
// OR?: if ( result ) result = object.Equals( x._first, y._first ) && object.Equals( x._second, y._second );
// OR?: if ( result ) result = object.ReferenceEquals( x._first, y._first ) && object.Equals( x._second, y._second );
return result;
但是比较“_first”和“_second”字段有一点问题。 默认情况下,引用类型使用前相等比较“object.ReferenceEquals”方法,他们可以覆盖它们。因此,正确的解决方案取决于您的比较方法“究竟应该做什么”。应该使用“_first”和“_second”字段的“Equals”方法,还是 object.ReferenceEquals ?或者更复杂的东西?
【讨论】:
【参考方案4】:关于警告,您可以使用 default(T) 和 default(K) 代替 null。
我看不出您想要实现什么,但您不应该使用哈希码来比较相等性 - 不能保证两个不同的对象不会具有相同的哈希码。此外,即使您的结构是不可变的,成员 _first 和 _second 也不是。
【讨论】:
是否所有内置值类型的默认值的哈希码都为 0? 如果不是这样,我会感到非常惊讶,但我不想保证。 另外,default(T) 和 default(K) 不起作用“不能将 '==' 应用于类型 'T' 和 'T'”【参考方案5】:首先,这段代码违反了 SRP 原则。 Pair 类用于保存物品,对吧?将相等性比较功能委托给它是不正确的。
接下来让我们看看你的代码:
如果参数之一为空,Equals 方法将失败 - 不好。 Equals 使用 Pair 类的哈希码,但看一下 GetHashCode 的定义,它只是对成员哈希码的组合——它与项目的相等性无关。我希望 Equals 方法将比较实际数据。不幸的是,我现在太忙了,无法提供正确的实施。但是乍一看,您的代码似乎是错误的。如果您向我们提供您想要实现的目标的描述会更好。相信SO成员一定能给你一些建议。
【讨论】:
首先,在正常的代码流中,Equals 方法如何接受空参数?其次,哈希码可以用于相等。事实上,这就是它最常用的(比较项目)。这段代码的唯一问题是 First 和 Second 可能不是不可变的。 我很抱歉。两个相等的对象共享相同的hashcode,但是具有相同hashcode的两个对象可能不相等。 “不可变对”类持有不可变的一对项目并不违反 SRP,并实现等价关系,即如果两对中的对应项目等价,则对本身也是等价的。所有类型都应该有一个定义的等价关系;可变引用类型应该使用默认的引用-等价关系,而不可变类型和值类型通常应该认为它们自己是等价的,如果相应的字段被认为是等价的。【参考方案6】:我可以建议使用 Lambda 表达式作为参数吗? 这将允许您指定如何比较内部泛型类型。
【讨论】:
【参考方案7】:编译时我没有收到任何警告,但我假设您正在谈论 == null 比较?演员阵容似乎会让这一切变得更干净,是的。
PS。你真的应该为比较器使用一个单独的类。填补两个角色(作为一对和比较对)的这个类简直丑陋。
【讨论】:
以上是关于如何在不可变的泛型 Pair 结构上实现 IEqualityComparer?的主要内容,如果未能解决你的问题,请参考以下文章