Réponse courte:
Il n'y a pas d'instruction "compare-not-equal" dans IL, donc l' !=
opérateur C # n'a pas de correspondance exacte et ne peut pas être traduit littéralement.
Il existe cependant une instruction "comparer-égal" ( ceq
, une correspondance directe avec l' ==
opérateur), donc dans le cas général, x != y
se traduit comme son équivalent légèrement plus long (x == y) == false
.
Il existe également une instruction "comparer plus grand que" dans IL ( cgt
) qui permet au compilateur de prendre certains raccourcis (c'est-à-dire générer du code IL plus court), l'un étant que les comparaisons d'inégalité des objets contre null,, obj != null
se traduisent comme si elles étaient " obj > null
".
Entrons dans un peu plus de détails.
S'il n'y a pas d'instruction "compare-not-equal" dans IL, comment la méthode suivante sera-t-elle traduite par le compilateur?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Comme déjà dit ci-dessus, le compilateur transformera le x != y
en (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
}
Il s'avère que le compilateur ne produit pas toujours ce modèle assez long. Voyons ce qui se passe lorsque nous remplaçons y
par la constante 0:
static bool IsNotZero(int x)
{
return x != 0;
}
L'IL produite est un peu plus courte que dans le cas général:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
Le compilateur peut tirer parti du fait que les entiers signés sont stockés dans le complément à deux (où, si les modèles de bits résultants sont interprétés comme des entiers non signés - c'est ce que .un
signifie - 0 a la plus petite valeur possible), donc il se traduit x == 0
comme s'il était unchecked((uint)x) > 0
.
Il s'avère que le compilateur peut faire la même chose pour les vérifications d'inégalité contre null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
Le compilateur produit presque le même IL que pour IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Apparemment, le compilateur est autorisé à supposer que le modèle de bits de la null
référence est le plus petit modèle de bits possible pour toute référence d'objet.
Ce raccourci est explicitement mentionné dans le Common Language Infrastructure Annotated Standard (1ère édition d'octobre 2003) (à la page 491, en tant que note de bas de page du tableau 6-4, «Comparaisons binaires ou opérations de succursales»):
" cgt.un
est autorisé et vérifiable sur ObjectRefs (O). Ceci est couramment utilisé lors de la comparaison d'un ObjectRef avec null (il n'y a pas d'instruction" compare-not-equal ", ce qui serait autrement une solution plus évidente)."
int
la plage de s aient la même représentation dansint
que dansuint
. C'est une exigence bien plus faible que le complément à deux.