Suche…


Bemerkungen

Jede Implementierung von Equals muss die folgenden Anforderungen erfüllen:

  • Reflexiv : Ein Objekt muss sich selbst gleich sein.
    x.Equals(x) gibt true .

  • Symmetrisch : Es gibt keinen Unterschied, wenn ich x mit y oder y mit x vergleiche - das Ergebnis ist das gleiche.
    x.Equals(y) liefert den gleichen Wert wie y.Equals(x) .

  • Transitiv : Wenn ein Objekt einem anderen Objekt entspricht und dieses Objekt einem dritten Objekt entspricht, muss das erste Objekt dem dritten Objekt entsprechen.
    wenn (x.Equals(y) && y.Equals(z)) true zurückgibt, gibt x.Equals(z) true .

  • Konsistent : Wenn Sie ein Objekt mehrmals mit einem anderen Objekt vergleichen, ist das Ergebnis immer dasselbe.
    Aufeinanderfolgende x.Equals(y) von x.Equals(y) denselben Wert, solange die von x und y referenzierten Objekte nicht geändert werden.

  • Vergleich mit null : Kein Objekt ist gleich null .
    x.Equals(null) gibt false .

Implementierungen von GetHashCode :

  • Kompatibel mit Equals : Wenn zwei Objekte gleich sind (dh Equals gibt true zurück), muss GetHashCode für jedes GetHashCode denselben Wert zurückgeben.

  • Große Reichweite : Wenn zwei Objekte nicht gleich sind ( Equals sagt falsch), sollten die Hash-Codes mit hoher Wahrscheinlichkeit unterschiedlich sein. Perfektes Hashing ist oft nicht möglich, da eine begrenzte Anzahl von Werten zur Auswahl steht.

  • Günstig : Die Berechnung des Hash-Codes sollte in allen Fällen kostengünstig sein.

Siehe: Richtlinien zum Überladen von Equals () und Operator ==

Standard Gleiches Verhalten.

Equals wird in der Object Klasse selbst deklariert.

public virtual bool Equals(Object obj);

Standardmäßig hat Equals das folgende Verhalten:

  • Wenn es sich bei der Instanz um einen Referenztyp handelt, gibt Equals nur dann true zurück, wenn die Referenzen identisch sind.

  • Wenn es sich bei der Instanz um einen Werttyp handelt, gibt Equals nur dann true zurück, wenn Typ und Wert gleich sind.

  • string ist ein Sonderfall. Es verhält sich wie ein Werttyp.

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //areFooClassEqual: False
            Foo fooClass1 = new Foo("42");
            Foo fooClass2 = new Foo("42");
            bool areFooClassEqual = fooClass1.Equals(fooClass2);
            Console.WriteLine("fooClass1 and fooClass2 are equal: {0}", areFooClassEqual);
            //False

            //areFooIntEqual: True
            int fooInt1 = 42;
            int fooInt2 = 42;
            bool areFooIntEqual = fooInt1.Equals(fooInt2);
            Console.WriteLine("fooInt1 and fooInt2 are equal: {0}", areFooIntEqual);

            //areFooStringEqual: True
            string fooString1 = "42";
            string fooString2 = "42";
            bool areFooStringEqual = fooString1.Equals(fooString2);
            Console.WriteLine("fooString1 and fooString2 are equal: {0}", areFooStringEqual);
        }
    }

    public class Foo
    {
        public string Bar { get; }

        public Foo(string bar)
        {
            Bar = bar;
        }
    }
}

Eine gute GetHashCode-Überschreibung schreiben

GetHashCode hat erhebliche Auswirkungen auf die Leistung von Dictionary <> und HashTable.

Gute GetHashCode Methoden

  • sollte eine gleichmäßige Verteilung haben
    • Jede ganze Zahl sollte eine ungefähr gleiche Chance haben, für eine zufällige Instanz zurückzukehren
    • Wenn Ihre Methode für jede Instanz dieselbe Ganzzahl zurückgibt (z. B. die Konstante '999'), haben Sie eine schlechte Leistung
  • sollte schnell sein
    • Hierbei handelt es sich NICHT um kryptographische Hashes, bei denen Langsamkeit eine Funktion ist
    • Je langsamer Ihre Hash-Funktion ist, desto langsamer ist Ihr Wörterbuch
  • muss denselben HashCode in zwei Instanzen zurückgeben, die Equals als true auswertet
    • Wenn dies nicht der GetHashCode (z. B. weil GetHashCode eine Zufallszahl zurückgibt), werden Elemente möglicherweise nicht in einer List , einem Dictionary oder ähnlichem gefunden.

Eine gute Methode zum Implementieren von GetHashCode ist die Verwendung einer Primzahl als Startwert und das Hinzufügen der Hashcodes der Felder des Typs multipliziert mit anderen Primzahlen.

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 3049; // Start value (prime number).

        // Suitable nullity checks etc, of course :)
        hash = hash * 5039 + field1.GetHashCode();
        hash = hash * 883 + field2.GetHashCode();
        hash = hash * 9719 + field3.GetHashCode();
        return hash;
    }
}

Nur die Felder, die in der Equals Methode verwendet werden, sollten für die Hash-Funktion verwendet werden.

Wenn Sie denselben Typ für Dictionary / HashTables auf unterschiedliche Weise behandeln müssen, können Sie IEqualityComparer verwenden.

Überschreiben Sie Equals und GetHashCode für benutzerdefinierte Typen

Für eine Klasse Person wie:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); //false because it's reference Equals

Definieren Sie jedoch Equals und GetHashCode wie folgt:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }

    public override bool Equals(object obj)
    {
        var person = obj as Person;
        if(person == null) return false;
        return Name == person.Name && Age == person.Age; //the clothes are not important when comparing two persons
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode()*Age;
    }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); // result is true

GetHashCode LINQ verwenden, um verschiedene Abfragen zu Personen GetHashCode werden sowohl Equals als auch GetHashCode :

var persons = new List<Person>
{
     new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
     new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
     new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();//distinctPersons has Count = 2

Equals und GetHashCode in IEqualityComparator

Für den angegebenen Typ Person :

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

List<Person> persons = new List<Person>
{
    new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
    new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
    new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();// distinctPersons has Count = 3

Definieren von Equals und GetHashCode in einem IEqualityComparator :

public class PersonComparator : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name && x.Age == y.Age; //the clothes are not important when comparing two persons;
    }

    public int GetHashCode(Person obj) { return obj.Name.GetHashCode() * obj.Age; }
}

var distinctPersons = persons.Distinct(new PersonComparator()).ToList();// distinctPersons has Count = 2

Beachten Sie, dass für diese Abfrage zwei Objekte als gleich betrachtet wurden, wenn sowohl Equals als auch GetHashCode denselben Hashcode für die beiden Personen zurückgegeben haben.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow