Auto 属性 初始化IL指令顺序

Auto property initialization IL instruction order

我想获取自动设置的默认值 属性 以使用 Fody 进行一些 IL 编织。

据我了解,初始化只是在构造函数中设置支持字段的语法糖。所以我认为默认值是使用从最后一个 属性 的初始化结束到设置当前 属性.[=12 的支持字段的 stfld 指令的指令创建的=]

然而,这假定初始化始终作为构造函数中的第一件事完成。这是一个正确的假设吗?是否有任何边缘情况需要考虑,例如优化?

我发现这个 pdf 文件标题为 C# 即将推出的功能,它描述了 C# 6 的新语言功能。

这是关于自动 属性 初始化器的部分(重点是我的):

The initializer directly initializes the backing field; it doesn’t work through the setter of the autoproperty.

The initializers are executed in order as written, just as – and along with – field initializers.

Just like field initializers, auto-property initializers cannot reference ‘this’ – after all they are executed before the object is properly initialized. This would mean that there aren’t a whole lot of interesting choices for what to initialize the auto-properties to. However, primary constructors change that. Autoproperty initializers and primary constructors thus enhance each other.

由于字段初始化器和自动 属性 初始化器被同等对待,因此 C# 规范中的以下部分也应适用于自动 属性 初始化。

10.11.3 Constructor execution

Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

...

It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor-body.

首先,一般说明:你说 "the value" 就好像你可以从分析 IL 中得到它一样。这通常不是真的,因为表达式可以是任何东西,只要它不涉及 this。你只能得到计算值的指令,这对你的应用程序来说可能就足够了。对于边缘情况,请特别考虑以下事实:表达式可能会初始化 属性:

的支持字段以外的字段
public int i { get; set; } = s = t = 1;
public static int s { get; set; } = t;
public static int t = 2;

这个语义是可以预见的但并不明显:s的默认值是0(t此时还没有显式初始化,因为初始化是按声明顺序进行的),默认t 的值为 2,i 的默认值为 1,其初始化的扭曲,如果发生,也会设置 t 然后 s 为 1 . 在这种情况下,从 IL 代码中导出值并不简单,甚至识别初始化器也需要您考虑将哪些 stfld/stsfld 指令分配给 属性 支持字段,而不是任何旧字段。

说到琐碎的事情,如果 属性 被初始化为类型的默认值,编译器可能会完全忽略 属性 初始化的生成,因为它将由 [=22= 处理].在 IL 级别,以下可能会导致相同的代码:

int a { get; set; } = 3 / 4;

int a { get; set; }

从某种意义上说,这是一种边缘情况,在代码中看起来像初始化程序的东西可能根本不存在于 IL 中。如果你只关心值,而不是是否使用显式初始化器,这当然没问题。