实体类的GetHashCode()的正确实现是什么?

时间:2009-02-03 17:38:10

标签: c# .net orm

下面是一个覆盖实体基类的Object.Equals()的示例实现,应用程序中的所有其他实体都从该实体基类派生。

所有实体类都具有属性Id,它是一个可以为null的int。 (它是实体类对应的任何表的主键。)

public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
                return false;

            if (base.Equals(obj))
                return true;

            return Id.HasValue && ((EntityBase) obj).Id.HasValue &&
                   Id.Value == ((EntityBase) obj).Id.Value;
        }

鉴于Equals()的这种实现,你如何正确实现GetHashCode()?

4 个答案:

答案 0 :(得分:23)

如果您从已经覆盖的内容GetHashCode派生出来,我将其实现为:

public override int GetHashCode()
{
    unchecked
    {
        int hash = 37;
        hash = hash * 23 + base.GetHashCode();
        hash = hash * 23 + Id.GetHashCode();
        return hash;
    }
}

对于Id.GetHashCode(),Id的空值将返回0。

如果你的类只是派生自Object,我只返回Id.GetHashCode() - 你想要在你的哈希码中包含object.GetHashCode实现,因为它基本上是最终成为对象身份。

请注意,如果两个实体都没有Id,则等式定义将不会返回true,但是将从两个对象返回相同的哈希码。您可能希望考虑更改您的Equals实施。

答案 1 :(得分:2)

Jon Skeet回答的是一个很好的解决方案,但是,您可能希望添加一个未经检查的代码块以允许整数溢出

unchecked
{
  int hash = ...;
  return hash
}

https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx

  

如果既未指定也未取消选中,则默认上下文取决于外部因素,例如编译器选项。

我还想再次补充一点,在POCO上使用base.GetHashCode()会调用默认的object.GetHashCode。这绝对不是你想要的......

答案 2 :(得分:2)

使用类型作为哈希码的一部分怎么样?
这会是一个很好的实施吗?

public class Foo
{
    public int Id { get; set; }

    // other properties here
    // ......

    public override int GetHashCode()
    {
        int hash = 37;
        hash = hash * 23 + typeof(Foo).GetHashCode();
        hash = hash * 23 + Id.GetHashCode();
        return hash;
    }
}

答案 3 :(得分:1)

如果GetHashCode()属性在实例的生命周期中是不可变的(或者至少在其哈希需要使用的时间,例如对象是时),则只能正确实现Id在需要哈希的地图或其他集合中。

假设它是,您可以使用Id的值作为所有有效值的哈希,然后使用固定哈希值为null。我不记得最合适的是什么,但我会假设一个随机选择的null值(在编译之前随机选择,而不是在运行时)或有效Id值的中间值(即中间值) 0和int.Max)。