J'ai par pur hasard découvert que le compilateur C # transforme cette méthode:
static bool IsNotNull(object obj)
{
return obj != null;
}
… Dans ce CIL :
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0 // obj
ldnull
cgt.un
ret
}
… Ou, si vous préférez regarder le code C # décompilé:
static bool IsNotNull(object obj)
{
return obj > null; // (note: this is not a valid C# expression)
}
Comment se fait-il que le !=
est traduit par ">
"?
Il n'y a pas d'instruction "compare-not-equal" en 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 "compare-equal" (ceq
, une correspondance directe avec l'opérateur ==
), Donc dans le cas général, x != y
Est traduit comme un peu plus long équivalent (x == y) == false
.
Il y a aussi une instruction "compare-supérieur-que" en IL (cgt
) qui permet au compilateur de prendre certains raccourcis (ie générer un code IL plus court), l'une étant que les comparaisons d'inégalité d'objets par rapport à null, obj != null
, sont traduites comme si elles étaient "obj > null
".
S'il n'y a pas d'instruction "compare-not-equal" en IL, alors 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 produit est un peu plus court 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 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 signifie .un
- 0 a la plus petite valeur possible), il traduit donc x == 0
comme s'il s'agissait de unchecked((uint)x) > 0
.
Il s'avère que le compilateur peut faire de même 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 binaire de la référence null
est le plus petit modèle binaire 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 branche"):
"
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 ", qui serait autrement une solution plus évidente). "