The topic of this post is the beforefieldinit flag. Part I is basically a recap, all those things are already documented and there are great posts about that. Part II is where the fun begins. I googled very long, but I did not find that information on the web (that's why I write this now).
Introduction
So first of all what is beforefieldinit? There are many great blog posts about that. Read this and this.
In short: classes which contain static field initializers, but do not contain a static constructor are marked with this flag and they can be initialized basically at any time.
So here is our sample class for the experimentation:
class MyClassWithStatic
{
public static readonly int Number = GetData();
public static int GetData()
{
Console.WriteLine("GetData runs");
return 42;
}
public static void StaticMethod()
{
Console.WriteLine("StaticMethod runs");
}
}
If you compile this class and look into the generated IL code you see this:
.class private auto ansi beforefieldinit
Sample
.MyClassWithStatic
extends [mscorlib]System.Object
{
...
}
Bamm, there it is, our little class is marked with the beforefieldinit flag. Note that if you add a static constructor to the class then the flag won't be added to the generated code.
Part I: Differences between pre and post .NET 4 versions
As Jon Skeet describes in the referenced posts different framework versions behave differently. Pre 4.0 versions tend to be more eager and from 4.0 the initialization is done lazy. So let's test this with this sample app:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(String.Format("CLR Version: {0}",
Environment.Version.ToString()));
InitService();
MyClassWithStatic.StaticMethod();
Console.ReadKey();
}
private static void InitService()
{
Console.WriteLine("InitService runs");
}
}
So let's see all the different outputs. This is on the same machine (and again, the same code).
If I run this on a 2.0 CLR the output both in debug and in release is this:
CLR Version: 2.0.50727.8745
InitService runs
GetData runs
StaticMethod runs
So first the InitService method is called, then the static class is initalized and then the StaticMethod is called. Note that the Number field is initialized, but never used.
If we move to .NET 4.6 in debug we see the same output, but in release mode it prints this:
CLR Version: 4.0.30319.42000
InitService runs
StaticMethod runs
Great, this is the lazy initialization, which was the improvement in (i guess) 4.0.
Part II: Difference between ngen and the CLR JIT compiler
Until this point there is nothing new, many people blogged about this. So let's modify the sample a little bit to this:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(String.Format("CLR Version: {0}",
Environment.Version.ToString()));
InitService();
var value = MyClassWithStatic.Number;
Console.WriteLine(value);
Console.ReadKey();
}
private static void InitService()
{
Console.WriteLine("InitService runs");
}
}
So the change is that we really use the value. Now the question is: when will be Number initialized? Since the class is marked with the beforefieldinit flag this can be any time before we use the value.
By running this code from Visual Studio both in Debug and in Release mode and both under CLR 4.0 and CLR 2.0 the output is this:
GetData runs
CLR Version: 4.0.30319.42000
InitService runs
42
This run is obviously from CLR4.0 and as we see this initialization is not lazy at all! As soon as the execution hits the method the field Number gets initialized.
Now after compiling this with ngen.exe the output is this:
CLR Version: 4.0.30319.42000
InitService runs
GetData runs
42
And this was the point when I decided that I write a post about it. So the AOT compiler (=Ahead of time compiler, in this case the ngen.exe) of the same framework does the initialization differently as the JIT compiler. Both of them are absolutely valid, since the field can be initialized at any time before the first usage and this normally should not matter. But a) it's very interesting and good to know b) maybe someone will run into this and this helps to understand it.