I've done some searching and I think the following code is guaranteed to produce output:
B.X = 7
B.X = 0
A.X = 1
A = 1, B = 0
static class B
{
public static int X = 7;
static B() {
Console.WriteLine("B.X = " + X);
X = A.X;
Console.WriteLine("B.X = " + X);
}
}
static class A
{
public static int X = B.X + 1;
static A() {
Console.WriteLine("A.X = " + X);
}
}
static class Program
{
static void Main() {
Console.WriteLine("A = {0}, B = {1}", A.X, B.X);
}
}
I've run this numerous times and always get the output above the code section; I just wanted to verify it will never change? Even if textually, class A and class B are re-arranged?
Is it guaranteed that the first use of a static object will trigger the initialization of its static members, followed by instantiating its static constructor? For this program, using A.X in main will trigger the initialization of A.X, which in turn initializes B.X, then B() and after finishing the initialization of A.X, will proceed to A(). Finally Main() will output A.X and B.X.
About four different rules in the C# spec are involved in making this guarantee, and it is specific to C#. The only guarantee made by the .NET runtime is that type initialization begins before the type is used.
Relying on this is a very bad idea because it is likely to confuse anyone reading your code, especially if they are familiar with languages with a similar syntax which do not make all four of the above guarantees.
Please note that Porges comment was related to my initial statement (based on the .NET behavior) that the guarantees are too weak to assure the observed behavior. Porges is right that the guarantees are strong enough, but in fact a far more complex chain is involved than he suggests.
Deterministic initialization of static members is indeed guaranteed ... but it's not in "textual order". Furthermore, it may not be performed in a completely lazy fashion (meaning only when the static variable is first referenced). However, in your example using integers it wouldn't make a difference.
In some cases, it's desirable to get lazy initialization (particularly with expensive Singletons) - in which case you sometimes have to jump through some hoops to get it right.
Straight from ECMA-334:
And:
So the order is:
A.X
used, sostatic A()
called.A.X
needs to be initialized, but it usesB.X
, sostatic B()
called.B.X
needs to be initialized, and it is initialized to 7.B.X = 7
B
are initialized, sostatic B()
is called.X
is printed ("7"), then it is set toA.X
.A
has already started being initialized, so we get the value ofA.X
, which is the default value ("when a class is initialized, all static fields in that class are first initialized to their default value");B.X = 0
, and is printed ("0").B
, and the value ofA.X
is set toB.X+1
.A.X = 1
.A
are initialized, sostatic A()
is called.A.X
is printed ("1").Main
, the values ofA.X
andB.X
are printed ("1", "0").It actually comments upon this in the standard:
You may be interested to know that it's even possible to assign values to a field between its default initialization and variable initialization.
outputs