Sök…


Anmärkningar

Varje implementering av Equals måste uppfylla följande krav:

  • Reflexivt : Ett objekt måste lika sig.
    x.Equals(x) returnerar true .

  • Symmetrisk : Det finns ingen skillnad om jag jämför x till y eller y till x - resultatet är detsamma.
    x.Equals(y) returnerar samma värde som y.Equals(x) .

  • Transitiv : Om ett objekt är lika med ett annat objekt och det här är lika med ett tredje måste det första vara lika med det tredje.
    om (x.Equals(y) && y.Equals(z)) returnerar true , x.Equals(z) true .

  • Konsekvent : Om du jämför ett objekt med en annan flera gånger är resultatet alltid detsamma.
    På varandra följande invokationer av x.Equals(y) samma värde så länge de objekt som refereras av x och y inte ändras.

  • Jämförelse med noll : Inget objekt är lika med null .
    x.Equals(null) returnerar false .

Implementeringar av GetHashCode :

  • Kompatibel med Equals : Om två objekt är lika (vilket innebär att Equals returnerar true), sedan GetHashCode måste returnera samma värde för var och en av dem.

  • Stort intervall : Om två objekt inte är lika ( Equals säger falskt) bör det vara stor sannolikhet att deras hashkoder är distinkta. Perfekt hashing är ofta inte möjligt eftersom det finns ett begränsat antal värden att välja mellan.

  • Billigt : Det bör vara billigt att beräkna hashkoden i alla fall.

Se: Riktlinjer för överbelastning av lika () och operatör ==

Standard Lika beteende.

Equals är deklarerade i själva Object .

public virtual bool Equals(Object obj);

Som standard har Equals följande beteende:

  • Om instansen är en referenstyp, kommer Equals att gälla om referenserna är desamma.

  • Om instansen är en värdetyp, kommer Equals att gälla om typen och värdet är desamma.

  • string är ett speciellt fall. Det uppför sig som en värdetyp.

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

Att skriva en bra åsidosättande av GetHashCode

GetHashCode har stora resultateffekter på Dictionary <> och HashTable.

Bra GetHashCode metoder

  • bör ha en jämn fördelning
    • varje heltal bör ha en ungefär lika stor chans att återvända för en slumpmässig instans
    • Om din metod returnerar samma heltal (t.ex. konstanten '999') för varje instans har du dålig prestanda
  • borde vara snabb
    • Dessa är INTE kryptografiska hash, där långsamhet är en funktion
    • ju långsammare din hash-funktion, desto långsammare din ordbok
  • måste returnera samma HashCode i två fall som Equals utvärderar till true
    • om de inte gör det (t.ex. för att GetHashCode returnerar ett slumpmässigt antal), kan det hända att objekt inte finns i en List , Dictionary eller liknande.

En bra metod för att implementera GetHashCode är att använda ett primtal som ett startvärde och lägga till hashkoderna för fälten av typen multiplicerat med andra primtal till det:

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

Endast fälten som används i Equals metoden ska användas för hash-funktionen.

Om du har behov av att behandla samma typ på olika sätt för Dictionary / HashTables, kan du använda IEqualityComparer.

Överskridande Equals och GetHashCode på anpassade typer

För en Person som:

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

Men definiera Equals och GetHashCode enligt följande:

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

Om du använder LINQ för att göra olika frågor på personer kommer du att kontrollera både Equals och 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

Lika och GetHashCode i IEqualityComparator

För given 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

Men att definiera Equals och GetHashCode till en 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

Observera att för denna fråga har två objekt betraktats som lika om både Equals returnerade sanna och GetHashCode har returnerat samma hash-kod för de två personerna.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow