1670 Stimmen

Warum ist es wichtig, GetHashCode zu überschreiben, wenn die Methode Equals überschrieben wird?

Gegeben die folgende Klasse

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null) 
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

Ich habe die Equals Methode, weil Foo stellen eine Zeile für die Foo s Tisch. Welches ist die bevorzugte Methode zum Überschreiben der GetHashCode ?

Warum ist es wichtig, die GetHashCode ?

8voto

ILoveFortran Punkte 3281

Das ist nicht unbedingt wichtig; es hängt von der Größe Ihrer Sammlungen und Ihren Leistungsanforderungen ab und davon, ob Ihre Klasse in einer Bibliothek verwendet wird, wo Sie die Leistungsanforderungen möglicherweise nicht kennen. Ich weiß häufig, dass meine Sammlungen nicht sehr groß sind und meine Zeit wertvoller ist als ein paar Mikrosekunden Leistung, die ich durch die Erstellung eines perfekten Hash-Codes gewinne; daher verwende ich (um die lästige Warnung des Compilers loszuwerden) einfach:

   public override int GetHashCode()
   {
      return base.GetHashCode();
   }

(Natürlich könnte ich auch ein #pragma verwenden, um die Warnung abzuschalten, aber ich bevorzuge diesen Weg).

Wenn Sie sich in der Position befinden, die Sie tun die Leistung benötigen, dann gelten natürlich alle von anderen hier genannten Probleme. Das Wichtigste - sonst erhalten Sie falsche Ergebnisse, wenn Sie Elemente aus einem Hash-Set oder Wörterbuch abrufen: der Hash-Code darf sich während der Lebensdauer eines Objekts nicht ändern (genauer gesagt, während der Zeit, in der der Hash-Code benötigt wird, z. B. als Schlüssel in einem Wörterbuch): Das folgende Beispiel ist falsch, da Value öffentlich ist und daher während der Lebensdauer der Instanz von außen an der Klasse geändert werden kann, so dass Sie es nicht als Grundlage für den Hash-Code verwenden dürfen:

   class A
   {
      public int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //WRONG! Value is not constant during the instance's life time
      }
   }    

Wenn der Wert jedoch nicht geändert werden kann, ist es in Ordnung, ihn zu verwenden:

   class A
   {
      public readonly int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //OK  Value is read-only and can't be changed during the instance's life time
      }
   }

6voto

Hossein Ebrahimi Punkte 318

Ab dem C# 9 (.net 5 oder .net core 3.1), sollten Sie vielleicht Datensätze wie sie es tut Wertebasierte Gleichstellung .

5voto

Vasil Kosturski Punkte 131

Sie sollten immer sicherstellen, dass zwei Objekte denselben Hash-Code zurückgeben, wenn sie gleich sind, wie durch Equals() definiert. Wie in einigen anderen Kommentaren erwähnt, ist dies theoretisch nicht zwingend erforderlich, wenn das Objekt nie in einem Hash-basierten Container wie HashSet oder Dictionary verwendet wird. Ich würde Ihnen jedoch raten, diese Regel immer zu befolgen. Der Grund dafür ist einfach, dass es für jemanden viel zu einfach ist, eine Sammlung von einem Typ in einen anderen zu ändern, mit der guten Absicht, die Leistung zu verbessern oder einfach die Semantik des Codes besser zu vermitteln.

Nehmen wir zum Beispiel an, dass wir einige Objekte in einer Liste aufbewahren. Irgendwann später stellt jemand fest, dass ein HashSet eine viel bessere Alternative ist, zum Beispiel wegen der besseren Sucheigenschaften. Das ist der Zeitpunkt, an dem wir in Schwierigkeiten geraten können. List würde intern den Standard-Gleichheitsvergleicher für den Typ verwenden, was in Ihrem Fall Equals bedeutet, während HashSet GetHashCode() verwendet. Wenn sich die beiden unterschiedlich verhalten, wird dies auch Ihr Programm tun. Und denken Sie daran, dass solche Probleme nicht so einfach zu beheben sind.

Ich habe dieses Verhalten zusammen mit einigen anderen GetHashCode()-Fallen in einem ブログ記事 wo Sie weitere Beispiele und Erklärungen finden können.

1voto

user2855602 Punkte 37

Meines Erachtens gibt die ursprüngliche Funktion GetHashCode() die Speicheradresse des Objekts zurück, so dass es unerlässlich ist, sie zu überschreiben, wenn Sie zwei verschiedene Objekte vergleichen möchten.

BEARBEITET: Das war falsch, die ursprüngliche GetHashCode()-Methode kann die Gleichheit von 2 Werten nicht sicherstellen. Obwohl Objekte, die gleich sind, den gleichen Hash-Code zurückgeben.

-8voto

Guanxi Punkte 3091

Unten mit Reflexion scheint mir eine bessere Option unter Berücksichtigung der öffentlichen Eigenschaften, wie mit diesem Sie nicht haben, um über das Hinzufügen / Entfernen von Eigenschaften (obwohl nicht so häufig Szenario) zu kümmern. Dies fand ich auch besser durchführen.(Verglichene Zeit mit Diagonistics Stoppuhr).

    public int getHashCode()
    {
        PropertyInfo[] theProperties = this.GetType().GetProperties();
        int hash = 31;
        foreach (PropertyInfo info in theProperties)
        {
            if (info != null)
            {
                var value = info.GetValue(this,null);
                if(value != null)
                unchecked
                {
                    hash = 29 * hash ^ value.GetHashCode();
                }
            }
        }
        return hash;  
    }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X