0Pricing
C# Academy · Aula

Implementando IEquatable

Forneça igualdade com segurança de tipos usando Equals.

Implementando IEquatable é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 1 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 IEquatable<T>?

IEquatable<T> fornece um método Equals(T other) fortemente tipado. Ele evita o encaixotamento para tipos de valor e conversões de tipo para tipos de referência, tornando a igualdade mais rápida e clara.

Implementando a interface

Implemente IEquatable<T> e forneça Equals(T other). Você também deve substituir o object.Equals herdado e GetHashCode para manter todos os caminhos consistentes.

using System;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }

    public bool Equals(Point other) => X == other.X && Y == other.Y;
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Point(1, 2).Equals(new Point(1, 2)));
    }
}

Evitando o encaixotamento de estruturas

O método não genérico object.Equals(object) encaixota um argumento de estrutura. O método tipado Equals(Point other) de IEquatable<T> recebe a estrutura diretamente, portanto nenhuma alocação ocorre.

using System;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }
    public bool Equals(Point other) => X == other.X && Y == other.Y; // no boxing
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        Point a = new Point(3, 4);
        Point b = new Point(3, 4);
        Console.WriteLine(a.Equals(b)); // calls the typed overload
    }
}

As coleções usam IEquatable<T>

List<T>.Contains, Dictionary e HashSet dão preferência a IEquatable<T>.Equals quando ele está disponível. Implementá-lo torna essas operações eficientes e corretas para o seu tipo.

using System;
using System.Collections.Generic;

public struct Point : IEquatable<Point>
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }
    public bool Equals(Point other) => X == other.X && Y == other.Y;
    public override bool Equals(object obj) => obj is Point p && Equals(p);
    public override int GetHashCode() => HashCode.Combine(X, Y);
}

public class Program
{
    public static void Main()
    {
        var list = new List<Point> { new Point(0, 0), new Point(1, 1) };
        Console.WriteLine(list.Contains(new Point(1, 1)));
    }
}

Tipos de referência e IEquatable<T>

As classes também podem implementar IEquatable<T>. O método Equals tipado evita uma conversão de tipo, e você trata null explicitamente para o parâmetro de referência.

using System;

public class Person : IEquatable<Person>
{
    public string Name;
    public Person(string name) { Name = name; }

    public bool Equals(Person other) => other != null && other.Name == Name;
    public override bool Equals(object obj) => Equals(obj as Person);
    public override int GetHashCode() => Name?.GetHashCode() ?? 0;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Person("Ada").Equals(new Person("Ada")));
        Console.WriteLine(new Person("Ada").Equals((Person)null));
    }
}

Encaminhando object.Equals para o método tipado

Um padrão claro é escrever a comparação uma única vez em Equals(T) e fazer com que object.Equals a delegue a esse método. Isso garante que ambos se comportem de forma idêntica.

using System;

public struct Id : IEquatable<Id>
{
    public int Value;
    public Id(int value) { Value = value; }
    public bool Equals(Id other) => Value == other.Value;
    public override bool Equals(object obj) => obj is Id other && Equals(other);
    public override int GetHashCode() => Value;
}

public class Program
{
    public static void Main()
    {
        object boxed = new Id(5);
        Console.WriteLine(boxed.Equals(new Id(5)));
    }
}

Comparando vários campos

Para tipos com vários campos, compare todos eles em Equals. Duas instâncias são iguais somente se todos os campos relevantes coincidirem.

using System;

public struct DateRange : IEquatable<DateRange>
{
    public int Start, End;
    public DateRange(int start, int end) { Start = start; End = end; }
    public bool Equals(DateRange other) => Start == other.Start && End == other.End;
    public override bool Equals(object obj) => obj is DateRange d && Equals(d);
    public override int GetHashCode() => HashCode.Combine(Start, End);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 5)));
        Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 6)));
    }
}

Combinando com operadores

Combine IEquatable<T> com == e != para que os usuários possam comparar usando qualquer uma das sintaxes, sempre com a mesma comparação tipada como base.

using System;

public struct Money : IEquatable<Money>
{
    public decimal Amount;
    public Money(decimal a) { Amount = a; }
    public bool Equals(Money other) => Amount == other.Amount;
    public override bool Equals(object obj) => obj is Money m && Equals(m);
    public override int GetHashCode() => Amount.GetHashCode();
    public static bool operator ==(Money a, Money b) => a.Equals(b);
    public static bool operator !=(Money a, Money b) => !a.Equals(b);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Money(9.99m) == new Money(9.99m));
    }
}

Os registros implementam isso automaticamente

Um record C# gera IEquatable<T>, Equals baseado em valor e GetHashCode para você. Quando a igualdade por valor é tudo de que você precisa, um registro elimina código repetitivo.

using System;

public record Point(int X, int 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 == b);
    }
}

Quando implementar manualmente

Implemente IEquatable<T> manualmente quando precisar de regras de igualdade personalizadas, como nomes que não diferenciam maiúsculas de minúsculas ou a ignorância de determinados campos, que um registro não consegue expressar diretamente.

using System;

public class CaseInsensitiveTag : IEquatable<CaseInsensitiveTag>
{
    public string Name;
    public CaseInsensitiveTag(string name) { Name = name; }
    public bool Equals(CaseInsensitiveTag other)
        => other != null && string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
    public override bool Equals(object obj) => Equals(obj as CaseInsensitiveTag);
    public override int GetHashCode() => Name?.ToLowerInvariant().GetHashCode() ?? 0;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new CaseInsensitiveTag("Red").Equals(new CaseInsensitiveTag("RED")));
    }
}

Experimente você mesmo

Implemente IEquatable<T> para um pequeno tipo de referência e encaminhe todos os caminhos de igualdade pelo método tipado.

using System;
using System.Collections.Generic;

public class Coordinate : IEquatable<Coordinate>
{
    public int Lat, Lng;
    public Coordinate(int lat, int lng) { Lat = lat; Lng = lng; }
    public bool Equals(Coordinate other) => other != null && other.Lat == Lat && other.Lng == Lng;
    public override bool Equals(object obj) => Equals(obj as Coordinate);
    public override int GetHashCode() => HashCode.Combine(Lat, Lng);
}

public class Program
{
    public static void Main()
    {
        var set = new HashSet<Coordinate> { new Coordinate(10, 20) };
        Console.WriteLine(set.Contains(new Coordinate(10, 20)));
    }
}

Verificação rápida

Relembre o benefício de IEquatable.

Recapitulação

IEquatable<T> fornece igualdade com segurança de tipos.

  • Fornece Equals(T other), evitando o encaixotamento para estruturas e conversões de tipo para classes.
  • As coleções dão preferência a ele para pesquisas corretas e eficientes.
  • Encaminhe object.Equals e os operadores pelo método tipado.
  • Os registros o implementam automaticamente; implemente-o manualmente para regras personalizadas.

Perguntas Frequentes

A aula “Implementando IEquatable” é grátis?

Sim — o texto completo de “Implementando IEquatable” é 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 “Implementando IEquatable”?

Forneça igualdade com segurança de tipos usando Equals. 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 1 de 4.

Quanto tempo leva a aula “Implementando IEquatable”?

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