elvis - C#.Equals(),.ReferenceEquals() y== operador
operador and en c# (7)
Mi comprensión de estos tres fue:
.Equals()
prueba la igualdad de datos (por la falta de una mejor descripción)..Equals()
puede devolver True para diferentes instancias del mismo objeto, y este es el método reemplazado más comúnmente..ReferenceEquals()
prueba si dos objetos son la misma instancia o no y no pueden anularse.==
es lo mismo queReferenceEquals()
de forma predeterminada, pero esto PUEDE ser anulado.
Pero la estación C # declara:
En la clase de objeto, los métodos
Equals
yReferenceEquals
son semánticamente equivalentes, excepto queReferenceEquals
solo funciona en instancias de objetos. El métodoReferenceEquals
es estático.
Ahora no lo entiendo ¿Alguien puede arrojar algo de luz sobre esto?
Echa un vistazo a este artículo de MSDN sobre el tema.
Creo que los puntos pertinentes son:
Para verificar la igualdad de referencia, use ReferenceEquals. Para verificar la igualdad de valor, use Igual o Igual.
De forma predeterminada, el operador == prueba la igualdad de referencia al determinar si dos referencias indican el mismo objeto, por lo que los tipos de referencia no necesitan implementar operator == para obtener esta funcionalidad. Cuando un tipo es inmutable, lo que significa que los datos contenidos en la instancia no se pueden modificar, la sobrecarga del operador == para comparar la igualdad de valores en lugar de la igualdad de referencia puede ser útil porque, como objetos inmutables, pueden considerarse iguales siempre que tengan el mismo valor.
¡Espero que esto ayude!
El origen de su confusión parece ser que hay un error tipográfico en el extracto de la estación C #, que debería decir: "... excepto que Equals solo funciona en instancias de objetos. El método ReferenceEquals es estático".
Usted es vagamente correcto acerca de las diferencias en los significados semánticos de cada uno (aunque "diferentes instancias del mismo objeto" parece un poco confusa, probablemente debería leer "instancias diferentes del mismo tipo ) y sobre las cuales puede anularse.
Si dejamos eso a un lado, vamos a tratar con el último bit de su pregunta, es decir, cómo funcionan con instancias simples de System.Object
y referencias de System.Object
(necesitamos que ambos eludan la naturaleza no polimórfica de ==
). Aquí, las tres operaciones funcionarán de manera excelente , pero con una advertencia: no se puede invocar Equals
en null
.
Equals
es un método de instancia que toma un parámetro (que puede ser null
). Como es un método de instancia (debe invocarse en un objeto real), no se puede invocar en una referencia null
.
ReferenceEquals
es un método estático que toma dos parámetros, cualquiera de los cuales puede ser null
. Como es estático (no está asociado con una instancia de objeto), no lanzará una NullReferenceException
bajo ninguna circunstancia.
==
es un operador que, en este caso ( object
), se comporta de forma idéntica a ReferenceEquals
. Tampoco arrojará una NullReferenceException
.
Para ilustrar:
object o1 = null;
object o2 = new object();
//Technically, these should read object.ReferenceEquals for clarity, but this is redundant.
ReferenceEquals(o1, o1); //true
ReferenceEquals(o1, o2); //false
ReferenceEquals(o2, o1); //false
ReferenceEquals(o2, o2); //true
o1.Equals(o1) //NullReferenceException
o1.Equals(o2) //NullReferenceException
o2.Equals(o1) //false
o2.Equals(o2) //true
En la clase de objetos .Equals implementa identidad, no igualdad. Verifica si las referencias son iguales. El código podría ser así:
public virtual Boolean Equals(Object other) {
if (this == other) return true;
return false;
}
Al implementar .Equals en su clase, debe llamar a la clase base .Equals solo si la clase base no es Object. Yeh, eso es complicado.
Aún más, dado que las clases derivadas pueden anular .Equals y, por lo tanto, no se puede llamar para verificar la identidad Microsoft agregó el método estático .ReferenceEquals.
Si utiliza alguna clase, lógicamente para usted. Iguala los controles de igualdad y .ReferenceEquals comprueba la identidad.
He ampliado la excelente respuesta de Ani para mostrar las diferencias clave cuando se trata de tipos de referencia y métodos de igualdad anulados.
- Puede ver una versión funcional de este código aquí: https://dotnetfiddle.net/dFKMhB
- Alternativamente, este código se LinqPad a LinqPad y se ejecutará como
Language: C# Program
.
.
void Main()
{
//odd os are null; evens are not null
object o1 = null;
object o2 = new object();
object o3 = null;
object o4 = new object();
object o5 = o1;
object o6 = o2;
Demo d1 = new Demo(Guid.Empty);
Demo d2 = new Demo(Guid.NewGuid());
Demo d3 = new Demo(Guid.Empty);
Debug.WriteLine("comparing null with null always yields true...");
ShowResult("ReferenceEquals(o1, o1)", () => ReferenceEquals(o1, o1)); //true
ShowResult("ReferenceEquals(o3, o1)", () => ReferenceEquals(o3, o1)); //true
ShowResult("ReferenceEquals(o5, o1)", () => ReferenceEquals(o5, o1)); //true
ShowResult("o1 == o1", () => o1 == o1); //true
ShowResult("o3 == o1", () => o3 == o1); //true
ShowResult("o5 == o1", () => o5 == o1); //true
Debug.WriteLine("...though because the object''s null, we can''t call methods on the object (i.e. we''d get a null reference exception).");
ShowResult("o1.Equals(o1)", () => o1.Equals(o1)); //NullReferenceException
ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
ShowResult("o3.Equals(o1)", () => o3.Equals(o1)); //NullReferenceException
ShowResult("o3.Equals(o2)", () => o3.Equals(o2)); //NullReferenceException
ShowResult("o5.Equals(o1)", () => o5.Equals(o1)); //NullReferenceException
ShowResult("o5.Equals(o2)", () => o5.Equals(o1)); //NullReferenceException
Debug.WriteLine("Comparing a null object with a non null object always yeilds false");
ShowResult("ReferenceEquals(o1, o2)", () => ReferenceEquals(o1, o2)); //false
ShowResult("ReferenceEquals(o2, o1)", () => ReferenceEquals(o2, o1)); //false
ShowResult("ReferenceEquals(o3, o2)", () => ReferenceEquals(o3, o2)); //false
ShowResult("ReferenceEquals(o4, o1)", () => ReferenceEquals(o4, o1)); //false
ShowResult("ReferenceEquals(o5, o2)", () => ReferenceEquals(o3, o2)); //false
ShowResult("ReferenceEquals(o6, o1)", () => ReferenceEquals(o4, o1)); //false
ShowResult("o1 == o2)", () => o1 == o2); //false
ShowResult("o2 == o1)", () => o2 == o1); //false
ShowResult("o3 == o2)", () => o3 == o2); //false
ShowResult("o4 == o1)", () => o4 == o1); //false
ShowResult("o5 == o2)", () => o3 == o2); //false
ShowResult("o6 == o1)", () => o4 == o1); //false
ShowResult("o2.Equals(o1)", () => o2.Equals(o1)); //false
ShowResult("o4.Equals(o1)", () => o4.Equals(o1)); //false
ShowResult("o6.Equals(o1)", () => o4.Equals(o1)); //false
Debug.WriteLine("(though again, we can''t call methods on a null object:");
ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
ShowResult("o1.Equals(o4)", () => o1.Equals(o4)); //NullReferenceException
ShowResult("o1.Equals(o6)", () => o1.Equals(o6)); //NullReferenceException
Debug.WriteLine("Comparing 2 references to the same object always yields true");
ShowResult("ReferenceEquals(o2, o2)", () => ReferenceEquals(o2, o2)); //true
ShowResult("ReferenceEquals(o6, o2)", () => ReferenceEquals(o6, o2)); //true <-- Interesting
ShowResult("o2 == o2", () => o2 == o2); //true
ShowResult("o6 == o2", () => o6 == o2); //true <-- Interesting
ShowResult("o2.Equals(o2)", () => o2.Equals(o2)); //true
ShowResult("o6.Equals(o2)", () => o6.Equals(o2)); //true <-- Interesting
Debug.WriteLine("However, comparing 2 objects may yield false even if those objects have the same values, if those objects reside in different address spaces (i.e. they''re references to different objects, even if the values are similar)");
Debug.WriteLine("NB: This is an important difference between Reference Types and Value Types.");
ShowResult("ReferenceEquals(o4, o2)", () => ReferenceEquals(o4, o2)); //false <-- Interesting
ShowResult("o4 == o2", () => o4 == o2); //false <-- Interesting
ShowResult("o4.Equals(o2)", () => o4.Equals(o2)); //false <-- Interesting
Debug.WriteLine("We can override the object''s equality operator though, in which case we define what''s considered equal");
Debug.WriteLine("e.g. these objects have different ids, so we treat as not equal");
ShowResult("ReferenceEquals(d1,d2)",()=>ReferenceEquals(d1,d2)); //false
ShowResult("ReferenceEquals(d2,d1)",()=>ReferenceEquals(d2,d1)); //false
ShowResult("d1 == d2",()=>d1 == d2); //false
ShowResult("d2 == d1",()=>d2 == d1); //false
ShowResult("d1.Equals(d2)",()=>d1.Equals(d2)); //false
ShowResult("d2.Equals(d1)",()=>d2.Equals(d1)); //false
Debug.WriteLine("...whilst these are different objects with the same id; so we treat as equal when using the overridden Equals method...");
ShowResult("d1.Equals(d3)",()=>d1.Equals(d3)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we''d expect)
ShowResult("d3.Equals(d1)",()=>d3.Equals(d1)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we''d expect)
Debug.WriteLine("...but as different when using the other equality tests.");
ShowResult("ReferenceEquals(d1,d3)",()=>ReferenceEquals(d1,d3)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
ShowResult("ReferenceEquals(d3,d1)",()=>ReferenceEquals(d3,d1)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
ShowResult("d1 == d3",()=>d1 == d3); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
ShowResult("d3 == d1",()=>d3 == d1); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
Debug.WriteLine("For completeness, here''s an example of overriding the == operator (wihtout overriding the Equals method; though in reality if overriding == you''d probably want to override Equals too).");
Demo2 d2a = new Demo2(Guid.Empty);
Demo2 d2b = new Demo2(Guid.NewGuid());
Demo2 d2c = new Demo2(Guid.Empty);
ShowResult("d2a == d2a", () => d2a == d2a); //true
ShowResult("d2b == d2a", () => d2b == d2a); //false
ShowResult("d2c == d2a", () => d2c == d2a); //true <-- interesting
ShowResult("d2a != d2a", () => d2a != d2a); //false
ShowResult("d2b != d2a", () => d2b != d2a); //true
ShowResult("d2c != d2a", () => d2c != d2a); //false <-- interesting
ShowResult("ReferenceEquals(d2a,d2a)", () => ReferenceEquals(d2a, d2a)); //true
ShowResult("ReferenceEquals(d2b,d2a)", () => ReferenceEquals(d2b, d2a)); //false
ShowResult("ReferenceEquals(d2c,d2a)", () => ReferenceEquals(d2c, d2a)); //false <-- interesting
ShowResult("d2a.Equals(d2a)", () => d2a.Equals(d2a)); //true
ShowResult("d2b.Equals(d2a)", () => d2b.Equals(d2a)); //false
ShowResult("d2c.Equals(d2a)", () => d2c.Equals(d2a)); //false <-- interesting
}
//this code''s just used to help show the output in a friendly manner
public delegate bool Statement();
void ShowResult(string statementText, Statement statement)
{
try
{
Debug.WriteLine("/t{0} => {1}",statementText, statement());
}
catch(Exception e)
{
Debug.WriteLine("/t{0} => throws {1}",statementText, e.GetType());
}
}
class Demo
{
Guid id;
public Demo(Guid id) { this.id = id; }
public override bool Equals(object obj)
{
return Equals(obj as Demo); //if objects are of non-comparable types, obj will be converted to null
}
public bool Equals(Demo obj)
{
if (obj == null)
{
return false;
}
else
{
return id.Equals(obj.id);
}
}
//if two objects are Equal their hashcodes must be equal
//however, if two objects hash codes are equal it is not necessarily true that the objects are equal
//i.e. equal objects are a subset of equal hashcodes
//more info here: https://.com/a/371348/361842
public override int GetHashCode()
{
return id.GetHashCode();
}
}
class Demo2
{
Guid id;
public Demo2(Guid id)
{
this.id = id;
}
public static bool operator ==(Demo2 obj1, Demo2 obj2)
{
if (ReferenceEquals(null, obj1))
{
return ReferenceEquals(null, obj2); //true if both are null; false if only obj1 is null
}
else
{
if(ReferenceEquals(null, obj2))
{
return false; //obj1 is not null, obj2 is; therefore false
}
else
{
return obj1.id == obj2.id; //return true if IDs are the same; else return false
}
}
}
// NB: We also HAVE to override this as below if overriding the == operator; this is enforced by the compiler. However, oddly we could choose to override it different to the below; but typically that would be a bad idea...
public static bool operator !=(Demo2 obj1, Demo2 obj2)
{
return !(obj1 == obj2);
}
}
Quiero agregar mis cinco centavos para comparar con "nulo".
ReferenceEquals (object, object) es lo mismo que "(object) arg1 == arg2" (por lo tanto, en el caso de los tipos de valor, obtienes boxeo y lleva tiempo). Pero este método es la única forma 100% segura de verificar su argumento de nulo en varias situaciones, como
- a) antes de llamar a sus miembros a través de. operador
- b) verificar el resultado del operador AS.
== e Igual () . ¿Por qué digo que ReferenceEquals es 100% seguro con null-checkings? Imagine que escribe extensiones genéricas en las librerías centrales de proyectos cruzados, y digamos que restringe el tipo de parámetro genérico a algún tipo de dominio. Este tipo puede introducir el operador "==" - ahora o más tarde (y créanme, he visto mucho, este operador puede tener una lógica muy "extraña", especialmente si se trata de objetos de dominio o de persistencia). Intenta comprobar su argumento para nulo y luego llamar a la operación miembro en él. Sorpresa, PUEDES tener NullRef aquí. Porque == operador es casi lo mismo que Equals () - muy personalizado y muy impredecible. Sin embargo, hay una diferencia, que debe tenerse en cuenta: si no restringe su parámetro genérico a algún tipo personalizado (== puede usarse solo si su tipo es "clase"), == operador es lo mismo que objeto .ReferenceEquals (..). La implementación igual siempre se usa del tipo final, ya que es virtual.
Así que mi recomendación es que, cuando escriba sus propios tipos o derive de tipos bien conocidos, puede usar == para comprobar nulo. De lo contrario, use object.ReferenceEquals (arg, null).
Su comprensión de .ReferenceEquals es correcta.
.Equals verifica la igualdad de datos para los tipos de valores, y la igualdad de referencia para los tipos sin valor (objetos generales).
.Equals puede anularse para que los objetos realicen alguna forma de verificación de igualdad de datos
EDITAR: Además, .ReferenceEquals no se puede utilizar en los tipos de valores (así puede, pero siempre será falso)
Equals()
comprueba el código hash o la equivalencia según el tipo subyacente (valor / referencia) y ReferenceEquals()
está destinado a verificar siempre el código hash. ReferenceEquals
devuelve true
si ambos objetos apuntan a la misma ubicación de memoria.
double e = 1.5;
double d = e;
object o1 = d;
object o2 = d;
Console.WriteLine(o1.Equals(o2)); // True
Console.WriteLine(Object.Equals(o1, o2)); // True
Console.WriteLine(Object.ReferenceEquals(o1, o2)); // False
Console.WriteLine(e.Equals(d)); // True
Console.WriteLine(Object.Equals(e, d)); // True
Console.WriteLine(Object.ReferenceEquals(e, d)); // False