Für den Gleichheitsvergleich eines Typs "T", überladen Sie diese Methoden:
int GetHashCode() //Overrides Object.GetHashCode
bool Equals(object other) //Overrides Object.Equals; would correspond to IEquatable, if such an interface existed
bool Equals(T other) //Implements IEquatable<T>; do this for each T you want to compare to
static bool operator ==(T x, T y)
static bool operator !=(T x, T y)
Ihr typspezifischer Vergleichscode sollte an einer Stelle erfolgen : die typsichere IEquatable<T>
Interface-Methode Equals(T other)
. Wenn Sie mit einem anderen Typ (T2) vergleichen, implementieren Sie IEquatable<T2>
und setzen Sie den Feldvergleichscode für diesen Typ in Equals(T2 other).
Alle überladenen Methoden und Operatoren sollten die Aufgabe des Gleichheitsvergleichs an die typsichere Hauptinstanzmethode Equals(T other) weiterleiten, so dass eine saubere Abhängigkeitshierarchie beibehalten wird und auf jeder Ebene strengere Garantien eingeführt werden, um Redundanz und unnötige Komplexität zu vermeiden.
bool Equals(object other)
{
if (other is T) //replicate this for each IEquatable<T2>, IEquatable<T3>, etc. you may implement
return Equals( (T)other) ); //forward to IEquatable<T> implementation
return false; //other is null or cannot be compared to this instance; therefore it is not equal
}
bool Equals(T other)
{
if ((object)other == null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return false;
//if ((object)other == this) //possible performance boost, ONLY if object instance is frequently compared to itself! otherwise it's just an extra useless check
//return true;
return field1.Equals( other.field1 ) &&
field2.Equals( other.field2 ); //compare type fields to determine equality
}
public static bool operator ==( T x, T y )
{
if ((object)x != null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return false; //x was null, y is not null
return true; //both null
}
public static bool operator !=( T x, T y )
{
if ((object)x != null)
return !x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return true; //x was null, y is not null
return false; //both null
}
Diskussion:
Die vorangehende Implementierung zentralisiert den typspezifischen (d.h. feldgleichen) Vergleich an das Ende der IEquatable<T>
Implementierung für den Typ. Die ==
y !=
Operatoren haben eine parallele, aber entgegengesetzte Implementierung. Ich bevorzuge dies gegenüber einem Verweis auf den anderen, so dass es einen zusätzlichen Methodenaufruf für den abhängigen Operator gibt. Wenn die !=
Operator wird einfach die ==
Operator zu verwenden, anstatt einen gleichwertigen Operator anzubieten, dann können Sie auch einfach !(obj1 == obj2)
und vermeiden Sie den zusätzlichen Methodenaufruf. Der Vergleich mit sich selbst wird vom Gleichheitsoperator weggelassen und die IEquatable<T>
Implementierungen, da es 1. in einigen Fällen zu unnötigem Overhead und/oder 2. zu inkonsistenter Leistung führen kann, je nachdem, wie oft eine Instanz mit sich selbst und anderen Instanzen verglichen wird.
Eine Alternative, die ich nicht mag, aber erwähnen sollte, ist die Umkehrung dieses Aufbaus, die Zentralisierung des typspezifischen Gleichheitscodes im Gleichheitsoperator und die Abhängigkeit der Gleichheitsmethoden von diesem. Man könnte dann die Abkürzung von ReferenceEquals(obj1,obj2)
um gleichzeitig auf Referenzgleichheit und Nullgleichheit zu prüfen, wie Philip in einem früheren Beitrag erwähnte, aber diese Idee ist irreführend. Es scheint, als würden Sie zwei Fliegen mit einer Klappe schlagen, aber Sie verursachen tatsächlich mehr Arbeit - nachdem Sie festgestellt haben, dass die Objekte weder beide null noch dieselbe Instanz sind, müssen Sie dann zusätzlich noch prüfen, ob jede Instanz null ist. In meiner Implementierung prüfen Sie genau einmal, ob eine einzelne Instanz null ist. Wenn die Instanzmethode Equals aufgerufen wird, ist bereits ausgeschlossen, dass das erste zu vergleichende Objekt null ist, so dass nur noch geprüft werden muss, ob das andere null ist. Nach höchstens zwei Vergleichen springen wir also direkt in die Feldprüfung, egal welche Methode wir verwenden ( Equals(object),Equals(T),==,!=
). Wie ich bereits erwähnt habe, könnten Sie, wenn Sie wirklich die meiste Zeit ein Objekt mit sich selbst vergleichen, diese Prüfung in der Equals-Methode hinzufügen, bevor Sie in die Feldvergleiche eintauchen. Der Grund für das Hinzufügen als Letztes ist, dass Sie die Fluss-/Abhängigkeitshierarchie beibehalten können, ohne auf jeder Ebene eine redundante/unnötige Prüfung einzuführen.