国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

編寫高質量代碼改善C#程序的157個建議——建議12:重寫Equals時也要重寫GetHashCode

2019-11-14 14:10:15
字體:
來源:轉載
供稿:網友

建議12: 重寫Equals時也要重寫GetHashCode

除非考慮到自定義類型會被用作基于散列的集合的鍵值;否則,不建議重寫Equals方法,因為這會帶來一系列的問題。

如果編譯上一個建議中的Person這個類型,編譯器會提示這樣一個信息:

“重寫 Object.Equals(object o)但不重寫 Object.GetHashCode()”

如果重寫Equals方法的時候不重寫GetHashCode方法,在使用如FCL中的Dictionary類時,可能隱含一些潛在的Bug。還是針對上一個建議中的Person進行編碼,代碼如下所示:

    static Dictionary<Person, PersonMoreInfo> PersonValues = new Dictionary<Person, PersonMoreInfo>();      static void Main(string[] args)      {          AddAPerson();          Person mike = new Person("NB123");          //Console.WriteLine(mike.GetHashCode());          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 

理論上來說,在上一個建議中我們已經重寫了Person的Equals方法;也就是說,在AddAPerson方法中的mike和Main方法中的 mike屬于“值相等”。于是,將該“值”作為key放入Dictionary中,再在某處根據mike將mikeValue取出來,這會是理所當然的事 情。可是,從上面的代碼段中我們發現,針對同一個示例,這種結論是正確的,若是針對不同的mike示例,這種結果就有問題了。

基于鍵值的集合(如上面的Dictionary)會根據Key值來查找Value值。CLR內部會優化這種查找,實際上,最終是根據Key值的 HashCode來查找Value值。代碼運行的時候,CLR首先會調用Person類型的GetHashCode,由于發現Person沒有實現 GetHashCode,所以CLR最終會調用Object的GetHashCode方法。將上面代碼中的兩行注釋代碼去掉,運行程序得到輸出,我們會發 現,Main方法和AddAPerson方法中的兩個mike的HashCode是不同的。這里需要解釋為什么兩者實際對應調用的 Object.GetHashCode會不相同。

Object為所有的CLR類型都提供了GetHashCode的默認實現。每new一個對象,CLR都會為該對象生成一個固定的整型值,該整型值 在對象的生存周期內不會改變,而該對象默認的GetHashCode實現就是對該整型值求HashCode。所以,在上面代碼中,兩個mike對象雖然屬 性值都一致,但是它們默認實現的HashCode不一致,這就導致Dictionary中出現異常的行為。若要修正該問題,就必須重寫 GetHashCode方法。Person類的一個簡單的重寫可以是如下的形式:

    public override int GetHashCode()      {          return this.IDCode.GetHashCode();      } 

 

此時再運行本條建議開始時代碼的輸出,就會發現兩者的HashCode是一致的,而Dictionary也會找到相應的鍵值,輸出:True。

細心的讀者可能已經發現一個問題,Person類的IDCode屬性是一個只讀屬性。從語法特性本身來講,可以將IDCode設置為可寫;然而從現 實的角度考慮,一個“人”一旦踏入社會,其IDCode就不應該改變,如果要改變,就相當于是另外一個人了。所以,我們應該只實現該IDCode的只讀屬 性。同理,GetHashCode方法也應該基于那些只讀的屬性或特性生成HashCode。

GetHashCode方法還存在另外一個問題,它永遠只返回一個整型類型,而整型類型的容量顯然無法滿足字符串的容量,以下的例子就能產生兩個同樣的HashCode。

    string str1 = "NB0903100006";      string str2 = "NB0904140001";      Console.WriteLine(str1.GetHashCode());      Console.WriteLine(str2.GetHashCode()); 

 

為了減少兩個不同類型之間根據字符串產生相同的HashCode的幾率,一個稍作改進版本的GetHashCode方法如下:

    public override int GetHashCode()      {          return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode();      } 

 

注意 重寫Equals方法的同時,也應該實現一個類型安全的接口IEquatable,所以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;          }      }

 

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 临西县| 上蔡县| 大厂| 青田县| 涞水县| 沁源县| 云安县| 开鲁县| 祁门县| 泸溪县| 合水县| 嘉黎县| 汝阳县| 文安县| 手机| 临武县| 岳阳市| 马尔康县| 玉溪市| 东丰县| 辉县市| 安图县| 罗田县| 专栏| 工布江达县| 金溪县| 鹤峰县| 明水县| 油尖旺区| 明星| 马鞍山市| 鄯善县| 四子王旗| 西华县| 阳山县| 济宁市| 吉木乃县| 运城市| 陆河县| 女性| 英德市|