C# Language
Lika och GetHashCode
Sök…
Anmärkningar
Varje implementering av Equals måste uppfylla följande krav:
Reflexivt : Ett objekt måste lika sig.
x.Equals(x)returnerartrue.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 somy.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))returnerartrue,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 avx.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)returnerarfalse.
Implementeringar av GetHashCode :
Kompatibel med
Equals: Om två objekt är lika (vilket innebär attEqualsreturnerar true), sedanGetHashCodemåste returnera samma värde för var och en av dem.Stort intervall : Om två objekt inte är lika (
Equalssä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
Equalsatt gälla om referenserna är desamma.Om instansen är en värdetyp, kommer
Equalsatt 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
Equalsutvärderar till true- om de inte gör det (t.ex. för att
GetHashCodereturnerar ett slumpmässigt antal), kan det hända att objekt inte finns i enList,Dictionaryeller liknande.
- om de inte gör det (t.ex. för att
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.