Ich beschäftige mich vertieft mit C # und spiele mit nullbaren Wertetypen. Nur für experimentelle Zwecke habe ich einen Code geschrieben:
%Vor%Und ich war froh zu sehen, dass impliziteConversion / test2 Variablen mit initialisiert werden mit:
%Vor%Anweisung, während wenn MethodThatTakesNullableInt aufgerufen wird, kann ich sehen:
%Vor%und
%Vor%was ich verstehe. Ich dachte, dass ich auch newobj Anweisungen für impliziteConversion / test2 sehen werde.
Dies ist voller IL-Code:
%Vor% Zunächst scheint es so, als hätten Sie im Debug-Modus kompiliert (basierend auf nop
s) - es ist möglich, dass Sie einen anderen Code sehen, wenn Sie im Release-Modus kompilieren.
In Abschnitt I.12.1.6.2.1 der ECMA CLR-Spezifikation (Initialisieren von Instanzen von Werttypen) heißt es:
Es gibt drei Optionen zum Initialisieren des Ausgangs eines Werttyps Beispiel. Sie können es auf Null setzen, indem Sie die Adresse des Hauses laden (siehe Tabelle I.8: Adresse und Typ der Home-Standorte) und die
initobj
verwenden Anweisung (für lokale Variablen wird dies auch durch Einstellung erreicht daslocalsinit
-Bit im Header der Methode. Sie können eine anrufen benutzerdefinierter Konstruktor durch Laden der Adresse des Hauses (siehe Tabelle I.8: Adresse und Typ der Home - Standorte) und rufen dann die Konstruktor direkt. Oder Sie kopieren eine vorhandene Instanz in die Zuhause, wie in §I.12.1.6.2.2 beschrieben.
Die ersten drei Verwendungen von Nullable-Typen in Ihrem Code führen zu Nullwerten in Locals. Daher ist dieser Kommentar relevant (Locals sind ein Typ von home für Werte): die ersten beiden sind die Locals implicitConversion
und test
, die Sie deklariert haben, und die dritte ist ein Compiler-generiertes temporäres namens CS
. Wie die ECMA-Spezifikation angibt, können diese Locals mit initobj
CS
0039
newobj
00MethodThatTakesNullableInt(null)
initialisiert werden (was dem Standardkonstruktor für keine Struktur für eine Struktur entspricht und in diesem Fall für MethodThatTakesNullableInt(39)
verwendet wird) oder indem die Adresse und der Aufruf der lokalen Adresse geladen werden ein Konstruktor (wird für die anderen zwei Einheimischen verwendet).
Für die letzte Nullable-Instanz (die durch die implizite Konvertierung von initobj
erstellt wurde) wird das Ergebnis jedoch nicht in einem lokalen gespeichert - es wird auf dem Stack generiert, sodass die Regeln für das Initialisieren eines Home hier nicht gelten. Stattdessen verwendet der Compiler nur newobj
, um den Wert auf dem Stack zu erstellen (wie bei jedem Wert oder Referenztyp).
Sie fragen sich vielleicht, warum der Compiler für den Aufruf von newobj
, nicht aber für newarr
eine lokale Variable generiert hat. Ich vermute, dass die Antwort ist, dass der Compiler immer initobj
verwendet, um den Standardkonstruktor aufzurufen (der dann ein lokales oder anderes Home für den Wert benötigt), aber newobj
verwendet, um andere Konstruktoren aufzurufen und das Ergebnis auf dem Stack zu speichern Es gibt nicht schon ein angemessenes Zuhause für den Wert.
Weitere Informationen finden Sie auch in diesem Kommentar aus Abschnitt III.4.21 (newobj) aus der Spezifikation:
Werttypen werden normalerweise nicht mit %code% erstellt. Sie sind normalerweise entweder als Argumente oder als lokale Variablen mit %code% (für nullbasierte, eindimensionale Arrays) oder als Felder von Objekten. Einmal zugeordnet, werden sie mit %code% initialisiert. Der %code% Anweisung kann verwendet werden, um eine neue Instanz eines Werttyps zu erstellen der Stapel, der dann als ein Argument übergeben werden kann, gespeichert in einem lokalen, usw.