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.int
wurden bereits mit demselben Wert in aufgenommenuint
, daher müssen alle Darstellungen, die den negativen Werten vonint
entsprechen, einem Wert vonuint
größer als entsprechen0x7FFFFFFF
, aber es spielt keine Rolle, welcher Wert das ist ist. (Eigentlich ist alles, was wirklich benötigt wird, dass Null in beidenint
und gleich dargestellt wirduint
.)cgt.un
einint
als behandelt wird,uint
ohne das zugrunde liegende Bitmuster zu ändern. (Stellen Sie sich vor , dasscgt.un
zunächst durch Abbildung aller negativen Zahlen auf 0 In diesem Fall fix Unterschreitungen versuchen würde man natürlich nicht ersetzen konnte> 0
für!= 0
.)>
IL überprüfbar ist. Auf diese Weise könnte man zwei Nicht-Null-Objekte vergleichen und ein boolesches Ergebnis erhalten (das nicht deterministisch ist). Das ist kein Problem mit der Speichersicherheit, aber es fühlt sich wie ein unreines Design an, das nicht dem allgemeinen Geist von sicher verwaltetem Code entspricht. Dieses Design verliert die Tatsache, dass Objektreferenzen als Zeiger implementiert sind. Scheint ein Designfehler der .NET CLI zu sein.ldnull
,initobj
undnewobj
). Die Verwendung voncgt.un
zum Vergleichen von Objektreferenzen mit der Nullreferenz scheint also Abschnitt III.1.1.4 in mehr als einer Hinsicht zu widersprechen.