What do you think of the following piece of code?
int x = 1;
int y = x + 1;
Looks simple, except that it doesn’t compile (error CS0236: A field initializer cannot reference the non-static field, method, or property ‘Program.x’)
Try making x and y static – the code will compile fine now. I have been trying to find out why – so far, I haven’t got a clue. Just in case it’s not obvious, I do know that moving y = x + 1 into the body of Program’s constructor will get me what I want, but I was (and am) curious to know why field initializers, and only instance field initializers, aren’t allowed to access non-static fields.
The C# spec says that field initializers “correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors”, just before the call to the base class constructor. When I generated IL for Program’s constructor after replacing y = x + 1 with y = 2, it looked like this
L_0002: stfld int32 ConsoleApplication1.Program::x
L_0009: stfld int32 ConsoleApplication1.Program::y
L_000f: call instance void [mscorlib]System.Object::.ctor()
The generated IL matches with the spec – 1 and 2 are stfld’ed into x and y before calling the base class constructor (System.Object::.ctor()).
The C# spec also says that a field initializer is not allowed to reference the this pointer – “ variable initializer for an instance field cannot reference the instance being created.” It doesn’t say why though.
The IL shown above clearly indicates that this (ldarg.0) is valid when storing 1 and 2 into x and y. So why doesn’t C# allow y = x + 1?
I initially wondered if that rule is to prevent code like this
int x = y + 1;
int y = x + 1;
but then, the C# spec clearly says that the variable initializers will be run in “textual” order. If you combine that with the fact that x and y will be assigned their default values even before their variable initializers run, you’ll see that the above code should run fine, resulting in x = 1 (because y is zero) and y = 2. You can make x and y static and run the code, you will see the same result.
So again, why? Can you figure out why? We’ll do some more digging into this in the next post.