Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Os == operadores (igualdade) e != (desigualdade) verificam se seus 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 referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em versões preliminares públicas para a próxima versão da linguagem.
A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.
Sugestão
Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.
Você pode usar o is operador de correspondência de padrão como uma alternativa a um == teste quando você testa 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 retorna true se seus operandos forem iguais, false caso contrário.
Igualdade de tipos de valor
Os operandos dos tipos de valor internos sã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
Nota
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, menor ou igual a qualquer outro double (ou float) valor, incluindo NaN. Para obter mais informações e exemplos, consulte o Double.NaN artigo ou Single.NaN referência.
Dois operandos do mesmo tipo de enum são iguais se os valores correspondentes do tipo integral subjacente forem iguais.
Os tipos struct definidos pelo usuário não suportam o == operador por padrão. Para dar suporte ao == operador, uma struct definida pelo usuário deve sobrecarregá-lo .
As tuplas C# têm suporte integrado para os == operadores e != . Para obter mais informações, consulte a seção Igualdade de tupla do artigo Tipos de tupla.
Igualdade dos tipos de referência
Por padrão, os operandos de tipo de referência, excluindo registros, sã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 utilizador suportam o == operador por defeito. No entanto, um tipo de referência pode sobrecarregar o == operador. Se um tipo de referência sobrecarregar o == operador, use o Object.ReferenceEquals método para verificar se duas referências desse tipo se referem ao mesmo objeto.
Igualdade de tipos de registo
Os tipos de registro suportam os 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 valores correspondentes de todos os campos e 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 dos membros do tipo de referência é comparada usando as suas implementações específicas de igualdade.
Igualdade de cadeia de caracteres
Dois operandos de cadeia de caracteres são iguais quando ambos são null ou ambas as instâncias de cadeia de caracteres são do mesmo comprimento e têm 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
As 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, consulte Como comparar cadeias de caracteres em C#.
Delegar igualdade
Dois operandos delegados do mesmo tipo de tempo de execução são iguais quando ambos null ou as 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 recetor. O recetor é a instância de um objeto representado por this quando 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)
Para obter mais informações, consulte a seção Delegar operadores de igualdade da especificação da linguagem C#.
Os delegados que resultam da avaliação de expressões lambda semanticamente idênticas não são iguais, como mostra o exemplo seguinte:
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 retorna true se os seus operandos não forem iguais, e false caso contrário. Para os operandos dos tipos internos, a expressão x != y produz o mesmo resultado que a expressão !(x == y). Para obter mais informações sobre igualdade de tipo, consulte a seção Operador de igualdade.
O exemplo seguinte 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 classes
Os registos gerem a herança corretamente, sem trabalho manual. A igualdade gerada pelo compilador verifica tanto o tipo de runtime como todas as propriedades declaradas, pelo que satisfaz automaticamente os requisitos de simetria e transitividade. Prefere record uma hierarquia manual e não selada quando a igualdade de valores é o objetivo.
Importante
Use record sempre que possível — o compilador gera todos os membros de igualdade necessários para si. A implementação manual só é necessária quando o seu tipo deve derivar de uma classe não-registo ou tem outras restrições que impeçam record.
Implementar manualmente a comparação de igualdade quando um tipo não pode ser um registo
Aqui está uma implementação manual mínima para um tipo de valor que não pode ser um registo:
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 obrigatórios: Equals(T?) como comparação principal, override Equals(object?) para chamadas ao nível do objeto, e override GetHashCode() assim as coleções baseadas em hash funcionam corretamente.
HashCode.Combine é um auxiliar de biblioteca que constrói um hash a partir dos mesmos valores usados por Equals. Implementar IEquatable<T> (a Equals(T?) sobrecarga) é opcional, mas evita o boxing quando os que chamam já têm o tipo de betão.
Quando também defines == e !=, a linguagem requer que sejam um par; os avisos CS0660 e CS0661 lembram-te de manter os quatro membros consistentes.
Com os três membros acima no lugar, Equals reflete igualdade de valor, mas == ainda assim testa a identidade porque ainda não foi declarado nenhum == operador:
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 deve também satisfazer o contrato de equivalência (assuma x, y, e z são não nulos):
-
Reflexivo:
x.Equals(x)devolvetrue. -
Simétrico:
x.Equals(y)devolve o mesmo valor quey.Equals(x). -
Transitivo: se
x.Equals(y)ey.Equals(z)são ambostrue, entãox.Equals(z)devem sertrue. -
Consistente: chamadas sucessivas para
x.Equals(y)devolver o mesmo valor desde que nenhum objeto mude. -
Comportamento nulo:
x.Equals(null)devolvefalse;x.Equals(y)não deve lançar quando chamado a um não-nulox.
A igualdade de valores numa hierarquia de classes não selada requer mais cuidado do que numa classe selada para satisfazer as regras simétricas e transitivas. O risco é que IEquatable<T>.Equals(T? other) o despacho segue o tipo declarado (o tipo escrito na declaração da variável) da variável, e não o seu tipo de runtime. Se Shape declara um não-virtualEquals(Shape? other), uma variável tipada como Shape que tem um Circle em tempo de execução Shape.Equalsinvoca — ignorando Circlesilenciosamente campos específicos de . Dois Circle objetos com raios diferentes podem comparar-se como iguais quando acedidos através de uma Shape variável.
O padrão correto requer dois requisitos de cooperação: fazer o método virtual tipado Equals para que cada classe derivada possa estender a comparação, e adicionar um GetType() == other.GetType() guardião na implementação da classe base para que objetos de diferentes tipos de execução nunca sejam considerados iguais.
Implementação da 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:
-
virtualtipadoEquals: cada classe derivada sobrescreve este método para aumentar a comparação com os seus próprios campos. -
GetType()Guarda:GetType() == other.GetType()impede que ACircleseja igual a AShapecom a mesma cor, e impede que objetos de diferentes tipos derivados sejam iguais entre si. -
GetHashCodeincluiGetType(): porque dois objetos são iguais apenas quando os seus tipos de tempo de execução coincidem,GetHashCodedeve fazer hash do tipo de execução bem como dos campos de dados. OmitirGetType()aqui causa comportamento incorreto emDictionary<TKey,TValue>eHashSet<T>. -
==Delega aEquals: mantém a igualdade de operador e método consistente.
Implementação de classes derivadas
Uma classe derivada que adiciona campos sobrepõe o tipo Equals, lança para o seu próprio tipo, chama base.Equals, e depois compara os 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) faz cumprir a GetType() guarda e verifica os campos partilhados. O cast via other is Circle c falha rapidamente quando o argumento é de qualquer outro tipo derivado Shape .
Utilização através de uma variável do 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)
As classes seladas são mais simples
Não se pode subclassificar uma sealed classe, por isso os tipos de compilação e runtime coincidem sempre. Não precisas do GetType() guarda nem virtual do despacho. O IEquatable<T> padrão mostrado em Implementar igualdade por si próprio quando um tipo não pode ser um registo é correto e completo para uma classe selada.
Capacidade de sobrecarga do operador
Pode sobrecarregar os == operadores e != num tipo definido pelo utilizador. Se sobrecarregar um destes dois operadores, também deve sobrecarregar o outro operador.
Não se pode sobrecarregar explicitamente os == operadores e != num tipo de registo. Para alterar o comportamento dos == operadores e != para o tipo Tde registo , 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 relacionais e de teste de tipo da especificação da linguagem C#.
Para mais informações sobre igualdade de tipos de registo, consulte a secção Membros da Igualdade da especificação da linguagem C#.