C#中的静态类初始化的顺序是确定性的吗?
我做了一些search,我认为下面的代码是保证产生输出:
BX = 7
BX = 0
AX = 1
A = 1,B = 0
static class B { public static int X = 7; static B() { Console.WriteLine("BX = " + X); X = AX; Console.WriteLine("BX = " + X); } } static class A { public static int X = BX + 1; static A() { Console.WriteLine("AX = " + X); } } static class Program { static void Main() { Console.WriteLine("A = {0}, B = {1}", AX, BX); } }
我已经运行了这么多次,总是得到代码部分上面的输出; 我只是想validation它永远不会改变? 即使在文本上,A类和B类重新安排?
是否保证静态对象的第一次使用将触发其静态成员的初始化,然后实例化其静态构造函数? 对于这个程序,在main中使用AX会触发AX的初始化,AX又初始化BX,然后B(),在完成AX的初始化之后,将进入A()。 最后Main()会输出AX和BX
直接从ECMA-334:
17.4.5.1: “ 如果类中存在一个静态构造函数(第17.11节),那么在执行该静态构造函数之前立即执行静态字段初始化符,否则静态字段初始化符会在执行之前首先使用该类的静态字段“。
和:
17.11:静态构造函数的执行是由应用程序域内发生以下第一个事件触发的:
- 该类的一个实例被创build。
- 任何类的静态成员都被引用。
如果一个类包含执行开始的Main方法(第10.1节),则该类的静态构造函数在Main方法被调用之前执行。 如果一个类包含任何具有初始值设定项的静态字段,那么在执行静态构造函数(§17.4.5)之前,这些初始值设定项将按文本顺序执行。
所以顺序是:
-
AX
使用,所以static A()
调用。 -
AX
需要被初始化,但它使用BX
,所以调用static B()
。 -
BX
需要初始化,初始化为BX = 7
-
B
所有静态字段都被初始化,所以调用static B()
。 打印X
(“7”),然后将其设置为AX
。A
已经开始初始化,所以我们得到AX
的值,这是默认值(“当一个类被初始化,该类中的所有静态字段都被初始化为它们的默认值”);BX = 0
,并打印(“0”)。 - 完成初始化
B
,AX
的值被设置为B.X+1
。AX = 1
。 -
A
所有静态字段都被初始化,所以static A()
被调用。AX
打印(“1”)。 - 返回
Main
,打印AX
和BX
的值(“1”,“0”)。
它实际上在标准中对此进行了评论:
17.4.5: 可以在默认值状态下观察具有variables初始值设定项的静态字段。 然而,这是一个风格强烈的不鼓励。
在C#规范中,大约有四个不同的规则参与了这个保证,并且它是C#特有的。 .NET运行时所做的唯一保证就是在使用types之前开始types初始化。
- 这个静态字段是零初始化的,直到types初始化器运行。
- 该静态字段初始化器在静态构造函数之前立即运行。
- 该静态构造函数在第一个实例构造函数调用或第一个静态成员引用中被调用。
- 该函数参数按照从左到右的顺序进行评估。
依赖于这是一个非常糟糕的想法,因为它可能会混淆读取您的代码的任何人,特别是如果他们熟悉语言与类似的语法不能使上述四个保证。
请注意,Porges评论与我最初的声明(基于.NET行为)有关,即担保太弱,无法保证观察到的行为。 波格斯说得对,保证是足够强大的,但实际上涉及到一个比他所build议的要复杂得多的链条。
静态成员的确定性初始化确实得到保证…但不是“文本顺序”。 此外,它可能不会以完全懒惰的方式执行(仅当首先引用静态variables时才意味着)。 然而,在你的例子中使用整数它不会有所作为。
在某些情况下,需要进行惰性初始化(特别是对于昂贵的单例) – 在这种情况下,有时您需要跳过一些环节才能正确使用它。