我想在auto属性上设置默认值,用Fody做一些IL编织。
据我所知,初始化只是一个语法糖,它在构造函数中设置了支持字段。所以我认为默认值是使用从最后一个属性的初始化结束到设置当前属性的支持字段的stfld
指令的指令创建的。
然而,这假设初始化始终是构造函数中的第一件事。这是正确的假设吗?是否有任何边缘情况需要考虑,例如优化?
答案 0 :(得分:5)
我发现这个名为 C#中的即将推出的功能的pdf文件描述了C#6的新语言功能。
以下是关于auto属性初始值设定项的部分(Emphasis is mine):
初始化程序直接初始化后备字段;它不能通过autoproperty的setter工作。
初始化程序按照书面顺序执行,就像 - 和字段初始化程序一样。
就像字段初始值设定项一样,自动属性初始值设定项不能引用'this' - 毕竟它们被执行了 在对象正确初始化之前。这意味着没有太多有趣的东西 选择初始化自动属性的内容。但是,主要构造函数改变了这一点。 Autoproperty 初始化者和主要构造者因此相互促进。
由于字段初始值设定项和自动属性初始值设定项被同等对待,因此C#规范中的以下部分也应适用于自动属性初始化。
10.11.3构造函数执行
将变量初始值设定项转换为赋值语句,并在调用基类实例构造函数之前执行这些赋值语句。此排序可确保在执行有权访问该实例的任何语句之前,所有实例字段均由其变量初始值设定项初始化。
...
将实例变量初始值设定项和构造函数初始值设定项视为语句是很有用的 在构造函数体之前自动插入。
答案 1 :(得分:1)
First, a general note: you speak of "the value" as though you could get it from analyzing the IL. This isn't true in general, because the expression could be anything, as long as it doesn't involve this
. You can only get the instructions that calculate the value, which may be enough for your application. For an edge case, consider the fact that in particular, the expression may initialize fields other than the backing field of the property:
public int i { get; set; } = s = t = 1;
public static int s { get; set; } = t;
public static int t = 2;
The semantics of this are predictable but not obvious: the default value of s
is 0 (t
has not been explicitly initialized at this point because initialization happens in declaration order), the default value of t
is 2, and the default value of i
is 1, with the twist that its initialization, if it occurs, also sets t
and then s
to 1. Deriving the values from the IL code in this case is not trivial, and even identifying the initializers requires that you consider which stfld
/stsfld
instructions assign to property backing fields, as opposed to any old field.
Speaking of trivial, if a property is initialized to the default value of the type, the compiler may elide generation of the property initialization altogether since it will be handled by newobj
. On the IL level, the following may result in the same code:
int a { get; set; } = 3 / 4;
and
int a { get; set; }
This is an edge case in the sense that what looks like an initializer in code may not be present in the IL at all. If all you're interested in is the value, not whether an explicit initializer was used, this is of course no problem.