This sample from C# in a Nutshell says it writes 0 followed by 3 due to "the field initializer that instantiates a Foo executes before X is initialized to 3".
class Program {
static void Main() { Console.WriteLine (Foo.X); } // 3
}
class Foo
{
public static Foo Instance = new Foo(); //static field 1
public static int X = 3; //static field 2
Foo() => Console.WriteLine (X); // 0
}
My question is, why does this not go into an infinite recursion due to static field 1 which makes a new Foo (which makes a new Foo, which makes a new Foo, etc.)?
CodePudding user response:
This does not go into an infinite recursion because of the way the C# language specification handles static field initialization. The static field initializers are executed in the order they appear in the code, and once the value of a static field has been assigned, it will not be re-assigned even if it is referenced later in the code.
In this case, the static field Instance is assigned the value of new Foo() which invokes the constructor for the Foo class, but the value of X is not assigned until after the constructor is executed. So, the constructor outputs the initial value of X (which is 0), and then the value of X is assigned to 3, but it's too late to change the output of the constructor.
So, in this case, the output is 0 followed by 3.
CodePudding user response:
Static fields and properties are shared across all instances of a class, and get initialized in a special constructor called .cctor
that gets called when the class is first referenced.
So the compiled code is something similar to
class Foo
{
public static Foo Instance;
public static int X;
.cctor() {
Instance = new Foo();
X = 3;
}
Foo() => Console.WriteLine (X);
}
The call flow would be Foo::.cctor
-> Foo::Foo()
-> Foo:get_X()
.
So there is no recursion, but X
will have its default value when .cctor
is called.