Mucking around with instance field initializers – Part 2

We saw in Part 1 that C# doesn’t allow an instance field initializer to refer another field in the class. Before trying to figure out why, let’s first see if this is a restriction imposed by C#, rather than by the CLR. 

Some disassembling and reassembling later, this is what the new IL code looks like.

    IL_0000:  ldarg.0
IL_0001: ldc.i4.1
IL_0002: stfld int32 ConsoleApplication1.Program::x
IL_0007: ldarg.0
IL_0008: ldarg.0
IL_000b: ldfld int32 ConsoleApplication1.Program::x
IL_0009: ldc.i4.1
IL_000a: add
IL_000c: stfld int32 ConsoleApplication1.Program::y
IL_000d: ldarg.0
IL_000e: ldfld int32 ConsoleApplication1.Program::y
IL_000f: call void [mscorlib]System.Console::WriteLine(int32)

 It’s basically the same code in Part 1, except for the initialization of y. Instead of initializing y to 2, the code tries to load this.x, add 1 to it, store the result in y (y = x + 1) and then print out the result. The call to the constructor of System.Object comes next, but I’ve left that out for clarity. 

Guess what, it works! The code assembles, checks out with peverify and runs fine, printing the value 2.

So, it looks like the CLR does not have any problems accessing other instance fields when initializing them. Then why doesn’t the C# compiler like them?

How about fields inherited from a base class? If C# doesn’t have the instance field initializer restriction, then the following piece of code should compile . What do you expect the value of y to be?

    class A
protected int x = 1;

class B : A
int y = x + 1;
B() { Console.WriteLine(y); }

 1? 2? It depends on where the C# compiler puts the instance field initialization code. According to the current spec, it’s placed before the call to the base class constructor, which means that y will be initialized to 1. Suddenly, the derived class now gets to access uninitialized base class state. Not good at all.

You could argue that the compiler knows about inherited fields and therefore can apply the restriction only to those fields – but that would add a nasty special case to the “you-cannot-reference-during-instance-field-initialization” list. I don’t think it will be intuitive either – a typical programmer will be surprised if a “Move to base class” refactor suddenly breaks working code.

Static fields don’t have this problem – they aren’t really “inherited”. Which could be why the compiler allows static field initializers to reference other fields.

What do you think?

One thought on “Mucking around with instance field initializers – Part 2”

  1. I think the C# guys have been introducing features and making rules which they felt is a drawback in C++. I guess this one is also something like that. In C++, the fields can be initialized inline with the ctor declaration but the order definition happens in the order of the members declared and NOT in the order in which the members are initialized. Static members are out of the stack and are initialized to the default values, which makes them less harmful.

    Not sure, if that aligns with what you intend to convey in the post. But anyways, the post is splendid.

Leave a Reply

Your email address will not be published. Required fields are marked *