Object.GetHashCode()上的MSDN文档描述了该方法应如何工作的3条矛盾规则。
规则1& 3对我来说是矛盾的。
Object.GetHashCode()是否根据对象的值或对象的引用返回唯一编号。如果我覆盖该方法,我可以选择使用什么,但是如果有人知道的话,我想知道内部使用的是什么。
答案 0 :(得分:29)
规则1& 3对我来说是矛盾的。
在某种程度上,他们是。原因很简单:如果对象存储在哈希表中,并且通过更改其值,您更改其哈希值,则哈希表已丢失该值,并且您无法通过查询哈希表再次找到它。重要的是,当对象存储在哈希表中时,它们会保留其哈希值。
要实现这一点,通常最简单的方法是使可清除对象不可变,从而避免整个问题。但是,只有那些字段不可变才能确定哈希值。
考虑以下示例:
struct Person {
public readonly string FirstName;
public readonly string Name;
public readonly DateTime Birthday;
public int ShoeSize;
}
人们很少改变他们的生日,大多数人都不会改变他们的名字(除非结婚时)。然而,他们的鞋子尺寸可能会随意增长,甚至会缩小。因此,使用他们的生日和名字而不是他们的鞋子大小来识别人是合理的。哈希值应该反映这一点:
public int GetHashCode() {
return FirstName.GetHashCode() ^ Name.GetHashCode() ^ Birthday.GetHashCode();
}
答案 1 :(得分:5)
不确定您所指的MSDN文档。查看Object.GetHashCode(http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx)上的当前文档提供了以下“规则”:
如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。
对象的GetHashCode方法必须始终返回相同的哈希码,只要对对象状态没有修改即可确定对象的Equals方法的返回值。请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。
为获得最佳性能,哈希函数必须为所有输入生成随机分布。
如果您指的是第二个项目符号点,则此处的关键短语是“只要没有对对象状态进行修改”和“仅对应用程序的当前执行为真”。
同样来自文档,
哈希函数用于快速生成与对象的值对应的数字(哈希码)。散列函数通常特定于每个类型,并且必须至少使用一个实例字段作为输入。 [增加的重点是我的。]
至于实际实现,它明确指出派生类可以遵循Object.GetHashCode实现当且仅当派生类将值相等定义为引用相等且类型不是值类型。换句话说,Object.GetHashCode的默认实现将基于引用相等,因为不存在要使用的实例字段,因此不保证不同对象的唯一返回值。否则,您的实现应该特定于您的类型,并且应该至少使用一个实例字段。例如,String.GetHashCode的实现为相同的字符串值返回相同的哈希码,因此如果两个String对象表示相同的字符串值,则返回相同的哈希码,并使用字符串中的所有字符生成该哈希值。
答案 2 :(得分:4)
规则1& 3并不是真正的矛盾。
对于引用类型,哈希代码是从对象的引用派生的 - 更改对象的属性,引用是相同的。
对于值类型,哈希码是从值派生的,更改值类型的属性,然后获得值类型的全新实例。
答案 3 :(得分:1)
有关如何处理GetHashCode
(超出Microsoft规则)的非常好的解释在Eric Lipperts(C#的设计者)博客中提供了文章" Guidelines and rules for GetHashCode" 。这里添加超链接是不好的做法(因为它们可能无效),但这个是值得的,并且如果超链接丢失,上面的信息可能仍会找到它。
答案 4 :(得分:0)
默认情况下,它基于对象的引用来完成它,但这意味着它是完全相同的对象,因此两者都将返回相同的哈希。但是散列应该基于值,就像字符串类一样。 “a”和“b”将具有不同的散列,但“a”和“a”将返回相同的散列。
答案 5 :(得分:0)
我无法确定Object.GetHashCode是如何在真正的 .NET Framework中实现的,但在Rotor中它将对象的SyncBlock索引用作哈希码。网上有一些关于它的博客文章,但大多数都来自2005年。