c# - sharp - swig python



Por que o compilador C#traduz isso!=Comparação como se fosse uma comparação? (1)

Por puro acaso, descobri que o compilador C # vira esse método:

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

... neste CIL :

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

… Ou, se você preferir examinar o código C # descompilado:

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

Como é que o != traduzido como " > "?


Resposta curta:

Como não há instrução "compare-not-equal" em IL, o operador C # != Não tem correspondência exata e não pode ser traduzido literalmente.

Existe, no entanto, uma instrução "compare-equal" ( ceq , uma correspondência direta com o operador == ); portanto, no caso geral, x != y é traduzido como seu equivalente um pouco mais longo (x == y) == false .

Também existe uma instrução "compare-maior-que" em IL ( cgt ), que permite ao compilador pegar certos atalhos (por exemplo, gerar código IL mais curto), sendo que comparações de desigualdade de objetos com null, obj != null , são traduzidas como se fossem " obj > null ".

Vamos entrar em mais alguns detalhes.

Se não houver instrução "compare-not-equal" em IL, como o método a seguir será traduzido pelo compilador?

static bool IsNotEqual(int x, int y)
{
    return x != y;
}

Como já foi dito acima, o compilador transformará x != y em (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
}

Acontece que o compilador nem sempre produz esse padrão bastante longo. Vamos ver o que acontece quando substituímos y pela constante 0:

static bool IsNotZero(int x)
{
    return x != 0;
}

A IL produzida é um pouco menor do que no caso geral:

.method private hidebysig static bool IsNotZero(int32 x) cil managed 
{
    ldarg.0    // x
    ldc.i4.0   // 0
    cgt.un     // (note: just one comparison)
    ret
}

O compilador pode tirar proveito do fato de que números inteiros assinados são armazenados no complemento de dois (onde, se os padrões de bits resultantes forem interpretados como números inteiros não assinados - é isso que o .un significa - 0 tem o menor valor possível), portanto ele converte x == 0 como se estivesse unchecked((uint)x) > 0 .

Acontece que o compilador pode fazer o mesmo para verificações de desigualdade contra null :

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

O compilador produz quase o mesmo IL que o IsNotZero :

.method private hidebysig static bool IsNotNull(object obj) cil managed 
{
    ldarg.0
    ldnull   // (note: this is the only difference)
    cgt.un
    ret
}

Aparentemente, o compilador pode assumir que o padrão de bits da referência null é o menor padrão de bits possível para qualquer referência de objeto.

Este atalho é mencionado explicitamente no Common Language Infrastructure Annotated Standard (1ª edição de outubro de 2003) (na página 491, como nota de rodapé da Tabela 6-4, "Comparações binárias ou operações de filial"):

" cgt.un é permitido e verificável em ObjectRefs (O). Isso é comumente usado ao comparar um ObjectRef com null (não há instruções" compare-not-equal ", que de outra forma seriam uma solução mais óbvia)."





binary-operators