首先先談一下Equals()這個方法:測試
Equals()方法,來自於Object,是咱們常常須要重寫的方法。此方法的默認實現大概是這樣的:spa
1 public virtual bool Equals(object obj) 2 3 { 4 5 if(obj==null) return false; 6 7 if(GetType() != obj.GetType()) return false; 8 9 Return true; 10 11 }
由此能夠看出,默認的實現其實比較的是兩個對象的內存地址(==操做符默認比較內存地址)。值類型和string類型除外,由於全部值類型繼承於System.ValueType()(System.ValueType()一樣繼承於Object,可是System.ValueType()自己倒是引用類型),而System.ValueType()對Equals()和==操做符進行了重寫,是逐字節比較的。而string類型是比較特殊的引用類型,因此strIng在不少地方都是特殊處理的,此處就不作深究了。3d
說完Equals()後再來聊一聊GetHashCode()。code
其實GetHashCode()在操做值類型的時候也是被System.ValueType()重寫的。通過樓主測試的幾個經常使用值類型來看,值類型的GetHashCode()基本都是原值輸出(特指整數,Int32除外),真實性有待驗證。結果以下:對象
說完值類型,說一下引用類型,先看下面這張運行結果:blog
從上圖的結果能夠看出,雖然string是引用類型,可是隻要值同樣,返回的HashCode也是同樣的,這取決於它的特殊性。而咱們本身寫的類型Coordinates一樣的值但返回的HashCode卻不同,咱們能夠簡單的理解爲是coor1與coor2的內存地址不一樣,因此CLR認爲它們是不同的。繼承
Ps:在程序的生命週期中,相同的對象、變量返回的HashCode是相同的,而且是惟一的。可是絕對不容許作持久性存儲,程序一旦結束並從新啓動後,一樣的對象沒法得到上次程序運行時的HashCode。生命週期
瞭解了兩個方法後,開始今天的重點話題。內存
其實在上面的兩個對象中(coor1、coor2),coor1.Equals(coor2)的返回結果爲false(由於內存地址不一樣),若是咱們想讓它們的返回結果爲true的話,只能重寫Equals方法(以下圖)。string
重點來了,重寫完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。其結果就是,存儲的時候你可能任性的存,在取值的時候就是你哭着找不着娘了。
好了,說了這麼多你應該對這兩個方法有了從新的認識了吧。若是仍是不明白的話,用代碼實現一下,保準明白。