tags:

views:

128

answers:

3

Do you have any idea, why the following code:

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

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

Having:

        int aa = A.i;
        int bb = B.i;

Says that aa = 2 (!!!) and bb = 1.

I have a STACK OVERFLOW in my brain!!! As far as i understand, recursion stops on static methods, but why? If you remake int i to the getters (to debug and understand why on earth it works like that), you get the stack overflow exception.

+3  A: 

A field is not like a property getter. It just stores the data, not any operation. In fact, the initialization will be moved to another method (static constructor, .cctor) which will initialize static variables in the class.

At the beginning, both will equal to 0. Before you access A.i for the first time, the method .cctor for class A runs. It tries to access B.i for the first time which will cause execution of the static constructor of class B. .cctor of B will try to access A.i but since it's not the first time a field is being accessed, static constructor of A will not run anymore. It just fetches the current value of A which is still 0. Consequently, B.i will be equal to 1 when B..cctor finishes execution and control is returned to A..cctor. It will see the value B.i and adds 1 to it and stores it in A.i. Thus, A.i will be equal to 2 (1+1) and B.i will be equal to 1.

The only guarantee you get is that the static constructor will be called before any static member of that class is accessed.

Mehrdad Afshari
+6  A: 

No doubt execution is happening like so:

The B.i static initializer runs first, and sets B.i = A.i + 1. Since A.i hasn't been initialized yet, A.i is equal to default(int), which is 0. B.i gets the value 1.

The A.i static initializer runs second, and sets A.i = B.i + 1 = 2.

Since static initializers run in an undefined order, you might find that a different field is 2 each time when you run it, or that they switch when you make other structural changes to your code. However, one should always wind up as 2, and one should always be 1.

P.S. This has nothing to do with polymorphism.

EDIT: For some further understanding of the timing of static initializers and constructors, you might want to examine this relevant portion of the C# specification.

mquander
ok there's nothing common with polymorphism. but why doesn't it fall into the recursive call???
ifesdjeen
There is no recursive call (unless you change it to be a property getter.) Field initializers run once and do their thing; they aren't "callable."
mquander
What I mean is that when the B.i initializer (for example) wants the value of A.i, it doesn't call A.i and say "hey, will you calculate the value, please." It just looks at what A.i actually is in memory. Before A.i's initializer runs, that means A.i is 0.
mquander
+1  A: 

Here's what's happening.

  • A is accessed for the first time.
  • A.i is created with value 0.
  • The static initializer for A runs.
  • B is accessed for the first time.
  • A.i is accessed. It currently has value 0.
  • B.i is set to 1. A.i is set to 2.

The really important thing to note is that the static properties are being initialized to their defaults and then the static initializer runs.

Personally, I avoid static initialization, static state and the rest as much as possible. There's too many gotchas.

Julian Birch