C# 两个类的静态成员相互引用
我想知道为什么这段代码不会以无限递归结束。我猜它与静态成员自动初始化为默认值有关,但是有人可以告诉我“一步一步”“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();
}
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();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
有趣的是,当我更改示例代码中的输出顺序时:
我得到了相反的结果:
所以看起来这与访问它们的顺序有关
因此,考虑到输出可以通过添加以下之一的早期使用来改变变量看起来像这样递归定义的值是一个坏主意(TM):-)
Interestingly when I changed the order of output in your sample code around:
I got the opposite results:
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) :-)
由于 Aa 在 Console.WriteLine 中首先被引用,因此它首先被加载,这导致 B 加载时 Aa 的值为 0 => Bb = 1 => Aa 变成 2
反转打印并观察它以相反的方式发生。
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.
我假设:
Aa
,这会导致A
静态初始化器触发。Bb
,导致B
静态初始化程序Aa
的 ;类型初始值设定项已激活(但尚未发生赋值),因此字段(尚未赋值)被读取为0
0
+1
为1
,被赋值给Bb
<========================== ==B
cctor 并返回到A
cctor1
+1
is2
,被分配给Aa
<============================A
cctorAa
,返回2
(WriteLine
)(在WriteLine
上) >)Bb
; cctor 已经被触发,所以我们看到1
I would suppose:
A.a
is queried, which causes theA
static initializer to fireB.b
, causing theB
static initializer to fireA.a
is queried; the type initializer is already activated (but no assignment has yet occurred), so the field (not yet assigned) is read as0
0
+1
is1
, which is assigned toB.b
<===========================B
cctor and go back to theA
cctor1
+1
is2
, which is assigned toA.a
<===========================A
cctor2
is returned (WriteLine
) forA.a
WriteLine
)B.b
; the cctor has already fired so we see1
马克是对的。我只想在他的回答中补充一点,规范的第 10.5.5.1 节回答了您的问题,其中指出:
请注意最后一点。该规范继续引用您的确切示例,作为规范允许任一订购的情况;所有规范保证字段初始值设定项是在静态构造函数运行之前按文本顺序完成的。 它不保证一种类型的字段在另一种类型的字段之前或之后初始化。
例如,允许 jit 编译器说“嘿,我看到类型 A 和 B 用于第一次使用即将被抖动的方法时,让我花点时间确保这些类型已加载。”此时允许 jitter 执行字段初始化程序,并且可以自行决定选择先执行 A 还是先执行 B。
简而言之:(1) 你不能依赖这种行为;它是实现定义的,并且 (2) 规范回答了您的确切问题; 当您对语言语义有疑问时,请考虑阅读规范。
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:
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.
它与访问静态属性的顺序有关。
第一个计算的是 Aa 当计算 Aa 时,Bb 被初始化。由于对a的实际赋值尚未完成,a的值仍为0,因此Bb变为1。
Bb初始化后,可以将值赋给Aa,即1+1,即2
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
第一个加载的类型恰好是
A
。因此,该类型被加载,并且它的静态成员a
获得默认值零。之后,A
的静态构造函数被调用。该构造函数引用类型B
,因此B
也会被加载,并且它的静态构造函数也会被调用。反过来,该构造函数引用类型A
,但A
已加载,因此这里什么也没有发生,并且b
的值为零(a
的当前值)加一,即为一。之后,B
的静态构造函数返回,并计算a
的值。The first type to load happens to be
A
. So the type gets loaded, and it's static membera
gets it's default value of zero. After that,A
's static constructor get called. That constructor references typeB
, soB
also gets loaded and it's static constructor gets called. That constructor, in turn, references typeA
, butA
is already loaded, so nothing happens here, andb
gets it's value of zero (current value ofa
) plus one, which is one. After that, static constructor ofB
returns, anda
's value is calculated.