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:


using System;

class StaticConstructorType
{
    private static int x = Log();
    
    // Force “precise” initialization
    static StaticConstructorType() {}
    
    private static int Log()
    {
        Console.WriteLine(“Type initialized”);
        return 0;
    }
    
    public static void StaticMethod() {}
}

class StaticConstructorTest
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine(“No args”);
        }
        else
        {
            StaticConstructorType.StaticMethod();
        }
    }
}

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:


using System;

class Eager
{
    private static int x = Log();
    
    private static int Log()
    {
        Console.WriteLine(“Type initialized”);
        return 0;
    }
    
    public static void StaticMethod() {}
}

class EagerTest
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine(“No args”);
        }
        else
        {
            Eager.StaticMethod();
        }
    }
}

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:


using System;

class Lazy
{
    private static int x = Log();
    private static int y = 0;
    
    private static int Log()
    {
        Console.WriteLine(“Type initialized”);
        return 0;
    }
    
    public static void StaticMethod()
    {
        Console.WriteLine(“In static method”);
    }

    public static void StaticMethodUsingField()
    {
        Console.WriteLine(“In static method using field”);
        Console.WriteLine(“y = {0}”, y);
    }
    
    public void InstanceMethod()
    {
        Console.WriteLine(“In instance method”);
    }
}

class LazyTest
{
    static void Main(string[] args)
    {
        Console.WriteLine(“Before static method”);
        Lazy.StaticMethod();
        Console.WriteLine(“Before construction”);
        Lazy lazy = new Lazy();
        Console.WriteLine(“Before instance method”);
        lazy.InstanceMethod();
        Console.WriteLine(“Before static method using field”);
        Lazy.StaticMethodUsingField();
        Console.WriteLine(“End”);
    }
}

This time the output is:


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.


I’ve got one final twist… what would you expect this program to do?


using System;

class CachingSideEffect
{
    private static int x = Log();

    private static int Log()
    {
        Console.WriteLine(“Type initialized”);
        return 0;
    }
    
    public CachingSideEffect()
    {
        Action action = () => Console.WriteLine(“Action”);
    }
}

class CachingSideEffectTest
{
    static void Main(string[] args)
    {
        new CachingSideEffect();
    }
}

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 :)

13 thoughts on “Type initialization changes in .NET 4.0”

  1. Very interesting, thanks for sharing this.

    Do you know what happens if you access the static field via reflection? Since I don’t have the environment set up, I cannot quickly try it for myself…

  2. @Lucero: I really don’t know, to be honest. I *suspect* that any reflection initializes the type, but I haven’t checked.

  3. @Lucero: Just checked, and it initializes the type when you set the field’s value… not at the point at which you get the FieldInfo though, interestingly.

  4. 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.

  5. 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.

  6. And what about performance of static constructor or initializer, for both frameworks ?
    (What about CA1810 rule ?)

  7. That’s actually really interesting, if only because I now know more about C# language features than I did a few minutes ago – I hadn’t realized C# had static constructors.

    Thanks!

  8. “If you pass don’t in a command line argument” should be replaced with “If you don’t pass in a command line argument”. (In the last paragraph before the “Lazy initialization: .NET 4.0″ part.)

  9. 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.

    Is the behaviour of C# deterministic only on expression level?

  10. @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.

  11. Jon,

    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.

    -Reed

Comments are closed.