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