2009-06-07 10 views
8

Jedes Mal, wenn ich eine Datenklasse schreibe, verbringe ich normalerweise so viel Zeit damit, die IEquatable-Implementierung zu schreiben. IchEntspricht Methodenimplementierungshelfern (C#)

Die letzte Klasse schrieb, war so etwas wie:

public class Polygon 
{ 
    public Point[] Vertices { get; set; } 
} 

Implementierung IEquatable exaustive war. Sicherlich hilft C# 3.0/LINQ sehr, aber die Vertices können verschoben und/oder in umgekehrter Reihenfolge angeordnet werden, was der Equals-Methode eine Menge Komplexität hinzufügt. Nach vielen Komponententests und der entsprechenden Implementierung gab ich auf und änderte meine Anwendung, um nur Dreiecke zu akzeptieren, wobei die IEquatable-Implementierung nur 11 Unit-Tests benötigte, um vollständig abgedeckt zu werden.

Gibt es ein Werkzeug oder eine Technik, die bei der Implementierung von Equals und GetHashCode hilft?

+0

Sie scheinen nicht für Equals Codegenerierung suchen werden, aber für einen generischen Algorithmus Implementierer. Ich denke du hast kein Glück hier. – erikkallen

Antwort

8

Ich benutze ReSharper, um Gleichheitselemente zu erzeugen. Es implementiert optional IEquatable<T> sowie Operator überschreiben, wenn Sie das wollen (was Sie natürlich nie tun, aber es ist sowieso cool).

Die Implementierung von Equals enthält eine Überschreibung von Object.Equals(Object), sowie eine stark typisierte Variante (die unnötige Typprüfung vermeiden kann). Die weniger typisierte Version ruft die stark typisierte Version nach einer Typprüfung auf. Die stark typisierte Version führt eine Referenzgleichheitsprüfung durch (Object.ReferenceEquals(Object,Object)) und vergleicht dann die Werte aller Felder (nun, nur diejenigen, die Sie dem Generator mitteilen).

Was GetHashCode, eine Smart-Faktorisierung des GetHashCode Werte des Feldes kombiniert (unchecked mit Überlauf Ausnahmen zu vermeiden, wenn Sie die Option checked Compiler verwenden). Jeder der Feldwerte (abgesehen von dem ersten) wird vor der Kombination mit Primzahlen multipliziert. Sie können auch angeben, welche Felder niemals null sein würden, und es werden alle Nullprüfungen gelöscht.

Hier ist, was Sie für Ihre Polygon Klasse erhalten, indem ALT+Insert dann die Taste „generieren Equality Mitglieder“ Auswahl:

public class Polygon : IEquatable<Polygon> 
{ 
    public Point[] Vertices { get; set; } 

    public bool Equals(Polygon other) 
    { 
     if (ReferenceEquals(null, other)) return false; 
     if (ReferenceEquals(this, other)) return true; 
     return Equals(other.Vertices, Vertices); 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != typeof (Polygon)) return false; 
     return Equals((Polygon) obj); 
    } 

    public override int GetHashCode() 
    { 
     return (Vertices != null ? Vertices.GetHashCode() : 0); 
    } 
} 

Einige der Funktionen, die ich oben gesprochen nicht gelten, da es nur ein Feld ist . Beachten Sie auch, dass der Inhalt des Arrays nicht überprüft wurde.

Im Allgemeinen pumpt ReSharper in wenigen Sekunden eine Menge exzellenten Code aus. Und diese Eigenschaft ist ziemlich niedrig auf meiner Liste von Dingen, die ReSharper so ein erstaunliches Werkzeug macht.

+0

Ist nicht magisch, aber es hilft sicher! Danke –

+2

Das Equals (obj) override sieht für mich nicht wie ausgezeichneter Code aus. Wäre es nicht natürlicher, einfach zu sagen, dies zurückzugeben.Equals (obj als Polygon); – mquander

+0

Vielleicht einfacher Code zu lesen, ja. Aber die Leistungsmerkmale wären schlechter. Der JIT wird die erste Zeile auf einen einfachen Maschinencode-Befehl (cmp mit Null) reduzieren, der sehr schnell ausgeführt werden kann.Die zweite Zeile reduziert sich auf einen weiteren einfachen Maschinencode-Befehl (cmp-Obj-Zeiger), der auch sehr schnell ausgeführt werden kann. Die dritte Zeile führt eine Typprüfung durch (das JIT optimiert dies auf eine andere Zeigerüberprüfung basierend auf dem Typcode des Objekts), und nur wenn die obigen Überprüfungen fehlschlagen, wird schließlich tatsächlich Equals aufgerufen. –

2

Zum Vergleichen von zwei Arrays von Elementen verwende ich die SequenceEqual Erweiterungsmethode.

Wie für eine generische Equals und GetHashCode, gibt es eine Technik auf Serialisierung, die für Sie arbeiten könnte.

Using MemoryStream and BinaryFormatter for reuseable GetHashCode and DeepCopy functions

+0

Vielen Dank für das Hinzufügen zu meinem Toolset. –

+0

Ich konnte SequenceEqual in 3.5/System.Core.dll nicht finden, weißt du wo es ist? Ich verwende Red Gate's Reflector, um den Quellcode anzuzeigen. –

+0

Großer Artikel! Jetzt erkannte ich, dass das Schreiben der Equals-Implementierung dumm ist. Sie müssen lediglich GetHashCode implementieren und dann Equals verwenden. –