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

下面是一个覆盖实体基类的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()?

如果您从已经覆盖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实施。

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

 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; } } 

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

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

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

如果未指定checked或unchecked,则默认上下文取决于外部因素,例如编译器选项。

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

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

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