1. 程式人生 > >【轉載】聊一聊C#的Equals()和GetHashCode()方法

【轉載】聊一聊C#的Equals()和GetHashCode()方法

table rule != tle 繼承 操作符 內存 png blog

首先先談一下Equals()這個方法:

Equals()方法,來自於Object,是我們經常需要重寫的方法。此方法的默認實現大概是這樣的:

public virtual bool Equals(object obj)

{

  if(obj==null) return false;

  if(GetType() != obj.GetType()) return false;

  Return true;

}

由此可以看出,默認的實現其實比較的是兩個對象的內存地址(==操作符默認比較內存地址)。值類型和string類型除外,因為所有值類型繼承於System.ValueType()(System.ValueType()同樣繼承於Object,但是System.ValueType()本身卻是引用類型),而System.ValueType()對Equals()和==操作符進行了重寫,是逐字節比較的。而string類型是比較特殊的引用類型,所以strIng在很多地方都是特殊處理的,此處就不做深究了。

Ps:按Jeffrey Richter的說法,在值類型使用Equals()時,因為Equals()使用了反射,在比較時會影響效率。

說完Equals()後再來聊一聊GetHashCode()。

其實GetHashCode()在操作值類型的時候也是被System.ValueType()重寫的。經過樓主測試的幾個常用值類型來看,值類型的GetHashCode()基本都是原值輸出(特指整數,Int32除外),真實性有待驗證。結果如下:

技術分享圖片

說完值類型,說一下引用類型,先看下面這張運行結果:

技術分享圖片

從上圖的結果可以看出,雖然string是引用類型,但是只要值一樣,返回的HashCode也是一樣的,這取決於它的特殊性。而我們自己寫的類型Coordinates同樣的值但返回的HashCode卻不一樣,我們可以簡單的理解為是coor1與coor2的內存地址不同,所以CLR認為它們是不一樣的。

Ps:在程序的生命周期中,相同的對象、變量返回的HashCode是相同的,並且是唯一的。但是絕對不允許做持久性存儲,程序一旦結束並重新啟動後,同樣的對象無法獲得上次程序運行時的HashCode。

了解了兩個方法後,開始今天的重點話題。

其實在上面的兩個對象中(coor1、coor2),coor1.Equals(coor2)的返回結果為false(因為內存地址不同),如果我們想讓它們的返回結果為true的話,只能重寫Equals方法(如下圖)。

技術分享圖片

重點來了,重寫完Equals以後,vs發出了警告,雖然程序猿從來都是無視警告的,但這個警告確實有必要了解一下,先來看下面這三段代碼。

代碼段一、二:

技術分享圖片

代碼段三:

技術分享圖片

看完這三段代碼,應該就理解為什麽要重寫Equal時有必要重寫GetHashCode了。

當然,如果你沒打算在代碼中使用Dictionary或HashTable就無所謂寫不寫了,換句話說,如果要把引用類型做為Dictionary或HashTable的key使用時,必須重寫這兩個方法。

原因:當我們把引用類型(string除外)做為Dictionary或HashTable的key時,有可能永遠無法根據Key獲得value的值,或者說兩個類型的HashCode永遠不會相等。就拿Dictionary來說,雖然我們存儲的時候是鍵值對,但是CLR會先把key轉成HashCode並且驗證Equals後再做存儲,根據key取值的時候也是把key轉換成HashCode並且驗證Equals後再取值,一定要註意驗證時HashCode和Equals的關系是並且(&&)的關系。也就是說,只要GetHashCode和Equlas中有一個方法沒有重寫,在驗證時沒有重寫的那個方法會調用基類的默認實現,而這兩個方法的默認實現都是根據內存地址判斷的,也就是說,其實一個方法的返回值永遠會是false。其結果就是,存儲的時候你可能任性的存,在取值的時候就是你哭著找不著娘了。

好了,說了這麽多你應該對這兩個方法有了重新的認識了吧。如果還是不明白的話,用代碼實現一下,保準明白。

【轉自】https://www.cnblogs.com/xiaochen-vip8/p/5506478.html

【轉載】聊一聊C#的Equals()和GetHashCode()方法