获取哈希码和相等性

5

我一直在思考这个问题,所以我想问一下。

大多数情况下,为了实现成员逐一相等的语义逻辑,我们会使用与 GetHashCode 相同的方式重写 Equals…但通常会使用不同的实现:

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }
        var other = (MyType)obj;
        if (other.Prop1 != Prop1)
        {
            return false;
        }
        return true;
    }

    public override int GetHashCode()
    {
        int hash = -657803396;
        num ^= Prop1.GetHashCode();
        return num;
    }

如果您正在为类型实现成员相等性(比如存储在字典中),为什么不重写GetHashCode,然后对Equals进行以下操作:
    public override bool Equals(object obj)
    {
        return this.HashEqualsAndIsSameType(obj);
    }

    public static bool HashEquals(this object source, object obj)
    {
        if (source != null && obj != null)
        {
            return source.GetHashCode() == obj.GetHashCode();
        }
        if (source != null || obj != null)
        {
            return false;
        }
        return true;
    }

    public static bool HashEqualsAndIsSameType<T>(this T source, object obj)
    {
        return (obj == null || obj.GetType() == typeof(T)) && source.HashEquals(obj);
    }
2个回答

10

因为存在真正的冲突风险。哈希码不是唯一的。它们可以(当不同)证明不等式,但永远不能证明相等。查找项时:

  • 获取哈希码
  • 如果哈希码不同,则对象不同;丢弃它
  • 如果哈希码相同,请检查Equals:
  • 如果Equals报告true,则它们是相同的
  • 否则请丢弃

考虑long...由于哈希码是int,因此很容易看出存在很多冲突。


那么,您如何建议使用最少的成员引用实现成员逐一的GetHashCode/Equals?这样的重复可能会导致意外遗漏并引起重大问题(这就是我今天提出问题的原因)。另外,如果我们确实想要为对象生成唯一的校验和,是否已经存在一个良好的接口来定义类型提供计算校验和的能力? - Jeff
@jeff,你很少需要这样做,但是像resharper这样的工具可以为您完成它。 - Marc Gravell
一个例子 - 离线乐观缓存和/或锁定各种类型的对象(换句话说,在数据库表中没有列用于标记版本ID)。客户端和服务器都需要一种计算哈希或校验和(你会称之为哪个)的方法,以便如果客户端向服务器发送过时的版本,服务器知道不保存它并抛出异常。你将如何计算此目的的哈希/校验和? - Jeff
通常情况下,如果您检查相等性,equals应该首先比较哈希码,如果相同,则比较实际值。没有必要手动先比较哈希码。 - Jeroen Landheer

1

哈希值不是一对一的,你可以有多个不同的值映射到同一个哈希值上,但这些值在比较时应该被视为不相等。所以你不能真正地通过 GetHashCode 来实现 Equals 方法。这就是为什么哈希表中会出现冲突,而哈希表查找必须涉及 GetHashCode 和 Equals 方法的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接