Implementing GetHashCode correctly
我想听听社区关于如何为我的对象实现GetHashCode(或重写它)。我知道如果我重写equals方法,就需要这样做。我已经实现了很多次,有时只是调用基方法。我理解,如果对象包含相同的细节(成员),那么它应该等于该对象的另一个实例。从类成员中获取哈希代码的最佳方法是什么?
- 考虑将其关闭为stackoverflow.com/a/720282/93922的副本
- 在此之前,我建议您阅读并理解我关于它的文章:blogs.msdn.com/b/ericlippet/archive/2011/02/28/…
- 谢谢,我知道这可能是一个常见的问题,我应该多做些调查。谢谢你的帮助
- 如果不打算将类用作哈希表的键,则实际上不需要重写GetHashCode
假设你的班级是这样的:
1 2 3 4 5
| class Frob {
public string Foo { get; set; }
public int Bar { get; set; }
public double FooBar { get; set; }
} |
假设您定义equal s,那么如果它们的Foo和Bar相等,那么Frob的两个实例是相等的,但是FooBar并不重要。
然后你应该用Foo和Bar来定义GetHashCode。有一种方法是这样的:
1
| return this.Foo.GetHashCode() * 17 + this.Bar.GetHashCode(); |
基本上,您只需要合并定义相等的所有字段。一种方法是像我做的那样,不断地积累和乘以17。它很快,很简单,很正确,而且通常分布得很好。
- 我觉得这应该是一个问题本身,但为什么是23?
- 使2,1和1,2不同的主要因素。
- 可能会指出gethashcode只告诉您两个对象是否可以被视为相等。仍然存在哈希冲突的可能性。
- @geofftnz,或者更具体地说,gethashcode用于确定将对象存储在哈希中时要放入哪个bucket。如果gethashcode的差异性很差,它将创建一个长列表,当从hashtable返回项时,该列表将线性搜索,并且对于字典来说,它将是一个非常糟糕的键(读取:导致冲突错误)。
- stackoverflow.com/a/720282/93922对此进行了一点扩展。
- @Tetsujinnooni更进一步,在某些情况下,应该很难逆转散列,使攻击者更难将散列表转换为链接列表(复杂度方面),并杀死您的CPU。因此,我们为什么在2012年1月初运行修补服务器。
- 注意实现GetHashCode()和Equals(),因为这取决于可变数据,如这里所示。如果您将这样的对象放入一个基于哈希的字典中,然后对其进行变异,该字典将无法再正常工作。理想情况下,GetHashCode()和Equals()应该只依赖于不可变的数据。
- GetHashCode()实现的一个期望是它们必须在对象的生命周期内返回相同的值。基于可变状态的实现是无效的,会导致非常可怕的错误诊断。
- @悲剧:根本的要求是,GetHashCode必须反映Equals的行为。如果两个对象在同一时刻可能相等,但后来可能不相等,或者相反,则有必要确保对这些对象的引用不会暴露于可能意外更改其状态的任何内容。不幸的是,类型系统中没有任何东西可以表明这一点。
- @悲剧:.net没有工具来实际控制突变。要求不可变既不有用也不实际,因此-一般来说,您需要不可变的数据,而不是不可变的数据。当然,如果readonly成员是实际的——无论如何,使用它们——只是不要期望它在任何地方都是实际的。
- 我认为应该用unchecked关键字包装计算,比如:unchecked(this.Foo.GetHashCode() * 17 + this.Bar.GetHashCode())。总和可能大于int.maxvalue。
- @mortb-我的理解是,unchecked仅在编译时检测到溢出/下溢时适用(即表达式只包含常量)。包含非常量项的表达式默认为未选中。
- @昆特斯:我接受纠正。在分配Bar = int.MaxValue; Foo ="something"时,运行GetHashCode()函数,不使用unchecked关键字,代码将运行并产生一个负哈希代码,不会崩溃,因此不需要unchecked关键字