Auto property initialization IL instruction order
Asked Answered
E

2

6

I want to get the default value set on an auto property to do some IL weaving with Fody.

As I understand, the initialization is just a syntactic sugar that sets the backing field in the constructor. So I thought the default value is created with the instructions from the end of the last property's initialization to the stfld instruction that sets the backing field of the current property.

However this assumes the initialization is always done as the first thing in the constructor. Is that a correct assumption? Is there any edge cases to consider such as optimizations?

Expecting answered 19/10, 2016 at 18:34 Comment(2)
Write a few samples in C# and check the generated ILPatrology
I have already done that. That lead to me the assumption I mentioned. I want to be sure that there are no exceptions.Aubine
E
8

I found this pdf file titled Upcoming Features in C# which describes the new language features for C# 6.

Here is the section about auto property initializers (Emphasis is mine):

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.

Since the field initializers and the auto property initializers are treated equally, the following section from C# specification should apply to the auto property initialization as well.

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.

Expecting answered 19/10, 2016 at 19:15 Comment(4)
Your link seems to attempt to link to your harddrive.Dump
Sorry. I will fix it.Aubine
You asked about edge cases, did you forget to answer the question?Unmixed
I thought these quotes imply that there are no edge cases.Aubine
S
2

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.

Saccharose answered 20/10, 2016 at 13:6 Comment(4)
Wouldn't it be enough to get the value just before the first stsfld or stfld call that sets the backing field. That should give the default value regardless of how complex the semantics are. By the way I have seen the last thing you mentioned but actually it does not change the result, therefore I ignored it.Aubine
@qqww2: Yes, if you need the value only at runtime, at the point in the constructor where it's actually set, you can insert instructions there. You should consider whether or not you want to handle the case where the programmer doesn't use an initializer, but just sets the property in the constructor (which will result in a call to the setter). This isn't the same as initializing the backing field, but has the same semantics to clients of the class.Saccharose
Actually I am not interested in the value that is set in the constructor.Aubine
We're on the IL level; everything happens in the constructor. To be future proof, it's slightly risky to assume that a constructor always starts with assignments to auto-properties first. There is no guarantee that a new feature won't change that and insert more stuff before. Or for that matter some custom IL rewriter... However, since the problem would become unsolvable in the general case (there's no way to clearly separate auto-property assignment code from the rest only through static analysis), you may as well assume it for now.Saccharose

© 2022 - 2024 — McMap. All rights reserved.