Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Los operadores == (igualdad) y != (desigualdad) comprueban si los operandos son iguales. Los tipos de valor son iguales cuando su contenido es igual. Los tipos de referencia son iguales cuando las dos variables hacen referencia al mismo almacenamiento.
La documentación de referencia del lenguaje C# cubre la versión más reciente publicada del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.
La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.
Sugerencia
Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.
Puede usar el is operador de coincidencia de patrones como alternativa a una == prueba al probar con un valor constante. El is operador usa la semántica de igualdad predeterminada para todos los tipos de valor y referencia.
Operador de igualdad ==
El operador de igualdad == devuelve true si sus operandos son iguales; en caso contrario, devuelve false.
Igualdad entre tipos de valor
Los operandos de los tipos de valor integrados son iguales si sus valores son iguales:
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 los ==operadores ,< , >, <=y >= , si alguno de los operandos no es un número (Double.NaN o Single.NaN), el resultado de la operación es false. Esa condición significa que el NaN valor no es mayor que, menor o igual que cualquier otro double valor (o float), incluido NaN. Para obtener más información y ejemplos, vea el artículo de referencia Double.NaN o Single.NaN.
Dos operandos del mismo tipo enum son iguales si los valores correspondientes del tipo entero subyacente son iguales.
Los tipos struct definidos por el usuario no son compatibles con el operador == de forma predeterminada. Para admitir el operador ==, un elemento struct definido por el usuario debe sobrecargarlo.
Las tuplas de C# tienen compatibilidad integrada con los == operadores y != . Si desea más información, consulte la sección Igualdad de tupla del artículo Tipos de tupla.
Igualdad entre tipos de referencia
De forma predeterminada, los operandos de tipo de referencia, excepto los registros, son iguales si hacen referencia al mismo 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 se muestra en el ejemplo anterior, los tipos de referencia definidos por el usuario admiten el == operador de forma predeterminada. Sin embargo, un tipo de referencia puede sobrecargar el operador ==. Si un tipo de referencia sobrecarga el operador ==, use el método Object.ReferenceEquals para comprobar si dos referencias de ese tipo hacen referencia al mismo objeto.
Igualdad entre tipos de registro
Los tipos de registro admiten los operadores de == y != que, de forma predeterminada, proporcionan semántica de igualdad de valores. Es decir, dos operandos de registro son iguales cuando ambos son null o los valores correspondientes de todos los campos y las propiedades implementadas automáticamente son iguales.
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 se muestra en el ejemplo anterior, la igualdad de miembros de tipo de referencia se compara con sus implementaciones de igualdad específicas.
Igualdad entre cadenas
Dos operandos string son iguales si ambos son null, o bien si las instancias de ambas cadenas tienen la misma longitud y los mismos caracteres en cada posición de caracteres:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Las comparaciones de igualdad de cadenas son comparaciones ordinales que distinguen mayúsculas de minúsculas. Para obtener más información sobre la comparación de cadenas, vea Cómo comparar cadenas en C# .
Igualdad entre delegados
Dos operandos delegados del mismo tipo en tiempo de ejecución son iguales cuando ambas son null o sus listas de invocación tienen la misma longitud y tienen entradas iguales en cada posición:
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
Las entradas iguales de una lista de invocación incluyen todos los parámetros fijos de la invocación, incluido el receptor. El receptor es la instancia de un objeto representado por this cuando se invoca la entrada.
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 obtener más información, vea la sección sobre los operadores de igualdad entre delegados de la Especificación del lenguaje C#.
Los delegados que proceden de evaluar expresiones lambda semánticamente idénticas no son iguales , como se muestra en el ejemplo siguiente:
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 desigualdad !=
El operador != de desigualdad devuelve true si sus operandos no son iguales y false , de lo contrario, . Para los operandos de los tipos integrados, la expresión x != y genera el mismo resultado que la expresión !(x == y). Para obtener más información sobre la igualdad de tipos, vea la sección Operador de igualdad.
En el ejemplo siguiente se muestra cómo usar el != 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
Igualdad en jerarquías de clases
Los registros controlan correctamente la herencia sin trabajo manual. La igualdad generada por el compilador comprueba tanto el tipo en tiempo de ejecución como todas las propiedades declaradas, por lo que satisface automáticamente los requisitos de simetría y transitividad. Se prefiere record sobre una jerarquía manual sin secar cuando la igualdad de valores es el objetivo.
Importante
Use record siempre que sea posible: el compilador genera todos los miembros de igualdad necesarios automáticamente. La implementación manual solo es necesaria cuando el tipo debe derivar de una clase que no es de registro o tiene otras restricciones que impiden record.
Implemente la igualdad manualmente si un tipo no puede ser un record
Esta es una implementación manual mínima para un tipo de valor que no puede ser un 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);
}
La implementación proporciona tres miembros necesarios: Equals(T?) como comparación principal, override Equals(object?) para llamadas de nivel de objeto, y override GetHashCode() por lo que las colecciones basadas en hash funcionan correctamente.
HashCode.Combine es un asistente de biblioteca que compila un hash a partir de los mismos valores usados por Equals. La implementación IEquatable<T> (la Equals(T?) sobrecarga) es opcional, pero evita la conversión boxing cuando los autores de llamadas ya tienen el tipo concreto.
Cuando también define == y !=, el lenguaje los requiere como par; las advertencias CS0660 y CS0661 le recuerdan que mantenga coherentes los cuatro miembros.
Con los tres miembros anteriores en su lugar, Equals refleja la igualdad de valores, pero == sigue probando la identidad porque aún no se ha declarado ningún == 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)
Una implementación correcta también debe cumplir el contrato de equivalencia (supongamos xque , yy z no son NULL):
-
Reflexivo:
x.Equals(x)devuelvetrue. -
Simétrico:
x.Equals(y)devuelve el mismo valor quey.Equals(x). -
Transitivo: si
x.Equals(y)yy.Equals(z)son ambostrue,x.Equals(z)debe sertrue. -
Coherente: llamadas sucesivas para
x.Equals(y)devolver el mismo valor siempre que ninguno cambie el objeto. -
Comportamiento null:
x.Equals(null)devuelvefalse;x.Equals(y)no debe iniciarse cuando se llama a en un no nullx.
La igualdad de valores en una jerarquía de clases no sellada requiere más cuidado que en una clase sellada para satisfacer las reglas simétricas y transitivas. El peligro es que IEquatable<T>.Equals(T? other) el envío sigue al tipo declarado (el tipo escrito en la declaración de variable) de la variable, no su tipo en tiempo de ejecución. Si Shape declara una variable que novirtualEquals(Shape? other) es , una variable con Shape tipo que contiene una Circle invocación Shape.Equalsen tiempo de ejecución, omite Circlede forma silenciosa los campos específicos. Dos Circle objetos con radios diferentes pueden compararse como iguales cuando se accede a través de una Shape variable.
El patrón correcto requiere dos requisitos de cooperación: convierta el método con Equals tipo para que cada clase derivada pueda ampliar la comparación y agregar una GetType() == other.GetType() protección en la implementación de clase base, por lo que los objetos de diferentes tipos en tiempo de ejecución nunca se consideran igualesvirtual.
Implementación de la clase 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);
}
Puntos clave:
-
virtualtypedEquals: cada clase derivada invalida este método para aumentar la comparación con sus propios campos. -
GetType()guard:GetType() == other.GetType()impide que unCircleelemento sea igual aShapeun con el mismo color y evita que los objetos de diferentes tipos derivados se iguale entre sí. -
GetHashCodeincluyeGetType(): dado que dos objetos son iguales solo cuando coinciden sus tipos en tiempo de ejecución,GetHashCodedebe aplicar un hash al tipo en tiempo de ejecución, así como a los campos de datos. Si se omiteGetType()aquí, se produce un comportamiento incorrecto enDictionary<TKey,TValue>yHashSet<T>. -
==delega enEquals: mantiene la igualdad de operadores y métodos coherente.
Implementación de clases derivadas
Una clase derivada que agrega campos invalida los Equalstipos , convierte en su propio tipo, llama base.Equalsa y, a continuación, compara sus propios 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) aplica la GetType() protección y comprueba los campos compartidos. La conversión a través other is Circle c de produce un error rápido cuando el argumento es de Shape cualquier otro tipo derivado.
Uso a través de una variable 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)
Las clases selladas son más sencillas
No se puede subclase una sealed clase, por lo que los tipos en tiempo de compilación y tiempo de ejecución siempre están de acuerdo. No necesita la GetType() guardia ni virtual el envío. El IEquatable<T> patrón que se muestra en Implementar la igualdad usted mismo cuando un tipo no puede ser un registro es correcto y completo para una clase sellada.
Posibilidad de sobrecarga del operador
Puede sobrecargar los == operadores y != en un tipo definido por el usuario. Si sobrecarga uno de estos dos operadores, también debe sobrecargar el otro operador.
No se pueden sobrecargar explícitamente los == operadores y != en un tipo de registro. Para cambiar el comportamiento de los == operadores y != para el tipo Tde registro , implemente el IEquatable<T>.Equals método con la firma siguiente:
public virtual bool Equals(T? other);
Especificación del lenguaje C#
Para obtener más información, vea la sección Operadores de comprobación de tipos y relacionales de la Especificación del lenguaje de C#.
Para obtener más información sobre la igualdad de tipos de registro, consulte la sección Miembros de igualdad de la especificación del lenguaje C#.