規則1表示,兩 個相等的對象(用操作符==定義的)必須返回相同的散列值。這一規則被大多數值 類型遵守著,但你可以破壞它, just as you could with for reference types. ValueType的操作符==()與其它成員一起來比較結構的第一個字段。這是 滿足第一條規則的。只要在任何時候你所重寫的==操作符用第一個字段,這將可 以正常工作。任何不以第一個字段斷定相等的結構將違反這一規則,從而破壞 GetHashCode().
第二條規則表示,散列值必須是實例不變的。這一規則 只有當結構的第一個成員字段是恆定類型時才被遵守。如果第一個字段的值發生 了改變,那麼散列值也會發生改變。這就破壞了這規則。是的,如果你的結構的 實例對象在它的生存期內改變了結構的第一個字段,那麼GetHashCode()就破壞 了這一規則。這也就是另一個原因,你最好的原則就是把值類型設置為恆定類型 (參見原則7)。
第三個規則依懶於第一個字段以及是如何使用它的。如果 你的第一個字段能保證產生一個在整型范圍上的隨機分布,並且第一個字段的分 布能復蓋結構的所有其它值,那麼這個結構就很好的保證了一個均衡的分布(譯 注:就是說結構的第一個字段可以唯一的決定一個實例)。然而,如果第一個字 段經常具有相同的值,那麼這一規則也會被破壞。考慮對前面的結構做一個小的 修改:
public struct MyStruct
{
private DateTime _epoch;
private string _msg;
private int _id;
}
如果_epoch字段設置的是當前日期(不包含時間) ,所有在同一給定日期裡創建的對象具有相同的散列值。這防礙了在所有散列值 中進行均衡的分布。
概括一個默認的行為,Object.GetHashCode()可以 正確的在引用類型上工作,盡管它不是必須保證一個高效的分布。(如果你有一 個對Object的==操作符的重載,你會破壞GetHashCode())。 ValueType.GetHashCode()僅在你的結構的第一個字段是只讀的時候才能正確工 作。而當你的結構的第一個字段的值,復蓋了它所能接受的輸入的有意義的子集 時,ValueType.GetHashCode()就可以保證一個高效的散列值。
如果你准 備創建一個更好的散列值,你須要為你的類型建立一些約束。再次驗證上面的三 條規則,現在我們來實現一個可工作的GetHashCode()。
首先,如果兩個 對象相等,就是由操作符==所定義的,它們必須返回同樣的散列值。類型的任何 承擔散列值的屬性或者數據值也必須參與相等比較。顯然,這就意味著同樣用於 相等比較的的屬性也用於散列值的生成。然而很可能所有與相等比較的屬性,並 不用於散列值的計算。System.ValueType的默認行為就是這樣的,也就是說它經 常違反規則3。同樣的數據元素應該參同時參與兩個運算(比較和散列)。
第二條規則就是GetHashCode()返回的值必須是實例不變的。想象你已經了一個 引用類型,Customer:
public class Customer
{
private string _name;
private decimal _revenue;
public Customer( string name )
{
_name = name;
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public override int GetHashCode()
{
return _name.GetHashCode();
}
}
假設你運行下面的代碼片斷:
Customer c1 = new Customer( "Acme Products" );
myHashMap.Add( c1, orders );
// Oops, the name is wrong:
c1.Name = "Acme Software";