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.int
ont déjà été reprises par la même valeur dansuint
, donc toutes les représentations correspondant aux valeurs négatives deint
doivent correspondre à une valeuruint
supérieure à0x7FFFFFFF
, mais peu importe quelle valeur est. (En fait, tout ce qui est vraiment nécessaire, c'est que zéro soit représenté de la même manière dans les deuxint
etuint
.)cgt.un
traiter unint
comme unuint
sans changer le motif de bits sous-jacent. (Imaginez quecgt.un
faudrait d' abord essayer de résoudre sousverses en cartographiant tous les numéros négatifs à 0. Dans ce cas , vous ne pouvait évidemment pas se substituer> 0
à!= 0
.)>
est IL vérifiable. De cette façon, on pourrait comparer deux objets non nuls et obtenir un résultat booléen (qui n'est pas déterministe). Ce n'est pas un problème de sécurité de la mémoire, mais cela ressemble à une conception impure qui n'est pas dans l'esprit général du code géré sécurisé. Cette conception fuit le fait que les références d'objet sont implémentées en tant que pointeurs. Cela ressemble à un défaut de conception de la CLI .NET.ldnull
,initobj
etnewobj
). Ainsi, l'utilisation decgt.un
pour comparer les références d'objet à la référence nulle semble contredire la section III.1.1.4 de plus d'une manière.