Warum ist das Emittieren C # Compiler eine Anweisung für einen callvirt GetType () Methodenaufruf?

StackOverflow https://stackoverflow.com/questions/845657

Frage

Ich bin neugierig zu wissen, warum dies geschieht. Bitte lesen Sie das folgende Codebeispiel und die entsprechenden IL, die in den Kommentaren unter jedem Abschnitt emittieren wurden:

using System;

class Program
{
    static void Main()
    {
        Object o = new Object();
        o.GetType();

        // L_0001: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0006: stloc.0 
        // L_0007: ldloc.0 
        // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

        new Object().GetType();

        // L_000e: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
    }
}

Warum hat der Compiler eine callvirt für den ersten Abschnitt emittiert, sondern ein call für den zweiten Abschnitt? Gibt es einen Grund, dass der Compiler immer einen callvirt Befehl für eine nicht-virtuelle Methode emittieren würde? Und wenn es Fälle gibt, in denen der Compiler eine callvirt für eine nicht-virtuelle Methode emittieren geht diese Probleme für Typsicherheit schaffen?

War es hilfreich?

Lösung

Nur auf der sicheren Seite.

Technisch C # Compiler nicht immer Verwendung callvirt

Für statische Methoden und Methoden definiert auf Werttypen, verwendet es call. Die Mehrheit wird über die callvirt IL Anweisung zur Verfügung gestellt.

Der Unterschied, dass die Abstimmung zwischen den beiden geschwungen ist die Tatsache, dass call das „Objekt verwendet wird, um den Anruf zu tätigen“ geht davon ist nicht null. callvirt auf der anderen Seite prüft, ob nicht null und wirft eine Nullreferenceexception, falls erforderlich.

  • Für statische Methoden, ist das Objekt ein Typ Objekt und kann nicht null sein. Das Gleiche gilt für Werttypen. Daher call ist für sie verwendet -. Eine bessere Leistung
  • Für die anderen entschieden sich die Sprachdesigner mit callvirt zu gehen, damit der JIT-Compiler überprüft, ob das Objekt den Anruf zu tätigen verwendet wird, ist nicht null. Auch für nicht-virtuelle Instanz Methoden .. bewertet sie Sicherheit über die Leistung.

Siehe auch: Jeff Richter hat einen besseren Job bei dieser - in seinem Kapitel in CLR via C # 2nd Ed 'Typen im Sinne der'

Andere Tipps

Siehe diese alter Blog-Eintrag von Eric Gunnerson.

Hier ist der Text der Post:

Warum C # immer callvirt verwenden?

Diese Frage kam auf einem internen C # Alias, und ich dachte, die Antwort von allgemeinem Interesse sein würde. Das wird unter der Annahme, dass die Antwort richtig ist -. Es ist schon eine ganze Weile

Die .NET IL Sprache bietet sowohl einen Anruf und callvirt Anweisung, mit der virtuellen callvirt Funktionen aufrufen verwendet wird. Aber wenn Sie den Code schauen durch die C # erzeugt, werden Sie sehen, dass es eine „callvirt“ erzeugt auch in Fällen, in denen es keine virtuelle Funktion beteiligt. Warum es das tun?

Ich ging zurück durch die Sprache Design Notizen, die ich habe, und sie sagen, ganz klar, dass wir auf 1999.12.13 zu callvirt entschieden. Leider, sie erfassen nicht unser Grundprinzip, dass zu tun, also werde ich aus meinem Gedächtnis haben, zu gehen.

Wir hatten einen Bericht von jemandem bekommen (wahrscheinlich eine der .NET-Gruppen mit C # (dachte, es war noch nicht C # zu dieser Zeit genannt)), den Code geschrieben hatte, das eine Methode auf einem Null-Zeiger genannt, aber sie didn ‚t eine Ausnahme, weil das Verfahren keine Felder Zugriff haben (dh‚das‘war null, aber nichts in dem Verfahren verwendet es). Diese Methode genannt dann ein anderes Verfahren, das die diesen Punkt tat verwenden und hat eine Ausnahme, und ein wenig Kopf-Kratzen folgt. Nachdem sie es herausgefunden, sie schickte uns eine Notiz davon.

Wir dachten, dass eine Methode auf einer Instanz null nennen zu können, ein bisschen komisch war. Peter Golde habe einige Tests, um zu sehen, was die Perf Wirkung war immer mit callvirt, und es war klein genug, dass wir uns entschieden, die Änderung vorzunehmen.

Als (perhaps-) interessant beiseite ... GetType() ungewöhnlich ist, dass es nicht virtual - dies einige führt sehr, sehr seltsame Dinge .

(markiert als Wiki, da es etwas vom Thema der eigentlichen Frage ist)

Der Compiler kennt nicht die wirkliche Art von o im ersten Ausdruck, aber es die reale Art in dem zweiten Ausdruck nicht kennt. Es sieht aus wie es nur an einer Aussage zu einem Zeitpunkt, an.

Das ist in Ordnung, weil C # stark von der JIT zur Optimierung abhängt. Es ist sehr wahrscheinlich, dass in einem so einfachen Fall, dass beiden Anrufe werden Instanz zur Laufzeit aufruft.

Ich glaube nicht, callvirt jemals für nicht-virtuelle Methoden emittiert wird, aber selbst wenn es ist, wäre es kein Problem sein, da das Verfahren würde (aus offensichtlichen Gründen) nicht außer Kraft gesetzt werden.

Ich würde eine Vermutung Gefahr, dass es ist, weil die ersten Abtretungsempfänger auf eine Variable, die möglicherweise eine downcasted Instanz eines anderen Typs enthalten könnten, die außer Kraft gesetzten GetType haben könnten (obwohl wir es nicht sehen können); die zweite nie etwas anderes als Object sein könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top