支持字段的自动初始化

Automatic initialization of backing fields

这个问题不是关于如何初始化支持字段...

让我们假设这个 class:

public class Test
{
    public int PropertyA { get; set; }
    public int PropertyB { get; set; }
    private int _propertyC;
    public int PropertyC { get { return _propertyC; } set { _propertyC = value; } }
}

定义了许多属性,自动和显式实现。

让我们仔细看看 PropertyA 是如何编译的(64 位调试)。 MSIL 输出是这样的:

.field private int32 'k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
.custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )

.property instance int32 PropertyA()
{
  .get instance int32 TestCode.Test::get_PropertyA()
  .set instance void TestCode.Test::set_PropertyA(int32)
} // end of property Test::PropertyA

.method public hidebysig specialname instance int32 
        get_PropertyA() cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldfld      int32 TestCode.Test::'k__BackingField'
  IL_0006:  ret
} // end of method Test::get_PropertyA

.method public hidebysig specialname instance void 
        set_PropertyA(int32 'value') cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       8 (0x8)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  stfld      int32 TestCode.Test::'k__BackingField'
  IL_0007:  ret
} // end of method Test::set_PropertyA

这是非常基本的,它定义了支持字段、属性,然后是 get__set 加载或设置支持的 属性 方法场.

我不明白的是(直到现在我都认为这是理所当然的),这是一个完全合法的构造函数:

public Test()
{
    Debug.WriteLine($"{PropertyA.ToString()}");
    Debug.WriteLine($"{_propertyC.ToString()}");
}

虽然这显然是一个编译器错误:

public Test()
{
    int i;
    Debug.WriteLine($"{i.ToString()}");
}

正如预期的那样,给出了 CS0165 "Use of unassigned local variable 'i'"。在为此做一些研究并挖掘 ECMA Spec, and the Microsoft C# spec(5.0 是最后一个正式文件,6.0 似乎松散地分布在 Roslyn 项目中),我找不到任何与字段初始化相关的内容。

我确实发现 this reference from VS2003 避开了 class 字段被初始化为类型的 default 值,但这表明这是语言的一部分而不是运行时.

所以问题是...运行时是否执行字段的初始化,或者我是否在初始化这些值的编译器的 MSIL 中遗漏了某些内容?我假设编译器会这样做,否则我希望两个示例构造函数中的 CS0165 相同。

Does the runtime perform the initialization of the fields, or am I missing something in the MSIL from the compiler that is initializing these values?

规范的 §5.2 和 §5.3 部分分别讨论了默认值和明确赋值。

引自规范 (§5.2):

Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before it is allocated for use. For this reason, it is convenient to use all-bits-zero to represent the null reference.

这继续显示内存管理器和 GC 负责默认值初始化。