【发布时间】:2013-10-31 15:04:28
【问题描述】:
好的,在您因为互联网上发布了数百个类似的问题而生气之前,我可以向您保证,我刚刚花了几个小时阅读所有这些问题并且还没有找到了我的问题的答案。
背景:
基本上,我的一个大型应用程序遇到了这样一种情况:ListBox.SelectedItem 属性上的某些Bindings 将停止工作,或者在对当前选定的项目进行编辑后程序会崩溃。我最初在这里问了'An item with the same key has already been added' Exception on selecting a ListBoxItem from code 的问题,但没有得到答案。
直到本周我才有时间解决这个问题,当时我有几天的时间来解决这个问题。现在长话短说,我找到了问题的原因。这是因为我的数据类型类覆盖了 Equals 方法,因此也覆盖了 GetHashCode 方法。
现在对于那些不知道这个问题的人,我发现你只能使用 不可变 字段/属性来实现 GetHashCode 方法。使用 Harvey Kwok 对Overriding GetHashCode() 帖子的回答的摘录来解释这一点:
问题在于 Dictionary 和 HashSet 集合使用 GetHashCode 将每个项目放入存储桶中。如果hashcode是根据一些可变字段计算出来的,而对象放入HashSet或Dictionary后字段确实发生了变化,则无法再从HashSet或Dictionary中找到该对象。
所以实际问题是因为我在GetHashCode方法中使用了可变属性引起的。当用户在 UI 中更改这些属性值时,对象的相关哈希码值会更改,然后无法在其集合中找到项目。
问题:
那么,我的问题是处理需要在没有不可变字段的类中实现 GetHashCode 方法的情况的最佳方法是什么?抱歉,让我说得更具体一些,因为 这个问题 以前被问过。
Overriding GetHashCode() 帖子中的答案表明,在这些情况下,最好简单地返回一个常量值......有些人建议返回值 1,而另一些人建议返回一个素数。就个人而言,我看不出这些建议之间有什么区别,因为我原以为它们中的任何一个都只会使用一个存储桶。
此外,Eric Lippert 博客中的 Guidelines and rules for GetHashCode 文章有一个标题为指南:哈希码的分布必须是“随机的”的部分,它强调了使用导致不够的算法的缺陷正在使用的桶。他警告说,算法会减少使用的存储桶数量,并在存储桶变得非常大时导致性能问题。当然,返回一个常量就属于这一类。
我想在我的所有数据类型类(仅在 C# 中,而不是数据库中)添加一个额外的 Guid 字段,专门用于并且仅在 GetHashCode 方法中使用。所以我想在这篇长篇介绍的最后,我的实际问题是哪个实现更好?总结一下:
总结:
在没有不可变字段的类中重写 Object.GetHashCode() 时,最好从 GetHashCode 方法返回一个常量,还是为每个类创建一个额外的 readonly 字段,仅用于GetHashCode 方法?如果我应该添加一个新字段,它应该是什么类型,我不应该将它包含在Equals 方法中?
虽然我很高兴收到任何人的回答,但我真的希望收到对此主题有深入了解的高级开发人员的回答。
【问题讨论】:
-
如果你手头有一本 Effective C# 的副本,如果你还没有读过的话,第 7 条就是关于这个的。
-
如果使用仅限于一个位置,一个简单的解决方法是将类型包装在另一个提供唯一不可变值的类中,可能是
Guid,并使用它来获取哈希码。我个人会尽量不要将Guid添加到仅用于字典的类型中。或者,您可以使用身份映射之类的东西来为基于分离的不可变 ID 的对象设置键(同样可能是Guid,因此效果相同)。或者,不要修改字典中的项目。将它们键入、删除、修改、重新添加。 -
很不清楚为什么重写这些方法是必要的。一个好的起点是完全删除 Equals 和 GetHashCode 覆盖,从 Object 继承的默认实现非常好,并保证了对象的唯一性。您永远不会从他们那里收到“重复密钥”错误。
-
好吧,它不必命名为 Equals() 是吗?您可以随意调用它,Equals() 仅在对 .NET Framework 代码很重要时才需要被覆盖。重要的是,WPF 不太关心 Equals() 返回值在项目绑定时的变化。
-
@HansPassant,如何删除
Equals和GetHashCode覆盖?在 MSDN 上的IEquatable<T>Interface 页面上,它说 它应该为可能存储在通用集合中的任何对象实现,然后在IEquatable<T>.EqualsMethod 页面上它说 如果你实现 Equals,您还应该覆盖 Object.Equals(Object) 和 GetHashCode 的基类实现,以便它们的行为与 IEquatable的行为一致。
标签: c# class overriding mutable gethashcode