2010-12-12 4 views
10

Was ist Ihr Ansatz zum Schreiben von Gleichheitsprüfungen für die structs und classes Sie erstellen?C# Gleichheitsprüfung

1) Ist die "voll" Gleichheitsprüfung, dass viel von Standardcode (wie override Equals, override GetHashCode, generische Equals, operator==, operator!=)?

2) Spezifizieren Sie explizit, dass Ihre Klassen die IEquatable<T> Schnittstelle modellieren?

3) Habe ich das richtig verstanden, dass es keine tatsächliche Art und Weise ist Equals überschreibt automatisch anwenden, wenn ich so etwas wie a == b aufrufen, und ich habe immer sowohl die Equals und operator== Mitglieder zu implementieren?

Antwort

20

Du hast recht, das ist eine Menge Kesselblech Code, und Sie müssen alles separat implementieren.

würde ich empfehlen:

  • Wenn Sie Wert Gleichheit gehen überhaupt umzusetzen, außer Kraft setzt GetHashCode und Equals(object) - Überlastungen für == Erstellen und IEquatable<T> Umsetzung ohne dass
  • in sehr unerwartetem Verhalten führen könnte zu tun, würde ich immer IEquatable<T> implementieren, wenn Sie zwingende Equals(object) und GetHashCode
  • ich überlastete nur den Operator == seltene
  • Gleichheit für unversiegelte Klassen richtig Implementierung ist schwierig und kann überraschend/unerwünschte Ergebnisse noch produzieren. Wenn Sie Gleichheit für Typen in einer Hierarchie benötigen, implementieren Sie IEqualityComparer<T>, der den Vergleich ausdrückt, an dem Sie interessiert sind.
  • Gleichheit für veränderbare Typen ist normalerweise eine schlechte Idee, wie zwei Objekte gleich und dann ungleich später sein können ...Wenn ein Objekt (in einer gleichstellungsbeeinflussenden Weise) mutiert wird, nachdem es als Schlüssel in einer Hash-Tabelle verwendet wurde, können Sie es nicht erneut finden.
  • Einige der Boiler-Platte ist etwas anders für Strukturen ... aber wie Marc, schreibe ich sehr selten meine eigenen Strukturen.

Hier ist eine Beispielimplementierung:

using System; 

public sealed class Foo : IEquatable<Foo> 
{ 
    private readonly string name; 
    public string Name { get { return name; } } 

    private readonly int value; 
    public int Value { get { return value; } } 

    public Foo(string name, int value) 
    { 
     this.name = name; 
     this.value = value; 
    } 

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

    public override int GetHashCode() 
    { 
     int hash = 17; 
     hash = hash * 31 + (name == null ? 0 : name.GetHashCode()); 
     hash = hash * 31 + value; 
     return hash; 
    } 

    public bool Equals(Foo other) 
    { 
     if ((object) other == null) 
     { 
      return false; 
     } 
     return name == other.name && value == other.value; 
    } 

    public static bool operator ==(Foo left, Foo right) 
    { 
     return object.Equals(left, right); 
    } 

    public static bool operator !=(Foo left, Foo right) 
    { 
     return !(left == right); 
    } 
} 

Und ja, das ist ein verdammt viel von vorformulierten, sehr wenig von denen zwischen Implementierungen ändert :(

Die Implementierung von == ist leicht weniger effizient als es sein könnte, wie es durch Equals(object) aufrufen wird, die die dynamische Typprüfung tun müssen ... aber die Alternative ist noch mehr Boiler-Platte, wie folgt:

public static bool operator ==(Foo left, Foo right) 
{ 
    if ((object) left == (object) right) 
    { 
     return true; 
    } 

    // "right" being null is covered in left.Equals(right) 
    if ((object) left == null) 
    { 
     return false; 
    } 
    return left.Equals(right); 
} 
+1

^^ Ihr jeden Beitrag vermeiden ist ein C# Lernkapitel .. :) – Dienekes

+0

2 kleinere Vorschläge für den zweiten Codeblock: 1) Solltest du '(object) left == (object) right' nicht' '' '' 'generic' 'Equals'' verschieben? Das gibt also die Geschwindigkeit (natürlich hängt es davon ab, aber unter Annahme des schlimmsten Falls), die Referenzgleichheit sogar für die generische Equals-Methode zu überprüfen. 2) Sie brauchen nicht den zweiten Null-Check '(Objekt) rechts == null' in' == 'wie Sie das im generischen' Equals' tun. Siehe meinen Beitrag. – nawfal

+0

@Nawfal: Ich denke nicht, dass es viel Sinn macht, es im generischen 'Equals'-Fall zu tun - es wird sowieso schnell in den Fällen sein, in denen es * wahr ist, und für die Fälle, in denen es gilt * ist nicht * wahr, es fügt einen zusätzlichen Scheck ohne Nutzen hinzu. Was den Nullteil betrifft - das würde die dynamische Typprüfung erneut erfordern. Ja, Sie könnten für beide argumentieren - aber ich bin glücklich genug mit dem, was ich vor zwei Jahren geschrieben habe ... –

1

Sie müssen nur operator == für a == b implementieren.
Da ich meine Daten in Wörterbüchern manchmal mag, überschreibe ich GetHashCode.
Als nächstes implementiere ich Equals (als unerwähnten Standard ... dies liegt daran, dass es bei Verwendung von Generics keine Einschränkung für die Gleichheit gibt) und spezifiziere die Implementierung von IEquatable. Da ich dies tun werde, kann ich auch meine == und! = Implementierungen auf Equals verweisen. :)

6

Ich mache selten etwas Spezielles für Klassen; Für die meisten regulären Objekte funktioniert die Referenzgleichheit gut.

Ich schreibe noch seltener eine struct; aber da structs Werte darstellen, ist es in der Regel angemessen, die Gleichheit usw. bereitzustellen. Dies würde normalerweise alles beinhalten; Equals, ==,! = Und IEquatable<T> (da diese Boxen in Szenarien vermeidet EqualityComparer<T>.Default verwenden.

Die vorformulierten ist normalerweise nicht allzu problematisch, aber IIRC-Tools wie ReSharper kann hier helfen.

Ja, ist es ratsam, Equals und == synchron zu halten, und dies muss explizit erfolgen.

Verwandte Themen