0Pricing
C# Academy · Aula

Substituindo GetHashCode

Mantenha a igualdade e o cálculo de hash consistentes.

Substituindo GetHashCode é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

Por que GetHashCode é importante

Coleções baseadas em códigos hash, como Dictionary e HashSet, usam GetHashCode para distribuir os itens em compartimentos. Se você substituir Equals, mas não GetHashCode, essas coleções poderão não encontrar itens iguais.

A regra de ouro

O contrato é simples: se dois objetos forem iguais, eles deverão retornar o mesmo código hash. O inverso não é obrigatório; objetos diferentes podem compartilhar um código hash (uma colisão).

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

Use HashCode.Combine

O auxiliar HashCode.Combine combina os valores de vários campos em um código hash bem distribuído. Essa é a forma moderna recomendada de implementar 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());
    }
}

Mantenha igualdade e hash em conjunto

Sempre substitua Equals e GetHashCode em conjunto, usando os mesmos campos em ambos. Usar campos diferentes faz com que objetos iguais produzam códigos hash diferentes.

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

Por que os dicionários precisam disso

Um Dictionary primeiro calcula o código hash da chave para localizar um compartimento e depois usa Equals dentro dele. Um código hash incorreto envia a pesquisa para o compartimento errado, e a chave nunca é encontrada.

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

Calculando o hash de campos imutáveis

Os códigos hash devem permanecer estáveis para um objeto enquanto ele estiver em uma coleção baseada em hash. Baseie o código em campos imutáveis; se uma chave for alterada após a inserção, a coleção poderá perder o controle dela.

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

Lidando com campos nulos

Quando um campo pode ser nulo, HashCode.Combine lida com isso com segurança. Se você calcular o código manualmente, trate o nulo para evitar uma 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());
    }
}

Colisões são normais

Os códigos hash têm 32 bits, portanto as colisões são inevitáveis em grandes conjuntos de dados. Um bom código apenas distribui os valores para manter as colisões raras; Equals resolve as que ocorrerem.

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

Os registros geram isso para você

Um record gera automaticamente um GetHashCode correto a partir de suas propriedades, em concordância com seu Equals baseado em valor. Essa é a forma mais fácil de obter uma implementação correta de hash.

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

Um tipo equatável correto

Reunindo tudo: os mesmos campos em Equals e GetHashCode, chaves imutáveis e HashCode.Combine para combinar os valores. Esse tipo funciona perfeitamente como chave de um dicionário.

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

Experimente você mesmo

Comprove que o contrato é respeitado: crie duas chaves iguais e confirme que tanto a igualdade quanto os códigos hash coincidem; depois use-as em um set.

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ção rápida

Relembre o contrato de hash.

Recapitulação

Um cálculo correto de hash mantém as coleções baseadas em hash funcionando.

  • Objetos iguais devem retornar o mesmo código hash.
  • Use HashCode.Combine com os mesmos campos usados por Equals.
  • Calcule o hash de campos imutáveis para que as chaves continuem localizáveis.
  • Colisões são normais; Equals resolve-as.
  • Os registros geram automaticamente uma implementação correta.

Perguntas Frequentes

A aula “Substituindo GetHashCode” é grátis?

Sim — o texto completo de “Substituindo GetHashCode” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Substituindo GetHashCode”?

Mantenha a igualdade e o cálculo de hash consistentes. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Substituindo GetHashCode”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Implementando IEquatable
  2. Substituindo GetHashCode
  3. Implementando IComparable
  4. IComparer para classificação personalizada
← Voltar para C# Academy