2009-02-19 15 views
25

stosunku do kategorii punktem2 i dodaje się wynosi:Jak najlepiej wdrożyć równe dla niestandardowych typów?

public override bool Equals (object obj) 

public bool Equals (Point2 obj) 

to taka, która jest pokazana w celu skutecznego 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))); 
} 

Odpowiedz

18

Istnieje cały zestaw guidelines on MSDN również. Powinieneś je dobrze przeczytać, jest to zarówno trudne, jak i ważne.

Kilka punktów znalazłem najbardziej pomocne:

  • Typy wartości nie posiada tożsamość, więc w struct Point będzie zwykle zrobić członkiem przez członka porównania.

  • Typy referencyjne zwykle mają tożsamość, a zatem test równań zwykle zatrzymuje się w ReferenceEquals (domyślnie, bez potrzeby przesłonięcia). Ale są wyjątki, takie jak string i twoje, gdzie obiekt nie ma użytecznej tożsamości, a następnie przesłonisz członków równości, aby zapewnić sobie własną semantykę. W takiej sytuacji postępuj zgodnie ze wskazówkami, aby najpierw przejść przez przypadki zerowe i inne.

  • Istnieją również dobre powody, aby zsynchronizować również GethashCode() i operator==.

28

w jednym, która przyjmuje obj, jeżeli typ obiektu to Point2, wywołaj typ, który jest równy. Wewnątrz typu Equals należy upewnić się, że wszyscy członkowie mają tę samą wartość.

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. 
} 

Prawdopodobnie powinno przesłonić GetHashCode, jak również, aby upewnić się, że przedmioty, które są „równe” mają ten sam kod skrótu.

+0

Wont to wysadzić jeśli 'obiekt obj' jest struct? – row1

+0

@ row1 Nie. Struktura będzie pudełkowa, a struktura pudełkowa będzie miała wartość 'null', gdy oceniany jest operator' as'. Jeśli zdefiniowany typ jest strukturą, to musisz użyć rzutowania jawnego, zamiast używać 'as' (lub użyć zerowej wersji struktury). Usunąłbyś także kontrole zerowe, gdyby była to struktura nie-nullable. – Servy

+0

Woow bardzo prosta i spójna Wdrażanie, podoba mi się, thanx. –

2
  • Określ, co oznacza tożsamość. Jeśli tożsamość referencyjna będzie działać, domyślnie odziedziczone równe będą działać.
  • Jeśli musisz zdefiniować typ wartości (a tym samym tożsamość wartości).
  • Jeśli typ klasy, ale ma semantykę wartości, to definiują.

Prawdopodobnie chcesz zarówno przesłanianie Equals (object) i zdefiniować equals (MyType), ponieważ ten ostatni unika boks. I zastąpić operatora równości.

Książka .NET Framework Guidelines (edycja 2) ma większy zasięg.

0

Lie Daniel L powiedział

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

Technika, której użyłem, która zadziałała, jest następująca. Uwaga: Porównuję tylko na podstawie jednej właściwości (Id), a nie dwóch wartości. Dostosuj w razie potrzeby

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

Niewielkie warianty formularzy opublikowane przez kilka innych ...

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); 
} 

Starając się znaleźć sposób, aby realizować za pomocą operatora == równa uniknąć duplikowania porównania wartość logiki ... bez zbędnych testów (ReferenceEquals wzywa w/takich samych parametrach) lub niepotrzebnych badań (to nie może be null w metodzie instance.Equals) i bez żadnych wyraźnych warunków warunkowych ("ifs"). Więcej zwiastunu niż cokolwiek pożytecznego.

Najbliższy mogę myśleć to jest, ale to czuje jak powinno być możliwe bez dodatkowej metody :)

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; 
} 

pamiętając, jak żmudne i podatne na błędy to wszystko (jestem niemal pewny jest błąd w powyższym kodzie ... który wciąż jest do bani, ponieważ kto chce podklasować Type po prostu, aby kontrole równości były nieco prostsze?), myślę, że po prostu utworzę statyczne metody, które będą obsługiwać wszystkie kontrole zerowe i zaakceptuj delegata lub wymagaj i interfejs do przeprowadzenia porównania wartości (jedyna część, która naprawdę zmienia typ na typ).

Byłoby świetnie, gdybyśmy mogli po prostu dodać atrybuty do pól/właściwości/metod, które muszą być porównane i pozwolić kompilatorowi/środowisku wykonawczemu obsłużyć wszystkie nudy.

Upewnij się również, że wartości GetHashCode() są równe dla wszystkich wystąpień, w których może wystąpić wyrażenie .Equals (object) true lub crazy shit.

Powiązane problemy