Risposta breve:
Non esiste alcuna istruzione "confronta-non-uguale" in IL, quindi l' !=
operatore C # non ha una corrispondenza esatta e non può essere tradotto letteralmente.
Esiste tuttavia un'istruzione "confronta-uguale" ( ceq
, una corrispondenza diretta con l' ==
operatore), quindi nel caso generale x != y
viene tradotta come il suo equivalente leggermente più lungo (x == y) == false
.
Esiste anche un'istruzione "compare-maggiore di" in IL ( cgt
) che consente al compilatore di prendere alcune scorciatoie (ovvero generare un codice IL più breve), uno dei quali è il confronto delle disuguaglianze tra oggetti e null obj != null
, che viene tradotto come se fosse " obj > null
".
Andiamo in qualche dettaglio in più.
Se non esiste alcuna istruzione "confronta-non-uguale" in IL, come verrà tradotto dal compilatore il seguente metodo?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Come già detto sopra, il compilatore trasformerà x != y
in (x == y) == false
:
.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed
{
ldarg.0 // x
ldarg.1 // y
ceq
ldc.i4.0 // false
ceq // (note: two comparisons in total)
ret
}
Si scopre che il compilatore non produce sempre questo modello piuttosto prolisso. Vediamo cosa succede quando sostituiamo y
con la costante 0:
static bool IsNotZero(int x)
{
return x != 0;
}
L'IL prodotto è leggermente più breve rispetto al caso generale:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
Il compilatore può trarre vantaggio dal fatto che gli interi con segno sono memorizzati nel complemento a due (dove, se i pattern di bit risultanti sono interpretati come numeri interi senza segno - ecco cosa .un
significa - 0 ha il valore più piccolo possibile), quindi si traduce x == 0
come se fosse unchecked((uint)x) > 0
.
Si scopre che il compilatore può fare lo stesso per i controlli di disuguaglianza rispetto a null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
Il compilatore produce quasi lo stesso IL di IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Apparentemente, al compilatore è consentito assumere che il modello di bit del null
riferimento sia il modello di bit più piccolo possibile per qualsiasi riferimento di oggetto.
Questa scorciatoia è esplicitamente menzionata nello standard annotato per l'infrastruttura linguistica comune (prima edizione da ottobre 2003) (a pagina 491, come nota a piè di pagina della Tabella 6-4, "Confronti binari o operazioni di succursale"):
" cgt.un
è consentito e verificabile su ObjectRefs (O). Questo è comunemente usato quando si confronta un ObjectRef con null (non esiste un'istruzione" confronta-non-uguale ", che sarebbe altrimenti una soluzione più ovvia)."
int
nell'intervallo abbiano la stessa rappresentazione inint
cui sono presentiuint
. È un requisito molto più debole del complemento a due.