IL中的调用实例与newobj实例之间的区别 [英] Difference between call instance vs newobj instance in IL

查看:48
本文介绍了IL中的调用实例与newobj实例之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在深入研究C#,并研究可空值类型。出于实验目的,我编写了一段代码:

I'm delving into C# in Depth, and playing with nullable value types. Just for experimental purposes I wrote a piece of code:

    private static void HowNullableWorks()
    {
        int test = 3;
        int? implicitConversion = test;
        Nullable<int> test2 = new Nullable<int>(3);

        MethodThatTakesNullableInt(null);
        MethodThatTakesNullableInt(39);
    }

我很惊讶地看到 implicitConversion / test2 变量的初始化方式为:

And I was supprised to see that implicitConversion / test2 variables are initialized with:

call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

指令,而当 MethodThatTakesNullableInt 称为我可以看到:

instruction, whereas when MethodThatTakesNullableInt is called I can see:

IL_0017:  initobj    valuetype [mscorlib]System.Nullable`1<int32>

IL_0026:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

我了解。我认为我也会看到 newobj 关于 implicitConversion / test2 的说明。

which I understand. I thought that I'll see newobj instruction for implicitConversion / test2 as well.

这是完整的IL代码:

.method private hidebysig static void  HowNullableWorks() cil managed
{
  // Code size       50 (0x32)
  .maxstack  2
  .locals init ([0] int32 test,
           [1] valuetype [mscorlib]System.Nullable`1<int32> implicitConversion,
           [2] valuetype [mscorlib]System.Nullable`1<int32> test2,
           [3] valuetype [mscorlib]System.Nullable`1<int32> CS$0$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  stloc.0
  IL_0003:  ldloca.s   implicitConversion
  IL_0005:  ldloc.0
  IL_0006:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_000b:  nop
  IL_000c:  ldloca.s   test2
  IL_000e:  ldc.i4.3
  IL_000f:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_0014:  nop
  IL_0015:  ldloca.s   CS$0$0000
  IL_0017:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
  IL_001d:  ldloc.3
  IL_001e:  call       void csharp.in.depth._2nd.Program::MethodThatTakesNullableInt(valuetype [mscorlib]System.Nullable`1<int32>)
  IL_0023:  nop
  IL_0024:  ldc.i4.s   39
  IL_0026:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_002b:  call       void csharp.in.depth._2nd.Program::MethodThatTakesNullableInt(valuetype [mscorlib]System.Nullable`1<int32>)
  IL_0030:  nop
  IL_0031:  ret
} // end of method Program::HowNullableWorks


推荐答案

首先,它看起来像你已在Debug模式下进行了编译(基于 nop s)-如果在Release模式下进行编译,则可能会看到不同的代码。

First of all, it looks like you've compiled in Debug mode (based on the nops) - it's possible that you'll see different code emitted if you compile in Release mode.

ECMA CLR规范的I.12.1.6.2.1节(初始化值类型的实例)说:

Section I.12.1.6.2.1 of the ECMA CLR spec (Initializing instances of value types) says:

有三个选项可用于初始化值类型
实例的宿主。您可以通过加载房屋的地址(请参见
表I.8:房屋的地址和类型)并使用 initobj
将其清零。指令(对于局部变量,这也可以通过将方法标题中的
设置为 localsinit 位来实现)。您可以通过加载房屋的地址来调用
用户定义的构造函数(请参见表
I.8:住所位置的地址和类型),然后直接调用
构造函数。或者,您也可以按照§I.12.1.6.2.2中的描述将现有实例复制到
住宅中。

There are three options for initializing the home of a value type instance. You can zero it by loading the address of the home (see Table I.8: Address and Type of Home Locations) and using the initobj instruction (for local variables this is also accomplished by setting the localsinit bit in the method’s header). You can call a user-defined constructor by loading the address of the home (see Table I.8: Address and Type of Home Locations) and then calling the constructor directly. Or you can copy an existing instance into the home, as described in §I.12.1.6.2.2.

代码中前三种使用可空类型的结果导致将null值存储在本地变量中,因此此注释是相关的(本地变量是 home 的一种类型,用于值):前两个是本地变量<$ c您已经声明的$ c> implicitConversion 和 test ,第三个是编译器生成的临时文件,称为 CS $ 0 $ 0000 。如ECMA规范所示,可以使用 initobj (与struct的默认no-args构造函数等效,并且用于 CS $ 0 $ 0000 ),或通过加载本地地址并调用构造函数(用于其他两个本地人)。

The first three uses of nullable types in your code result in null values stored in locals, so this comment is relevant (locals are one type of home for values): the first two are the locals implicitConversion and test that you've declared, and the third is a compiler-generated temporary called CS$0$0000. As the ECMA spec indicates, these locals can be initialized by using initobj (which is equivalent to the default no-args constructor for a struct, and is used for CS$0$0000 in this case) or by loading the local's address and calling a constructor (used for the other two locals).

但是,对于最终的可为空的实例(通过 39 的隐式转换创建),结果未存储在本地中-它是在堆栈上生成的,因此初始化规则家在这里不适用。而是,编译器仅使用 newobj 在堆栈上创建值(对于任何值或引用类型都将如此)。

However, for the final nullable instance (created by the implicit conversion from 39), the result is not stored in a local - it's generated on the stack, so the rules for initializing a home don't apply here. Instead, the compiler just uses newobj to create the value on the stack (as it would for any value or reference type).

您可能想知道为什么编译器为调用 MethodThatTakesNullableInt(null)而不是为 MethodThatTakesNullableInt(39)生成本地变量code>。我怀疑答案是编译器始终使用 initobj 来调用默认构造函数(然后需要使用本地或其他宿主作为值),但使用的是 newobj 调用其他构造函数,并将结果存储在堆栈中(当该值没有合适的宿主时)。

You may be wondering why the compiler generated a local for the call to MethodThatTakesNullableInt(null) but not for MethodThatTakesNullableInt(39). I suspect that the answer is that the compiler always uses initobj to call the default constructor (which then requires a local or other home for the value), but uses newobj to call other constructors and store the result on the stack when there's not already an appropriate home for the value.

更多信息,另请参见规范中第III.4.21节(newobj)的注释:

For more information, see also this comment from Section III.4.21 (newobj) from the spec:


通常不使用<$ c $创建值类型c> newobj 。通常使用 newarr (对于
基于零的一维数组)或作为字段将
分配为参数或局部变量。对象。一旦分配了
,它们就会使用 initobj 进行初始化。但是, newobj
指令可用于在堆栈
上创建值类型的新实例,然后可以将其作为参数传递,存储在本地,
等中。

Value types are not usually created using newobj. They are usually allocated either as arguments or local variables, using newarr (for zero-based, one-dimensional arrays), or as fields of objects. Once allocated, they are initialized using initobj. However, the newobj instruction can be used to create a new instance of a value type on the stack, that can then be passed as an argument, stored in a local, etc.

这篇关于IL中的调用实例与newobj实例之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