Type initialization changes in .NET 4.0

This morning, while checking out an email I’d received about my brain-teasers page, I discovered an interesting change to the CLR in .NET 4.0. At least, I think it’s interesting. It’s possible that different builds of the CLR have exhibited different behaviour for a while – I only have 32-bit versions of Windows installed, so that’s what I’m looking at for this whole post. (Oh, and all testing was done under .NET 4.0b2 – it could still change before release.)

Note: to try any of this code, build in release mode. Running in the debugger or even running a debug build without the debugger may well affect the behaviour.

Precise initialization: static constructors

I’ve written before about static constructors in C# causing types to be initialized immediately before the type is first used, either by constructing an instance or referring to a static member. In other words, consider the following program:

Note how the static variable x is initialized using a method that writes to the console. This program is guaranteed to write exactly one line to the console: StaticConstructorType will not be initialized unless you give a command line argument to force it into the “else” branch. The way the C# compiler controls this is using the beforefieldinit flag.

So far, so boring. We know exactly when the type will be initialized – I’m going to call this “precise” initialization. This behaviour hasn’t changed, and couldn’t change without it being backwardly incompatible. Now let’s consider what happens without the static constructor.

Eager initialization: .NET 3.5

Let’s take the previous program and just remove the (code-less) static constructor – and change the name of the type, for clarity:

Under .NET 3.5, this either writes both “Type initialized” and “No args” (if you don’t pass any command line arguments) or just “Type initialized” (if you do). In other words, the type initialization is eager. In my experience, a type is initialized at the start of execution of the first method which refers to that type.

So what about .NET 4.0? Under .NET 4.0, the above code will never print “Type initialized”.

If you don’t pass in a command line argument, you see “No args” as you might expect… if you do, there’s no output at all. The type is being initialized extremely lazily. Let’s see how far we can push it…

Lazy initialization: .NET 4.0

The CLR guarantees that the type initializer will be run at some point before the first reference to any static field. If you don’t use a static field, the type doesn’t have to be initialized… and it looks like .NET 4.0 obeys that in a fairly lazy way. Another test app:

Before static method In static method Before construction Before instance method In instance method Before static method using field Type initialized In static method using field y = 0 End

As you can see, the type initialized when StaticMethodUsingField is called. It’s not as lazy as it could be – the first line of the method could execute before the type is initialized. Still, being able to construct an instance and call a method on it without triggering the type initializer is slightly surprising.

In .NET 4.0, using the Microsoft C# 4 compiler, this does print “Type initialized”… because the C# compiler has created a static field in which to cache the action. The lambda expression doesn’t capture any variables, so the same delegate instance can be reused every time. That involves caching it in a static field, triggering type initialization. If you change the action to use Console.WriteLine(this) then it can’t cache the delegate, and the constructor no longer triggers initialization.

This bit is completely implementation-specific in terms of the C# compiler, but I thought it might tickle your fancy anyway.

Conclusion

I’d like to stress that none of this should cause your code any problems. The somewhat eager initialization of types without static constructors was entirely legitimate according to the C# and CLR specs, and so the new lazy behaviour of .NET 4.0. If your code assumed that just calling a static method, or creating an instance, would trigger initialization, then that’s your own fault to some extent. That doesn’t stop it being an interesting change to spot though :)

I must say I find the behavior of .NET 3.5 as you describe it surprising. I far as I knew static fields were often initialized right after the first method who used them was JITted. The behavior of .NET 4.0 seems to be more in sync with my expectations.

Oh, that is interesting. I can actually think of some real cases (in real code that I have in front of me) where that is going to change the behaviour, so fantastic spot. Now I need to go an add some static field access in a few scaffolding places… drat.

Interesting. I was under the impression that C# and Java have completely deterministic behaviour (excluding GC and finalizers of course). I thought that stuff like the famous C/C++ examples of g() + f() where it is implementation specific if g or f is executed first or i++ + ++i where the behaviour is undefined cannot happen in C# and Java but as it seems it is possible.

@Stilgar: I try to avoid making generalisations about things like determinism – things like memory models make it hard to do anything more than give rules about what you can rely on and what you can’t.

Just a little FYI – this change actually completely breaks the string and exception handling used by SWIG (http://www.swig.org/) for their C# wrapper generators. They rely on some static fields to be initialized before the type is used in order to register callbacks in the native side.

Thanks for the detailed description, though – it helped me work out a clean workaround.

I got a different output @ framework 4.5; The static fields are initialized before the first method is called. My different trials also shows, static fields are initialized before a instance is created or a static member is accessed.

The output

Before static method
Type initialized
In static method
Before construction
Before instance method
In instance method
Before static method using field
In static method using field
y = 0
End