2010-03-25 5 views
5

Ich habe eine komplexe Klasse in meinem C# -Projekt, auf der ich Gleichheitstests durchführen möchte. Es ist keine triviale Klasse; Es enthält eine Vielzahl von Skalareigenschaften sowie Verweise auf andere Objekte und Sammlungen (z. B. IDictionary). Für was es wert ist, ist meine Klasse versiegelt.Überschreiben der Object.Equals() - Instanzmethode in C#; jetzt Code Analyse/FxCop Warnung CA2218: "sollte GetHashCode neu definieren". Soll ich das unterdrücken?

Um eine Leistungsoptimierung an anderer Stelle in meinem System zu ermöglichen (eine Optimierung, die einen kostspieligen Netzwerk-Round-Trip vermeidet), muss ich Instanzen dieser Objekte für Gleichheit – anders als die integrierte Referenz vergleichen können Gleichheit – und so überschreibe ich die Object.Equals() - Instanzmethode. Aber jetzt, dass ich das getan Visual Studio 2008 die Code-Analyse aka FxCop, die ich standardmäßig aktiviert halten, hebt die folgende Warnung:

Warnung: CA2218: Microsoft.Usage: Da 'MySuperDuperClass' definiert Equals neu, es sollte auch GetHashCode neu definieren.

Ich denke, dass ich den Grund für diese Warnung verstehen:Wenn Ich werde solche Objekte als Schlüssel in einer Sammlung zu verwenden, ist der Hash-Code wichtig. d.h. see this question. Allerdings werde ich diese Objekte nicht als Schlüssel in einer Sammlung verwenden. Je.

Gefühl gerechtfertigt, die Warnung zu unterdrücken, ich code CA2218 in the MSDN documentation nachgeschlagen der vollständige Name der Warnung zu erhalten, so konnte ich ein SuppressMessage Attribut auf meine Klasse gelten wie folgt:

[SuppressMessage("Microsoft.Naming", 
     "CA2218:OverrideGetHashCodeOnOverridingEquals", 
     Justification="This class is not to be used as key in a hashtable.")] 

Während jedoch weiter zu lesen, ich bemerkte, wie folgt vor:

Wie Fix Verletzungen

Um einen Verstoß gegen diese Regel zu beheben, stellen Sie eine Implementierung von GetHashCode bereit. Für ein Objektpaar des gleichen Typs müssen Sie sicherstellen, dass die Implementierung denselben Wert zurückgibt, wenn Ihre Implementierung von Equals für das Paar den Wert true zurückgibt.

Wenn Warnungen zu unterdrücken

----->Sie eine Warnung dieser Regel nicht unterdrücken. [Pfeil & Hervorhebung von mir]

Also, ich würde gerne wissen: Warum sollte ich nicht unterdrücken diese Warnung als ich vorhatte? Garantiert mein Fall keine Unterdrückung? Ich möchte keine Implementierung von GetHashCode() für dieses Objekt kodieren, das niemals aufgerufen wird, da mein Objekt niemals der Schlüssel in einer Sammlung sein wird. Wenn ich pedantisch sein wollte, statt zu unterdrücken, wäre es vernünftiger für mich, GetHashCode() mit einer Implementierung zu überschreiben, die eine NotImplementedException auslöst?


Update: ich dieses Thema gerade nachgeschlagen wieder in Bill Wagner gutes Buch Effective C#, und er sagt, in "Punkt 10: Verstehen Sie die Fallstricke der GetHashCode()":

Wenn Sie‘ Er definiert einen Typ, der nicht als Schlüssel in einem Container verwendet wird, der nicht von Bedeutung ist. Die Typen , die Fenstersteuerelemente, Web Seitensteuerelemente oder Datenbankverbindungen darstellen, werden wahrscheinlich nicht als Schlüssel in einer Auflistung verwendet. In diesen Fällen tun Sie nichts. Alle Referenztypen haben einen Hash-Code, der korrekt ist, auch , wenn es sehr ineffizient ist. [...] In meisten Arten, die Sie erstellen, ist die beste Ansatz, die Existenz von GetHashCode() vollständig zu vermeiden.

... das ist, wo ich ursprünglich diese Idee hatte, dass ich nicht immer über GetHashCode() besorgt sein muss.

Antwort

5

Sie sollten es nicht unterdrücken. Schau dir an, wie deine equals-Methode implementiert ist. Ich bin sicher, es vergleicht ein oder mehrere Mitglieder der Klasse, um die Gleichheit zu bestimmen. Eines dieser Elemente reicht oft aus, um ein Objekt von einem anderen zu unterscheiden, und Sie können daher GetHashCode implementieren, indem Sie membername.GetHashCode(); zurückgeben.

+0

Es vergleicht tatsächlich fast alle Mitglieder der Klasse, um Gleichheit zu bestimmen. Die Klasse enthält Annahmen für eine Reihe von Berechnungen. Jede von ihnen ist anders, um die verschiedenen Objekte zu betrachten. Einige der Annahmen sind Arrays oder Sammlungen anderer Zahlen. Also wäre GetHashCode notwendigerweise so komplex, wenn es (richtig) implementiert wäre. –

+1

@Chris: Es muss nicht sein. Eine einfache, aber oft akzeptable Implementierung besteht darin, die GetHashCode-Werte der Mitglieder zu XOR zu machen. –

