Es ist lange her, aber ich denke trotzdem, dass es immer noch notwendig ist, eine korrekte Antwort auf diese Frage zu geben, einschließlich Erklärungen über das Warum und Wie. Die bisher beste Antwort ist die, die den MSDN ausführlich zitiert - versuchen Sie nicht, Ihre eigenen Regeln zu erstellen, die MS-Leute wussten, was sie taten.
Aber das Wichtigste zuerst: Die in der Frage zitierte Richtlinie ist falsch.
Nun das Warum - es gibt zwei von ihnen
Erster Grund : Wenn der Hashcode so berechnet wird, dass er sich während der Lebensdauer eines Objekts nicht ändert, selbst wenn sich das Objekt selbst ändert, würde dies den Gleichstellungsvertrag brechen.
Denken Sie daran: "Wenn zwei Objekte als gleich verglichen werden, muss die GetHashCode-Methode für jedes Objekt denselben Wert zurückgeben. Wenn jedoch zwei Objekte nicht als gleich verglichen werden, müssen die GetHashCode-Methoden für die beiden Objekte keine unterschiedlichen Werte zurückgeben."
Der zweite Satz wird oft falsch interpretiert als "Die einzige Regel ist, dass zum Zeitpunkt der Objekterstellung der Hashcode gleicher Objekte gleich sein muss". Ich weiß nicht genau warum, aber das ist auch hier die Essenz der meisten Antworten.
Stellen Sie sich zwei Objekte vor, die einen Namen enthalten, wobei der Name in der Methode equals verwendet wird: Gleicher Name -> Gleiches. Instanz A erstellen: Name = Joe Instanz B erstellen: Name = Peter
Hashcode A und Hashcode B werden höchstwahrscheinlich nicht identisch sein. Was würde jetzt passieren, wenn der Name von Instanz B in Joe geändert wird?
Gemäß der Richtlinie aus der Frage würde sich der Hashcode von B nicht ändern. Das Ergebnis wäre: A.Equals (B) ==> true, aber gleichzeitig: A.GetHashCode () == B.GetHashCode () ==> false.
Aber genau dieses Verhalten ist im Equals & Hashcode-Vertrag ausdrücklich verboten.
Zweitens warum : Während es natürlich wahr ist, dass Änderungen im Hashcode Hash-Listen und andere Objekte unter Verwendung des Hashcodes beschädigen können, ist das Gegenteil auch der Fall. Wenn Sie den Hashcode nicht ändern, erhalten Sie im schlimmsten Fall Hash-Listen, in denen viele verschiedene Objekte denselben Hashcode haben und sich daher im selben Hash-Bin befinden. Dies geschieht beispielsweise, wenn Objekte mit einem Standardwert initialisiert werden.
Nun zum Hows Nun, auf den ersten Blick scheint es einen Widerspruch zu geben - so oder so wird Code brechen. Kein Problem kommt jedoch von geändertem oder unverändertem Hashcode.
Die Ursache der Probleme ist im MSDN gut beschrieben:
Aus dem Hashtabelleneintrag von MSDN:
Schlüsselobjekte müssen unveränderlich sein, solange sie als Schlüssel in der Hashtabelle verwendet werden.
Dies bedeutet:
Jedes Objekt, das einen Hashwert erstellt, sollte den Hashwert ändern, wenn sich das Objekt ändert, aber es darf keine Änderungen an sich selbst zulassen, wenn es in einer Hashtabelle verwendet wird (oder natürlich in einem anderen Hash-verwendenden Objekt). .
Erstens, wie einfach es natürlich wäre, unveränderliche Objekte nur für die Verwendung in Hashtabellen zu entwerfen, die bei Bedarf als Kopien der normalen, veränderlichen Objekte erstellt werden. Innerhalb der unveränderlichen Objekte ist es offensichtlich in Ordnung, den Hashcode zwischenzuspeichern, da er unveränderlich ist.
Zweitens: Oder geben Sie dem Objekt ein "Sie sind jetzt gehasht" -Flag, stellen Sie sicher, dass alle Objektdaten privat sind, überprüfen Sie das Flag in allen Funktionen, die Objektdaten ändern können, und lösen Sie Ausnahmedaten aus, wenn keine Änderung zulässig ist (dh das Flag ist gesetzt ). Wenn Sie das Objekt jetzt in einem Hash-Bereich platzieren, stellen Sie sicher, dass Sie das Flag setzen und das Flag deaktivieren, wenn es nicht mehr benötigt wird. Aus Gründen der Benutzerfreundlichkeit würde ich empfehlen, das Flag innerhalb der "GetHashCode" -Methode automatisch zu setzen - auf diese Weise kann es nicht vergessen werden. Und der explizite Aufruf einer "ResetHashFlag" -Methode stellt sicher, dass der Programmierer überlegen muss, ob er die Objektdaten jetzt ändern darf oder nicht.
Ok, was auch gesagt werden sollte: Es gibt Fälle, in denen es möglich ist, Objekte mit veränderlichen Daten zu haben, in denen der Hashcode dennoch unverändert bleibt, wenn die Objektdaten geändert werden, ohne den Gleichheits- und Hashcode-Vertrag zu verletzen.
Dies setzt jedoch voraus, dass die Equals-Methode nicht auch auf den veränderlichen Daten basiert. Wenn ich also ein Objekt schreibe und eine GetHashCode-Methode erstelle, die einen Wert nur einmal berechnet und im Objekt speichert, um ihn bei späteren Aufrufen zurückzugeben, muss ich erneut eine Equals-Methode erstellen, die verwendet wird gespeicherte Werte für den Vergleich, so dass A.Equals (B) niemals auch von false zu true wechselt. Andernfalls würde der Vertrag gebrochen. Das Ergebnis davon ist normalerweise, dass die Equals-Methode keinen Sinn ergibt - es ist nicht die ursprüngliche Referenz gleich, aber es ist auch kein Wert gleich. Manchmal kann dies beabsichtigtes Verhalten sein (dh Kundendatensätze), normalerweise jedoch nicht.
Nehmen Sie also einfach das Ergebnis von GetHashCode vor, wenn sich die Objektdaten ändern und wenn die Verwendung des Objekts innerhalb von Hash mithilfe von Listen oder Objekten beabsichtigt (oder nur möglich) ist, machen Sie das Objekt entweder unveränderlich oder erstellen Sie ein schreibgeschütztes Flag, das für das verwendet werden soll Lebensdauer einer Hash-Liste, die das Objekt enthält.
(Übrigens: All dies ist nicht C # oder .NET-spezifisch - es liegt in der Natur aller Hashtable-Implementierungen oder allgemeiner einer indizierten Liste, dass sich die Identifizierungsdaten von Objekten niemals ändern sollten, solange sich das Objekt in der Liste befindet Wenn diese Regel verletzt wird, tritt unerwartetes und unvorhersehbares Verhalten auf. Irgendwo kann es Listenimplementierungen geben, die alle Elemente in der Liste überwachen und die Liste automatisch neu indizieren - aber die Leistung dieser Elemente wird sicherlich bestenfalls grausam sein.)