如何在不可变的泛型Pair结构上实现IEqualityComparer?

问题描述:

目前我有这个(阅读意见后编辑):如何在不可变的泛型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); 
    } 
} 

的问题是,第一,第二可能不是引用类型(VS实际上提醒我这件事),但代码仍然编译。在比较它们之前,我是否应该将它们(第一和第二)投射到物体上,还是有更好的方法来做到这一点?

编辑: 请注意,我想结构,支持值类型和引用类型(换句话说,按类限制是不是一个有效的解决方案)

编辑2: 至于什么我想要实现,我希望这个工作在一个词典。其次,SRP对我来说现在并不重要,因为这不是这个问题的实质 - 它总是可以在以后重构。第三,与默认值(T)相比,不会比较null值 - 尝试它。

它看起来像你需要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); 
    } 
    } 
} 

您的IEqualityComparer实现应该是一个不同的类(并且肯定不是一个结构,因为您要重用该引用)。

此外,您的散列码不应该被缓存,因为结构的默认GetHashcode实现(您不会覆盖)会将该成员考虑在内。

关于警告,您可以使用默认(T)和默认(K)而不是null。

我看不到你要达到的目标,但是你不应该使用哈希码来比较是否相等 - 不能保证两个不同的目标不会有相同的哈希码。即使你的结构是不可变的,成员_first和_second也不是。

+0

是否所有内置值类型已经0的哈希码默认值吗? – ilitirit 2008-09-23 13:13:22

+0

如果不是这种情况,我会很惊讶,但不想保证。 – Joe 2008-09-23 13:31:49

首先,这段代码违反了SRP原则。 Pair类用于保存对,如果项目,对不对?将相等功能委托给它是不正确的。

接下来,让我们看看你的代码:

的Equals如果其中一个参数为null方法将失败 - 没有好。 Equals使用Pair类的哈希码,但看看GetHashCode的定义,它只是对成员哈希码的组合 - 它与项目的等式无关。我期望Equals方法将比较实际数据。不幸的是,我现在太忙,无法提供正确的实施。但从第一眼看,你的代码似乎是错误的。如果您向我们提供您想要实现的内容的描述会更好。我相信SO会员将能够给你一些建议。

可能我建议使用Lambda表达式作为参数吗? 这将允许您指定如何比较内部通用类型。

编译有关这个时,我没有收到任何警告,但我认为你是在谈论==空比较?演员似乎会让这一切变得更清洁,是的。

PS。你真的应该为比较器使用一个单独的类。这个填充两个角色(成对和比较对象)的类很丑陋。

如果在比较方法时使用了hashcode,如果哈希码相同,则应该检查“realy value”。

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”字段或object.ReferenceEquals的“Equals”方法吗?还是更复杂的东西?