我很好奇为什么会这样。请阅读下面的代码示例以及每个部分下方的注释中发出的相应IL:

using System;

class Program
{
    static void Main()
    {
        Object o = new Object();
        o.GetType();

        // L_0001: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0006: stloc.0
        // L_0007: ldloc.0
        // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

        new Object().GetType();

        // L_000e: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
    }
}

为什么编译器在第一部分发出callvirt,在第二部分发出call?编译器是否有理由为非虚拟方法发出callvirt指令?并且在某些情况下,编译器将为非虚拟方法发出callvirt,这是否会为类型安全性带来问题?

最佳答案

请放心。

从技术上讲,C#编译器并不总是使用callvirt
对于静态方法和在值类型上定义的方法,它使用call。多数通过callvirt IL指令提供。

两者之间引起争议的差异是call假定“用于进行调用的对象”不为null。另一方面,callvirt检查是否不为null并在需要时抛出NullReferenceException。

  • 对于静态方法,该对象是类型对象,不能为null。值类型的同上。因此,call用于它们-更好的性能。
  • 对于其他语言,语言设计者决定使用callvirt,以便JIT编译器验证用于进行调用的对象不为null。即使对于非虚拟实例方法,他们也重视安全而不是性能。

  • 另请参见:杰夫·里希特(Jeff Richter)在这方面做得更好-在他的CLR第二版“设计类型”一章中

    10-06 05:56