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
é traduzida como seu equivalente um pouco mais longo (x == y) == false
.
Há também uma instrução "compare-than-than" em IL ( cgt
), que permite ao compilador pegar certos atalhos (ou seja, gerar código IL mais curto), sendo que comparações de desigualdade de objetos com nulos 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á o arquivo 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 são interpretados como números inteiros não assinados - é isso que .un
significa - 0 tem o menor valor possível), portanto é traduzido x == 0
como se fosse 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 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, o compilador pode assumir que o padrão de bits da null
referência é 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)."
int
intervalo tenham a mesma representaçãoint
que elesuint
. Esse é um requisito muito mais fraco que o complemento de dois.