C# Academy · Oppitunti

GetHashCode-metodin ylikirjoittaminen

Pitäkää yhtäsuuruus ja hajautus yhdenmukaisina.

Oppitunti 2/413 vaihetta

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 kuin Equals-metodissa.
  • Hajauta muuttumattomat kentät, jotta avaimet pysyvät löydettävissä.
  • Törmäykset ovat normaaleja; Equals ratkaisee ne.
  • Record-tyypit luovat oikean toteutuksen automaattisesti.
Aloita maksutta

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

  1. IEquatable-rajapinnan toteuttaminen
  2. GetHashCode-metodin ylikirjoittaminen
  3. IComparable-rajapinnan toteuttaminen
  4. IComparer mukautettuun lajitteluun
← Takaisin: C# Academy