+1

Eine noch einfachere und akzeptable (aber normalerweise nicht optimale) Implementierung ist das Zurückgeben des GetHashCode() - Werts von nur einem Feld. Das passiert für eine Struktur. –

5

Meine $ 0,10 wert? Implementieren Sie GetHashCode.

So sehr du sagst, du wirst es nie brauchen, du wirst es dir vielleicht anders überlegen, oder jemand anders hat vielleicht andere Ideen, wie man den Code benutzt. Ein funktionierender GetHashCode ist nicht schwer zu machen und garantiert, dass es in der Zukunft keine Probleme geben wird.

+0

Wenn ich meine Klasse mit * "benutze dies nicht als Schlüssel in einer Sammlung" *, wäre es fair Spiel für mich, eine * NotImplementedException * für meine Klasse GetHashCode() zu werfen? Was sind die Probleme mit diesem Ansatz? –

+0

Sie könnten, aber was ist der wirkliche Vorteil? Eine Klasse mit einer speziellen Beschränkung, die Ausnahmen auslöst, wenn sie in ein Dictionary geworfen wird, ist eine Verbindlichkeit. Durch die Implementierung von GetHashCode entspricht es unseren vernünftigen Erwartungen. –

5

Sobald du es vergisst oder ein anderer Entwickler, der dies nicht weiß, benutzt dies, wird jemand einen schmerzhaften Fehler finden. Ich würde empfehlen, GetHashCode einfach zu implementieren, und dann müssen Sie sich nicht darum kümmern. Oder verwenden Sie Equals nicht für Ihren speziellen Gleichheitsvergleichsfall.

+0

Aber was ist mit der Idee in meinem letzten Absatz: Implementierung von GetHashCode(), um eine NotImplementedException zu werfen? Es wäre schwieriger für einen Entwickler, es fälschlicherweise als Schlüssel zu benutzen - es würde explodieren (durch Design). –

+1

Während es diesen Fehler lindern könnte, wäre es besser, die Semantik jetzt statt später zu definieren. – ermau

4

Die Methoden GetHashCode und Equals arbeiten zusammen, um eine wertebasierte Gleichheits-Semantik für Ihren Typ bereitzustellen - Sie sollten sie zusammen implementieren.

Weitere Informationen zu diesem Thema finden Sie in diesen Artikeln:

Shameless Stecker: Diese Artikel wurden von mir geschrieben.

+0

Wird GetHashCode() jemals vom Framework aufgerufen, ausgenommen, wenn ein Objekt als Schlüssel in einer Sammlung verwendet wird? –

+3

Ich bin mir nicht sicher, aber die wichtige Sache zu erinnern ist, dass zu irgendeinem Zeitpunkt in der Zukunft _could_ aufgerufen werden könnte und da die aktuelle Empfehlung ist, dass Sie die Methode implementieren, wäre es besser, dies zu tun. –

14

Wenn Sie reallio-Trulio absosmurfly positive sind, die Sie nie das Ding als Schlüssel zu einer Hash-Tabelle verwenden werden dann Ihr Vorschlag sinnvoll ist. Override GetHashCode; Lass es eine Ausnahme auslösen.

Beachten Sie, dass sich Hashtabellen an unwahrscheinlichen Orten verbergen. Viele LINQ-Sequenzoperatoren verwenden Hash-Tabellenimplementierungen intern, um die Dinge zu beschleunigen.Wenn Sie die Implementierung von GetHashCode ablehnen, lehnen Sie auch ab, Ihren Typ in einer Vielzahl von LINQ-Abfragen verwenden zu können. Ich mag es, Algorithmen zu erstellen, die Memoization für Geschwindigkeitssteigerungen verwenden; Memoizers verwenden normalerweise Hash-Tabellen. Sie lehnen daher auch die Möglichkeit ab, Methodenaufrufe zu protokollieren, die Ihren Typ als Parameter annehmen.

Alternativ, wenn Sie nicht so hart sein wollen: Override GetHashCode; Lass es immer Null zurückgeben. Das entspricht den semantischen Anforderungen von GetHashCode; dass zwei gleiche Objekte immer denselben Hash-Code haben. Wenn es jemals als ein Schlüssel in einem Wörterbuch verwendet wird, wird die Leistung schrecklich, aber Sie können mit diesem Problem fertig werden, wenn es entsteht, was Sie behaupten, dass es nie wird.

Alles, was gesagt hat: komm schon. Sie haben wahrscheinlich mehr Zeit damit verbracht, die Frage einzutippen, als sie richtig implementieren würde. Mach es einfach.

+0

Gute Antwort. Und re: * "Sie haben vermutlich mehr Zeit damit verbracht, die Frage einzutippen, als sie richtig implementieren würde." * ... Sie haben Recht * in diesem Fall *, aber ich habe tatsächlich eine * Reihe * von N Objekten die etwas wie Equals() benötigen, um meine Optimierung zu unterstützen. Versuchen, N * die Arbeit zu vermeiden. ;-) –

+4

Und danke besonders für den Hinweis, dass LINQ die Verwendung von GetHashCode() implizieren kann. Das ist besonders aufschlussreich. –

Verwandte Themen