【发布时间】:2011-05-11 18:07:19
【问题描述】:
如何判断一个方法需要用“Call”还是“Callvirt”来调用?
【问题讨论】:
-
这和C#有什么关系?
如何判断一个方法需要用“Call”还是“Callvirt”来调用?
【问题讨论】:
您可以按照这些简单的规则一一确定您应该使用哪个:
static吗?然后使用call。call。 (如果值被装箱,则此不适用——那么您实际上是在object 或某个接口上调用,这些是引用类型。)virtual 还是abstract?然后使用callvirt。callvirt。override,但方法和声明类型都没有声明sealed?然后使用callvirt。在所有其他情况下,不需要虚拟调度,因此您可以使用call——但您应该使用callvirt。原因如下:
在非虚拟方法上使用callvirt 等效于call,除非第一个参数为空。在这种情况下,callvirt 将立即抛出 NullReferenceException,而 call 不会。这是有道理的,因为callvirt 旨在用于需要虚拟方法分派的情况,如果您没有可以在其上进行 vtable 查找的对象,则无法进行虚拟方法分派。
请注意,如果第一个参数为空,callvirt 仍然会抛出异常,即使不需要 vtable 查找!
考虑到这些信息,对引用类型的所有非静态方法调用使用callvirt(正如 C# 编译器所做的那样)可能更可取,因为它会在调用站点立即导致NullReferenceException,而不是稍后某个时间@ 987654343@ 在方法内部(显式或隐式)使用。
【讨论】:
默认情况下,C# 编译器总是对除静态或值类型调用之外的所有内容使用 callvirt。这会导致对“this”(arg0)参数进行隐式空检查。您不必严格遵守此约定,但引用类型上的任何虚拟方法都肯定需要 callvirt。
【讨论】:
如果您在虚拟方法的动态方法中使用调用,则运行时会引发安全异常。
【讨论】: