IEqualityComparer实现中GetHashCode和Equals之间的关系是什么?

时间:2014-04-08 07:48:15

标签: c# interface gethashcode iequalitycomparer

我有一个继承自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
    }
}

2 个答案:

答案 0 :(得分:1)

实施IEqualityComparer<T>并非override GetHashCodeEquals的基本实施。

实现IEqualityComparer<T>允许您将实现者的实例作为T的相等比较器提供。这是几个linq扩展和泛型集合构造函数的通用参数。

覆盖EqualsGetHashCode影响类的实例测试相等的方式。利用调用EqualsGetHashCode的其他说明,例如基本=!=运算符以及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); } } 1}}。

这些概念类似但用于不同目的,它们不是部分可互换的


让我展开一个例子,

A

Equals的此实现正确覆盖GetHashCodevar distinct = aSequneceOfA.Distinct(); ,此更改足以确保在调用linq扩展后

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

不同的ingnores Equals的重载被覆盖GetHashCodeAComparerInsensitive并使用{{1}}来执行比较。

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