Mi comprensión de estos tres fue:
.Equals()
pruebas de igualdad de datos (a falta de una mejor descripción)..Equals()
puede devolver True para diferentes instancias del mismo objeto, y este es el método que se anula con más frecuencia..ReferenceEquals()
comprueba si dos objetos son la misma instancia y no se pueden anular.==
es el mismo que elReferenceEquals()
predeterminado, pero PUEDE anularse.
Pero la estación C # dice:
En la clase de objeto, los métodos
Equals
yReferenceEquals
son semánticamente equivalentes, excepto queReferenceEquals
funciona solo en instancias de objeto. ElReferenceEquals
método es estático.
Ahora no lo entiendo. ¿Alguien puede arrojar algo de luz sobre esto?
Respuestas:
La fuente 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".
Está ligeramente en lo cierto acerca de las diferencias en los significados semánticos de cada uno (aunque "diferentes instancias del mismo objeto" parece un poco confuso, probablemente debería leer "diferentes instancias del mismo tipo ) y sobre cuáles se pueden anular.
Si dejamos eso de lado, vamos a tratar con la última parte de su pregunta, es decir, cómo funcionan con
System.Object
instancias ySystem.Object
referencias simples (necesitamos ambos para esquivar la naturaleza no polimórfica de==
). Aquí, las tres operaciones funcionarán de manera equivalente , pero con una advertencia:Equals
no se puede invocarnull
.Equals
es un método de instancia que toma un parámetro (que puede sernull
). Dado que es un método de instancia (debe invocarse en un objeto real), no se puede invocar en unanull
referencia.ReferenceEquals
es un método estático que toma dos parámetros, cualquiera o ambos pueden sernull
. Dado que es estático (no asociado con una instancia de objeto ), no arrojará unNullReferenceException
bajo ninguna circunstancia.==
es un operador que, en este caso (object
), se comporta de forma idénticaReferenceEquals
. Tampoco arrojará unNullReferenceException
.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
fuente
.Equals()
)?object
clase" . Creo que te saltaste esa parte. Porque de lo contrario no estaría hablando de anularlo.object
clase.Equals
también es un método estático en elobject
que toma dos parámetros. Entonces uno o ambos pueden sernull
.Eche un vistazo a este artículo de MSDN sobre el tema.
Creo que los puntos pertinentes son:
¡Espero que esto ayude!
fuente
Su comprensión de .ReferenceEquals es correcta.
.Equals verifica la igualdad de datos para tipos de valor y la igualdad de referencia para tipos sin valor (objetos generales).
.Equals se puede anular para que los objetos realicen algún tipo de verificación de igualdad de datos
EDITAR: Además, .ReferenceEquals no se puede usar en tipos de valor (bueno, puede, pero siempre será falso)
fuente
Quiero agregar mis cinco centavos sobre la comparación con "nulo".
ReferenceEquals (objeto, objeto) es lo mismo que "(objeto) arg1 == arg2" (así que en el caso de tipos de valor, obtienes boxeo y lleva tiempo). Pero este método es la única forma 100% segura de verificar que su argumento sea nulo en varias situaciones, como
== y es igual a (). ¿Por qué digo que ReferenceEquals es 100% seguro con comprobaciones nulas? Imagine que escribe extensiones genéricas en bibliotecas 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 persistencia). Intenta verificar si su argumento es nulo y luego llama a la operación de miembro en él. Sorpresa, PUEDE tener NullRef aquí. Porque el 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 (== se puede usar solo si su tipo es "clase"), el operador == es el mismo que el objeto . ReferenceEquals (..). La implementación igual siempre se usa desde el tipo final, ya que es virtual.
Entonces, mi recomendación es que, cuando escriba sus propios tipos o derive de tipos conocidos, puede usar == para verificar si hay nulos. De lo contrario, use object.ReferenceEquals (arg, null).
fuente
En la clase Object .Equals implementa identidad, no igualdad. Comprueba 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; }
Mientras implementa .Equals en su clase, debe llamar a la clase base .Equals solo si la clase base no es Object. Sí, eso es complicado.
Aún más, como las clases derivadas pueden anular .Equals y, por lo tanto, no puede llamarlo para verificar la identidad, Microsoft agregó el método estático .ReferenceEquals.
Si usa alguna clase, entonces lógicamente .Equals comprueba la igualdad y .ReferenceEquals comprueba la identidad.
fuente
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.
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://stackoverflow.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); } }
fuente
Equals()
comprueba el código hash o la equivalencia según el tipo subyacente (valor / referencia) yReferenceEquals()
está destinado a comprobar siempre el código hash.ReferenceEquals
devuelvetrue
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
fuente