2008-09-16 5 views
6

Die Implementierung von Equals() für Referenztypen ist schwieriger als es scheint. Meine aktuelle kanonische Implementierung geht so:Was ist die "beste" kanonische Implementierung von Equals() für Referenztypen?

public bool Equals(MyClass obj) 
{ 
    // If both refer to the same reference they are equal. 
    if(ReferenceEquals(obj, this)) 
    return true; 

    // If the other object is null they are not equal because in C# this cannot be null. 
    if(ReferenceEquals(obj, null)) 
    return false; 

    // Compare data to evaluate equality  
    return _data.Equals(obj._data); 
} 

public override bool Equals(object obj) 
{ 
    // If both refer to the same reference they are equal. 
    if(ReferenceEquals(obj, this)) 
    return true; 

    // If the other object is null or is of a different types the objects are not equal. 
    if(ReferenceEquals(obj, null) || obj.GetType() != GetType()) 
    return false; 

    // Use type-safe equality comparison 
    return Equals((MyClass)obj); 
} 

public override int GetHashCode() 
{ 
    // Use data's hash code as our hashcode 
    return _data.GetHashCode(); 
} 

Ich glaube, das gilt für alle Ecke (Vererbung und so weiter) Fälle, aber ich kann falsch sein. Was denkt ihr?

Antwort

4

Ich schrieb eine ziemlich umfassende Anleitung zu diesem eine Weile zurück. Für einen Anfang sollten Ihre equals-Implementierungen geteilt werden (d. H. Die Überladung, die ein Objekt nimmt, sollte zu dem passieren, der ein stark typisiertes Objekt aufnimmt). Darüber hinaus müssen Sie Dinge berücksichtigen, z. B. dass Ihr Objekt unveränderbar sein sollte, da GetHashCode überschrieben werden muss. Mehr Infos hier:

http://gregbeech.com/blog/implementing-object-equality-in-dotnet

+0

Meine Implementierung ist "geteilt". Wie Sie sehen, gibt es am Ende von Equals (Object) einen Aufruf von Equals (MyClass). Ich bin mir der Probleme mit Mutabilität und GetHashCode() bewusst; aber es ist eine wichtige Beobachtung. Es hat mich mehrmals gebissen. Schade, dass es keine "einfache" Möglichkeit gibt, nur "Klassen" zu deklarieren. –

+0

Link ist für jetzt gebrochen. – Restuta

0

In Bezug auf die Vererbung denke ich, dass Sie das OO-Paradigma einfach magisch wirken lassen sollten.

Insbesondere sollte die GetType() Prüfung entfernt werden, könnte es Polymorphismus auf der ganzen Linie brechen.

+1

Ich stimme nicht zu. Stellen Sie sich vor, wir haben Apple- und Orange-Klassen, die aus der Fruit-Klasse stammen. Wenn wir die GetType() - Überprüfung in der Implementierung von Equals in Fruit entfernen, könnten wir Äpfel mit Orangen vergleichen, wenn nicht jede abgeleitete Klasse Equals() ebenfalls richtig überschreibt. Es könnte sehr schnell unordentlich werden. –

0

Ich stimme Chakrit zu, Objekte unterschiedlicher Typen sollten semantisch gleich sein, wenn sie die gleichen Daten oder ID haben.

Persönlich benutze ich folgendes:

public override bool Equals(object obj) 
    { 
     var other = obj as MyClass; 
     if (other == null) return false; 

     return this.data.Equals(other.data); 
    } 
+0

Ich stimme dir nicht zu. Dies würde uns erlauben, Äpfel mit Orangen zu vergleichen. –

+0

Der Punkt ist, dass Sie vielleicht wollen, dass sie gleich sind. Die GetType-Prüfung lässt zum Beispiel die Verwendung von Proxies nicht zu; Sie haben zwei identische Objekte, eines davon in einem Container eines anderen Typs, aber Sie möchten sie als identisch identifizieren. –

+0

@SantiagoPalladino: Es macht oft Sinn, Gleichheitsvergleiche zu definieren, die eine losere Definition der Äquivalenz verwenden als "Object.Equals". Zum Beispiel definiert .net verschiedene String-Vergleiche, bei denen die Groß-/Kleinschreibung nicht berücksichtigt wird, die "HELLO" als äquivalent zu "Hello" oder "heLlO" betrachten, obwohl die Strings unterschiedlich sind. Solche Vergleiche könnten als gleiche Objekte wie verschiedene Typen akzeptieren. Ich mag die Vorstellung von Objekten, die "Object.Equals" überschreiben, sehr wenig, um eine so weit entfernte Äquivalenzrelation zu definieren, dass Objekte unterschiedlicher Typen ungleich miteinander verglichen werden können, es sei denn, es wird ausdrücklich ... – supercat

1

Bessere Hoffnung, dass this._data nicht null ist, wenn es auch ein Referenztyp.

public bool Equals(MyClass obj) 
{ 
    if (obj == null) { 
     return false; 
    } 
    else { 
     return (this._data != null && this._data.Equals(obj._data)) 
         || obj._data == null; 
    } 
} 

public override bool Equals(object obj) 
{ 
    if (obj == null || !(obj is MyClass)) { 
     return false; 
    } 
    else { 
     return this.Equals((MyClass)obj); 
    } 
} 

public override int GetHashCode() { 
    return this._data == null ? 0 : this._data.GetHashCode(); 
} 
+0

Sie haben Recht. Dies ist nur eine "kanonische" Implementierung, um die Konzepte hinter "Equals" zu beweisen. Meine "echte" Implementierung wird normalerweise mit Equals (a, b) anstelle von a.Equals (b) implementiert. –