如何在不可变的通用Pair结构上实现IEqualityComparer?
目前,我有这个(阅读建议后进行编辑):
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进行比较。
解决方案
IEqualityComparer实现应该是一个不同的类(要重用该引用,绝对不能是一个结构)。
同样,哈希码永远都不应被缓存,因为结构的默认GetHashcode实现(我们不会重写)将考虑该成员。
关于警告,可以使用default(T)和default(K)代替null。
我看不到我们要实现的目标,但是我们不应该使用哈希码进行相等性比较,不能保证两个不同的对象不会具有相同的哈希码。同样,即使结构是不可变的,成员_first和_second也不是。
首先,此代码违反了SRP原则。配对类,用于容纳成对的物品,对吗?委托相等性比较功能是不正确的。
接下来让我们看一下代码:
如果参数之一为null,Equals方法将失败。 Equals使用Pair类的哈希码,但请看一下GetHashCode的定义,它只是Pair成员哈希码的组合,与项目的相等性无关。我希望Equals方法会比较实际数据。不幸的是,我现在很忙,无法提供正确的实现。但是从第一眼看,代码似乎是错误的。如果我们向我们提供我们要达到的目标的描述,那将更好。我敢肯定,SO成员将能够为我们提供一些建议。
我是否建议将Lambda表达式用作参数?
这将允许我们指定如何比较内部泛型类型。
对此进行编译时,我没有收到任何警告,但我假设我们正在谈论== null比较?强制转换似乎可以使这一切变得更干净,是的。
PS。我们确实应该为比较器使用一个单独的类。这个扮演两个角色(成对和比较对)的类很丑陋。
如果在比较方法中使用哈希码,则如果哈希码相同,则应检查"实际值"。
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"字段或者对象的"等于"方法。还是更复杂的东西?
看来我们需要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); } } }