Non c'è una risposta semplice a questa domanda. Chiunque dice che usa sempre l'uno o l'altro ti sta dando dei cattivi consigli, secondo me.
Esistono diversi metodi che è possibile chiamare per confrontare le istanze dell'oggetto. Dati due istanze di oggetti a
e b
, si potrebbe scrivere:
Object.Equals(a,b)
Object.ReferenceEquals(a,b)
a.Equals(b)
a == b
Questi potrebbero tutti fare cose diverse!
Object.Equals(a,b)
sarà (per default) eseguire confronto di uguaglianza riferimento tipi di riferimento e confronto bit per bit sui tipi di valori.Dalla documentazione MSDN:
L'implementazione predefinita di Equals sostiene l'uguaglianza di riferimento per tipi di riferimento, e l'uguaglianza bit per i tipi di valore. L'uguaglianza di riferimento significa che i riferimenti oggetto che sono confrontati con si riferiscono allo stesso oggetto. Equality bitwise significa che gli oggetti confrontati hanno la stessa rappresentazione binaria .
Si noti che un tipo derivato potrebbe sovrascrivere il metodo Equals a uguaglianza valore dell'attrezzo. Il valore equality significa che gli oggetti confrontati hanno lo stesso valore ma diverse rappresentazioni binarie .
Nota l'ultimo paragrafo sopra ... ne discuteremo un po 'più tardi.
Object.ReferenceEquals(a,b)
esegue solo il confronto dell'uguaglianza di riferimento. Se i tipi passati sono tipi di valore in box, il risultato è sempre false
.
a.Equals(b)
chiama il metodo di istanza virtuale di Object
, che il tipo di a
poteva ignorare di fare tutto ciò che vuole. La chiamata viene eseguita utilizzando la distribuzione virtuale, quindi il codice che viene eseguito dipende dal tipo di esecuzione di a
.
a == b
richiama l'operatore di overload statico del ** fase di compilazione tipo * di a
. Se l'implementazione di tale operatore richiama i metodi di istanza su a
o b
, potrebbe dipendere anche dai tipi di runtime dei parametri. Dal momento che l'invio si basa sui tipi nell'espressione, possono produrre risultati diversi:
Frog aFrog = new Frog();
Frog bFrog = new Frog();
Animal aAnimal = aFrog;
Animal bAnimal = bFrog;
// not necessarily equal...
bool areEqualFrogs = aFrog == bFrog;
bool areEqualAnimals = aAnimal = bAnimal;
Quindi, sì, c'è la vulnerabilità di controllo per i null che utilizzano operator ==
. In pratica, la maggior parte dei tipi non è sovraccarico ==
- ma non c'è mai una garanzia.
Il metodo di istanza Equals()
non è migliore qui. Mentre l'implementazione predefinita esegue controlli di uguaglianza di riferimento/bit per bit, è possibile che un tipo sovrascriva il metodo membro Equals()
, nel qual caso verrà chiamata questa implementazione. Un'implementazione fornita dall'utente può restituire ciò che vuole, anche quando si confronta con null.
Ma che dire della versione statica di Object.Equals()
che chiedi? Questo può finire con l'esecuzione del codice utente? Bene, si scopre che la risposta è SI. L'attuazione di Object.Equals(a,b)
espande per qualcosa sulla falsariga di:
((object)a == (object)b) || (a != null && b != null && a.Equals(b))
Si può provare questo per te:
class Foo {
public override bool Equals(object obj) { return true; } }
var a = new Foo();
var b = new Foo();
Console.WriteLine(Object.Equals(a,b)); // outputs "True!"
Di conseguenza, è possibile per l'istruzione: Object.Equals(a,b)
per eseguire codice utente quando nessuno dei tipi nella chiamata sono null
.Notare che Object.Equals(a,b)
non chiama chiama la versione di istanza di Equals()
quando uno degli argomenti è null.
In breve, il tipo di comportamento di confronto che si ottiene può variare in modo significativo, a seconda del metodo che si sceglie di chiamare. Un commento qui, tuttavia: Microsoft non documenta ufficialmente il comportamento interno di Object.Equals(a,b)
. Se avete bisogno di un gaurantee rivestito di confrontare un riferimento a nulla, senza qualsiasi altro codice in esecuzione di ferro, si vuole Object.ReferenceEquals()
:
Object.ReferenceEquals(item, null);
Questo metodo rende l'intento extremently chiaro - che non siano specificamente aspettate il risultato di essere il confronto di due riferimenti per l'uguaglianza di riferimento. Il vantaggio qui sopra usando qualcosa come Object.Equals(a,null)
, è che è meno probabile che qualcuno arriverà più tardi e dire:
"Hey, questo è imbarazzante, cerchiamo di sostituirlo con: a.Equals(null)
o a == null
che potenzialmente può essere diverso.
Diamo iniettare qualche pragmatismo qui, tuttavia. Finora abbiamo parlato della possibilità di diverse modalità di confronto per ottenere risultati diversi. Mentre questo è certamente il caso, ci sono alcuni tipi dove è sicuro per wr ite a == null
. Le classi .NET integrate come String
e Nullable<T>
hanno una semantica ben definita per il confronto. Inoltre, sono sealed
- impedendo qualsiasi modifica al loro comportamento attraverso l'ereditarietà. Quanto segue è abbastanza comune (e corretta):
string s = ...
if(s == null) { ... }
E 'inutile (e brutto) di scrivere:
if(ReferenceEquals(s,null)) { ... }
Quindi, in alcuni casi limitati, utilizzando ==
è sicuro, e appropriato.
Ho pensato che la mia risposta (la risposta di Microsoft per procura) è una risposta piuttosto semplice. – mquander
Domande come: * "dovrei sempre/mai fare X" * implicano un vuoto di conoscenza sulle sfumature del soggetto in questione. Ho sentito che un po 'più di dettaglio sarebbe utile qui per chiarire perché non penso che una risposta semplice sia significativa. – LBushkin
Il metodo statico 'object.Equals (a, b)' può, in effetti, chiamare il metodo 'Equals' sovraccarico/virtuale sull'oggetto' a'. Se ricordo correttamente, fa qualcosa come '((oggetto) a == (oggetto) b) || (a! = null && b! = null && a.Equals (b)) '. (Questo è irrilevante quando si confronta con 'null', che è ciò che l'OP chiede, ma è rilevante per il caso generale.) – LukeH