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.int
zostały już przyjęte przez tę samą wartość wuint
, więc wszystkie reprezentacje odpowiadające wartościom ujemnymint
muszą odpowiadać jakiejś wartościuint
większej niż0x7FFFFFFF
, ale tak naprawdę nie ma znaczenia, która wartość to jest. (Właściwie wszystko, co jest naprawdę wymagane, to to, że zero jest reprezentowane w ten sam sposób w obuint
iuint
.)cgt.un
traktujeint
jako anuint
bez zmiany bazowego wzoru bitowego. (Wyobraź sobie, żecgt.un
najpierw spróbuje niedomiarów fix poprzez mapowanie wszystkich liczb ujemnych na 0. W takim przypadku oczywiście nie może zastąpić> 0
w!= 0
.)>
używanym jest weryfikowalnym IL. W ten sposób można porównać dwa niezerowe obiekty i otrzymać wynik boolowski (który jest niedeterministyczny). Nie jest to kwestia bezpieczeństwa pamięci, ale wydaje się, że projekt jest nieczysty, który nie jest w ogólnym duchu bezpiecznego kodu zarządzanego. Ten projekt przecieka fakt, że odniesienia do obiektów są implementowane jako wskaźniki. Wygląda na to, że wada projektowa .NET CLI.ldnull
,initobj
inewobj
). Zatem użyciecgt.un
do porównywania odniesień do obiektów z odniesieniami zerowymi wydaje się być sprzeczne z sekcją III.1.1.4 na więcej niż jeden sposób.