我讨厌击败dead horse。在@ eric-lippert的blog中,他说:
对象的哈希值在整个生命周期中都是相同的
然后跟进:
但是,这只是一个理想的指导原则
所以,我的问题是这个。
对于POCO(什么也不能覆盖)或框架(例如FileInfo
或Process
)对象,是否保证GetHashCode()
方法的返回值在其生存期内是相同的?
P.S。我说的是预先分配的对象。 var foo = new Bar();
将foo.GetHashCode()
始终返回相同的值。
答案 0 :(得分:4)
如果您查看MSDN documentation,将会发现有关 GetHashCode 方法的默认行为的以下说明:
如果未覆盖GetHashCode,则引用类型的哈希码为 通过调用基类的Object.GetHashCode方法进行计算, 它基于对象的引用来计算哈希码;更多 有关信息,请参见RuntimeHelpers.GetHashCode。换句话说,两个 ReferenceEquals方法返回true的对象具有 相同的哈希码。如果值类型未覆盖GetHashCode,则 基类的ValueType.GetHashCode方法使用反射来 根据类型字段的值计算哈希码。在 换句话说,其字段具有相等值的值类型具有相等的值 哈希码
根据我的理解,我们可以假设:
重要编辑
正如上面的评论中指出的那样,.NET垃圾收集器可以决定在对象生存期内移动对象在内存中的物理位置,换句话说,可以将对象“重定位”到托管内存中。
这很有意义,因为垃圾回收器负责管理创建对象时分配的内存。
经过一些搜索并根据this stackoverflow question(阅读用户@supercat提供的注释),此重定位似乎不会在对象实例的生存期内更改其哈希码 ,因为哈希码只计算一次(第一次请求其值),然后将计算的值保存起来,以后再使用(当 再次请求哈希码值。)
总而言之,根据我的理解,您只能假设给定两个引用,它们指向内存中的同一对象,它们的哈希码将始终相同。换句话说,如果Object.ReferenceEquals(a,b),则a.GetHashCode()== b.GetHashCode()。此外,似乎给定对象实例的哈希码在整个生命周期中都将保持不变,即使对象的物理内存地址被垃圾收集器更改了。
使用哈希码的注意事项
始终记住重要的一点是,哈希码是在.NET框架中引入的,其唯一目的是处理哈希表数据结构。
为了确定要用于给定值的存储桶,采用相应的 key 并计算其哈希码(准确地说,存储桶索引是通过对GetHashCode调用返回的值应用一些规范化来获得的,但细节对于此讨论并不重要)。换句话说,在.NET哈希表实现中使用的哈希函数基于密钥的哈希码的计算。
这意味着哈希码的唯一安全用法是平衡哈希表,正如Eric Lippert here所指出的那样,因此请勿编写依赖于哈希码值的代码用于任何其他目的。 / p>
答案 1 :(得分:2)
有三种情况。
GetHashCode
GetHashCode
的结构GetHashCode
如果一个类没有覆盖GetHashCode
,则使用辅助函数RuntimeHelpers.GetHashCode
的返回值。每次为同一对象调用时,它将返回相同的值,因此,一个对象将始终具有相同的哈希码。请注意,此哈希代码特定于单个AppDomain-重新启动应用程序或创建另一个AppDomain,可能会导致您的对象获得不同的哈希代码。
如果结构未覆盖GetHashCode
,则根据其成员之一的哈希码生成哈希码。当然,如果您的结构是可变的,则该成员可以随时间变化,因此哈希码也可以随时间变化。即使该结构是不可变的,该成员本身也可能会发生突变,并可能返回不同的哈希码。
如果某个类或结构确实覆盖了GetHashCode
,则所有投注均关闭。有人可以通过返回一个随机数来实现GetHashCode
-这是一件很愚蠢的事情,但这是完全可能的。该对象很有可能是可变的,其哈希码可能基于其成员,这两个成员都可以随时间变化。
为可变对象或以哈希代码可以随时间变化的方式(在给定的AppDomain中)实现GetHashCode
通常是一个坏主意。在这种情况下,诸如Dictionary<TKey, TValue>
之类的类所做的许多假设都将分解,您可能会看到奇怪的行为。