Für den Gleichheitsvergleich eines Typs "T", überladen Sie diese Methoden:
int GetHashCode()
bool Equals(object other)
bool Equals(T other)
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)
return Equals( (T)other) );
return false;
}
bool Equals(T other)
{
if ((object)other == null)
return false;
return field1.Equals( other.field1 ) &&
field2.Equals( other.field2 );
}
public static bool operator ==( T x, T y )
{
if ((object)x != null)
return x.Equals( y );
if ((object)y != null)
return false;
return true;
}
public static bool operator !=( T x, T y )
{
if ((object)x != null)
return !x.Equals( y );
if ((object)y != null)
return true;
return false;
}
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.