C# Language
Is gelijk aan en GetHashCode
Zoeken…
Opmerkingen
Elke implementatie van Equals
moet voldoen aan de volgende vereisten:
Reflexief : een object moet zichzelf evenaren.
x.Equals(x)
geefttrue
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 alsy.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 geeftx.Equals(z)
true
.Consistent : als u een object meerdere keren met een ander vergelijkt, is het resultaat altijd hetzelfde.
Opeenvolgende aanroepen vanx.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)
retourneertfalse
.
Implementaties van GetHashCode
:
Geschikt voor
Equals
: Als twee objecten gelijk zijn (hetgeen betekent datEquals
rendement waar), danGetHashCode
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 eenList
,Dictionary
of vergelijkbaar.
- als ze dat niet doen (bijvoorbeeld omdat
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.