如何确定是否需要使用“call”或“callvirt”调用方法?
最佳答案
您可以按照这些简单的规则逐一确定应该使用哪些规则:
方法是否static
?然后使用call
。
是在值类型上调用的类型吗?然后使用call
。(如果值是装箱的,则不适用于此项——然后您实际上是在object
或某些接口上调用,而这些接口是引用类型。)
您调用的方法是否声明为virtual
或abstract
?然后使用callvirt
。
是否通过接口引用调用方法?然后使用callvirt
。
您正在调用的方法是否声明为override
,但该方法和声明类型都未声明为sealed
?然后使用callvirt
。
在所有其他情况下,不需要虚拟分派,因此您可以使用call
——但您应该使用callvirt
。原因如下:
对非虚方法使用callvirt
等同于call
,除非第一个参数为空。在这种情况下,callvirt
会立即抛出一个NullReferenceException
,而call
不会。这是有意义的,因为callvirt
用于需要虚拟方法分派的情况,并且如果没有要对其执行vtable查找的对象,则无法执行虚拟方法分派。
注意,如果第一个参数为空,即使不需要vtable查找,callvirt
仍会抛出异常!
考虑到这一信息,对于引用类型上的所有非静态方法调用使用callvirt
可能是可取的(正如c编译器所做的那样),因为它将在调用站点立即导致NullReferenceException
,而不是稍后在方法内部(显式或隐式)使用this
时。