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?

Yusuf Tarık Günaydın
  • 3,016
  • 2
  • 27
  • 41

2 Answers2

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.

Community
  • 1
  • 1
Yusuf Tarık Günaydın
  • 3,016
  • 2
  • 27
  • 41
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.

Jeroen Mostert
  • 27,176
  • 2
  • 52
  • 85
  • 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. – Yusuf Tarık Günaydın Oct 20 '16 at 13:45
  • @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. – Jeroen Mostert Oct 20 '16 at 14:03
  • Actually I am not interested in the value that is set in the constructor. – Yusuf Tarık Günaydın Oct 20 '16 at 14:30
  • 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. – Jeroen Mostert Oct 20 '16 at 15:15