为什么C#编译器为GetType()方法调用发出callvirt指令?

And*_*are 28 c# compiler-construction il type-safety

我很想知道为什么会这样.请阅读下面的代码示例以及每个部分下面的注释中发出的相应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()
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么编译器callvirt为第一部分发出a 而call第二部分发出a ?是否有任何理由编译器会发出callvirt非虚方法的指令?如果在某些情况下编译器将callvirt为非虚拟方法发出一个,这会产生类型安全问题吗?

Dus*_*ell 28

请参阅Eric Gunnerson撰写的这篇旧博客文章.

这是帖子的文字:

为什么C#总是使用callvirt?

这个问题出现在一个内部的C#别名上,我认为答案是普遍感兴趣的.这是假设答案是正确的 - 已经有一段时间了.

.NET IL语言提供call和callvirt指令,callvirt用于调用虚函数.但是如果你仔细查看C#生成的代码,即使在没有涉及虚函数的情况下,你也会看到它生成一个"callvirt".为什么这样做?

我回过头来看过我的语言设计笔记,他们非常清楚地说明我们决定在12/13/1999使用callvirt.不幸的是,他们没有抓住我们这样做的理由,所以我将不得不离开我的记忆.

我们收到了某人的报告(很可能是一个使用C#的.NET组(当时认为它还没有命名为C#))谁编写了一个在空指针上调用方法的代码,但是他们没有得到一个例外,因为该方法没有访问任何字段(即"this"为null,但方法中没有使用它).那个方法然后调用另一个方法,它确实使用了这一点并引发了一个异常,随后出现了一些令人头疼的问题.在他们弄清楚之后,他们给我们发了一张关于它的说明.

我们认为能够在null实例上调用方法有点奇怪.Peter Golde做了一些测试,看看总是使用callvirt的性能影响是什么,它足够小,我们决定做出改变.

  • 太糟糕了,非虚方法无法指定应该在没有`callvirt`的情况下调用它,因为允许像`string.IsNullOrEmpty`这样的方法在空字符串上可用是很有帮助的. (3认同)

Gis*_*shu 22

只是玩得安全.

从技术上讲,C#编译器并不总是使用callvirt

对于在值类型上定义的静态方法和方法,它使用call.大多数是通过callvirtIL指令提供的.

摆动两者之间投票的区别在于call假设"用于进行调用的对象"不为空.callvirt另一方面,检查not null并在需要时抛出NullReferenceException.

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

另请参阅:Jeff Richter在此处做得更好 - 在CLR中的"设计类型"一章中通过C#2nd Ed