0Pricing
C# Academy · Aula

Igualdade e hashing (valor versus referência)

Entenda a igualdade por referência versus por valor, substitua Equals/GetHashCode corretamente, implemente IEquatable e use comparadores personalizados com conjuntos e dicionários.

Igualdade e hashing (valor versus referência) é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 3. 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 3 aulas no total.

Noções básicas de igualdade

Objetivo: Fazer a igualdade funcionar como esperado.

  • Igualdade por referência versus igualdade por valor
  • Contrato entre Equals e GetHashCode
  • IEquatable<T> para obter mais velocidade
  • Comparadores personalizados quando não é possível modificar o tipo

Armadilha da igualdade por referência

As classes usam, por padrão, a igualdade por referência. Dois objetos separados com os mesmos dados não são iguais, a menos que implemente a igualdade por valor.

using System;
using System.Collections.Generic;

public sealed class Point // no overrides
{
  public int X;
  public int Y;
  public Point(int x, int y){ X = x; Y = y; }
}

public class Program
{
  public static void Main(string[] args)
  {
    Point a = new Point(1, 2);
    Point b = new Point(1, 2);

    Console.WriteLine("a == b ? " + (a == b)); // reference equality: False
    Console.WriteLine("a.Equals(b) ? " + a.Equals(b)); // False

    HashSet<Point> set = new HashSet<Point>();
    set.Add(a);
    Console.WriteLine("Contains b? " + set.Contains(b)); // False (unexpected)
  }
}

Igualdade por valor implementada

Implemente IEquatable<T> e substitua Equals e GetHashCode. O hash deve corresponder à igualdade: objetos iguais → mesmo hash.

using System;
using System.Collections.Generic;

public sealed class ValuePoint : IEquatable<ValuePoint>
{
  public int X;
  public int Y;
  public ValuePoint(int x, int y){ X = x; Y = y; }

  public bool Equals(ValuePoint other)
  {
    if (ReferenceEquals(other, null)) return false;
    if (ReferenceEquals(this, other)) return true;
    return X == other.X && Y == other.Y;
  }

  public override bool Equals(object obj)
  {
    return Equals(obj as ValuePoint);
  }

  public override int GetHashCode()
  {
    // Simple, stable combination (avoid randomness)
    unchecked
    {
      int hash = 17;
      hash = hash * 31 + X.GetHashCode();
      hash = hash * 31 + Y.GetHashCode();
      return hash;
    }
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    ValuePoint a = new ValuePoint(1, 2);
    ValuePoint b = new ValuePoint(1, 2);

    Console.WriteLine("a.Equals(b)? " + a.Equals(b)); // True

    HashSet<ValuePoint> set = new HashSet<ValuePoint>();
    set.Add(a);
    Console.WriteLine("Contains b? " + set.Contains(b)); // True (value semantics)
  }
}

Comparador personalizado

Passe um IEqualityComparer<T> personalizado para HashSet/Dictionary quando não puder alterar o próprio tipo.

using System;
using System.Collections.Generic;

public sealed class Person // imagine from a library; cannot edit
{
  public string Name;
  public int BirthYear;
  public Person(string name, int year){ Name = name; BirthYear = year; }
}

public sealed class PersonComparer : IEqualityComparer<Person>
{
  public bool Equals(Person a, Person b)
  {
    if (ReferenceEquals(a, b)) return true;
    if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
    return a.Name == b.Name && a.BirthYear == b.BirthYear;
  }

  public int GetHashCode(Person p)
  {
    if (ReferenceEquals(p, null)) return 0;
    unchecked
    {
      int h = 23;
      h = h * 31 + (p.Name == null ? 0 : p.Name.GetHashCode());
      h = h * 31 + p.BirthYear.GetHashCode();
      return h;
    }
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Person p1 = new Person("Ada", 1815);
    Person p2 = new Person("Ada", 1815);

    HashSet<Person> set = new HashSet<Person>(new PersonComparer());
    set.Add(p1);
    Console.WriteLine("Contains p2? " + set.Contains(p2)); // True via comparer
  }
}

Contrato e armadilhas

Regras:

  • Se Equals(a,b) for true ⇒ GetHashCode(a) == GetHashCode(b)
  • A igualdade deve ser reflexiva, simétrica e transitiva
  • Use campos imutáveis para gerar o hash sempre que possível
  • Não use valores aleatórios em GetHashCode

Observação sobre estrutura e classe

As estruturas são comparadas por seus campos por padrão (semântica de valor). As classes são comparadas por referência, a menos que implemente a igualdade por valor.

using System;

public struct PointS // struct: value type
{
  public int X;
  public int Y;
  public PointS(int x, int y){ X = x; Y = y; }
}

public class Program
{
  public static void Main(string[] args)
  {
    PointS a = new PointS(1, 2);
    PointS b = new PointS(1, 2);
    Console.WriteLine("Struct equality: " + a.Equals(b)); // True by default (field-wise)
  }
}

Contrato de igualdade para coleções

Verificação rápida: para atribuir igualdade por valor a uma classe, de modo que ela funcione em HashSet e Dictionary, o que deve ser implementado?

Recapitulação

Recapitulação: as classes precisam de Equals/GetHashCode (e frequentemente de IEquatable<T>) para ter semântica de valor; use comparadores personalizados quando não for possível editar os tipos.

Perguntas Frequentes

A aula “Igualdade e hashing (valor versus referência)” é grátis?

Sim — o texto completo de “Igualdade e hashing (valor versus referência)” é 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 3 aulas no total.

O que vou aprender em “Igualdade e hashing (valor versus referência)”?

Entenda a igualdade por referência versus por valor, substitua Equals/GetHashCode corretamente, implemente IEquatable e use comparadores personalizados com conjuntos e dicionários. 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 3 de 3.

Quanto tempo leva a aula “Igualdade e hashing (valor versus referência)”?

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. HashSet , SortedSet , Queue , Stack
  2. ConcurrentDictionary , coleções imutáveis
  3. Igualdade e hashing (valor versus referência)
← Voltar para C# Academy