首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Effective C# 原則 8:確保 0 對實值型別是有效狀態(三)

     編輯:關於C語言

Equals() 實作細節與陷阱

Equals() 決不應拋出例外:兩變數要麼相等要麼不等,無其他失敗餘地。所有失敗(null 參考、錯誤參數)直接回傳 false。深入討論各檢查用意:

  1. 檢查右側物件是否為 null(this 在 C# 方法中永不為 null)。
  2. 檢查兩物件參考是否相同(物件 ID 相同保證內容相同),高效捷徑。
  3. 檢查兩物件是否為相同資料型別。此步驟關鍵: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()。簡單明瞭。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved