Kurze Antwort:
In IL gibt es keine Anweisung "Vergleichen ungleich", daher hat der C # !=
-Operator keine genaue Entsprechung und kann nicht wörtlich übersetzt werden.
Es gibt jedoch eine Anweisung "Vergleich gleich" ( ceq
eine direkte Korrespondenz mit dem ==
Operator), die im allgemeinen Fall x != y
wie ihre etwas längere Entsprechung übersetzt wird (x == y) == false
.
Es gibt auch eine Anweisung "compare-größer-als" in IL ( cgt
), die es dem Compiler ermöglicht, bestimmte Verknüpfungen zu verwenden (dh kürzeren IL-Code zu generieren). Eine davon ist, dass Ungleichheitsvergleiche von Objekten gegen Null so obj != null
übersetzt werden, als ob sie " obj > null
".
Lassen Sie uns näher darauf eingehen.
Wenn es in IL keine Anweisung "compare-not-same" gibt, wie wird dann die folgende Methode vom Compiler übersetzt?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
Wie bereits oben erwähnt, wird sich der Compiler die x != y
in(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
}
Es stellt sich heraus, dass der Compiler dieses ziemlich langwierige Muster nicht immer erzeugt. Mal sehen, was passiert, wenn wir ersetzeny
die Konstante 0 :
static bool IsNotZero(int x)
{
return x != 0;
}
Die produzierte IL ist etwas kürzer als im allgemeinen Fall:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
Der Compiler kann die Tatsache ausnutzen, dass vorzeichenbehaftete Ganzzahlen im Zweierkomplement gespeichert sind (wobei, wenn die resultierenden Bitmuster als vorzeichenlose Ganzzahlen interpretiert werden - das ist, was die.un
Mittel - 0 den kleinstmöglichen Wert hat), so dass es so übersetzt, x == 0
als ob es wäre unchecked((uint)x) > 0
.
Es stellt sich heraus, dass der Compiler genau das Gleiche für Ungleichheitsprüfungen tun kann null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
Der Compiler erzeugt fast die gleiche IL wie für IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Anscheinend darf der Compiler davon ausgehen, dass das Bitmuster des null
Referenz das kleinstmögliche Bitmuster für jede Objektreferenz ist.
Diese Verknüpfung wird im Common Language Infrastructure Annotated Standard (1. Ausgabe ab Oktober 2003) ausdrücklich erwähnt (auf Seite 491 als Fußnote zu Tabelle 6-4, "Binäre Vergleiche oder Zweigoperationen"):
" cgt.un
ist für ObjectRefs (O) zulässig und überprüfbar. Dies wird häufig verwendet, wenn ein ObjectRef mit null verglichen wird (es gibt keine Anweisung" compare-not-same ", die ansonsten eine naheliegendere Lösung wäre)."
int
Bereich dieselbe Darstellung habenint
wie inuint
. Das ist eine weitaus schwächere Anforderung als das Zweierkomplement.