¿Por qué el compilador de C# traduce esto?= comparación como si fuera una> comparación?


He descubierto por pura casualidad que el compilador de C# convierte este método:

static bool IsNotNull(object obj)
{
    return obj != null;
}

Into en este CIL :

.method private hidebysig static bool IsNotNull(object obj) cil managed
{
    ldarg.0   // obj
    ldnull
    cgt.un
    ret
}

} o, si prefiere mirar código C # descompilado:

static bool IsNotNull(object obj)
{
    return obj > null;   // (note: this is not a valid C# expression)
}

¿Cómo es que el != se traduce como ">"?

Author: Peter Mortensen, 2015-02-28

1 answers

Respuesta corta:

No hay instrucción "comparar-no-igual" en IL, por lo que el operador C# != no tiene correspondencia exacta y no puede ser traducido literalmente.

Sin embargo, hay 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.

Hay también una instrucción "compare-greater-than" en IL (cgt) que permite al compilador para tomar ciertos atajos (es decir, generar código IL más corto), uno es que las comparaciones de desigualdad de objetos contra null, obj != null, se traducen como si fueran "obj > null".


Vamos a entrar en más detalles.

Si no hay instrucción "compare-not-equal" en IL, entonces ¿cómo será traducido el siguiente método por el compilador?

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 significa .un - 0 tiene el valor más pequeño posible), por lo x == 0 como si fuera unchecked((uint)x) > 0.

Resulta que el compilador puede hacer lo mismo para verificaciones 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, al compilador se le permite asumir que el patrón de bits de la referencia null es el patrón de bits más pequeño posible para cualquier referencia de objeto.

Este atajo se menciona explícitamente en el Common Language Infrastructure Annotated Standard (1st edición de octubre de 2003) (en la página 491, como nota al pie de la Tabla 6-4, "Comparaciones binarias u Operaciones de Rama"):

"cgt.un está permitido y verificable en ObjectRefs (O). Esto se usa comúnmente cuando se compara un ObjectRef con null (no hay instrucción "compare-not-equal", que de otro modo sería una solución más obvia)."

 200
Author: stakx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-20 07:42:52