這個程式設計練習展示了 C# 中相等的幾個性質:
Object.Equals()==和!=來測試是否相等。這比使用這些型別同樣提供的 Equals() 方法更道地。寫 Java 的開發者在處理字串時要留意:== 在 C# 中是依值比較,但在 Java 中卻是依參照比較,等他們回頭寫 Java 時別忘了這點。object的Equals()方法來比較。如果你做相等測試的目的是確認兩個物件是同一個執行個體,那麼直接沿用object的實作就夠了。如果不是,你就需要覆寫object.Equals()。Equals()。Equals()會包含對簡單型別成員使用==的相等測試,以及對參照型別成員遞迴呼叫Equals()的測試。class StatusBar
{
private readonly int width = 200, height = 20;
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return width == (other as StatusBar).width && height == (other as StatusBar).height;
}
}
class Window
{
private readonly string title = "Main";
private readonly StatusBar statusBar = new StatusBar();
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
}
}
object.ReferenceEquals()用來比較兩個物件,判斷它們是不是同一個執行個體。這能讓程式更清楚,而且在Equals()和==已被覆寫或重載的地方更是必要。var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true
public override bool Equals(object obj)之外,IDE 通常還會產生多載protected bool Equals(FacialFeatures other),供牽涉到繼承時使用。衍生類別可以呼叫基底類別的Equals(),再補上自己的測試。==運算子以及Equals()方法(參見operator-overloading練習),或者你只在意參照是否相等,否則不要使用==。structs 的相等測試會在structs練習中討論。tuples 的相等會在tuples練習中討論。protected bool Equals(T other)
{
return field1 == other.field1 && field2.Equals(other.field2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((T) obj);
}
SequenceEquals(),但沒有 LINQ 時,就只能逐一走訪兩個集合、個別比較項目。==和!=,請參見operator-overloading練習。Object.GetHashCode()object.GetHashCode() 會以 32 位元整數的形式回傳雜湊碼。雜湊碼會被字典和集合類別(例如 Dictionary<T> 和 HashSet<T>)用來有效率地存放和取出物件。以字典來說,雜湊是針對鍵進行的。Equals(),也應該覆寫GetHashCode()。Equals()和GetHashCode()之間有一種關係,必須成立才能讓字典、雜湊集合以及其他使用雜湊碼的類別正確運作。你應該好好實作這個方法,才不會為日後可能加入雜湊碼集合的維護者埋下陷阱。Equal() 回傳 true),那麼這兩個物件的GetHashCode()必須回傳相同的值。反過來則不成立,這個關係並不對稱。想像一個查找函式,它先根據雜湊碼找到某個「桶子」,再用相等測試挑出特定的項目。HashCode.Combine(),傳入相等測試所用的那些值(或其一部分)。請記住,你提供給Combine()的資訊越多,雜湊實作的效率通常就越好。public class Assessment
{
private int rating;
private Person boss;
public override int GetHashCode()
{
return HashCode.Combine(rating, boss);
}
}
為了稍微提升效能,特別是在物件隸屬於集合的情況下,你可以加入一個多載成員public bool Equals(T other)。
這可以省下參照型別的一些型別檢查,也能為實值型別省下一次裝箱,因為它們不需要為了當作public override bool Equals(object other)的引數而轉換成物件(裝箱)。
如果你在類別中加入IEquatable<T>介面,就會需要實作這個多載。除非你的程式碼含有接收IEquatable<T>型別物件的常式(而且大概會做一些與相等有關、且不管實作類別是什麼的有趣事情),否則實在沒有其他有力的理由要加入這個介面。
IEqualityComparer<T>如果你的類別可以用兩種不同方式唯一識別,例如一個Person類別同時有 SSID 和唯一的電子郵件地址,那麼 .NET 提供了一種方法,讓兩個不同的集合使用不同的雜湊碼和相等測試。兩者可以各自有IEqualityComparer<T>的不同實作,各帶自己的Equals()和GetHashCode()方法。你可以有一個以 SSID 為鍵的字典,以及另一個以電子郵件地址為鍵的字典。
當IEqualityComparer<T>派上用場時,你通常還是會覆寫項目類別上的Equals()和GetHashCode(),以避免在集合類別之外出問題。
使用IEqualityComparer<T>時要考慮的一點是,私有方法等等將無法供相等測試使用。
如果只會用到一個雜湊集合,那麼避免使用IEqualityComparer<T>、把相等和雜湊碼封裝在物件本身裡,可能會比較好。物件和雜湊集合之間這種關鍵相依性無法由編譯器強制檢查,本身就已經不理想了;但如果雜湊和比較的邏輯又放在別的地方,維護者就更容易在修改類別成員時忽略對集合造成的後果。
有個基本型別會讓不小心的程式設計者踢到鐵板,那就是測試浮點數值的相等。這在floating-point-numbers概念的 about.md 文件中有討論。
這篇文章說明了在相等與繼承方面必須做的一些決定。只有當你同時操作某個類別和衍生類別的執行個體,而且需要測試兩者是否相等時,這些才會與你有關。