C #. Equals (), .ReferenceEquals () e == operatore

La mia comprensione di questi tre era:

Ma la stazione C # afferma:

Nella class dell’object, i metodi Equals e ReferenceEquals sono semanticamente equivalenti, tranne per il fatto che ReferenceEquals funziona solo su istanze di object. Il metodo ReferenceEquals è statico.

Ora non capisco. Qualcuno può far luce su questo?

La fonte della tua confusione sembra essere che ci sia un errore di battitura nell’estratto dalla stazione C #, che dovrebbe contenere: “… eccetto che l’ Equals funziona solo su istanze di oggetti.” Il metodo ReferenceEquals è statico. ”


Sei vagamente corretto riguardo le differenze nei significati semantici di ciascuna (anche se “diverse istanze dello stesso object” sembrano un po ‘confuse, dovrebbe probabilmente leggere “istanze diverse dello stesso tipo” e su quali possono essere ignorate.

Se lasciamo da parte, prendiamo in considerazione l’ultimo bit della tua domanda, cioè come funzionano con semplici istanze System.Object e riferimenti System.Object (abbiamo bisogno sia di evitare la natura non polimorfica di == ). Qui, tutte e tre le operazioni funzioneranno in modo equivalente , ma con un avvertimento: Equals non può essere invocato su null .

Equals è un metodo di istanza che accetta un parametro (che può essere null ). Poiché è un metodo di istanza (deve essere invocato su un object reale), non può essere invocato su un riferimento null .

ReferenceEquals è un metodo statico che accetta due parametri, entrambi i quali possono essere null . Poiché è statico (non associato a un’istanza dell’object), non genererà NullReferenceException in nessuna circostanza.

== è un operatore che, in questo caso ( object ), si comporta in modo identico a ReferenceEquals . NullReferenceException una NullReferenceException .

Illustrare:

 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 

Dai un’occhiata a questo articolo di MSDN sull’argomento.

Penso che i punti pertinenti siano:

Per verificare l’uguaglianza di riferimento, utilizzare ReferenceEquals. Per verificare l’uguaglianza dei valori, utilizzare Uguale o Uguale.

Per impostazione predefinita, l’operatore == verifica l’uguaglianza di riferimento determinando se due riferimenti indicano lo stesso object, quindi i tipi di riferimento non devono implementare l’operatore == per ottenere questa funzionalità. Quando un tipo è immutabile, il che significa che i dati contenuti nell’istanza non possono essere modificati, l’operatore di overload == per confrontare l’uguaglianza del valore anziché l’uguaglianza di riferimento può essere utile perché, come oggetti immutabili, possono essere considerati uguali purché abbiano il stesso valore

Spero che questo ti aiuti!

La tua comprensione di .ReferenceEquals è corretta.

.Equals verifica l’uguaglianza dei dati per i tipi di valore e l’uguaglianza di riferimento per i tipi non di valore (oggetti generali).

. Gli oggetti possono essere sovrascritti affinché gli oggetti eseguano una qualche forma di controllo di uguaglianza dei dati

EDIT: Inoltre, .ReferenceEquals non può essere utilizzato sui tipi di valore (beh, può, ma sarà sempre falso)

Vuoi aggiungere i miei cinque centesimi sul confronto con “null”.

  1. ReferenceEquals (object, object) è lo stesso di “(object) arg1 == arg2” (quindi in caso di tipi di valore, si ottiene la boxe e ci vuole tempo). Ma questo metodo è l’unico modo sicuro al 100% per verificare il tuo argomento per null in diverse situazioni, come

    • a) prima di chiamare i membri tramite. operatore
    • b) verifica il risultato dell’operatore AS.
  2. == e uguale a () . Perché sto dicendo che ReferenceEquals è sicuro al 100% con controlli null? Immagina di scrivere estensioni generiche nelle librerie core cross-project e ti consente di limitare il tipo di parametro generico a un tipo di dominio. Questo tipo può introdurre l’operatore “==” – ora o dopo (e credetemi, ho visto molto, questo operatore può avere una logica molto “strana”, specialmente se si tratta di oggetti di dominio o persistenza). Si tenta di verificare l’argomento per null e quindi chiamare l’operazione membro su di esso. Sorpresa, puoi avere NullRef qui. Perché l’operatore == è quasi uguale a Equals () – molto personalizzato e molto imprevedibile. C’è una differenza però, che dovrebbe essere presa in considerazione – se non si limita il parametro generico ad alcuni tipi personalizzati (== può essere usato solo se il proprio tipo è “class”), l’operatore == è lo stesso dell’object .ReferenceEquals (..). L’implementazione uguale è sempre utilizzata dal tipo finale, in quanto è virtuale.

Quindi la mia raccomandazione è, quando scrivi i tuoi tipi o derivano da tipi noti, puoi usare == per verificare null. Altrimenti usa object.ReferenceEquals (arg, null).

Nella class Object. Equals implementa l’identity framework, non l’uguaglianza. Controlla se i riferimenti sono uguali. Il codice potrebbe essere così:

 public virtual Boolean Equals(Object other) { if (this == other) return true; return false; } 

Durante l’implementazione di .Equals nella class dovresti chiamare la class base .Equals solo se la class base non è Object. Sì, è complicato.

Ancora di più, dato che le classi derivate possono sovrascrivere .Equal e quindi non è ansible chiamarlo per controllare l’id quadro. Microsoft ha aggiunto il metodo statico .ReferenceEquals.

Se usi qualche class per te logicamente . Controlli di equità per l’uguaglianza e controlli .ReferenceEquals per l’identity framework.

Ho ampliato l’eccellente risposta di Ani per mostrare le principali differenze quando si tratta di tipi di riferimento e metodi di uguaglianza sovrascritti.

  • Puoi vedere una versione funzionante di questo codice qui: https://dotnetfiddle.net/dFKMhB
  • In alternativa, questo codice viene incollato su LinqPad ed eseguito come 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 (ie 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 (ie 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("eg 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 //ie 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); } } 

Equals() controlla il codice hash o l’equivalenza a seconda del tipo sottostante (Value / Reference) e ReferenceEquals() scopo di verificare sempre il codice hash. ReferenceEquals restituisce true se entrambi gli oggetti puntano alla stessa posizione di 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