2009-08-12 11 views
9

Hat null einen Typ? Wie wird ein Nullwert intern dargestellt? Was passiert im folgenden Code?In .Net/C#, ist null stark typisiert?

void Foo(string bar) {...} 
void Foo(object bar) {...} 

Foo((string)null); 

Edit: Die Antworten waren bisher unspezifisch und zu hochrangig. Ich verstehe, dass ein Objekt vom Referenztyp aus einem Zeiger auf dem Stapel besteht, der auf eine Stelle im Heap zeigt, die einen Sync-Block-Index, einen Typ-Handle und die Felder des Objekts enthält. Wenn ich eine Instanz eines Objekts auf null setze, wo genau zeigt der Zeiger auf dem Stapel auf? Und in der Code-Snippet, ist die Besetzung einfach durch den C# -Compiler verwendet, um zu entscheiden, welche Überlast zu nennen, und es gibt keine wirklich Casting von null geht?

Ich bin auf der Suche nach einer eingehenden Antwort von jemandem, der die CLR-Interna versteht.

Antwort

16

Die Besetzung zu string in Ihrem Codebeispiel gibt dem null einen Typ nicht, da null keinen Typ selbst haben kann. Wenn Sie Beweis dafür wollen, führen Sie dann den folgenden Code, wo können Sie sehen, dass null sich immer gleich, unabhängig davon, was die Art der Variablen zugewiesen wurde, ist:

string s = null; 
IPAddress i = null; 
Console.WriteLine(object.Equals(s, i)); // prints "True" 
Console.WriteLine(object.ReferenceEquals(s, i)); // prints "True" 

Was die Besetzung tut, ist zu sagen der Compiler welche Überladung zu wählen.Da null keinen Typ hat, weiß es nicht, ob die Überladung gewählt werden soll, die eine object oder eine string nimmt, da der Wert als entweder interpretiert werden könnte. Sie helfen also, indem Sie sagen: "Hier ist ein Null-Wert, der behandelt werden sollte, als wäre es eine Zeichenkette".


Wenn Sie sehen möchten, was darunter läuft, dann schauen Sie sich die IL aus Ihrem Code an. Das entsprechende Bit für den Methodenaufruf ist so etwas wie die folgend in Text IL (je nach Namespace und Klassennamen, usw.):

ldnull 
call void ConsoleApplication1.Program::Foo(string) 

Also alles, was passiert, ist, dass eine Null auf dem Stapel geladen wird, und dann wird dies von der Überladung verbraucht, die den String übernimmt, da die Überladungsauflösung zur Kompilierungszeit durchgeführt wird, so dass die Methode, die aufgerufen wird, in die IL eingebettet wird.

