在没有不可变字段的类中重写Object.GetHashCode()时要返回什么?

She*_*dan 10 c# overriding class mutable gethashcode

好吧,在你因为互联网上发布了数百个类似的声音问题而疯狂之前,我可以向你保证,我刚刚花了几个小时阅读所有这些问题并且没有找到我的问题的答案.

背景:

基本上,我的一个大型应用程序遇到了这样的情况:属性Binding上的某些s ListBox.SelectedItem将停止工作,或者在对当前所选项目进行编辑后程序将崩溃.我最初询问'已经添加了相同键的项目'从代码问题中选择ListBoxItem的例外,但没有得到答案.

直到本周,我才有时间解决这个问题.现在简而言之,我找出了问题的原因.这是因为我的数据类型类已经覆盖了Equals方法,因此也覆盖了方法GetHashCode.

现在对于那些不知道这个问题的人,我发现你只能GetHashCode使用不可变字段/属性来实现该方法.使用Harvey Kwok对Overriding GetHashCode()帖子的回答摘录来解释这个:

问题是Dictionary和HashSet集合正在使用GetHashCode将每个项目放在存储桶中.如果基于某些可变字段计算哈希码,并且在将对象放入HashSet或Dictionary后实际更改了字段,则无法再从HashSet或Dictionary中找到该对象.

所以实际问题是因为我在方法中使用了可变属性GetHashCode.当用户在UI中更改这些属性值时,对象的关联哈希码值会发生更改,然后在其集合中无法再找到项目.

题:

所以,我的问题是处理我需要GetHashCode在没有不可变字段的类中实现方法的情况的最佳方法是什么?对不起,让我更加具体,因为问题被问过.

Overriding GetHashCode()帖子中的答案表明,在这些情况下,最好只返回一个常量值...一些建议返回值1,而其他建议返回一个素数.就个人而言,我看不出这些建议之间有任何区别,因为我认为只有一个桶用于其中任何一个.

此外,Eric Lippert博客中关于GetHashCode指南和规则有一个标题为指南的部分:哈希码的分布必须是"随机的",这突出了使用导致使用不足的桶的算法的缺陷.他警告说,算法会减少使用的桶数,并在桶变得非常大时导致性能问题.当然,返回常数属于这一类.

我想到了Guid为我的所有数据类型类(仅在C#中,而不是数据库中)添加一个额外的字段,特别是在GetHashCode方法中使用.所以我想在这个长篇介绍的最​​后,我的实际问题是哪个实现更好?总结一下:

摘要:

在没有不可变字段的类中重写Object.GetHashCode()时,最好从GetHashCode方法返回一个常量,还是readonly为每个类创建一个附加字段,仅用于GetHashCode方法?如果我应该添加一个新字段,它应该是什么类型,我不应该将它包含在Equals方法中?

虽然我很高兴收到任何人的答案,但我真的希望得到高级开发人员的答案,他们对这个主题有充分的了解.

Eri*_*ert 14

回到基础.你看了我的文章; 再读一遍.与您的情况相关的两个铁定规则是:

  • 如果x等于y,那么x的哈希码必须等于y的哈希码.等价地:如果x的哈希码不等于y的哈希码,那么x和y必须是不相等的.
  • 当x在哈希表中时,x的哈希码必须保持稳定.

这些是正确性的要求.如果你不能保证这两件简单的事情,那么你的程序将是不正确的.

你提出两个解决方案.

您的第一个解决方案是始终返回常量.这符合两个规则的要求,但您将在哈希表中简化为线性搜索.你也可以使用一个列表.

您建议的另一个解决方案是以某种方式为每个对象生成哈希码并将其存储在对象中.如果相等的项具有相同的哈希码,则这是完全合法的.如果您这样做,那么您受到限制,如果哈希码不同,则x等于y 必须为false.这似乎使价值平等基本上不可能.因为如果你想要引用相等性,你不会首先重写Equals,这似乎是一个非常糟糕的主意,但是如果equals是一致的,它是合法的.

我提出了第三种解决方案,即:永远不要将对象放在哈希表中,因为哈希表首先是错误的数据结构.哈希表的要点是快速回答问题"这组不可变值中的给定值是什么?" 并且您没有一组不可变值,因此请勿使用哈希表.使用正确的工具完成工作.使用列表,并忍受线性搜索的痛苦.

第四个解决方案是:对用于相等的可变字段进行哈希处理,在每次变异之前从所有哈希表中删除该对象,然后将其放回原处.这符合两个要求:哈希代码同意相等,哈希表中的对象哈希是稳定的,并且您仍然可以快速查找.