Unterschied zwischen Call-Instanz und newobj-Instanz in IL

8

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%     
dragonfly 15.08.2012, 09:28
quelle

1 Antwort

2

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   das localsinit -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 CSinitobjCS39newobj0000 . Wie die ECMA-Spezifikation angibt, können diese Locals mit MethodThatTakesNullableInt(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.

    
kvb 15.08.2012, 16:22
quelle

Tags und Links