gpt4 book ai didi

c# - IL 中调用实例与 newobj 实例之间的区别

转载 作者:太空狗 更新时间:2023-10-29 19:46:02 25 4
gpt4 key购买 nike

我正在深入研究 C#,并尝试使用可为 null 的值类型。只是出于实验目的,我写了一段代码:

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

MethodThatTakesNullableInt(null);
MethodThatTakesNullableInt(39);
}

我很惊讶地看到 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/test2newobj 指令。

这是完整的 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)——如果您在 Release 模式下编译,您可能会看到不同的代码。

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

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.

代码中前三种可空类型的使用导致空值存储在局部变量中,因此此注释是相关的(局部变量是值的一种类型home):前两个是局部变量您已声明的 implicitConversiontest,第三个是名为 CS$0$0000 的编译器生成的临时文件。正如 ECMA 规范所指出的,这些局部变量可以使用 initobj 进行初始化(相当于结构的默认无参数构造函数,用于 CS$0$0000在这种情况下)或通过加载本地地址并调用构造函数(用于其他两个本地地址)。

然而,对于最终的可空实例(由 39 的隐式转换创建),结果不存储在本地 - 它在堆栈上生成,因此初始化 home 的规则在这里申请。相反,编译器只使用 newobj 在堆栈上创建值(就像对任何值或引用类型一样)。

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

有关更多信息,另请参阅规范中第 III.4.21 节 (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.

关于c# - IL 中调用实例与 newobj 实例之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11966930/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com