18

我想知道为什么这段代码不会以无限递归结束。我猜它与静态成员自动初始化为默认值有关,但有人可以告诉我“逐步” 'a'如何获得 2 的值和 1 的 'b'?

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();
}
4

6 回答 6

16

我想:

  • A.a被查询,这会导致A静态初始化程序触发
  • 这访问B.b,导致B静态初始化程序触发
  • A.a被查询;类型初始值设定项已经激活(但尚未发生赋值),因此该字段(尚未赋值)被读取为0
  • 0+11, 分配给B.b<============================
  • 我们现在退出Bcctor 并返回到Acctor
  • 1+12, 分配给A.a<============================
  • 我们现在退出Acctor
  • 2返回 ( WriteLine) 为A.a
  • 我们查询 (on WriteLine) B.b;cctor 已经被解雇了,所以我们看到了1
于 2010-05-06T21:20:59.843 回答
12

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.

于 2010-05-06T22:11:05.157 回答
6

它与您访问静态属性的顺序有关。第一个评估的是 Aa 当评估 Aa 时,Bb 被初始化。由于实际对a的赋值还没有完成,所以a的值保持为0,因此Bb变为1。Bb初始化后,可以将值赋给Aa,即1+1,即2

于 2010-05-06T21:20:07.770 回答
2

有趣的是,当我更改示例代码中的输出顺序时:

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

我得到了相反的结果:

B.b=2 A.a=1

所以看起来这与访问它们的顺序有关

因此,鉴于通过添加变量之一的早期使用可能会改变输出,因此这种递归定义的值似乎是一个 BAD IDEA(TM) :-)

于 2010-05-06T21:24:05.657 回答
2

第一个加载的类型恰好是A. 所以类型被加载,它的静态成员a得到它的默认值为零。之后,A的静态构造函数被调用。该构造函数引用 type B,因此B也被加载并调用它的静态构造函数。反过来,该构造函数引用 type A,但A已经加载,所以这里什么都没有发生,并b得到它的值零(当前值a)加一,即一。之后,B返回静态构造函数,并a计算 ' 值。

于 2010-05-06T21:21:56.860 回答
1

由于在 Console.WriteLine 中首先引用了 Aa,因此首先加载它,这导致 B 加载了 Aa 的值为 0 => Bb = 1 => Aa 变为 2

反转打印并观察它以另一种方式发生。

于 2010-05-06T21:27:20.563 回答