GetHashCode-metodin ylikirjoittaminen
Pitäkää yhtäsuuruus ja hajautus yhdenmukaisina.
GetHashCode-metodin ylikirjoittaminen on ilmainen C# Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu C# Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. C# Academy-kurssilla on yhteensä 4 oppituntia.
Miksi GetHashCode on tärkeä
Hajautukseen perustuvat kokoelmat, kuten Dictionary ja HashSet, käyttävät GetHashCode-metodia alkioiden sijoittamiseen lokeroihin. Jos korvaat Equals-metodin mutta et GetHashCode-metodia, nämä kokoelmat eivät välttämättä löydä samanarvoisia alkioita.
Kultainen sääntö
Sopimus on yksinkertainen: jos kaksi oliota ovat yhtäsuuria, niiden on palautettava sama hajautuskoodi. Käänteistä ehtoa ei vaadita: eri oliot voivat tuottaa saman hajautuskoodin eli törmätä.
using System;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public override bool Equals(object obj) => obj is Point p && p.X == X && p.Y == Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var a = new Point(1, 2);
var b = new Point(1, 2);
Console.WriteLine(a.Equals(b));
Console.WriteLine(a.GetHashCode() == b.GetHashCode());
}
}Käytä HashCode.Combine-metodia
HashCode.Combine-apumetodi sekoittaa useiden kenttien arvot tasaisesti jakautuvaksi hajautusarvoksi. Se on suositeltu moderni tapa toteuttaa GetHashCode.
using System;
public struct Color
{
public int R, G, B;
public Color(int r, int g, int b) { R = r; G = g; B = b; }
public override bool Equals(object obj) => obj is Color c && c.R == R && c.G == G && c.B == B;
public override int GetHashCode() => HashCode.Combine(R, G, B);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Color(255, 0, 0).GetHashCode() == new Color(255, 0, 0).GetHashCode());
}
}Toteuta yhtäsuuruus ja hajautus yhdessä
Korvaa aina Equals- ja GetHashCode-metodit yhdessä ja käytä molemmissa samoja kenttiä. Eri kenttien käyttäminen saa samanarvoiset oliot tuottamaan erilaiset hajautusarvot.
using System;
public class Book
{
public string Title;
public int Year;
public Book(string title, int year) { Title = title; Year = year; }
public override bool Equals(object obj)
=> obj is Book b && b.Title == Title && b.Year == Year;
public override int GetHashCode() => HashCode.Combine(Title, Year);
}
public class Program
{
public static void Main()
{
var a = new Book("C#", 2020);
var b = new Book("C#", 2020);
Console.WriteLine(a.GetHashCode() == b.GetHashCode());
}
}Miksi Dictionary tarvitsee sitä
Dictionary laskee ensin avaimen hajautusarvon löytääkseen lokeron ja käyttää sitten Equals-metodia kyseisen lokeron sisällä. Virheellinen hajautusarvo ohjaa haun väärään lokeroon, jolloin avainta ei koskaan löydy.
using System;
using System.Collections.Generic;
public struct Coord
{
public int X, Y;
public Coord(int x, int y) { X = x; Y = y; }
public override bool Equals(object obj) => obj is Coord c && c.X == X && c.Y == Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var map = new Dictionary<Coord, string> { [new Coord(2, 3)] = "hit" };
Console.WriteLine(map[new Coord(2, 3)]);
}
}Muuttumattomien kenttien käyttäminen hajautuksessa
Hajautuskoodin pitäisi säilyä samana olion elinkaaren ajan, kun olio on hajautukseen perustuvassa kokoelmassa. Perusta hajautus muuttumattomiin kenttiin; jos avain muuttuu lisäämisen jälkeen, kokoelma voi kadottaa sen.
using System;
public class Account
{
public readonly int Id; // immutable, safe to hash
public string Nickname; // mutable, do not hash
public Account(int id, string nick) { Id = id; Nickname = nick; }
public override bool Equals(object obj) => obj is Account a && a.Id == Id;
public override int GetHashCode() => Id.GetHashCode();
}
public class Program
{
public static void Main()
{
var acc = new Account(42, "old");
int h1 = acc.GetHashCode();
acc.Nickname = "new"; // does not change the hash
Console.WriteLine(h1 == acc.GetHashCode());
}
}Null-kenttien käsittely
Kun kenttä voi olla null, HashCode.Combine käsittelee sen turvallisesti. Jos lasket hajautusarvon itse, tarkista null-arvo välttääksesi NullReferenceException-poikkeuksen.
using System;
public class Tag
{
public string Name;
public Tag(string name) { Name = name; }
public override bool Equals(object obj) => obj is Tag t && t.Name == Name;
public override int GetHashCode() => HashCode.Combine(Name); // null-safe
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Tag(null).GetHashCode() == new Tag(null).GetHashCode());
}
}Törmäykset ovat normaaleja
Hajautuskoodit ovat 32-bittisiä, joten törmäykset ovat väistämättömiä suurissa aineistoissa. Hyvä hajautus vain jakaa arvot tasaisesti ja pitää törmäykset harvinaisina; Equals ratkaisee mahdolliset törmäykset.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var set = new HashSet<int>();
for (int i = 0; i < 5; i++) set.Add(i);
// Equality still works perfectly even though hashing is imperfect in general
Console.WriteLine(set.Contains(3));
Console.WriteLine(set.Contains(99));
}
}Record-tyypit luovat sen automaattisesti
record luo automaattisesti ominaisuuksiinsa perustuvan oikean GetHashCode-toteutuksen, joka vastaa sen arvoperustaista Equals-toteutusta. Tämä on helpoin tapa toteuttaa hajautus oikein.
using System;
using System.Collections.Generic;
public record Coord(int X, int Y);
public class Program
{
public static void Main()
{
var set = new HashSet<Coord> { new Coord(1, 1) };
Console.WriteLine(set.Contains(new Coord(1, 1)));
}
}Toimiva yhtäsuuruutta tukeva tyyppi
Kun kaikki yhdistetään, Equals ja GetHashCode käyttävät samoja kenttiä, avaimet ovat muuttumattomia ja arvojen yhdistämiseen käytetään HashCode.Combine-metodia. Tämä tyyppi toimii moitteettomasti sanakirjan avaimena.
using System;
using System.Collections.Generic;
public struct ProductKey
{
public string Sku;
public int Variant;
public ProductKey(string sku, int variant) { Sku = sku; Variant = variant; }
public override bool Equals(object obj)
=> obj is ProductKey k && k.Sku == Sku && k.Variant == Variant;
public override int GetHashCode() => HashCode.Combine(Sku, Variant);
}
public class Program
{
public static void Main()
{
var stock = new Dictionary<ProductKey, int>
{
[new ProductKey("ABC", 1)] = 10
};
Console.WriteLine(stock[new ProductKey("ABC", 1)]);
}
}Kokeile itse
Varmista sopimuksen toteutuminen: luo kaksi samanarvoista avainta ja varmista, että sekä niiden yhtäsuuruus että hajautuskoodit täsmäävät, ja käytä niitä sitten joukossa.
using System;
using System.Collections.Generic;
public struct Name
{
public string First, Last;
public Name(string first, string last) { First = first; Last = last; }
public override bool Equals(object obj) => obj is Name n && n.First == First && n.Last == Last;
public override int GetHashCode() => HashCode.Combine(First, Last);
}
public class Program
{
public static void Main()
{
var a = new Name("Ada", "Lovelace");
var b = new Name("Ada", "Lovelace");
Console.WriteLine(a.Equals(b) + " " + (a.GetHashCode() == b.GetHashCode()));
var people = new HashSet<Name> { a };
Console.WriteLine(people.Contains(b));
}
}Pikatarkistus
Palauta hajautussopimus mieleen.
Kertaus
Oikein toteutettu hajautus pitää hajautukseen perustuvat kokoelmat toimivina.
- Samanarvoisten olioiden on palautettava sama hajautuskoodi.
- Käytä
HashCode.Combine-metodia samoille kentille kuinEquals-metodissa. - Hajauta muuttumattomat kentät, jotta avaimet pysyvät löydettävissä.
- Törmäykset ovat normaaleja;
Equalsratkaisee ne. - Record-tyypit luovat oikean toteutuksen automaattisesti.
Opi C# tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 93
- Oppitunnit
- 346
Usein kysytyt kysymykset
Onko oppitunti ”GetHashCode-metodin ylikirjoittaminen” ilmainen?
Kyllä – oppitunnin ”GetHashCode-metodin ylikirjoittaminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko C# Academy-kurssin, päivitä CoddyKit PROhon. C# Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”GetHashCode-metodin ylikirjoittaminen”?
Pitäkää yhtäsuuruus ja hajautus yhdenmukaisina. Harjoittelet C# Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni C# Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin C# Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.
Kuinka kauan ”GetHashCode-metodin ylikirjoittaminen”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä C# Academy-oppitunnilla?
Kyllä. Jokainen C# Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- IEquatable-rajapinnan toteuttaminen
- GetHashCode-metodin ylikirjoittaminen
- IComparable-rajapinnan toteuttaminen
- IComparer mukautettuun lajitteluun