这是《Effective C#》中讲的唯一一个要避免写的方法。。。
GetHashCode()方法用于获取一个对象的哈希值,它通常是在基于哈希的容器中被用作键值,例如HashSet<T>和Dictionary<K, V>。
然而,问题在于,默认的GetHashCode()并不是很好用,更糟糕的是,有时甚至写不出一个较好的实现。
哈希值应遵守以下三个原则:
1. 如果两个对象相等,它们的哈希值必须相等。否则,哈希值就不能用于在哈希容器中查找值了。
2. 一个对象的哈希值必须在它的生存周期中不变。它确保对象在哈希容器中总是被放在正确的位置。
3. 哈希值必须在int范围随机地分布。它使得哈希容器有较高的效率。
现在用着三个标准来评判默认的哈希值。
所有引用类型在创建时会拥有一个唯一的对象键值。这个值对于应用程序的生存周期中的第一个对象是1,然后每创建一个对象,这个值就会递增。显然,它符合前两个原则,除非你重定义了相等性。但是,这个哈希值都集中在int的一个较小范围内,所以如果在哈希容器中使用默认的哈希值,性能会受到很大的影响。
ValueType重写了GetHashCode()方法。值类型依据对象的第一个字段来生成哈希值。和引用类型一样,除非你重定义了相等性,第一个原则是被遵守的。但是对于第二个原则,糟糕的事情发生了,如果修改了第一个字段,哈希值就会改变,这违反了第二个原则,所以,值类型的哈希值有可能是错误的。一个较好的解决方案是使对象“不可变”,这将在Item 20中被讨论。第三个原则则不一定,它取决于值类型的第一个字段。
下面看一个例子。
如果字段epoch被设置为当前日期(不包括时间),那么MyStruct对象的哈希值就很用可能相同,这会在使用哈希容器时造成性能损失。
下面是另一个例子。
在修改了c1的Name后,它的哈希值就改变了,myDic中加入的那个对象丢失了,因为myDic中记录的哈希值是加入时生成的。
下面是修改了以后的Customer类。
现在name字段是不可变的,修改它时实际上生成了一个新的对象。现在就不会造成上面的问题了。虽然的代码要长一些,但总比出错好。
对于第三条原则,还可以多做一些讨论。为了生成随机分布的数字,一个不错的算法是取所有字段的哈希值异或起来的值。当然,计算时应该排除可变的字段以符合第二条原则。