当空间大于32位时,如何实现GetHashCode兼容的Equals方法?

Jad*_*ias 1 .net equals iequatable gethashcode

在.NET中,您需要Equals(object)和GetHashCode()兼容.但有时你不能:

public class GreaterThan32Bits
{
    public int X { get; set; }
    public int Y { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

因为数据密度大于32位,并且GetHashCode返回Int32,所以您将有3个解决方案(假设正确实现了GetHashCode):

  1. 避免将代码重复 丢弃为不正确

    public override bool Equals(object other)
    {
        if(ReferenceEquals(null, other)) return false;
        if(ReferenceEquals(this, other)) return true;
        return this.GetHashCode() == other.GetHashCode();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 与GetHashCode()分开实现Equals

    public override bool Equals(object obj)
    {
        if(ReferenceEquals(null, other)) return false;
        if(ReferenceEquals(this, other)) return true;
        var other = obj as GreaterThan32Bits;
        if(this.X == other.X) return this.Y == other.Y;
        return false;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 实现更高精度的GetHashCode64,重写的GetHashCode(32位)将返回(int)GetHashCode64(),而Equals将返回this.GetHashCode64()== other.GetHashCode64()

你会实施哪一个?

第一种解决方案是不精确的不正确,但更清洁.第二个选项看起来很干净,但是当类有更多属性时会变得非常复杂.第三种选择是妥协.

Dan*_*iel 5

要求如下:if(a.Equals(b)),然后a.GetHashCode()== b.GetHashCode()

不是相反.

你不应该在GetHashCode()方面实现Equals().GetHashCode碰撞完全有效,但Equals()不得返回误报.

我建议这个实现:

public override int GetHashCode()
{
    return unchecked( this.X * p1 + this.Y * p2 );
}

public override bool Equals(object obj) 
{
    var other = obj as GreaterThan32Bits;
    // you must do the null test after the cast, otherwise the
    // function crashes when obj is not a GreaterThan32Bits instance
    if (ReferenceEquals(other, null)) return false;
    return this.X == other.X && this.Y == other.Y;
}
Run Code Online (Sandbox Code Playgroud)

其中p1和p2是大素数.这通常会产生良好的哈希函数(少数哈希冲突 - >词典变得高效).如果X和Y值是独立的(例如,你不希望像X = Y这样的直线上有很多点),那么即使是简单的东西X ^ Y也可以是一个很好的散列函数.

但话说回来,如果你真的将这个类用作字典(或其他哈希表)中的键,那么你只需要一个好的哈希函数.

实际上,总是在GetHashCode()中返回0并且只实现Equals()是完全正确的.字典仍然可以正常使用像键这样的对象,它只是效率低下.