In un precedente post si parla di eguaglianza, volevo fare una piccola precisazione perché in un commento ho detto una cosa un po' sbagliata e confusa rispondendo a Diego di getto, ho per questo cancellato il commento riprendendo in maniera più estesa e spero più chiara il discorso. Prendete il seguente snippet:
Object TO3 = "test";
Object TO4 = new StringBuilder().Append("test").ToString();
Console.WriteLine("TO3 == TO4 is {0}", TO3 == TO4);
Console.WriteLine("Object.Equals(TO3,TO4) is {0}", Object.Equals(TO3, TO4));
Console.WriteLine("Object.ReferenceEquals(TO3, TO4) is {0}", Object.ReferenceEquals(TO3, TO4));
Stampa false, true, false; di questi, il terzo false è chiaro dato che il ReferenceEquals() compara per riferimento e le due stringhe sono chiaramente istanze differenti, ma per l'operatore == e per Object.Equals() avere un risultato differente può generalmente causare apprensione :D. Per capire il perché di questo comportamento bisogna prima di tutto capire come è implementato Objects.Equals (Fatto tramite reflector):
public static bool Equals(object objA, object objB) {
if (objA == objB) {
return true;
}
if ((objA != null) && (objB != null)) {
return objA.Equals(objB);
}
return false;
}
Come potete vedere prima di tutto viene fatta la comparazione con == (comparazione per riferimento) e poi se falsa viene chiamato l'equal dell'oggetto, quindi utilizzando polimorfismo, e dunque si chiama nel nostro caso lo String.Equals(), per questa ragione il secondo risultato è true, perché le istanze sono differenti ma il contenuto è eguale. In sostanza Object.Equals effettua la comparazione con l'effettivo metodo Equals implementato dall'oggetto specifico.
A questo punto basta disassemblare con il reflector il metodo Object.ReferenceEquals() e notare che la sua implementazione è return (objA == objB), quindi l'operatore == base a tutti gli effetti è la stessa cosa del ReferenceEquals() da qui si capisce perché TO3 == TO4 è false.
Se non ci fidiamo prendiamo lo snippet precedente e vediamo come è tradotto in MSIL l'istruzione TO3 == TO4:
L_0021: ldloc.0
L_0022: ldloc.1
L_0023: ceq
In sostanza si caricano nello stack le istanze dei due oggetti e poi viene chiamato ceq, l'istruzione MSIL che controlla se due oggetti nello stack sono uguali. Questo accade perché il compilatore vede l'operatore == applicato a due Object, sa che object non fa overload dell'operatore == e quindi fa un confronto per riferimento. Differente è la situazione se avessimo utilizzato l'operatore == tra due stringhe, il compilatore avrebbe prima di tutto cercato un metodo statico String.op_Equality(), e trovandolo avrebbe chiamato quello invece di fare una comparazione per riferimento.
Ora questo può suonare strano perché significa che due oggetti possono risultare differenti con un confronto tramite operatore ==, ma essere nello stesso tempo uguali chiamando un Object.Equals(objA, objB), per questo bisogna fare molta attenzione al concetto di eguaglianza. Personalmente trovo infatti questa cosa un po' fuorviante e preferisco nelle mie classi che lo richiedono fare l'override del solo metodo Equals() e non creare nessun operatore ==, in questo modo so che nelle mie classi l'operatore == è sempre e comunque una comparazione per riferimento.
Alk.