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” ( ceqbezpośrednia zgodność z ==operatorem), więc w ogólnym przypadku x != yjest tłumaczona jak jej nieco dłuższy odpowiednik (x == y) == false.
W IL ( ) jest również instrukcja "porównaj-większe-niż", cgtktó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 != yna (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 ystałą 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 .unoznacza - 0 ma najmniejszą możliwą wartość), więc przekłada się x == 0tak, 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 nullodniesienia 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.unjest 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ęintjak wuint. To znacznie słabszy wymóg niż uzupełnienie do dwóch.