0Pricing
C# Academy · Lezione

Override di GetHashCode

Mantenga coerenti uguaglianza e hashing.

Override di GetHashCode è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Perché GetHashCode è importante

Le raccolte basate su hash, come Dictionary e HashSet, usano GetHashCode per assegnare gli elementi ai bucket. Se esegue l'override di Equals ma non di GetHashCode, queste raccolte potrebbero non trovare gli elementi uguali.

La regola d'oro

Il contratto è semplice: se due oggetti sono uguali, devono restituire lo stesso codice hash. Non è necessario il contrario; oggetti diversi possono condividere un codice hash (una collisione).

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

Usare HashCode.Combine

Il metodo di supporto HashCode.Combine combina diversi valori dei campi in un hash ben distribuito. È il modo moderno consigliato per implementare 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());
    }
}

Gestire insieme uguaglianza e hashing

Esegua sempre l'override di Equals e GetHashCode come coppia, usando gli stessi campi in entrambi. Usare campi diversi fa sì che oggetti uguali producano hash diversi.

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

Perché i dizionari ne hanno bisogno

Un Dictionary calcola prima l'hash della chiave per trovare un bucket, poi usa Equals all'interno di quel bucket. Un hash errato indirizza la ricerca al bucket sbagliato e la chiave non viene mai trovata.

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

Calcolare l'hash dei campi immutabili

I codici hash devono rimanere stabili per un oggetto finché questo si trova in una raccolta basata su hash. Basi l'hash su campi immutabili; se una chiave cambia dopo l'inserimento, la raccolta può perderne le tracce.

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

Gestire i campi null

Quando un campo può essere null, HashCode.Combine lo gestisce in modo sicuro. Se calcola l'hash manualmente, gestisca il valore null per evitare una NullReferenceException.

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

Le collisioni sono normali

I codici hash sono a 32 bit, quindi le collisioni sono inevitabili per insiemi di dati di grandi dimensioni. Un buon hash distribuisce i valori in modo da mantenere rare le collisioni; Equals risolve quelle che si verificano.

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

I record lo generano automaticamente

Un record genera automaticamente un GetHashCode corretto a partire dalle sue proprietà, coerente con il suo Equals basato sul valore. È il modo più semplice per ottenere un hashing corretto.

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

Un tipo con uguaglianza corretta

In sintesi: stessi campi in Equals e GetHashCode, chiavi immutabili e HashCode.Combine per combinarli. Questo tipo funziona perfettamente come chiave di un dizionario.

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

Provi Lei

Dimostri che il contratto è rispettato: crei due chiavi uguali e verifichi che uguaglianza e codici hash coincidano, poi le utilizzi in un insieme.

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

Verifica rapida

Ricordi il contratto dei codici hash.

Riepilogo

Il calcolo corretto degli hash mantiene operative le raccolte basate su hash.

  • Gli oggetti uguali devono restituire lo stesso codice hash.
  • Usi HashCode.Combine sugli stessi campi di Equals.
  • Calcoli l'hash dei campi immutabili, così le chiavi restano trovabili.
  • Le collisioni sono normali; Equals le risolve.
  • I record generano automaticamente un'implementazione corretta.

Domande Frequenti

La lezione «Override di GetHashCode» è gratuita?

Sì — il testo completo di «Override di GetHashCode» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Override di GetHashCode»?

Mantenga coerenti uguaglianza e hashing. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Override di GetHashCode»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Implementazione di IEquatable
  2. Override di GetHashCode
  3. Implementazione di IComparable
  4. IComparer per l'ordinamento personalizzato
← Torna a C# Academy