.NET CIL Call或CallVirt?

Wil*_*ill 6 .net c# cil .net-3.5

如何确定是否需要使用"Call"或"Callvirt"调用方法?

cdh*_*wie 6

你可以一一遵循这些简单的规则来决定你应该使用哪一个:

  • 方法是static?然后使用call.
  • 您正在调用的类型是值类型吗?然后使用call. (如果该值被装箱,则这适用——那么您实际上是在调用object某个接口,而这些是引用类型。)
  • 您调用的方法是声明的virtual还是abstract?然后使用callvirt.
  • 您是否通过接口引用调用该方法?然后使用callvirt.
  • 您调用的方法是否已声明override,但方法和声明类型均未声明sealed?然后使用callvirt.

在所有其他情况下,不需要虚拟分派,因此您可以使用call- 但您应该使用callvirt. 原因如下:

使用callvirt在非虚拟方法相当于call除了当第一参数为空。在那种情况下callvirtNullReferenceException立即抛出一个,而call不会。这是有道理的,因为callvirt它旨在用于需要虚拟方法分派的情况,如果您没有可以在其上进行 vtable 查找的对象,则无法进行虚拟方法分派。

请注意,callvirt即使不需要 vtable 查找,如果第一个参数为空,它仍然会抛出异常!

考虑到此信息,callvirt对引用类型的所有非静态方法调用(如 C# 编译器所做的那样)可能更可取,因为它会NullReferenceException立即在调用站点而不是稍后在this内部使用(显式或隐式)方法。

  • 是的,因为这些方法不是虚拟的,所以我希望输出使用“调用”。再读一遍我的回答。(此外,结构*不能*具有虚拟成员,因为它们不能被继承。) (2认同)
  • @High,struct 实例_do_ 有虚方法;它们从 Object 继承/覆盖的方法。但是,只有当您通过装箱实例(作为对象)引用结构时,才会虚拟调用这些方法。 (2认同)

Dan*_*ant 6

默认情况下,C#编译器始终将callvirt用于除静态或值类型调用之外的所有内容.这会导致对'this'(arg0)参数进行隐式空值检查.您并不是严格要求遵循此约定,但引用类型上的任何虚拟方法肯定都需要callvirt.