5 Stimmen

Überschreiben von Equals und GetHash

Ich habe gelesen, dass, wenn Sie überschreiben Equals auf eine Klasse/Objekt Sie GetHashCode überschreiben müssen.

 public class Person : IEquatable<Person>
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person(int personId, string firstName, string lastName)
        {
            PersonId = personId;
            FirstName = firstName;
            LastName = lastName;

        }

        public bool Equals(Person obj)
        {
            Person p = obj as Person;

            if (ReferenceEquals(null, p)) 
                return false;
            if (ReferenceEquals(this, p)) 
                return true;

            return Equals(p.FirstName, FirstName) && 
                   Equals(p.LastName, LastName);
        }

    }

Nun ist folgendes gegeben:

 public static Dictionary<Person, Person> ObjDic= new Dictionary<Person, Person>();
 public static Dictionary<int, Person> PKDic = new Dictionary<int, Person>();

Wirkt sich das Überschreiben von GetHashCode nicht auf die beiden oben genannten Dictionarys aus? Was ich im Grunde frage ist, wie wird GetHashCode generiert? Wenn ich trotzdem nach einem Objekt in PKDic suche, kann ich es dann anhand des PK finden? Wenn ich GetHashCode außer Kraft setzen wollte, wie würde ich das tun?

4voto

SLaks Punkte 832502

Sie sollten immer Überschreiben Sie GetHashCode .

A Dictionary<int, Person> funktioniert ohne GetHashCode aber sobald Sie LINQ-Methoden wie Distinct o GroupBy wird es nicht mehr funktionieren.

Beachten Sie übrigens, dass Sie die Funktion nicht überschrieben haben. Equals entweder. Die IEquatable.Equals Methode ist nicht dasselbe wie die virtual bool Equals(object obj) geerbt von Object . Obwohl der Standard IEqualityComparer<T> wird die IEquatable<T> Schnittstelle, wenn die Klasse sie implementiert, sollten Sie trotzdem die Equals weil andere Codes das nicht können.

In Ihrem Fall sollten Sie die Funktion Equals y GetHashCode wie diese:

public override bool Equals(object obj) { return Equals(obj as Person); }
public override int GetHashCode() {
    return FirstName.GetHashCode() ^ LastName.GetHashCode();
}

4voto

Adam Robinson Punkte 176996

In Ihrem Szenario, nicht überschreiben GetHashCode auf Ihren Typ wirkt sich nur auf das erste Wörterbuch aus, da der Schlüssel für das Hashing verwendet wird, nicht der Wert.

Bei der Suche nach dem Vorhandensein eines Schlüssels wird die Dictionary<TKey,TValue> verwendet den Hash-Code, um herauszufinden, ob irgendwelche Schlüssel könnte gleich sein. Es ist wichtig zu wissen, dass ein Hash ein Wert ist, der bestimmen kann, ob zwei Dinge sein könnte gleich oder sehr wahrscheinlich sind gleich. Ein Hash kann streng genommen nicht feststellen, ob zwei Gegenstände sind gleich.

Zwei gleiche Objekte sind erforderlich um denselben Hash-Code zurückzugeben. Zwei ungleiche Objekte sind jedoch no erforderlich, um verschiedene Hash-Codes zurückzugeben. Mit anderen Worten: Wenn die Hash-Codes nicht übereinstimmen, ist garantiert, dass die Objekte nicht gleich sind. Wenn die Hash-Codes hacer übereinstimmen, dann werden die Objekte könnte gleich sein.

Aus diesem Grund ist die Dictionary ruft nur Equals auf zwei Objekte, wenn ihre Hash-Codes übereinstimmen.

Zur Frage, wie man "außer Kraft setzt GetHashCode ", das ist eine komplizierte Frage. Grundsätzlich sollte ein Hashing-Algorithmus ein Gleichgewicht zwischen einer gleichmäßigen Verteilung der Codes über die Wertemenge und einer geringen Kollisionsrate (eine Kollision liegt vor, wenn zwei nicht gleiche Objekte denselben Code erzeugen) herstellen. Dies ist einfach zu beschreiben und sehr schwierig zu erreichen. Es ist leicht, das eine oder das andere zu tun, aber schwer, beides in Einklang zu bringen.

Aus praktischer Sicht (d. h. ohne Rücksicht auf die Leistung) könnten Sie einfach XOR alle Zeichen des Vor- und Nachnamens (oder sogar deren jeweilige Hash-Codes, wie Joel vorschlägt) als Hash-Code. Dies führt zu einem geringen Grad an Kollisionen, aber nicht zu einer besonders gleichmäßigen Verteilung. Solange Sie nicht mit sehr großen Mengen oder sehr häufigen Suchvorgängen zu tun haben, wird das kein Problem sein.

3voto

Joel Coehoorn Punkte 377088

Ihre GetHashCode()- und Equals()-Methoden sollten wie folgt aussehen:

public int GetHashCode()
{
    return (FirstName.GetHashCode()+1) ^ (LastName.GetHashCode()+2);
}

public bool Equals(Object obj)
{
    Person p = obj as Person;

    if (p == null) 
        return false;

    return this.Firstname == p.FirstName && this.LastName == p.Lastname;
}

Die Regel lautet, dass GetHashCode() genau die Felder verwenden muss, die bei der Bestimmung der Gleichheit für die Methode .Equals() verwendet werden.

Was den Wörterbuchteil Ihrer Frage betrifft, so wird .GetHashCode() zur Bestimmung des Schlüssels in einem Wörterbuch verwendet. Dies hat jedoch für jedes der Wörterbücher in Ihrer Frage eine andere Auswirkung.

Das Wörterbuch mit dem int Schlüssel (vermutlich Ihre Personen-ID) wird den GetHashCode() für die Ganzzahl verwenden, während das andere Wörterbuch (ObjDic) den GetHashCode() von Ihrem Person-Objekt verwendet. Daher wird PKDic immer zwischen zwei Personen mit unterschiedlichen IDs unterscheiden, während ObjDic zwei Personen mit unterschiedlichen IDs, aber demselben Vor- und Nachnamen als denselben Datensatz behandeln kann.

1voto

Brian Gideon Punkte 46450

So würde ich es machen. Da es üblich ist, dass zwei verschiedene Personen genau denselben Namen haben, ist es sinnvoller, einen eindeutigen Bezeichner zu verwenden (den Sie bereits haben).

public class Person : IEquatable<Person>
{
  public override int GetHashCode()
  {
    return PersonId.GetHashCode();
  }

  public override bool Equals(object obj)
  {
    var that = obj as Person;
    if (that != null)
    {
      return Equals(that);
    }
    return false;
  }

  public bool Equals(Person that)
  {
    return this.PersonId == that.PersonId;
  }
}

Um Ihre konkrete Frage zu beantworten: Dies ist nur von Bedeutung, wenn Sie Folgendes verwenden Person als Schlüssel in einer IDictionary Sammlung. Zum Beispiel, Dictionary<Person, string> o SortedDictionary<Person, Foo> pero no Dictionary<int, Person> .

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