Wenn Sie sehen wollen, was ldnull tut, und warum ist es anders als nur so etwas wie ldc.i4.0 mit einer Null auf den Stapel laden dann this answer sehen (wenn Sie nicht den Link folgen wollen, ist der Grund, dass es eine größenunabhängige Null, die ansonsten in der CLR nicht existiert.

+0

Also was genau wird auf dem Stapel sein, wenn ich mich auf 'null' beziehe? –

+0

@Matt - Bearbeitet, um weitere Details auf niedriger Ebene hinzuzufügen. Reicht das aus? –

+0

@Greg: Danke - Ich war auf der Arbeit beschäftigt und hatte keine Zeit, IL und die ECMA-Spezifikationen selbst zu überprüfen. Dies hat mein intellektuelles Jucken gekratzt. –

2

Es wird Foo (string bar) aufrufen.

Sie können null fein ausdrücken.

+0

Offensichtlich - siehe meine Bearbeitung. –

1

Das Null-Schlüsselwort ist ein Literal, das eine Nullreferenz darstellt, eine, die sich nicht auf ein Objekt bezieht. null ist der Standardwert von Variablen vom Referenztyp. Von MSDN. Also der Standardwert von String in Ihrem Beispiel.

// A null string is not the same as an empty string. 
string s = null; 
string t = String.Empty; // Logically the same as "" 

Was Sie tun, ist äquivalent zu Standard mit nach oben:

int equal = string.Compare((string)null, default(string)); 
2

Wenn Sie ein Referenzobjekt zu null gesetzt, der Zeiger (hinten) zeigt auf einen besonderen Platz im Speicher, der wird als null bezeichnet. Wenn Sie also in null umwandeln, erstellen Sie wirklich einen Zeiger des spezifischen Typs, der auf null verweist.

Wie von anderen gezeigt (1)(2), dies scheint nicht der Fall zu sein, weil der Compiler die IL Emittieren entsprechend den spezifischen null Guss der Überlastung aufgelöst wird.

Ich weiß, dass Referenzvariablen einen Typ zugeordnet haben. Ich dachte, dass dies hinter diesem null Casting wäre, aber ich lag falsch.

Der Opcode ldnull verschiebt eine Nullreferenz (Typ O) auf den Stapel. Auch wenn dieser Nullreferenz ein Typ zugeordnet ist, akzeptiert die call diese Referenz als ein gültiges Argument von string. Zumindest ist das mein Verständnis davon. Wenn jemand Korrekturen hat, können Sie mich gerne korrigieren.

1

NULL ist ein Marker, er hat keinen Datentyp. Wenn Sie einem Feld oder einer Variablen .NULL zuweisen, ändert sich der Wert in NULL, aber der Datentyp des Felds oder der Variablen ändert sich nicht.

Der Grund für die Zuweisung von String-Typ zu Null in Ihrem Beispiel Ich denke, um zu zeigen, Methode wird verwendet werden (in diesem Fall "void Foo (string bar) {...}", weil dies die Methode ist akzeptiert Zeichenfolgen als Argumente.)

Wenn Sie Foo ((Objekt) null) aufgerufen hätten; die andere Methode wäre verwendet worden. ("void Foo (Objektleiste) {...}")

6

Null hat keinen Typ und "Und in dem Codeausschnitt wird die Besetzung einfach von dem C# -Compiler verwendet um zu entscheiden, welche Überlast zu rufen ist, und es gibt nicht wirklich eine Null-Besetzung? " ist genau das, was vor sich geht. Schauen wir uns die generierte IL an.

IL_0001: ldnull 
    IL_0002: call  void ConsoleApplication1.Program::Foo(string) 

Beachten Sie, dass ldnull null auf den Stack lädt. Es ist einfach null, nicht null als String oder etwas anderes. Was zählt, ist die zweite Zeile, in der IL explizit die Überladung aufruft, die eine Zeichenfolge empfängt. Hier ist, was passiert, wenn Sie anrufen, Gießen zum Objekt:

IL_0001: ldnull 
    IL_0002: call  void ConsoleApplication1.Program::Foo(object) 

Also, ja, die Besetzung ist ein C# Artefakt so dass der Compiler weiß, was zu nennen überlasten.

1

Der Typ des null Wert wird durch ECMA-334 wie folgt definiert:

11.2.7 Der Null-Typ

Die Null literal (§9.4.4.6) auf den Nullwert auswertet, die wird verwendet, um eine Referenz zu bezeichnen, die auf kein Objekt oder Array oder auf das Fehlen eines Werts zeigt. Der Null-Typ hat einen einzelnen Wert, , der der Nullwert ist. Daher kann ein Ausdruck, dessen Typ der Null-Typ ist, nur den Nullwert auswerten. Es gibt keine Möglichkeit, den Null-Typ explizit zu schreiben und daher keine Möglichkeit , es in einem deklarierten Typ zu verwenden.

Der Null-Typ ist der unterste Typ der Typhierarchie - das Gegenteil von Objekt; Der Nulltyp kann als Untertyp für jeden Nullable-Typ betrachtet werden, da der Nullwert überall dort verwendet werden kann, wo ein NULL-fähiger Ausdruck auftritt.

Dies erzeugt eine Schwäche im Typsystem, da es bedeutet, dass jede Operation, deren Empfänger ein NULL-fähiger Typ sein kann, NULL sein kann und die Operation zur Laufzeit mit einer Ausnahme fehlschlagen kann. In einem starken System werden Operationen, die von einem Typ bereitgestellt werden, garantiert, wenn der Empfänger von diesem Typ ist (dh Sie würden keine Null-Zeiger-Ausnahme erhalten, die wirklich besagt, dass der Null-Typ keine Methode implementiert, die Sie haben aufgerufen, obwohl der Nullwert aus dem Ausdruck eines beliebigen Typs resultieren kann, von dem Sie dachten, dass Sie ihn benutzten.

In Ihrem Code sind die Methoden überladen und der statische Typ des Ausdrucks, der das Argument liefert, wird für die Überladungsauflösung verwendet. Wegen der Umwandlung in eine Zeichenkette hat der Ausdruck den Typ string (der Nulltyp ist ein Untertyp der Zeichenkette, dies ist ein Up-Cast, der gemäß dem C# -Typ-System so sicher ist). In erster Näherung wählt der Compiler die spezifischste sichtbare Überladung aus und wählt daher die String-Version Foo statt der Objektversion.

Verwandte Themen