5

我想在自动属性上设置默认值,以使用 Fody 进行一些 IL 编织。

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

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

4

2 回答 2

8

我找到了这个名为Upcoming Features in C#的pdf文件,它描述了 C# 6 的新语言特性。

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

初始化器直接初始化后备字段;它不能通过自动属性的设置器工作。

初始化程序按写入的顺序执行,就像 - 并与 - 字段初始化程序一样。

就像字段初始化器一样,自动属性初始化器不能引用“this”——毕竟它们是在对象正确初始化之前执行的。这意味着对于初始化自动属性的内容没有很多有趣的选择。然而,主构造函数改变了这一点。自动属性初始化器和主构造器因此相互增强。

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

10.11.3 构造函数执行

变量初始化器被转换为赋值语句,并且这些赋值语句在基类实例构造函数的调用之前执行。这种排序确保所有实例字段在执行任何有权访问该实例的语句之前由其变量初始化程序初始化。

...

将实例变量初始化器和构造器初始化器视为自动插入到构造器主体之前的语句是很有用的。

于 2016-10-19T19:15:20.260 回答
2

首先,一般性说明:您谈到“价值”,就好像您可以从分析 IL 中得到它。这通常不是真的,因为表达式可以是任何东西,只要它不涉及this. 您只能获取计算值的指令,这对于您的应用程序来说可能已经足够了。对于边缘情况,请考虑以下事实,特别是,表达式可能会初始化属性的支持字段以外的字段:

public int i { get; set; } = s = t = 1;
public static int s { get; set; } = t;
public static int t = 2;

this 的语义是可预测的,但并不明显:默认值为s0(t此时尚未显式初始化,因为初始化是按声明顺序进行的),默认值为t2,默认值为i1,其中扭曲它的初始化,如果它发生,也设置t然后s为 1。在这种情况下,从 IL 代码派生值并非易事,甚至识别初始化程序需要您考虑哪些stfld/stsfld指令分配给属性支持字段,而不是到任何旧领域。

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

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

int a { get; set; }

这是一个极端情况,因为代码中看起来像初始化程序的东西可能根本不存在于 IL 中。如果您只对值感兴趣,而不是是否使用了显式初始化程序,那么这当然没问题。

于 2016-10-20T13:06:44.393 回答