通常情況下引用類型的相等性是不應該被重定義/重寫的。 例如兩個引用類型的變數 x 和 y,如果這樣寫:if(x == y) {...},那麼大家都明白,這個比較的是引用的相等性。 但是有少數情況下,也可以為引用類型重寫相等性。 例如這個類: 這個類裡面只有兩個string類型的屬性和欄位,那麼對它的 ...
通常情況下引用類型的相等性是不應該被重定義/重寫的。
例如兩個引用類型的變數 x 和 y,如果這樣寫:if(x == y) {...},那麼大家都明白,這個比較的是引用的相等性。
但是有少數情況下,也可以為引用類型重寫相等性。
例如這個類:
這個類裡面只有兩個string類型的屬性和欄位,那麼對它的相等性來說,更合理的是去比較值,而不是引用。
還有一種情況,就是表示數學的引用類型。
例如有一個類表示矩陣 Matrix,那麼這樣寫 if(matrix1 == matrix2) {...} 更適合表示它們兩個的值相等。
上述的這兩個例子其實也不是十分的必要。所以想為引用類型重寫相等性的時候還是應該先想好,重寫後是否能夠更加的直觀,使理解便得更簡單了。
實際上如果想比較兩個應用類型裡面的值是否相等,你不必非得去重寫那些相等性的方法,你可以通過實現IEqualityComparer<T>介面來寫一個單獨的相等性比較器。但是這樣的話不能使用==操作符,需要這樣寫:if(eqComparer.Equals(x, y)) {...}
為引用類型重寫相等性
一個類:
首先重寫object.Equals()方法:
這個邏輯比較簡單,就是判斷null,引用和類型,然後再判斷各個屬性(欄位)的值是否相等。
然後還需要重寫object.GetHashCode()方法:
這個採用了Resharper生成的方法,以前說過,就不再介紹了。
最佳實踐還要求重寫C#的==操作符:
當然配套的!=也必須重寫。
在之前重寫值類型相等性的文章里,我還為值類型實現了IEquatable<T>介面,而對於引用類型來說,就沒有必要去實現該介面了,可以把相等性判斷邏輯放在object.Equals()方法里。
派生類
這是上面Citizen類的一個子類:
下麵我重寫object.Equals() 方法:
大部分邏輯都在base.Equals()方法里了,首先如果父類的Equals()方法返回false,那麼下麵也就不用做啥了。但是如果父類Equals()認為這兩個實例是相等的,這就意味著父類里所有的相等性檢查都通過了,然後我們仍然需要檢查派生類裡面的獨有欄位(屬性),而這個例子里只有一個欄位(屬性)。
然後別忘了實現GetHashCode()方法:
(resharper生成的代碼)
這個方法里使用了父類的GetHashCode()方法,把它按位異或IdCard的GetHashCode()的結果。
然後實現==和!=操作符:
好,現在我們來測試一下:
其結果如下:
這個結果還都是對值進行比較的,符合預期。
然後你可能以為這樣實現沒有問題了。。。。
陷阱
現在我在Citizen這個父類里修改一下==的實現,我想讓它更有效率:
然後我再執行和上面同樣的測試代碼,其結果輸入是: