I would like to get some clarification in regards to how loose the execution timing is for static field initializers in .NET. I'm getting a TypeInitializationException when running the code below in an online compiler(Fiddle), but I get the expected output of 1 in a test console app in VS.
Code:
using System;
using SimpleInjector;
public class Program
{
public static IFactory Factory { get; set; }
public interface IFactory { T GetInstance<T>() where T: class; }
public class SimpleFactory: IFactory
{
private readonly Container _container;
public SimpleFactory(Container container) { this._container = container; }
public T GetInstance<T>() where T : class { return _container.GetInstance<T>(); }
}
public class Bar { public int Get() { return 1; } }
public static class Foo
{
//static Foo() {}
public static readonly int foo = Factory.GetInstance<Bar>().Get();
}
public static void Main()
{
var container = new Container();
var factory = new SimpleFactory(container);
container.RegisterInstance(Factory = factory);
container.Register<Bar>();
Console.WriteLine(Foo.foo);
}
}
Stack trace from online compilation:
[System.NullReferenceException: Object reference not set to an instance of an object.]
at Program.Foo..cctor() :line 22
[System.TypeInitializationException: The type initializer for 'Foo' threw an exception.]
at Program.Main() :line 33
What gives? Is it safe to assume that when building under visual studio the code is always guaranteed to work correctly? Thank you for your time.
According to the C# language spec, this is specifically pointed out as an implementation-dependent thing:
If a static constructor exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.
The only guarantee you get, is that it must be some time before the first use of a static field.
However, as you probably have found out in your fiddle, adding a static constructor makes this work. A static constructor guarantees that static field initialisers to be run immediately before the static constructor. The good news is that the execution of the static constructor is not implementation-dependent (spec):
The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class type is created.
- Any of the static members of the class type are referenced.