【问题标题】:.NET CIL Call or CallVirt?.NET CIL 调用还是 CallVirt?
【发布时间】:2011-05-11 18:07:19
【问题描述】:

如何判断一个方法需要用“Call”还是“Callvirt”来调用?

【问题讨论】:

  • 这和C#有什么关系?

标签: c# .net cil .net-3.5


【解决方案1】:

您可以按照这些简单的规则一一确定您应该使用哪个:

  • 方法是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@ 在方法内部(显式或隐式)使用。

【讨论】:

  • 但是 .NET 在调用 Point.X 之类的东西时使用“调用”。从我正在阅读的内容来看,显然值类型方法使用“调用”。
  • 是的,因为这些方法不是虚拟的,所以我希望输出使用“调用”。再次阅读我的答案。 (此外,结构 不能 具有虚拟成员,因为它们不能被继承。)
  • 有道理,谢谢。奇怪的是,反射显示“IsVirtual”对于结构方法是正确的。
  • @High,结构实例有虚方法;他们从 Object 继承/覆盖的方法。但是,只有当您通过盒装实例(作为对象)引用结构时,才会虚拟调用这些方法。
  • @cdhowie,call 和 callvirt 之间的一个重要区别是,如果 arg0 为 null,则 call 不会引发异常。从纯代码发出的角度来看(没有生成源),这没什么大不了的,但从编译器/调试器的角度来看,在调用点抛出空引用而不是在最终使用 arg0 时任意深入内部是有帮助的.
【解决方案2】:

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

【讨论】:

  • 密封类上的虚拟方法除外。
【解决方案3】:

如果您在虚拟方法的动态方法中使用调用,则运行时会引发安全异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-07
    相关资源
    最近更新 更多