Using the following code as an example ...
public class ClassA<T> where T : new()
{
static ClassA()
{
Debug.WriteLine("static ctor A");
}
public static T TA = new T();
}
public class ClassB<T> : ClassA<T> where T : new()
{
static ClassB()
{
Debug.WriteLine("static ctor B");
}
public static T TB = new T();
}
public class ClassC : ClassB<ClassC>
{
static ClassC()
{
Debug.WriteLine("static ctor C");
}
public static ClassC TC = new ClassC();
}
public class Tests
{
[Test]
public void Test()
{
ClassC classC = ClassC.TC;
}
}
... the output is
static ctor A
static ctor B
static ctor C
So, are the constructors always guaranteed to be called in order from base to most inherited, when classes are defined in the above pattern? And why is ClassB's constructor getting triggered? Does it have anything to do with the fact I'm passing a self-reference through the generic argument?
I see there are plenty of other answers related to static constructors:
- Why aren't these static constructors called in generic inherited types? ... this doesn't mention the order they are called in, and by all accounts that answer suggests that the inherited class's static constructor is triggered before the first instance is created or any static members are referenced, which I am not doing in the example above, I only reference a property in
ClassC(which istypeof(ClassC)) and yetClassBstill has its constructor triggered - https://stackoverflow.com/a/5629446/5040941 ... again the order is not discussed
The trouble I have is that this is a very specific pattern of generic inheritance with a self-referencing argument, and I can't find an example online that matches, so I don't know what behaviour to expect, or what order the constructors should get triggered in.
UPDATED TO ADD
If I remove the static fields in ClassA and ClassB, the output becomes ...
static ctor B
static ctor A
static ctor C
... which seems to make even less sense!
The chapter 14 Classes / 14.12 Static constructors in the C# language specification:
Let's make a test:
Test:
Output:
This shows: the order of execution of the static constructors is defined by the order in which static members of these classes are accessed for the first time.
Your example is different in that the classes create instances of the other classes. Remember, instance creation is also a reason for the invocation of a static constructor. I added instance constructors to your example. Note that
TAandTBare creating aTinstance which is of the concrete typeClassC. So, 3ClassCinstances are created. With all the static fields the output of your example now looks like this:Without the
TAandTBfields, we get:If we delete the static field
TCas well and instantiatenew ClassC()directly, we get even a different order:This means that field initializers have an influence on this order. Especially when they create other instances. Without fields and recursive instance creations the static constructors are called in reverse order from the most derived to the less derived. The instance constructors are called afterwards (since they may call static fields) and are called starting with the base class.
The Remarks section of Static Constructors (C# Programming Guide) says:
In your second example the static constructor C is executed after the field initializer was executed. This explains the order B, A, C.
Your first example is dominated by the order of execution of the field initializers and is very convoluted, as those create other instances recursively.
Summary
The order of execution of static constructors can be hard to understand and can change at any time by adding or removing code. Therefore don't rely on it. C# still ensures that every member you are allowed to call is initialized.
Constructors should be used to initialize things, not to execute business logic. Module Initializers introduced in C# 9.0 can be a better place for such things.