C # static initializer with (and without) mixed static constructors

I went through the corresponding section of the C # Language Spec (v5.0), but I cannot find the part related to what I see.

If you have the code below, you will see the result below, which I expect:

using System; class Test { static int count = 0; static void Main() { Console.WriteLine("In Main(), AX=" + AX); } public static int F(string message) { Console.WriteLine(message); AX = ++count; Console.WriteLine("\tA.X has been set to " + AX); BY = ++count; Console.WriteLine("\tB.Y has been set to " + BY); return 999; } } class A { static A() { } public static int U = Test.F("Init AU"); public static int X = Test.F("Init AX"); } class B { static B() { } public static int R = Test.F("Init BR"); public static int Y = Test.F("Init BY"); } 

Output:

 Init AU AX has been set to 1 Init BR AX has been set to 3 BY has been set to 4 Init BY AX has been set to 5 BY has been set to 6 BY has been set to 2 Init AX AX has been set to 7 BY has been set to 8 In Main(), AX=999 

This is exactly what I expect. In particular, note that even if the F () method is executed with the β€œInit AU” parameter, it is called again (interrupted if you want) when a BY reference is encountered, which leads to the execution of the B initializers. When the B static constructor completes, we again return to the call to AU F (), which takes into account that BY is set to 6 and then to 2. Thus, we hope that this conclusion makes sense to everyone.

Here's what I don't understand: if you are commenting on the static constructor of B, this is the result you see:

 Init BR AX has been set to 1 BY has been set to 2 Init BY AX has been set to 3 BY has been set to 4 Init AU AX has been set to 5 BY has been set to 6 Init AX AX has been set to 7 BY has been set to 8 In Main(), AX=999 

Sections 10.5.5.1 and 10.12 of the C # specification (v5.0) indicate that the static constructor (and its static initializers) are executed when they refer to "any of the static members of the class." But here we have the AX referenced inside F (), and the static constructor is not running (since its static initializers do not work).

Since A has a static constructor, I would expect these initializers to start (and abort) the "Init BR" call to F (), just as B's static constructor is interrupted. The F () call in the "Init AU" call that I showed at the beginning.

Can anyone explain? Af has a meaning similar to a violation of the specification if there is no other part of the specification that allows it.

thanks

+7
c # static static-initializer
source share
1 answer

I think I see what is happening here, although I do not have a good explanation of why this is happening.

The testing program is too crude to understand what is happening. Let me make a small adjustment:

 class Test { static int count = 0; static void Main() { Console.WriteLine("In Main(), AX=" + AX); } public static int F(string message) { Console.WriteLine("Before " + message); return FInternal(message); } private static int FInternal(string message) { Console.WriteLine("Inside " + message); AX = ++count; Console.WriteLine("\tA.X has been set to " + AX); BY = ++count; Console.WriteLine("\tB.Y has been set to " + BY); return 999; } } class A { static A() { } public static int U = Test.F("Init AU"); public static int X = Test.F("Init AX"); } class B { static B() { } public static int R = Test.F("Init BR"); public static int Y = Test.F("Init BY"); } 

The result is similar to the one asked in the question, but in more detail:

 Before Init AU Inside Init AU AX has been set to 1 Before Init BR Inside Init BR AX has been set to 3 BY has been set to 4 Before Init BY Inside Init BY AX has been set to 5 BY has been set to 6 BY has been set to 2 Before Init AX Inside Init AX AX has been set to 7 BY has been set to 8 In Main(), AX=999 

No wonder here. Remove static constructor B, and this is what you get:

 Before Init AU Before Init BR Inside Init BR AX has been set to 1 BY has been set to 2 Before Init BY Inside Init BY AX has been set to 3 BY has been set to 4 Inside Init AU AX has been set to 5 BY has been set to 6 Before Init AX Inside Init AX AX has been set to 7 BY has been set to 8 In Main(), AX=999 

Now it is interesting. We see that the original result was misleading. Let's start by initializing the AU . This is not surprising, because A must be initialized first, because AX accessed in Main. The next part is interesting. It appears that when B does not have a static constructor, the CLR interrupts a method that will access B fields ( FInternal ) before it enters this method. Compare this to another case. There, the initialization of B was delayed until we got access to fields B.

I'm not quite sure why everything is done in this particular order, but you can see that the reason that the initialization of B is not interrupted for the initialization of A is because the initialization of A has already begun.

+2
source share

All Articles