如何在不可变的通用Pair结构上实现IEqualityComparer?

时间:2008-09-23 12:50:01

标签: c# generics struct

目前我有这个(在阅读建议后编辑):

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现在对我来说并不重要,因为这不是这个问题的本质 - 它总是可以在以后重构。第三,与默认(T)相比,无法与null进行比较 - 尝试它。

7 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分: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);
    }
  }
}

答案 2 :(得分:1)

如果在比较方法中使用哈希码,如果哈希码相同,则应检查“真值”。

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

首先,此代码违反了SRP原则。如果项目,对类用于保持对,对吧?委托平等比较功能是不正确的。

接下来让我们来看看您的代码:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

编译时我没有收到任何警告,但我假设你在谈论== null比较?演员似乎会让这一切变得更加清晰,是的。

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