为什么值类型字段可能被实例化,而不是被初始化?

Why value type fields may be instantiated, instead of initialized?

如果 S 声明了构造函数,则通过调用其构造函数(例如 a = new S(11))初始化值类型局部变量(例如 S s; 给定 struct S{})使用 int 参数。然后 new S(11) 编译为:

ldloca.s   V_0
ldc.i4.s   11
call       instance void S::.ctor(int32)

然而,当 s 是一个字段时(例如 class C { S s;),则它不会以相同的方式初始化。关于 class C 的以下实例方法: void setS(int n) { s = new S(n); } 它将编译为:

ldarg.0
ldarg.1
newobj     instance void S::.ctor(int32)
stfld      valuetype S C::s

我原以为它会编译成下面的代码,这更接近局部变量的情况:

ldarg.0
ldflda    valuetype S C::s
ldarg.1
call       instance void S::.ctor(int32)

另外,如果我理解清楚的话,newobj instance void S::.ctor(int32)的开销和GC负担都比较大。我说得对吗?

为什么 C# 编译器对值类型字段使用与局部变量不同的方法?

基本上,为了将构造函数调用与赋值分开,需要此行为。

预期的可观察行为是,如果构造函数抛出异常,则不会进行赋值。在构造函数直接写入 field/stack 槽的 "optimized" 版本中不会出现这种情况。

如果满足以下条件,您可以在分配给局部变量时看到相同的 IL:

  • 该变量之前已经声明并赋值
  • 如果抛出异常,变量仍然可以访问

不完全相同的问题,但有更多细节in this SO post and on Eric Lippert's blog