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

C# GetHashCode在对象的生命周期内是否保证相同?,c#,.net-core,C#,.net Core,我讨厌打篮球。在@eric lippert的书中,他说: 对象的哈希值在其整个生存期内是相同的 然后跟进: 然而,这只是一个理想的情况指导方针 所以,我的问题是 对于POCO(未重写任何内容)或框架(如FileInfo或Process)对象,GetHashCode()方法的返回值是否保证在其生命周期内相同 另外,我说的是预先分配的对象变量foo=新条()将foo.GetHashCode()始终返回相同的值。有三种情况 不重写GetHashCode 不重写GetHashCode 重写GetHash

我讨厌打篮球。在@eric lippert的书中,他说:

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

然后跟进:

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

所以,我的问题是

对于POCO(未重写任何内容)或框架(如
FileInfo
Process
)对象,
GetHashCode()
方法的返回值是否保证在其生命周期内相同

另外,我说的是预先分配的对象<代码>变量foo=新条()将
foo.GetHashCode()
始终返回相同的值。

有三种情况

  • 不重写
    GetHashCode
  • 不重写
    GetHashCode
  • 重写
    GetHashCode
  • 如果类未重写
    GetHashCode
    ,则使用helper函数
    RuntimeHelpers.GetHashCode
    的返回值。这将在每次为同一对象调用时返回相同的值,因此对象将始终具有相同的哈希代码。请注意,此哈希代码特定于单个AppDomain-重新启动应用程序或创建另一个AppDomain可能会导致对象获得不同的哈希代码

    如果结构未重写
    GetHashCode
    ,则会根据其成员之一的哈希代码生成哈希代码。当然,如果您的结构是可变的,那么该成员可以随时间而改变,因此哈希代码可以随时间而改变。即使结构是不可变的,该成员本身也可能发生变异,并可能返回不同的哈希代码

    如果一个类或结构确实重写了
    GetHashCode
    ,那么所有的赌注都是无效的。有人可以通过返回一个随机数来实现
    GetHashCode
    ——这有点愚蠢,但这是完全可能的。更有可能的是,对象可以是可变的,其哈希代码可以基于其成员,这两者都可以随时间而改变

    对于可变的对象,或者以哈希代码随时间变化的方式(在给定的AppDomain中)实现
    GetHashCode
    ,通常是一个坏主意。在这种情况下,
    Dictionary
    等类所做的许多假设都会失效,您可能会看到奇怪的行为。

    如果您查看,您会发现关于GetHashCode方法的默认行为的以下注释:

    如果未重写GetHashCode,则引用类型的哈希代码为 通过调用基类的Object.GetHashCode方法计算, 它根据对象的引用计算哈希代码;更多 有关详细信息,请参阅RuntimeHelpers.GetHashCode。换句话说,两个 ReferenceEquals方法返回true的对象具有 相同的散列码。如果值类型不重写GetHashCode,则 基类的ValueType.GetHashCode方法使用反射 根据类型字段的值计算哈希代码。在里面 换句话说,字段值相等的值类型具有相等的 散列码

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

    • 对于引用类型(不重写Object.GetHashCode) 给定实例的哈希代码的值保证为 在实例的整个生命周期中都是相同的(因为内存 对象的存储地址在运行期间不会更改 寿命)
    • 对于值类型(不重写Object.GetHashCode),这取决于:如果值类型是不可变的,则哈希代码不会 在其生命周期内发生变化。如果为,则为其字段的值 可以在创建后更改,则其哈希代码也将更改。 请注意,值类型通常是不可变的
    重要编辑

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

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

    经过一些搜索后,根据(阅读用户@supercat提供的注释),此重新定位似乎不会在对象实例的生存期内更改其哈希代码,因为哈希代码只计算一次(第一次请求其值),计算出的值会保存并在以后重新使用(何时) 再次请求哈希代码值)

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

    关于哈希代码使用的旁注

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

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

    这意味着哈希代码的唯一安全用法是平衡哈希表,正如Eric Lippert所指出的,所以不要编写代码