Por que o compilador C#traduz essa comparação!=Como se fosse uma comparação>?



swig python (1)

Eu descobri por acaso que o compilador C # transforma este método:

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

… Para este CIL :

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

… Ou, se você preferir olhar um código C # decompilado:

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

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


Resposta curta:

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

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

também uma instrução "compare-greater-than" em IL ( cgt ) que permite ao compilador tomar certos atalhos (ie gerar código IL mais curto), sendo um deles que comparações de desigualdade de objetos contra null, obj != null , get translated como se fossem " obj > null ".

Vamos entrar em mais detalhes.

Se não houver instrução "compare-not-equal" no 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 irá transformar o 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;
}

O IL produzido é um pouco mais curto 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 aproveitar o fato de que os inteiros assinados são armazenados no complemento de dois (onde, se os padrões de bits resultantes são interpretados como inteiros sem sinal - isso é o que o .un significa - 0 tem o menor valor possível), então ele traduz x == 0 como se estivesse unchecked((uint)x) > 0 .

Acontece que o compilador pode fazer o mesmo para verificações de desigualdade em relação a null :

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

O compilador produz quase o mesmo IL que para 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 supor que o padrão de bits da referência null é o menor possível para qualquer referência de objeto.

Este atalho é explicitamente mencionado no Padrão Anotado de Infraestrutura de Linguagem Comum (1ª edição de outubro de 2003) (na página 491, como uma 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 quando se compara um ObjectRef com null (não há instrução" compare-not-equal ", que de outra forma seria uma solução mais óbvia)."





binary-operators