2010-11-17 7 views

Antwort

4

Sie können diese einfachen Regeln eins nach dem anderen folgen, um zu bestimmen, welche Sie verwenden sollten:

  • Ist die Methode static? Dann verwenden Sie call.
  • Ist der Typ, den Sie aufrufen, ein Werttyp? Dann verwenden Sie call. (Diese nicht anzuwenden, wenn der Wert eingerahmt ist - dann tatsächlich object oder eine Schnittstelle auf Sie sich berufen, und das sind Referenztypen.)
  • Ist die Methode Sie virtual oder abstract erklärt sich berufen? Dann verwenden Sie callvirt.
  • 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 Deklarationstyp sealed deklariert? Dann verwenden Sie callvirt.

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.

+0

Aber .NET verwendet "Anruf" beim Aufruf von etwas wie Point.X. Von dem, was ich lese, verwenden anscheinend Werttypmethoden "Anruf". – Will

+2

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

+0

Das macht Sinn, danke. Nur seltsam, dass Reflection "IsVirtual" als wahr für Struct-Methoden anzeigt. – Will

6

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.

+1

Mit Ausnahme von virtuellen Methoden für versiegelte Klassen. –

0

Wenn Sie Aufruf in einer dynamischen Methode für eine virtuelle Methode verwenden, löst die Laufzeit eine Sicherheitsausnahme aus.