Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os operadores == (igualdade) e != (desigualdade) verificam se os operandos são iguais ou não. Os tipos de valor são iguais quando seu conteúdo é igual. Os tipos de referência são iguais quando as duas variáveis se referem ao mesmo armazenamento.
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
Você pode usar o is operador de correspondência de padrões como uma alternativa a um == teste ao testar em relação a um valor constante. O is operador usa a semântica de igualdade padrão para todos os tipos de valor e referência.
Operador de igualdade ==
O operador de igualdade == retornará true se seus operandos forem iguais; caso contrário, false.
Igualdade de tipos de valor
Os operandos dos tipos de valor internos serão iguais se seus valores forem iguais:
int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b); // output: True
char c1 = 'a';
char c2 = 'A';
Console.WriteLine(c1 == c2); // output: False
Console.WriteLine(c1 == char.ToLower(c2)); // output: True
Observação
Para os ==operadores , ,<> e <=>= , se qualquer um dos operandos não for um número (Double.NaN ou Single.NaN), o resultado da operação será false. Essa condição significa que o NaN valor não é maior que, menor ou igual a qualquer outro double valor (ou float) , incluindo NaN. Para obter mais informações e exemplos, consulte o artigo de referência Double.NaN ou Single.NaN.
Dois operandos do mesmo tipo de enum serão iguais se os valores correspondentes do tipo integral subjacente forem iguais.
Os tipos struct definidos pelo usuário não dão suporte ao operador ==, por padrão. Para dar suporte ao operador ==, um struct definido pelo usuário precisa sobrecarregá-lo.
As tuplas C# têm suporte interno para operadores == e operadores!=. Para obter mais informações, consulte a seção Igualdade de tupla do artigo Tipos de tupla.
Igualdade de tipos de referência
Por padrão, operandos de tipo de referência, excluindo registros, serão iguais se se referirem ao mesmo objeto:
public class ReferenceTypesEquality
{
public class MyClass
{
private int id;
public MyClass(int id) => this.id = id;
}
public static void Main()
{
var a = new MyClass(1);
var b = new MyClass(1);
var c = a;
Console.WriteLine(a == b); // output: False
Console.WriteLine(a == c); // output: True
}
}
Como mostra o exemplo anterior, os tipos de referência definidos pelo usuário dão suporte ao == operador por padrão. No entanto, um tipo de referência pode sobrecarregar o operador ==. Se um tipo de referência sobrecarregar o operador ==, use o método Object.ReferenceEquals para verificar se as duas referências desse tipo dizem respeito ao mesmo objeto.
Igualdade de tipos de registro
Os tipos de registro dão suporte aos operadores == e != que, por padrão, fornecem semântica de igualdade de valor. Ou seja, dois operandos de registro são iguais quando ambos são null ou os valores correspondentes de todos os campos e as propriedades implementadas automaticamente são iguais.
public class RecordTypesEquality
{
public record Point(int X, int Y, string Name);
public record TaggedNumber(int Number, List<string> Tags);
public static void Main()
{
var p1 = new Point(2, 3, "A");
var p2 = new Point(1, 3, "B");
var p3 = new Point(2, 3, "A");
Console.WriteLine(p1 == p2); // output: False
Console.WriteLine(p1 == p3); // output: True
var n1 = new TaggedNumber(2, new List<string>() { "A" });
var n2 = new TaggedNumber(2, new List<string>() { "A" });
Console.WriteLine(n1 == n2); // output: False
}
}
Como mostra o exemplo anterior, a igualdade de membros do tipo de referência é comparada usando suas implementações de igualdade específicas.
Igualdade da cadeia de caracteres
Dois operandos da cadeia de caracteres serão iguais quando ambos forem null ou ambas as instâncias da cadeia de caracteres tiverem o mesmo comprimento e caracteres idênticos em cada posição de caractere:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Comparações de igualdade de cadeia de caracteres são comparações ordinais que diferenciam maiúsculas de minúsculas. Para obter mais informações sobre a comparação de cadeias de caracteres, confira Como comparar cadeias de caracteres no C#.
Igualdade de delegado
Dois operandos delegados do mesmo tipo de tempo de execução são iguais quando ambos são null ou suas listas de invocação têm o mesmo comprimento e têm entradas iguais em cada posição:
Action a = () => Console.WriteLine("a");
Action b = a + a;
Action c = a + a;
Console.WriteLine(object.ReferenceEquals(b, c)); // output: False
Console.WriteLine(b == c); // output: True
Importante
Entradas iguais em uma lista de invocação incluem todos os parâmetros fixos na invocação, incluindo o receptor. O receptor é a instância de um objeto representado quando this a entrada é invocada.
var o1 = new object();
var o2 = new object();
var d1 = o1.ToString;
var d2 = o2.ToString;
Console.WriteLine(object.ReferenceEquals(d1, d2)); // output: False
Console.WriteLine(d1 == d2); // output: False (different receivers)
Saiba mais na seção Operadores de igualdade de delegados na Especificação da linguagem C#.
Delegados provenientes da avaliação de expressões lambda semanticamente idênticas não são iguais, como mostra o exemplo a seguir:
Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("a");
Console.WriteLine(a == b); // output: False
Console.WriteLine(a + b == a + b); // output: True
Console.WriteLine(b + a == a + b); // output: False
Operador de desigualdade !=
O operador != de desigualdade retornará true se seus operandos não forem iguais e false de outra forma. No caso dos operandos de tipos internos, a expressão x != y gera o mesmo resultado que a expressão !(x == y). Para obter mais informações sobre a igualdade de tipos, confira a seção Operador de igualdade.
O exemplo a seguir demonstra como usar o != operador:
int a = 1 + 1 + 2 + 3;
int b = 6;
Console.WriteLine(a != b); // output: True
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 != s2); // output: False
object o1 = 1;
object o2 = 1;
Console.WriteLine(o1 != o2); // output: True
Igualdade nas hierarquias de classe
Os registros manipulam a herança corretamente sem trabalho manual. A igualdade gerada pelo compilador verifica o tipo de runtime e todas as propriedades declaradas, portanto, atende automaticamente aos requisitos de simetria e transitividade. Prefira record uma hierarquia não selada manual quando a igualdade de valor for a meta.
Importante
Use record sempre que possível – o compilador gera todos os membros de igualdade necessários para você. A implementação manual só é necessária quando seu tipo deve derivar de uma classe que não seja de registro ou tem outras restrições que impedem record.
Implemente a igualdade por conta própria quando um tipo não pode ser um registro
Aqui está uma implementação manual mínima para um tipo de valor que não pode ser um registro:
class Color : IEquatable<Color>
{
public Color(int r, int g, int b)
{
R = r;
G = g;
B = b;
}
public int R { get; }
public int G { get; }
public int B { get; }
public bool Equals(Color? other) =>
other is not null && R == other.R && G == other.G && B == other.B;
public override bool Equals(object? obj) => obj is Color other && Equals(other);
public override int GetHashCode() => HashCode.Combine(R, G, B);
}
A implementação fornece três membros necessários: Equals(T?) como a comparação principal, override Equals(object?) para chamadas no nível do objeto e override GetHashCode() , portanto, as coleções baseadas em hash funcionam corretamente.
HashCode.Combine é um auxiliar de biblioteca que cria um hash com base nos mesmos valores usados por Equals.
IEquatable<T> Implementar (a Equals(T?) sobrecarga) é opcional, mas evita o boxe quando os chamadores já têm o tipo concreto.
Quando você também define == e !=, o idioma requer eles como um par; avisos CS0660 e CS0661 lembram você de manter todos os quatro membros consistentes.
Com os três membros acima em vigor, Equals reflete a igualdade de valor, mas == ainda testa a identidade porque nenhum == operador foi declarado ainda:
var red1 = new Color(255, 0, 0);
var red2 = new Color(255, 0, 0);
Console.WriteLine(red1.Equals(red2)); // => True
Console.WriteLine(red1 == red2); // => False (no == overload; identity check)
Uma implementação correta também deve satisfazer o contrato de equivalência (suponha xe yz não nulo):
-
Reflexivo:
x.Equals(x)retornatrue. -
Simétrico:
x.Equals(y)retorna o mesmo valor quey.Equals(x). -
Transitivo: se
x.Equals(y)ey.Equals(z)ambostruesão, entãox.Equals(z)deve sertrue. -
Consistente: chamadas sucessivas para
x.Equals(y)retornar o mesmo valor, desde que nenhum dos objetos mude. -
Comportamento nulo:
x.Equals(null)retornafalse;x.Equals(y)não deve ser gerado quando chamado em um não nulox.
A igualdade de valor em uma hierarquia de classe não lacrada requer mais cuidado do que em uma classe lacrada para satisfazer as regras simétricas e transitivas. O perigo é que IEquatable<T>.Equals(T? other) a expedição segue o tipo declarado (o tipo escrito na declaração de variável) da variável, não seu tipo de runtime. Se Shape declarar um nãovirtualEquals(Shape? other), uma variável digitada como Shape que contém um Circle em runtime invoca — ignorando Circlesilenciosamente campos específicosShape.Equals. Dois Circle objetos com raios diferentes podem ser comparados como iguais quando acessados por meio de uma Shape variável.
O padrão correto requer dois requisitos de cooperação: tornar o método virtual tipado para que cada classe derivada Equals possa estender a comparação e adicionar um GetType() == other.GetType() proteção na implementação da classe base para que objetos de diferentes tipos de runtime nunca sejam considerados iguais.
Implementação de classe base
// Shape is an unsealed base class. Making Equals virtual and guarding with GetType()
// ensures a derived instance is never equal to an instance of a different runtime type.
class Shape : IEquatable<Shape>
{
public string Color { get; }
public Shape(string color) => Color = color;
public override bool Equals(object? obj) => Equals(obj as Shape);
// virtual so derived classes can override and augment the comparison
public virtual bool Equals(Shape? other) =>
other is not null &&
GetType() == other.GetType() && // reject different runtime types
Color == other.Color;
// GetType() is included because equality requires matching runtime types
public override int GetHashCode() => HashCode.Combine(GetType(), Color);
public static bool operator ==(Shape? l, Shape? r) => l?.Equals(r) ?? r is null;
public static bool operator !=(Shape? l, Shape? r) => !(l == r);
}
Pontos principais:
-
virtualdigitadoEquals: cada classe derivada substitui esse método para aumentar a comparação com seus próprios campos. -
GetType()proteção:GetType() == other.GetType()impede que umCircleiguale umShapecom a mesma cor e impede que objetos de diferentes tipos derivados se igualem uns aos outros. -
GetHashCodeincluiGetType(): como dois objetos são iguais somente quando seus tipos de runtime correspondem,GetHashCodedevem hash do tipo de runtime, bem como os campos de dados. OmitirGetType()aqui causa um comportamento incorreto dentroDictionary<TKey,TValue>eHashSet<T>. -
==delegados paraEquals: mantém a igualdade de operador e método consistente.
Implementação de classe derivada
Uma classe derivada que adiciona campos substitui o tipo Equals, converte em seu próprio tipo, chama base.Equalse compara seus próprios campos:
class Circle : Shape
{
public double Radius { get; }
public Circle(string color, double radius) : base(color) => Radius = radius;
public override bool Equals(object? obj) => Equals(obj as Shape);
// Calls base.Equals to verify Color and runtime type, then adds Radius
public override bool Equals(Shape? other) =>
other is Circle c && base.Equals(c) && Radius == c.Radius;
public override int GetHashCode() => HashCode.Combine(GetType(), Color, Radius);
}
base.Equals(c) impõe o GetType() guarda e verifica os campos compartilhados. A conversão via other is Circle c falha rapidamente quando o argumento é um Shape de qualquer outro tipo derivado.
Uso por meio de uma variável de tipo base
Shape circle1 = new Circle("red", 5.0);
Shape circle2 = new Circle("red", 7.0);
Shape circle3 = new Circle("red", 5.0);
Shape shape1 = new Shape("red");
Console.WriteLine(circle1.Equals(circle2)); // => False (Radius differs)
Console.WriteLine(circle1.Equals(circle3)); // => True
Console.WriteLine(circle1.Equals(shape1)); // => False (different runtime types)
Classes lacradas são mais simples
Você não pode subclasse de uma sealed classe, portanto, os tipos de tempo de compilação e runtime sempre concordam. Você não precisa do GetType() guarda ou virtual expedição. O IEquatable<T> padrão mostrado em Implementar igualdade por conta própria quando um tipo não pode ser um registro está correto e concluído para uma classe lacrada.
Capacidade de sobrecarga do operador
Você pode sobrecarregar os operadores e == os != operadores em um tipo definido pelo usuário. Se você sobrecarregar um desses dois operadores, também deverá sobrecarregar o outro operador.
Você não pode sobrecarregar explicitamente os operadores e == os != operadores em um tipo de registro. Para alterar o comportamento dos operadores e == do != tipo Tde registro, implemente o IEquatable<T>.Equals método com a seguinte assinatura:
public virtual bool Equals(T? other);
Especificação da linguagem C#
Para obter mais informações, consulte a seção Operadores de teste de tipo e relacional na Especificação da linguagem C#.
Para obter mais informações sobre a igualdade de tipos de registro, consulte a seção Membros de igualdade da especificação da linguagem C#.