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;
}
}