Tip12 重写Equals时也要重写GetHashCode
如果重写Equals方法但不重写GetHashCode方法,在使用如FCL中的Dictionary类时,可能隐含一些潜在的Bug。例如:
class Person
{
public string IDCode { get; private set; }
public Person(string idCode)
{
this.IDCode = idCode;
}
public override bool Equals(object obj)
{
return IDCode == (obj as Person).IDCode;
}
}
class PersonMoreInfo
{
public string SomeInfo { get; set; }
}
static void Main(string[] args)
{
AddAPerson();
Person mike = new Person("NB123");
//Console.WriteLine(mike.GetHashCode());
//得到的HashCode与AddAPerson中的HashCode不一样
Console.WriteLine(PersonValues.ContainsKey(mike));
}
static void AddAPerson()
{
Person mike = new Person("NB123");
PersonMoreInfo mikeValue = new PersonMoreInfo() { SomeInfo = "Mike's info" };
PersonValues.Add(mike, mikeValue);
//Console.WriteLine(mike.GetHashCode());
Console.WriteLine(PersonValues.ContainsKey(mike));
}
输出的结果是
True
False
理论上来说,AddAPerson中的mike和Main方法中的mike属于“值相等”,将该“值”作为key放入Dictionary中,再在某处根据mike将mikeValue取出来,这是理所当然的。针对同一个示例,这种结论是正确的,但针对不同的mike示例额,这种结果就有问题了。
原因是基于键值的集合(如Dictionary)是根据Key值的HashCode(调用类型的GetHashCode方法)来查找Value值。
注:GetHashCode方法应该基于只读的属性或特性生成HashCode。
GetHashCode方法存在一个问题:永远只返回一个整型类型,而整型类型的容量无法满足字符串的容量,例如:
string str1 = "NB0903100006";
string str2 = "NB0904140001";
//输出结果是一样的
Console.WriteLine(str1.GetHashCOde());
Console.WriteLine(str2.GetHashCOde());
Person类型对GetHashCode方法稍作改进后可以减少产生相同HashCode的几率。
Person类型的最终版本应该如下:
class Person : IEquatable<Person>
{
public string IDCode { get; private set; }
public Person(string idCode)
{
this.IDCode = idCode;
}
public override bool Equals(object obj)
{
return IDCode == (obj as Person).IDCode;
}
public override int GetHashCode()
{
return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode();
}
public bool Equals(Person other)
{
return IDCode == other.IDCode;
}
}