呼叫实例之间的差值Vs在IL newobj实例(Difference between call in

2019-07-30 23:57发布

我钻研C#的深入开展,并与空值类型播放。 只是为了实验的目的,我写了一段代码:

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

        MethodThatTakesNullableInt(null);
        MethodThatTakesNullableInt(39);
    }

而我supprised看到implicitConversion / test2的变量与初始化:

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

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

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

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

我明白了。 我以为我会看到implicitConversion / test2的 newobj指令为好。

这是全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

Answer 1:

首先,它看起来就像你在调试模式(基于编译nop S) -它可能是你会看到不同的发射代码,如果你在Release模式编译。

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

有三个选项用于初始化值类型实例的家。 并使用:您可以通过加载家(地址和家庭位置的类型见表一.8)地址零它initobj指令(局部变量这也通过设置完成localsinit该方法的头位)。 然后直接调用构造函数:您可以通过加载家庭(家庭位置的地址和类型见表一.8)的地址调用用户定义的构造函数。 或者你可以将现有的实例复制到家庭,如§I.12.1.6.2.2描述。

前三种用途可空类型的存储在当地人空值代码的结果,所以这条评论是相关的(当地人都一个类型的家里值):前两个是当地人implicitConversiontest ,你已经声明,并第三个是一个编译器产生的暂时性称为CS$0$0000 。 随着ECMA规范指出,这些当地人可以通过使用初始化initobj (这相当于默认的无参数的构造函数的结构,以及用于CS$0$0000在这种情况下)或者通过将本地的地址和调用构造函数(用于其他两个当地人)。

然而,最终为空的情况下(从隐式转换创建39 ),结果不存储在本地-这是在栈上产生的,所以对于初始化一个家庭的规则并不适用于此。 相反,编译器只是使用newobj创建堆栈上的值(因为它会为任何值或引用类型)。

你可能想知道为什么编译器生成的本地呼叫到MethodThatTakesNullableInt(null)但不适用于MethodThatTakesNullableInt(39) 我怀疑,答案是,编译器总是使用initobj调用默认的构造函数(然后要求该值的本地或其他家庭),但使用newobj调用其他构造函数和结果存储在堆栈上时已经是有没有适当的家庭为数值。

欲了解更多信息,参见第从III.4.21(newobj)从规范此评论:

值类型通常不使用创建newobj 。 它们通常分配既可以作为参数或局部变量,使用newarr (对于从零开始,一维数组),或作为对象的领域。 分配后,他们使用的初始化initobj 。 然而, newobj指令可用于创建在栈上的值的类型,然后可以作为参数被传递的一个新实例,存储在本地,等



文章来源: Difference between call instance vs newobj instance in IL
标签: c# il