Krótka odpowiedź:
W języku IL !=
nie ma instrukcji „porównaj-nie-równe”, więc operator C # nie ma dokładnej zgodności i nie można go dosłownie przetłumaczyć.
Istnieje jednak instrukcja „porównaj równe” ( ceq
bezpośrednia zgodność z ==
operatorem), więc w ogólnym przypadku x != y
jest tłumaczona jak jej nieco dłuższy odpowiednik (x == y) == false
.
W IL ( ) jest również instrukcja "porównaj-większe-niż", cgt
która pozwala kompilatorowi na użycie pewnych skrótów (tj. Wygenerowanie krótszego kodu IL), jednym z nich jest to, że porównania nierówności obiektów względem wartości null obj != null
, są tłumaczone tak, jakby były " obj > null
”.
Przejdźmy do bardziej szczegółowych informacji.
Jeśli w IL nie ma instrukcji „porównaj-nie-równe”, w jaki sposób następująca metoda zostanie przetłumaczona przez kompilator?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Jak już wspomniano powyżej, kompilator zamieni plik x != y
na (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
}
Okazuje się, że kompilator nie zawsze tworzy ten dość długi wzorzec. Zobaczmy, co się stanie, gdy zastąpimy y
stałą 0:
static bool IsNotZero(int x)
{
return x != 0;
}
Wytworzony IL jest nieco krótszy niż w ogólnym przypadku:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
Kompilator może wykorzystać fakt, że liczby całkowite ze znakiem są przechowywane w uzupełnieniu do dwóch (gdzie, jeśli wynikowe wzorce bitowe są interpretowane jako liczby całkowite bez znaku - to właśnie .un
oznacza - 0 ma najmniejszą możliwą wartość), więc przekłada się x == 0
tak, jakby była unchecked((uint)x) > 0
.
Okazuje się, że kompilator może zrobić to samo w przypadku sprawdzania nierówności względem null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
Kompilator produkuje prawie taki sam IL jak dla IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Najwyraźniej kompilator może założyć, że wzorzec bitowy null
odniesienia jest najmniejszym możliwym wzorcem bitowym dla dowolnego odniesienia do obiektu.
Skrót ten jest wyraźnie wymieniony w dokumencie Common Language Infrastructure Annotated Standard (wydanie 1 z października 2003 r.) (Na stronie 491, jako przypis w tabeli 6-4, „Binary Comparisons or Branch Operations”):
„ cgt.un
jest dozwolone i weryfikowalne w ObjectRefs (O). Jest to często używane podczas porównywania ObjectRef z wartością null (nie ma instrukcji„ porównaj-nie równa ”, która w innym przypadku byłaby bardziej oczywistym rozwiązaniem).”
int
'mają taką samą reprezentacjęint
jak wuint
. To znacznie słabszy wymóg niż uzupełnienie do dwóch.