Wie kann ich feststellen, ob eine Methode mit "Call" oder "Callvirt" aufgerufen werden muss?.NET CIL Anruf oder CallVirt?
Antwort
Sie können diese einfachen Regeln eins nach dem anderen folgen, um zu bestimmen, welche Sie verwenden sollten:
- Ist die Methode
static
? Dann verwenden Siecall
. - Ist der Typ, den Sie aufrufen, ein Werttyp? Dann verwenden Sie
call
. (Diese nicht anzuwenden, wenn der Wert eingerahmt ist - dann tatsächlichobject
oder eine Schnittstelle auf Sie sich berufen, und das sind Referenztypen.) - Ist die Methode Sie
virtual
oderabstract
erklärt sich berufen? Dann verwenden Siecallvirt
. - Rufen Sie die Methode über eine Schnittstellenreferenz auf? Dann verwenden Sie
callvirt
. - Ist die Methode, die Sie aufrufen,
override
deklariert, aber weder die Methode noch der Deklarationstypsealed
deklariert? Dann verwenden Siecallvirt
.
In allen anderen Fällen ist keine virtuelle Versand erforderlich, so dass Sie call
verwenden können - aber Sie sollte Verwendung callvirt
. Hier ist der Grund:
Die Verwendung von callvirt
bei nicht virtuellen Methoden entspricht call
, außer wenn das erste Argument null ist. In diesem Fall wird callvirt
sofort eine NullReferenceException
werfen, während call
nicht wird. Dies ist sinnvoll, da callvirt
in Fällen verwendet werden soll, in denen der virtuelle Methodenversand gewünscht ist, und Sie keinen virtuellen Methodenversand durchführen können, wenn Sie kein Objekt haben, für das eine Vtable-Suche durchgeführt werden soll.
Beachten Sie, dass callvirt
immer noch eine Ausnahme auslöst, wenn das erste Argument null ist, auch wenn eine Vtable-Suche nicht erforderlich ist!
Unter Berücksichtigung dieser Informationen mit callvirt
für alle nicht-statische Methode Anrufungen auf Referenztypen (wie der C# -Compiler der Fall ist) kann vorteilhaft sein, da es eine NullReferenceException
sofort an der Aufrufstelle statt irgendwann später verursachen wird, wenn this
verwendet wird (explizit oder implizit) innerhalb der Methode.
Aber .NET verwendet "Anruf" beim Aufruf von etwas wie Point.X. Von dem, was ich lese, verwenden anscheinend Werttypmethoden "Anruf". – Will
Ja, da diese Methoden nicht virtuell sind, würde ich erwarten, dass die Ausgabe "call" verwendet. Lies meine Antwort noch einmal. (Außerdem können Strukturen * keine virtuellen Member haben, da sie nicht vererbt werden können.) – cdhowie
Das macht Sinn, danke. Nur seltsam, dass Reflection "IsVirtual" als wahr für Struct-Methoden anzeigt. – Will
Standardmäßig verwendet der C# -Compiler immer callvirt für alle außer statische oder Werttypaufrufe. Dies verursacht eine implizite Nullprüfung des Arguments 'this' (arg0). Sie sind nicht unbedingt verpflichtet, dieser Konvention zu folgen, aber jede virtuelle Methode für einen Referenztyp erfordert definitiv callvirt.
Mit Ausnahme von virtuellen Methoden für versiegelte Klassen. –
Wenn Sie Aufruf in einer dynamischen Methode für eine virtuelle Methode verwenden, löst die Laufzeit eine Sicherheitsausnahme aus.
Was hat das mit C# zu tun? – SLaks