views:

269

answers:

6

Hi,

I wonder why this code doesn't end up in endless recursion. I guess it's connected to the automatic initialization of static members to default values, but can someone tell me "step by step" how does 'a' get the value of 2 and 'b' of 1?

public class A
{
    public static int a = B.b + 1;
}
public class B
{
    public static int b = A.a + 1;
}

static void Main(string[] args)
{
    Console.WriteLine("A.a={0}, B.b={1}", A.a, B.b); //A.a=2, B.b=1
    Console.Read();
}
+5  A: 

It has to do with the order in which you access the static properties. The first evaluated is A.a. When evaluating A.a, B.b gets initialized. Since the actual assignment to a is not finished, the value of a remains 0, thus B.b becomes 1. After B.b is initialized, the value can be assigned to A.a, that is 1+1, thus 2

anchandra
+15  A: 

I would suppose:

  • A.a is queried, which causes the A static initializer to fire
  • This accesses B.b, causing the B static initializer to fire
  • A.a is queried; the type initializer is already activated (but no assignment has yet occurred), so the field (not yet assigned) is read as 0
  • 0 + 1 is 1, which is assigned to B.b <===========================
  • we now exit the B cctor and go back to the A cctor
  • 1 + 1 is 2, which is assigned to A.a <===========================
  • we now exit the A cctor
  • 2 is returned (WriteLine) for A.a
  • we query (on WriteLine) B.b; the cctor has already fired so we see 1
Marc Gravell
Ahh, thinking in C#, I like it! ;-)
Paul Kohler
And all this happens before you get inside your `Main(string[] args)`
mmcteam.com.ua
@mmcteam - I'd have to check on that point; I'm not sure it does....
Marc Gravell
@mmcteam - My experience has been that static constructors are not called until the class is referenced during runtime. So all this happens when A.a is queried.
Greg
@Greg - a hastily cobbled-together test shows the same thing (which is what I expected it would)
Marc Gravell
Static initializers execute in textual order before static constructors, which are guaranteed to execute only before static members are accessed (or the class is instantiated). http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx
Chris
@Greg you right, but compiler see usage and execute it first I think, I have debugged it and I came to static fields first then to that method(I have created test for that instead of `Main()` but I think it is not important)
mmcteam.com.ua
+2  A: 

The first type to load happens to be A. So the type gets loaded, and it's static member a gets it's default value of zero. After that, A's static constructor get called. That constructor references type B, so B also gets loaded and it's static constructor gets called. That constructor, in turn, references type A, but A is already loaded, so nothing happens here, and b gets it's value of zero (current value of a) plus one, which is one. After that, static constructor of B returns, and a's value is calculated.

Fyodor Soikin
+2  A: 

Interestingly when I changed the order of output in your sample code around:

    Console.WriteLine("B.b={0} A.a={1}", B.b, A.a);

I got the opposite results:

B.b=2 A.a=1

So it looks like it is to do with the order they are accessed

So, given that the output could change by adding an early usage of one of the variables it seems like such recursively defined values is A BAD IDEA(TM) :-)

hamishmcn
+1  A: 

Since A.a is referenced first in the Console.WriteLine, its loaded first, which causes B to be loaded with the Value of A.a as 0 => B.b = 1 => A.a becomes 2

Reverse the print and watch it happen the other way.

Preet Sangha
+11  A: 

Marc is correct. I would just add to his answer that your question is answered by section 10.5.5.1 of the specification, which states:

The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. If a static constructor exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.

Notice that last point. The spec goes on to quote your exact example as a case where either ordering is permitted by the specification; all the spec guarantees is that the field initializers are done in textual order before the static constructors run. It does not guarantee that fields of one type are initialized before or after fields of another type.

For example, the jit compiler is permitted to say "hey, I see that types A and B are used for the first time in this method that is about to be jitted, let me take a moment to make sure those types are loaded." The jitter is permitted to execute the field initializers at this time, and can choose to do A first or B first at its discretion.

In short: (1) you cannot rely on this behaviour; it is implementation-defined, and (2) the specification answers your exact question; consider reading the specification when you have a question about language semantics.

Eric Lippert
Damn; I was right but for the wrong reasons (i.e. what happens vs what is *guaranteed*). As always, a pleasure to have this extra level of detail.
Marc Gravell