程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Effective C#原則10:明白GetHashCode()的缺陷(3)

Effective C#原則10:明白GetHashCode()的缺陷(3)

編輯:關於C語言

c1在某些地方會丟失散列映射。當 你把c1放在映射中時,散列值是由字符串“Acme Products”來保證 的。當你把客戶的名字修改為“Acme Software”後,散列值也發生 了改變。現在是由新的名字:“Acme Software”來保證的了。c1存 儲在一個由“Acme Products”決定的“桶”內,而它不 應該存在於由“Acme Software”決定的“桶”內。你將 會在你自己的集合中丟失這個客戶。丟失的原因就是散列值不是實例不變的。你 在對象存儲後,改變了正確的“桶”。

前面的情形只有當 Customer是引用類型時才出現。而當是值類型時,會有不同的錯誤行為,而且同 樣是帶來麻煩的。如果Customer是一個值類型,c1的一個拷貝會存在在散列映射 中。因為裝箱與拆箱很會拷貝數據。這並不是很可靠,當你在把一個值類型對象 添加到集合之後,你還可以修改值類型的成員數據。

唯一安置好規則2的 方法就是,定義一個散列函數,它依懶於對象的一些不變的屬性來返回散列值。 System.Object是通過不變的對象ID來遵守這一規則的。System.ValueType希望 你的類型的第一個字段不發生改變。除了把你的類型設計為恆定類型以外,你沒 有更好的方法了。當你准備定義一個在散列容器中當關鍵詞使用的值類型時,它 必須是一個恆定的類型。如果不遵守勸告,你的用戶就可以把你的類型做為一個 關鍵詞在散列表中使用,進而找到一個方法破壞散列表。 更正Customer類,你 可以修改它,使客戶名成為一個恆定的:

public class Customer
{
 private readonly string _name;
 private decimal _revenue;
 public Customer( string name ) :
   this ( name, 0 )
 {
 }
 public Customer( string name, decimal revenue )
 {
  _name = name;
   _revenue = revenue;
 }
 public string Name
 {
  get { return _name; }
 }
 // Change the name, returning a new object:
 public Customer ChangeName( string newName )
 {
  return new Customer( newName, _revenue );
 }
 public override int GetHashCode()
 {
   return _name.GetHashCode();
 }
}

使名字成為 恆定的類型後,你要怎樣才能修改一個客戶對象的名字呢:

Customer c1 = new Customer( "Acme Products" );
myHashMap.Add( c1,orders );
// Oops, the name is wrong:
Customer c2 = c1.ChangeName( "Acme Software" );
Order o = myHashMap[ c1 ] as Order;
myHashMap.Remove( c1 );
myHashMap.Add( c2, o );

你已經移除了原來的客戶, 修改了名字,然後添加一個新的客戶對象到散列表中。這看上去比原來的更麻煩 ,但它可以正確工作。先前的版本充許程序員寫一些不正確的代碼。通過強制使 用恆定屬性來生成散列值後,你就增加了正確的行為。你的用戶就不會出錯了。 是的,這個版本可以更好的工作。你迫使開發人員寫更多的代碼,但這是因為只 有這樣才能寫正確的代碼。請確保參與散列運算的數據成員是恆定的。

第三條規則是說,GetHashCode()應該對所有的輸入隨機的生成一個分布在整型 范圍內的值。這一需求依懶於你實際創建的類型。如果有一個神奇的公式存在, 那它應該在System.Object中實現了,並且這一原則(譯注:這裡說的是全文這一 原則)也將不存在了。一個通用而且成功的算法就是XOR(異或)運算,對一個類型 內的所有字段的散列再進行異或後返回。如果你的類型裡包含一些持久字段,計 算時應該排除它們。

GetHashCode()具有很特殊的要求:相等的對象必須 產生相等的散列值,並且散列值必須是對象不變的,並且是均衡的高效分布。所 有這些只有對恆定類型才能滿足(譯注:本文前面已經說過了,.Net框架中的 System.Object.GetHashCode()其實並不滿足均衡高效分布這一規則)。對於其它 類型,就交給默認的行為吧,知道它的缺點就行了。

返回教程目錄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved