Respuesta corta:
No existe una instrucción "comparar-no-igual" en IL, por lo que el !=
operador C # no tiene correspondencia exacta y no puede traducirse literalmente.
Sin embargo, existe una instrucción "comparar-igual" ( ceq
una correspondencia directa con el ==
operador), por lo que, en el caso general, x != y
se traduce como su equivalente un poco más largo (x == y) == false
.
También hay una instrucción "compare-mayor-que" en IL ( cgt
) que permite al compilador tomar ciertos atajos (es decir, generar un código IL más corto), uno de ellos es que las comparaciones de desigualdad de objetos contra nulos obj != null
, se traducen como si fueran " obj > null
".
Vamos a entrar en más detalles.
Si no hay una instrucción "comparar-no-igual" en IL, entonces, ¿cómo el compilador traducirá el siguiente método?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Como ya se dijo anteriormente, el compilador convertirá el 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
}
Resulta que el compilador no siempre produce este patrón bastante largo. Veamos qué sucede cuando reemplazamos y
con la constante 0:
static bool IsNotZero(int x)
{
return x != 0;
}
La IL producida es algo más corta que en el caso general:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
El compilador puede aprovechar el hecho de que los enteros con signo se almacenan en el complemento de dos (donde, si los patrones de bits resultantes se interpretan como enteros sin signo, eso es lo que .un
significa - 0 tiene el valor más pequeño posible), por lo que se traduce x == 0
como si fuera unchecked((uint)x) > 0
.
Resulta que el compilador puede hacer lo mismo para las comprobaciones de desigualdad contra null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
El compilador produce casi el mismo IL que para IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Aparentemente, el compilador puede asumir que el patrón de bits de la null
referencia es el patrón de bits más pequeño posible para cualquier referencia de objeto.
Este acceso directo se menciona explícitamente en el Estándar Anotado de Infraestructura de Lenguaje Común (primera edición de octubre de 2003) (en la página 491, como una nota al pie de la Tabla 6-4, "Comparaciones binarias u operaciones de sucursal"):
" cgt.un
está permitido y es verificable en ObjectRefs (O). Esto se usa comúnmente cuando se compara un ObjectRef con un valor nulo (no existe una instrucción" comparar-no-igual ", que de lo contrario sería una solución más obvia)".
int
el rango tengan la misma representaciónint
que enuint
. Ese es un requisito mucho más débil que el complemento de dos.int
ya han sido tomadas por el mismo valor enuint
, por lo que todas las representaciones correspondientes a los valores negativos deint
tienen que corresponder a un valoruint
mayor que0x7FFFFFFF
, pero en realidad no importa qué valor es. (En realidad, todo lo que realmente se requiere es que el cero se represente de la misma manera en ambosint
yuint
.)cgt.un
trata a anint
como unuint
sin cambiar el patrón de bits subyacente. (Imagínese quecgt.un
empezarían por tratar de underflow fijos mediante la asignación de todos los números negativos a 0. En ese caso, es obvio que no puede sustituir> 0
a!= 0
.)>
IL sea verificable. De esa manera, uno podría comparar dos objetos no nulos y obtener un resultado booleano (que no es determinista). Ese no es un problema de seguridad de la memoria, pero se siente como un diseño sucio que no está en el espíritu general de un código administrado seguro. Este diseño filtra el hecho de que las referencias a objetos se implementan como punteros. Parece un defecto de diseño de .NET CLI.ldnull
,initobj
ynewobj
). Por lo tanto, el uso decgt.un
comparar referencias de objeto contra la referencia nula parece contradecir la sección III.1.1.4 en más de una forma.