Equals() 決不應拋出例外:兩變數要麼相等要麼不等,無其他失敗餘地。所有失敗(null 參考、錯誤參數)直接回傳 false。深入討論各檢查用意:
null(this 在 C# 方法中永不為 null)。this.GetType() 可能為衍生型別;比較確切型別無法保證右側參數可轉換為當前型別,會產生兩個細微 Bug。考慮簡單繼承階層:
public class B { public override bool Equals(object right) { ... B rightAsB = right as B; ... } }
public class D : B { public override bool Equals(object right) { ... D rightAsD = right as D; ... if (base.Equals(rightAsD) == false) return false; ... } }
測試:
B baseObject = new B();
D derivedObject = new D();
baseObject.Equals(derivedObject); // 可能回傳 true
derivedObject.Equals(baseObject); // 永遠回傳 false
預期兩者結果一致,但第二次比較永不為 true。基類 B 無法轉換為 D;但衍生類 D 可隱式轉為 B。若右側參數以 B 型別呈現的成員與左側相同,B.Equals() 判定相等,破壞對稱性。原因在於繼承關係中隱式上下轉換自動實現。
覆寫 Equals() 時,另一可行做法:呼叫基類 System.Object 或 System.ValueType 的比較方法(除非基類未實作)。前例中 D 呼叫 B 定義的 Equals(),但 B 未呼叫 base.Equals(),而是呼叫 System.Object 版本(僅比較參考相等),非預期行為。
Equals()。Equals()。Equals() 時遵循要點實作,並務必覆寫 GetHashCode()(原則 10)。建立實值型別時,總是重新定義 operator ==()。原因同執行個體 Equals():預設版本使用參考比較,效率遠不及自行實作。比較實值型別時,遵循原則 17 避免裝箱。注意:並非說覆寫執行個體 Equals() 就必須重載 ==,而是建立實值型別時才重載。.NET 框架類別仍期望參考型別的 == 保留參考語意。
C# 提供四種相等檢查方法,你只需考慮為其中兩個提供自訂實作:永不重載靜態 ReferenceEquals() 和靜態 Equals();為效能總是為實值型別提供執行個體 Equals() 和 ==;當參考型別相等與物件 ID 不同時,重載執行個體 Equals()。簡單明瞭。