In the ECMA standard under $14.5.6.2 it states that having a static constructor affects the static field initialization order. I want a way to enforce this, but cannot find a way to check for an explicit static constructor in a type. It seems like the C# runtime automatically generates one if there isn't one already (and if the class has some static fields). Is it possible to check if a class has an explicit static constructor using reflection?
For example - the following will return a constructor in both these cases, and I cannot see any difference between them:
static class NoStaticConstructor
{
public static string Field = "x";
}
static class HasStaticConstructor
{
static HasStaticConstructor() {}
}
public void Test()
{
typeof(NoStaticConstructor)
.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)
.ShouldBeEmpty(); // FAILS
typeof(HasStaticConstructor)
.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)
.ShouldNotBeEmpty(); // SUCCEEDS
}
If you look at the generated IL for both classes, you'll see that both of them have a static constructor:
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{ /*...*/ } // end of method HasStaticConstructor::.cctor
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{ /*...*/ } // end of method NoStaticConstructor::.cctor
So, querying the constructor won't help you because it exists in the compiled code regardless of whether or not it's explicitly written in the C# code.
The good news is, there's actually no need to check for the constructor. Instead, you should look for TypeAttributes.BeforeFieldInit
. As mentioned here, the beforefieldinit
flag is applied by default unless the class has a static constructor. That gives you the information that you're looking for with regards to the static field initialization order.
We can write a helper method to check for that:
static bool HasLazyInitialization(Type t) =>
t.Attributes.HasFlag(TypeAttributes.BeforeFieldInit);
Usage:
Console.WriteLine(HasLazyInitialization(typeof(NoStaticConstructor))); // true.
Console.WriteLine(HasLazyInitialization(typeof(HasStaticConstructor))); // false.