緣由主要有如下幾點:安全
因此建議是:全部供外部使用的struct都實現相等性。ide
具體來講:函數
重寫object.Equals()方法,是避免了反射,由於System.ValueType裏面對object.Equals()方法的重寫實現以下:性能
這裏用到了反射。spa
而實現IEquatable<T>.Equals()接口方法,能夠避免裝箱,而且保證類型安全。對象
而實現==和!=,也就容許值類型使用該操做符了,寫起來更方便直觀,易於理解。並且這兩個操做符必須一同實現。blog
而重寫object.GetHashCode(),則是一個最佳實踐。接口
全部爲值類型重定義相等性,一共分4步,每步都是必須的。string
先看實例struct:hash
有構造函數,涉及到一個enum,並重寫了ToString()方法。
首先來實現IEquatable<T>接口。
(若是你使用resharper或者Rider,那麼實現該接口的時候它會自動把object的Equals和GetHashCode方法都重寫了,而且自動完成了有意義的代碼)
這裏面我對三個屬性進行了比較,使用了==操做符。其中==對於string來講就是比較值,而enum其實就是int,DateTime也是值類型,而且已經實現了相等性判斷的功能。
這個代碼是resharper生成的。
代碼很簡單,首先檢查是否爲null,而後檢查這個object是否是一個Person,這裏使用了 is 操做符,並把它轉型爲Person,賦給了一個叫作other的變量。最後調用的這個Equals()方法,是咱們上面寫的那個強類型的方法,由於other變量的類型是Person。
可是這個方法仍然涉及到裝箱操做,因此仍是IEquatable<T>的實現方法更快一些。
若是隻重寫了object.Equals()方法,而沒有重寫GetHasCode()方法,那麼resharper會有提示:
這個很簡單,直接調用強類型的Equals()方法便可,並且因爲Person是值類型,因此不用檢查null,值類型不會爲null。
若是隻實現了其中一個操做符,那麼會報錯的。
GetHashCode()這個方法會返回一個32位的哈希碼,它表明着對象內容的哈希值。
而類型裏擁有GetHashCode()方法(返回Hash)的真正目的是,容許該類型在內部使用HashTable的集合中能夠做爲Key,由於HashTable須要這些哈希碼。例如Dictionary<TK, TV>。
爲了讓HashTable能夠正確的工做,Hash碼有一個要求:若是兩個實例被認爲是相等的,那麼它們必須返回相同的hash碼。若是沒有實現這個要求,那麼你可能會發現這個類型做爲Dictionary的Key的時候,會有一些意想不到的結果。
因此若是重寫了object.Equals()方法,那麼就得重寫object.GetHashCode()方法。
看一下resharper自動實現的代碼:
這裏使用了unchecked,防止拋出溢出異常。
Name是引用類型,可能爲null,因此判斷一下。
而後其它兩個int和DateTime類型,微軟都作好了其GetHashCode()的實現。
這裏對它們進行異或操做。之因此使用397這個數,可能由於397是一個足夠大的質數,能夠致使溢出,並混淆各位,之因此使用質數,是由於用質數相乘會獲得比用其餘任意數相乘更均勻的結果。
結果如預期,OK。
在這幾個動做裏,實際的邏輯寫在了IEquatable<T>.Equals()方法裏,object.Equals()就是檢查類型而後調用IEquatable<T>.Equals(),== 和 != 操做符也是調用IEquatable<T>.Equals(),而GetHashCode()則使用了按位異或。
最後再重複一次,爲值類型定義相等性必定要實現上述4各步驟的5個方法。