GetHashCode 是否保证在对象的生命周期内相同?

Ang*_*ker 0 c# .net-core

我讨厌打败一匹 。在@eric-lippert 的博客中,他说:

一个对象的哈希值在其整个生命周期内是相同的

然后跟进:

然而,这只是一个理想情况的指导方针

所以,我的问题是这个。

对于 POCO(没有被覆盖)或框架(如FileInfoProcess)对象,方法的返回值是否GetHashCode()保证在其生命周期内相同?

PS 我说的是预先分配的对象。 var foo = new Bar();foo.GetHashCode()始终返回相同的值。

Enr*_*one 6

如果您查看MSDN 文档,您会发现以下有关GetHashCode方法的默认行为的说明:

如果未覆盖 GetHashCode,则通过调用基类的 Object.GetHashCode 方法计算引用类型的哈希码,该方法根据对象的引用计算哈希码;有关详细信息,请参阅 RuntimeHelpers.GetHashCode。换句话说,ReferenceEquals 方法为其返回 true 的两个对象具有相同的哈希码。如果值类型不覆盖 GetHashCode,则基类的 ValueType.GetHashCode 方法使用反射根据类型字段的值计算哈希码。换句话说,字段具有相同值的值类型具有相同的哈希码

根据我的理解,我们可以假设:

  • 对于引用类型(不覆盖 Object.GetHashCode),给定实例的哈希码的值保证在实例的整个生命周期内相同(因为存储对象的内存地址将不会) t 在其生命周期内发生变化)
  • 对于值类型(不覆盖 Object.GetHashCode),它取决于:如果值类型是不可变的,那么哈希码在其生命周期内不会改变。否则,如果其字段的值可以在其创建后更改,则其哈希码也会更改。请注意,值类型通常是不可变的。

重要编辑

正如上面的一条评论所指出的,.NET 垃圾收集器可以决定在对象生命周期内移动对象在内存中的物理位置,换句话说,对象可以在托管内存中“重定位”。

这是有道理的,因为垃圾收集器负责管理创建对象时分配的内存。

经过一些搜索并根据this stackoverflow question(阅读用户@supercat提供的评论)似乎这种重定位在其生命周期内不会更改对象实例的哈希码,因为哈希码计算一次(第一次它的值被请求)并且计算出的值被保存并稍后重用(当再次请求哈希码值时)。

总而言之,根据我的理解,您唯一可以假设的是,给定指向内存中同一对象的两个引用,它们的哈希码将始终相同。换句话说,如果 Object.ReferenceEquals(a, b) 则 a.GetHashCode() == b.GetHashCode()。此外,似乎给定一个对象实例,它的哈希码将在其整个生命周期内保持不变,即使垃圾收集器更改了对象的物理内存地址。

关于哈希码使用的旁注

务必记住,在 .NET 框架中引入哈希码的唯一目的是处理哈希表数据结构。

为了确定用于给定的桶,取相应的并计算其哈希码(准确地说,桶索引是通过对 GetHashCode 调用返回的值应用一些规范化来获得的,但是细节对本次讨论不重要)。换句话说,哈希表的 .NET 实现中使用的哈希函数基于键的哈希码的计算。

这意味着哈希码的唯一安全用法是平衡哈希表,正如 Eric Lippert在这里指出的那样,所以不要为了任何其他目的编写依赖于哈希码值的代码。

  • “因为存储对象的内存地址在其生命周期内不会改变” - 我认为垃圾收集器可以重新定位对象? (3认同)