2009-02-19 71 views
25

譬如說POINT2類,和以下等於:如何最好地實現自定義類型的Equals?

public override bool Equals (object obj) 

public bool Equals (Point2 obj) 

這是在有效的C#3中所示的一個:

public override bool Equals (object obj) 
{ 
    // STEP 1: Check for null 
    if (obj == null) 
    { 
     return false; 
    } 

    // STEP 3: equivalent data types 
    if (this.GetType () != obj.GetType ()) 
    { 
     return false; 
    } 
    return Equals ((Point2) obj); 
} 

public bool Equals (Point2 obj) 
{ 
    // STEP 1: Check for null if nullable (e.g., a reference type) 
    if (obj == null) 
    { 
     return false; 
    } 
    // STEP 2: Check for ReferenceEquals if this is a reference type 
    if (ReferenceEquals (this, obj)) 
    { 
     return true; 
    } 
    // STEP 4: Possibly check for equivalent hash codes 
    if (this.GetHashCode () != obj.GetHashCode ()) 
    { 
     return false; 
    } 
    // STEP 5: Check base.Equals if base overrides Equals() 
    System.Diagnostics.Debug.Assert (
     base.GetType () != typeof (object)); 

    if (!base.Equals (obj)) 
    { 
     return false; 
    } 

    // STEP 6: Compare identifying fields for equality. 
    return ((this.X.Equals (obj.X)) && (this.Y.Equals (obj.Y))); 
} 

回答

18

有一整套guidelines on MSDN爲好。你應該仔細閱讀,這既棘手又重要。

有幾點我發現的最有用:

  • 值類型沒有身份,所以在struct Point你通常會做的部件的部件進行比較。

  • 引用類型通常具有標識,因此Equals測試通常在ReferenceEquals處停止(默認情況下,不需要覆蓋)。但也有例外情況,比如字符串和你的class Point2,其中一個對象沒有有用的身份,然後你重寫Equality成員來提供你自己的語義。在這種情況下,請按照指導方針先通過空值和其他類型的案例。

  • 還有很好的理由讓GethashCode()operator==同步。

28

在一個接受一個OBJ,如果類型的obj是Point2,調用類型特定的Equals。在類型特定的Equals中,確保所有成員具有相同的值。

public override bool Equals (object obj) 
{ 
    return Equals(obj as Point2); 
} 

public bool Equals (Point2 obj) 
{ 
    return obj != null && obj.X == this.X && obj.Y == this.Y ... 
    // Or whatever you think qualifies as the objects being equal. 
} 

您可能應該重寫GetHashCode以確保「相等」的對象具有相同的哈希碼。

+0

慣於如果`對象obj`是一個結構這炸燬? – row1 2013-01-08 13:29:16

2
  • 定義什麼是身份的意思..如果參考身份然後默認繼承等於將工作。
  • 如果您需要定義一個值類型(以及因此的值標識)。
  • 如果一個類的類型,但有價值的語義,然後定義。

可能你想覆蓋equals(對象),並定義equals(MyType的),因爲後者避免了拳擊。並重寫相等運算符。

.NET Framework Guidelines book(2nd ed)has more coverage。

0

烈丹尼爾大號說,

public override bool Equals(object obj) { 
    Point2 point = obj as Point2; // Point2? if Point2 is a struct 
    return point != null && this.Equals(point); 
} 

public bool Equals(Point2 point) { 
    ... 
} 
-1
public override bool Equals (object obj) 
{ 
    // struct 
    return obj is Point2 && Equals ( (Point2) value); 
    // class 
    //return Equals (obj as Point2); 
} 

public bool Equals (Point2 obj) 
7

我用過的技術對我有效如下。請注意,我只根據單個屬性(Id)比較兩個值進行比較。根據需要進行調整

using System; 
namespace MyNameSpace 
{ 
    public class DomainEntity 
    { 
     public virtual int Id { get; set; } 

     public override bool Equals(object other) 
     { 
      return Equals(other as DomainEntity); 
     } 

     public virtual bool Equals(DomainEntity other) 
     { 
      if (other == null) { return false; } 
      if (object.ReferenceEquals(this, other)) { return true; } 
      return this.Id == other.Id; 
     } 

     public override int GetHashCode() 
     { 
      return this.Id; 
     } 

     public static bool operator ==(DomainEntity item1, DomainEntity item2) 
     { 
      if (object.ReferenceEquals(item1, item2)) { return true; } 
      if ((object)item1 == null || (object)item2 == null) { return false; } 
      return item1.Id == item2.Id; 
     } 

     public static bool operator !=(DomainEntity item1, DomainEntity item2) 
     { 
      return !(item1 == item2); 
     } 
    } 
} 
0

幾種其他形式的輕微變體...

using System; 
... 
public override bool Equals (object obj) { 
    return Equals(obj as SomeClass); 
} 

public bool Equals (SomeClass someInstance) { 
    return Object.ReferenceEquals(this, someInstance) 
     || (!Object.ReferenceEquals(someInstance, null) 
      && this.Value == someInstance.Value); 
} 

public static bool operator ==(SomeClass lhs, SomeClass rhs) { 
    if(Object.ReferenceEquals(lhs, null)) { 
     return Object.ReferenceEquals(rhs, null); 
    } 
    return lhs.Equals(rhs); 
    //OR 
    return Object.ReferenceEquals(lhs, rhs) 
      || (!Object.ReferenceEquals(lhs, null) 
       && !Object.ReferenceEquals(rhs, null) 
       && lhs.Value == rhs.Value); 
} 

public static bool operator !=(SomeClass lhs, SomeClass rhs) { 
    return !(lhs == rhs); 
    // OR 
    return (Object.ReferenceEquals(lhs, null) || !lhs.Equals(rhs)) 
      && !Object.ReferenceEquals(lhs, rhs); 
} 

試圖找到一種方法使用equals,以避免重複的值比較邏輯...無任何多餘的測試(的ReferenceEquals調用瓦特/相同的參數),以實現運算符==或不必要的測試(這個不能在instance.Equals方法中爲null)並且沒有任何顯式條件(「ifs」)。比任何有用的東西更多的頭腦傳情。

最近我能想到的就是這一點,但它感覺像它應該不需要額外的方法:)

public bool Equals (SomeClass someInstance) { 
    return Object.ReferenceEquals(this, someInstance) 
     || (!Object.ReferenceEquals(someInstance, null) && EqualsNonNullInstance(someInstance); 
} 

public static bool operator ==(SomeClass lhs, SomeClass rhs) { 
    return Object.ReferenceEquals(lhs, rhs) 
    || (!Object.ReferenceEquals(lhs, null) && !Object.ReferenceEquals(rhs, null) && lhs.EqualsNonNullInstance(rhs)); 
} 

//super fragile method which returns logical non-sense 
protected virtual bool EqualsNonNullInstance (SomeClass someInstance) { 
    //In practice this would be a more complex method... 
    return this.Value == someInstance.Value; 
} 

記住多麼乏味,而且容易出錯,這一切都是可能的(我幾乎可以肯定上面的代碼中有一個錯誤...仍然很糟糕,因爲誰想要繼承類型只是以使得相等性檢查稍微簡單一點?),我想我會創建一些靜態方法來處理所有的空檢查並接受一個委託或要求和接口來執行值的比較(真正將Type更改爲Type的部分)。

如果我們可以將屬性添加到需要比較的字段/屬性/方法並讓編譯器/運行時處理所有的單調乏味,那就太好了。

還要確保GetHashCode()值對於哪些.Equals(對象)返回true或瘋狂垃圾可能發生的任何實例是相等的。

相關問題