Zoeken…


Opmerkingen

Elke implementatie van Equals moet voldoen aan de volgende vereisten:

  • Reflexief : een object moet zichzelf evenaren.
    x.Equals(x) geeft true terug.

  • Symmetrisch : er is geen verschil als ik x met y vergelijk of y met x - het resultaat is hetzelfde.
    x.Equals(y) retourneert dezelfde waarde als y.Equals(x) .

  • Overgankelijk : Als een object gelijk is aan een ander object en dit object gelijk is aan een derde, moet het eerste gelijk zijn aan het derde.
    als (x.Equals(y) && y.Equals(z)) true retourneert, dan geeft x.Equals(z) true .

  • Consistent : als u een object meerdere keren met een ander vergelijkt, is het resultaat altijd hetzelfde.
    Opeenvolgende aanroepen van x.Equals(y) retourneren dezelfde waarde zolang de objecten waarnaar wordt verwezen door x en y niet worden gewijzigd.

  • Vergelijking met null : Geen object is gelijk aan null .
    x.Equals(null) retourneert false .

Implementaties van GetHashCode :

  • Geschikt voor Equals : Als twee objecten gelijk zijn (hetgeen betekent dat Equals rendement waar), dan GetHashCode moet dezelfde waarde terug voor elk van hen.

  • Groot bereik : als twee objecten niet gelijk zijn ( Equals zegt false), moet de kans groot zijn dat hun hash-codes verschillend zijn. Perfecte hashing is vaak niet mogelijk omdat er een beperkt aantal waarden is om uit te kiezen.

  • Goedkoop : het moet goedkoop zijn om de hash-code in alle gevallen te berekenen.

Zie: Richtlijnen voor overbelasting is gelijk aan () en operator ==

Standaard is gelijk aan gedrag.

Equals wordt verklaard in de klasse Object zelf.

public virtual bool Equals(Object obj);

Standaard heeft Equals het volgende gedrag:

  • Als de instantie een referentietype is, retourneert Equals alleen true als de referenties hetzelfde zijn.

  • Als de instantie een waardetype is, retourneert Equals alleen true als het type en de waarde hetzelfde zijn.

  • string is een speciaal geval. Het gedraagt zich als een waardetype.

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;
        }
    }
}

Het schrijven van een goede GetHashCode-override

GetHashCode heeft grote prestatie-effecten op Dictionary <> en HashTable.

Goede GetHashCode methoden

  • moet een gelijkmatige verdeling hebben
    • elk geheel getal moet ongeveer dezelfde kans hebben om terug te keren voor een willekeurige instantie
    • als uw methode hetzelfde gehele getal (bijvoorbeeld de constante '999') voor elke instantie retourneert, hebt u slechte prestaties
  • zou snel moeten zijn
    • Dit zijn GEEN cryptografische hashes, waarbij traagheid een functie is
    • hoe langzamer je hash-functie, hoe langzamer je woordenboek
  • moet dezelfde HashCode retourneren op twee instanties die Equals aan true
    • als ze dat niet doen (bijvoorbeeld omdat GetHashCode een willekeurig getal retourneert), zijn items mogelijk niet gevonden in een List , Dictionary of vergelijkbaar.

Een goede methode om GetHashCode te implementeren is om één priemgetal als startwaarde te gebruiken en daar de hashcodes van de velden van het type vermenigvuldigd met andere priemgetallen aan toe te voegen:

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;
    }
}

Alleen de velden die worden gebruikt in de methode Equals moeten worden gebruikt voor de hash-functie.

Als u hetzelfde type op verschillende manieren voor Dictionary / HashTables wilt behandelen, kunt u IEqualityComparer gebruiken.

Negeren is gelijk aan en GetHashCode op aangepaste typen

Voor een klas Person zoals:

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

Maar het definiëren van Equals en GetHashCode als volgt:

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

Als u LINQ ook gebruikt om verschillende vragen over personen te maken, worden zowel Equals als 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

Is gelijk aan en GetHashCode in IEqualityComparator

Voor gegeven type 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

Maar het definiëren van Equals en GetHashCode in een 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

Merk op dat voor deze zoekopdracht twee objecten als gelijk worden beschouwd als zowel de waarde Equals true is geretourneerd als de GetHashCode dezelfde hash-code heeft geretourneerd voor de twee personen.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow