【问题标题】:Automatic initialization of backing fields支持字段的自动初始化
【发布时间】:2016-02-14 00:02:42
【问题描述】:

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

假设给定这个类:

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 私有 int32 'k__BackingField' .custom 实例无效 [​​mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00) .custom 实例无效 [​​mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (01 00 00 00 00 00 00 00) .property 实例 int32 PropertyA() { .get 实例 int32 TestCode.Test::get_PropertyA() .set 实例无效 TestCode.Test::set_PropertyA(int32) } // 属性结束 Test::PropertyA .method public hidebysig specialname instance int32 get_PropertyA() cil 托管 { .custom 实例无效 [​​mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00) // 代码大小 7 (0x7) .maxstack 8 IL_0000:ldarg.0 IL_0001:ldfld int32 TestCode.Test::'k__BackingField' IL_0006: 恢复 } // 方法结束 Test::get_PropertyA .method public hidebysig specialname 实例无效 set_PropertyA(int32 'value') cil 托管 { .custom 实例无效 [​​mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00) // 代码大小 8 (0x8) .maxstack 8 IL_0000:ldarg.0 IL_0001:ldarg.1 IL_0002: stfld int32 TestCode.Test::'k__BackingField' IL_0007: 恢复 } // 方法结束 Test::set_PropertyA

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

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

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

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

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

正如预期的那样,这给出了 CS0165“使用未分配的局部变量 'i'”。在为此做一些研究并挖掘ECMA SpecMicrosoft C# spec(5.0是最后一个正式文件,6.0似乎在Roslyn项目中分布松散)我找不到任何与初始化相关的东西字段。

我确实发现 this reference from VS2003 无法将类字段初始化为类型的 default 值,但它表明这是语言的一部分,而不是运行时。

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

【问题讨论】:

  • 所以您基本上想了解为什么您可以调用未分配的属性但不能调用未分配的局部变量?
  • 我明白为什么,但我不明白如何。初始化发生在哪里?
  • 这不是在规范的 §17.4.4 中描述的吗? “字段的初始值,无论是静态字段还是实例字段,都是字段类型的默认值(第 12.2 节)。在此默认初始化发生之前,无法观察字段的值,因此,一个字段永远不会“未初始化””?如果它不在 MSIL 中,我会假设运行时会处理它。编译器只需要知道规则,即规范说它将被初始化,所以没有警告。不多不少。
  • 规范的 5.3 部分涵盖了关于明确分配的部分。初始化将由运行时完成。
  • @WillemvanRumpt 感谢您在规范中找到它(从 2003 年开始移动了很多),但我明白为什么,我只是想知道在哪里实现了初始化。

标签: c# initialization


【解决方案1】:

运行时是否执行字段的初始化,还是我在初始化这些值的编译器中遗漏了 MSIL 中的某些内容?

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

引用规范(§5.2):

初始化为默认值通常是通过让内存管理器或垃圾收集器在分配内存之前将内存初始化为所有位为零来完成的。因此,使用全位零表示空引用很方便。

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

【讨论】:

    猜你喜欢
    • 2017-02-11
    • 2015-12-09
    • 2015-12-09
    • 2010-12-27
    • 1970-01-01
    • 2011-12-25
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    相关资源
    最近更新 更多