Field initializers and base constructors order

In this post we’ll explore what happens behind the scenes when we place code in field initializer compared to constructor body.

Field initializer and constructor

We’ll start from a simple example:

public class MyClass
{
private static int index = 1;

public int x = index++;
public int y;

public MyClass()
{
y = index++;
}
}

The result here is very intuitive, this code:

var instance = new MyClass();
Console.WriteLine("x = " + instance.x);
Console.WriteLine("y = " + instance.y);

Will output:
image

So far there’s no surprise, the field initializers code is performed prior to code of the explicit constructor.

Field initializer and base constructor

Let’s observe the next code:

public class Base
{
protected static int index = 1;

public int w = index++;
public int x;

public Base()
{
x = index++;
}
}

public class Derived : Base
{
public int y = index++;
public int z;

public Derived()
{
z = index++;
}
}

There are two outputs which one may expect:

  • The base class fields will be initialized first and the derived ones will be initialized afterwards.
  • The field y, which has a field initializer will be initialized first, then the base fields and lastly the derived constructor will be called.

What actually happens?

var instance = new Derived();
Console.WriteLine("Base.w = " + instance.w);
Console.WriteLine("Base.x = " + instance.x);
Console.WriteLine("Derived.y = " + instance.y);
Console.WriteLine("Derived.z = " + instance.z);

Outputs:
image

We can clearly see that the derived field initializer is called first, prior to the base constructor.

Why it happens?

Looking at the disassembled IL we can see that the field initializers code is placed at the beginning of the constructor code, prior to the base constructor call:

.method public hidebysig specialname rtspecialname 
instance void .ctor() cil managed
{
// Code size 48 (0x30)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldsfld int32 ConsoleApplication1.Base::index
IL_0006: dup
IL_0007: ldc.i4.1
IL_0008: add
IL_0009: stsfld int32 ConsoleApplication1.Base::index
IL_000e: stfld int32 ConsoleApplication1.Derived::y
IL_0013: ldarg.0
IL_0014: call instance void ConsoleApplication1.Base::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: ldarg.0
IL_001c: ldsfld int32 ConsoleApplication1.Base::index
IL_0021: dup
IL_0022: ldc.i4.1
IL_0023: add
IL_0024: stsfld int32 ConsoleApplication1.Base::index
IL_0029: stfld int32 ConsoleApplication1.Derived::z
IL_002e: nop
IL_002f: ret
} // end of method Derived::.ctor

We can see, obviously, that the compiler is protecting us from doing silly mistakes like accessing base fields before they’re initialized:

public class Base
{
protected int baseField = 100;
}

public class Derived : Base
{
public int x = baseField;
}

Leads to a compilation error, while this code works perfectly:

public class Derived : Base
{
public int x;

public Derived()
{
x = baseField;
}
}

Does it really matter?

Understanding our code

I guess that in almost all cases this will not cause noticeable difference. It will however make a difference when we’re depending on that order, or, when the field initializers call methods that depend on side effects. So for start it’s important that we understand how our code works.

Make use of it

The next thing we can do is to use the field initializers as a hack to precede base constructors code. For example, if we have this 3rd party logger:

public class MyLogger
{
public MyLogger()
{
if (!Directory.Exists(@"C:\Logs"))
{
throw new InvalidOperationException("Can't start logs");
}

File.WriteAllText(@"C:\Logs\Session.log", "Initialized");
}
}

And we want to change it’s behavior without rerouting all it’s methods (of course this is not the only way), we can initialize a dummy field with method call that creates the directory:

public class MyLoggerHack : MyLogger
{
private bool dummy = CreateDirectory();

private static bool CreateDirectory()
{
if (!Directory.Exists(@"C:\Logs"))
{
Directory.CreateDirectory(@"C:\Logs");
}

return true;
}
}

So using this hack, we can “add” new code to the beginning of an existing call constructor without actually changing the original class.