我很想知道为什么会发生这种情况。请阅读下面的代码示例以及每个部分下面的注释中发出的相应 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 假设“用于进行调用的对象”不为空。 callvirt 另一方面,检查是否为 null,并在需要时抛出 NullReferenceException。

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

也可以看看:Jeff Richter 在这方面做得更好 - 在 CLR via C# 第二版的“Designing Types”章节中

其他提示

请参阅老博客帖子由Eric Gunnerson。

下面是帖子的文字内容:

<强>为什么C#总是使用callvirt吗

这个问题想出了一个内部C#别名,我想答案应该是普遍关心的。这是假设,答案是正确的 - 它已经相当长的一段

在.NET IL语言既提供了呼叫和callvirt指令,与callvirt被用于调用虚函数。但如果你看看通过C#生成的代码,你会看到它产生,即使在情况下,“callvirt”,其中没有涉及虚拟函数。为什么会这样?

我回去通过语言设计的笔记,我已经和他们的状态很清楚,我们决定在1999年12月13日使用callvirt。不幸的是,他们不吸引我们的理由这样做,所以我将不得不从我的记忆中去。

我们已经得到了从别人的报告(使用C#的.NET组可能一个(还以为是尚未命名C#当时))谁写的代码,叫上一个空指针的方法,但是他们没“T得到一个例外,因为该方法没有访问任何字段(即‘本’为空,但没有在该方法中使用它)。该方法然后叫另一种方法,它的确使用了这一点,并引发了异常,和一点头刮接踵而至。他们想通了之后,他们给我们送来一记一下吧。

我们认为能够调用的方法在空实例是有点怪。彼得金果做了一些测试,看看在PERF影响总是使用callvirt的是什么,这是足够小,我们决定做出改变。

将作为(perhaps-)有趣的一边...... GetType()是不寻常的,因为它的virtual - 这会导致一些的非常,非常奇怪的事情的。

(标记为维基,因为它是稍微偏离主题的实际问题)

在编译器不知道在第一表达式的实数型o的,但它确实知道在第二个表达式的实数型。这看起来像是一次只能看一个声明。

这是好的,因为C#很大程度上依赖于JIT进行优化。这是在这样一个简单的情况下,这两个电话将成为实例调用在运行时非常有可能。

我不相信callvirt是不断发出对非虚方法,但即使是,那将是没有问题的,因为该方法永远不会(原因很明显)。

覆盖

我会大胆地猜测,这是因为第一次分配给一个变量,它潜在地含有另一种可能已替换GetType(尽管我们可以看到事实并非如此)的downcasted实例;第二永远不可能比Object其它任何东西。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top