简体   繁体   English

C#中静态类初始化的顺序是确定性的吗?

[英]Is the order of static class initialization in C# deterministic?

I've done some searching and I think the following code is guaranteed to produce output:我已经做了一些搜索,我认为以下代码可以保证产生输出:

B.X = 7

B.X = 0

A.X = 1

A = 1, B = 0
static class B
{
    public static int X = 7;
    static B() {
        Console.WriteLine("B.X = " + X);
        X = A.X;
        Console.WriteLine("B.X = " + X);
    }
}

static class A
{
    public static int X = B.X + 1;
    static A() {
        Console.WriteLine("A.X = " + X);
    }
}

static class Program
{
    static void Main() {
        Console.WriteLine("A = {0}, B = {1}", A.X, B.X);
    }
}

I've run this numerous times and always get the output above the code section;我已经运行了很多次,并且总是在代码部分上方获得输出; I just wanted to verify will it change?我只是想验证它会改变吗? Even if textually, class A and class B are re-arranged?即使在文字上, A类和B类重新排列?

Is it guaranteed that the first use of a static object will trigger the initialization of its static members, followed by instantiating its static constructor?是否保证第一次使用静态对象会触发其静态成员的初始化,然后实例化其静态构造函数? For this program, using AX in main will trigger the initialization of AX , which in turn initializes BX , then B() and after finishing the initialization of AX , will proceed to A() .对于这个程序,在 main 中使用AX将触发AX的初始化,依次初始化BX ,然后B()完成AX的初始化后,将进行A() Finally, Main() will output AX and BX`.最后, Main()将输出AX和 BX`。

Straight from ECMA-334:直接来自 ECMA-334:

17.4.5.1: " If a static constructor (§17.11) 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." 17.4.5.1:如果类中存在静态构造函数(第 17.11 节),则在执行该静态构造函数之前立即执行静态字段初始值设定项否则,静态字段初始值设定项在执行之前的依赖于实现的时间执行第一次使用该类的静态字段。”

And:和:

17.11: The execution of a static constructor is triggered by the first of the following events to occur within an application domain: 17.11:静态构造函数的执行由应用程序域中发生的以下第一个事件触发:

  • An instance of the class is created.类的一个实例被创建。
  • Any of the static members of the class are referenced.引用类的任何静态成员。

If a class contains the Main method (§10.1) in which execution begins, the static constructor for that class executes before the Main method is called.如果类包含开始执行的 Main 方法(第 10.1 节),则该类的静态构造函数在调用 Main 方法之前执行。 If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor (§17.4.5).如果一个类包含任何带有初始化器的静态字段,这些初始化器将在执行静态构造函数之前立即按文本顺序执行(第 17.4.5 节)。

So the order is:所以顺序是:

  • AX used, so static A() called.使用了AX ,因此调用了static A()
  • AX needs to be initialized, but it uses BX , so static B() called. AX需要初始化,但它使用BX ,因此调用static B()
  • BX needs to be initialized, and it is initialized to 7. BX = 7 BX需要初始化,初始化为7。 BX = 7
  • All static fields of B are initialized, so static B() is called. B所有静态字段都已初始化,因此调用了static B() X is printed ("7"), then it is set to AX . X被打印(“7”),然后它被设置为AX A has already started being initialized, so we get the value of AX , which is the default value ("when a class is initialized, all static fields in that class are first initialized to their default value"); A已经开始被初始化,所以我们得到AX的值,它是默认值(“当一个类被初始化时,该类中的所有静态字段首先被初始化为它们的默认值”); BX = 0 , and is printed ("0"). BX = 0 ,并打印(“0”)。
  • Done initializing B , and the value of AX is set to B.X+1 .完成初始化B ,并且AX的值设置为B.X+1 AX = 1 . AX = 1
  • All static fields of A are initialized, so static A() is called. A所有静态字段都已初始化,因此调用了static A() AX is printed ("1"). AX被打印(“1”)。
  • Back in Main , the values of AX and BX are printed ("1", "0").返回Main ,打印AXBX的值(“1”、“0”)。

It actually comments upon this in the standard:它实际上在标准中对此进行了评论:

17.4.5: It is possible for static fields with variable initializers to be observed in their default value state. 17.4.5:可以在默认值状态下观察带有变量初始值设定项的静态字段。 However, this is strongly discouraged as a matter of style.但是,作为一种风格,强烈建议不要这样做。

About four different rules in the C# spec are involved in making this guarantee, and it is specific to C#. C# 规范中大约有四种不同的规则参与了这种保证,并且它特定于 C#。 The only guarantee made by the .NET runtime is that type initialization begins before the type is used. .NET 运行时所做的唯一保证是在使用类型之前开始类型初始化。

  • That static fields are zero-initialized until the type initializer runs.在类型初始化程序运行之前,静态字段是零初始化的。
  • That static field initializers run immediately before the static constructor.静态字段初始值设定项在静态构造函数之前立即运行。
  • That static constructors are called at the first instance constructor call or first static member reference.在第一个实例构造函数调用或第一个静态成员引用时调用静态构造函数。
  • That function arguments are evaluated in left-to-right order.该函数参数按从左到右的顺序计算。

Relying on this is a very bad idea because it is likely to confuse anyone reading your code, especially if they are familiar with languages with a similar syntax which do not make all four of the above guarantees.依赖于这一点是一个非常糟糕的主意,因为它可能会使任何阅读您代码的人感到困惑,特别是如果他们熟悉具有类似语法的语言,而这些语言并不能满足上述所有四个保证。

Please note that Porges comment was related to my initial statement (based on the .NET behavior) that the guarantees are too weak to assure the observed behavior.请注意,Porges 评论与我最初的声明(基于 .NET 行为)有关,即保证太弱而无法保证观察到的行为。 Porges is right that the guarantees are strong enough, but in fact a far more complex chain is involved than he suggests. Porges 是对的,保证足够强大,但实际上涉及的链比他建议的要复杂得多。

You may be interested to know that it's even possible to assign values to a field between its default initialization and variable initialization.您可能有兴趣知道甚至可以在默认初始化和变量初始化之间为字段赋值。

private static int b = Foo();
private static int a = 4;

private static int Foo()
{
    Console.WriteLine("{0} - Default initialization", a);
    a = 3;
    Console.WriteLine("{0} - Assignment", a);
    return 0;
}

public static void Main()
{
    Console.WriteLine("{0} - Variable initialization", a);
}

outputs产出

0 - Default initialization
3 - Assignment
4 - Variable initialization

Deterministic initialization of static members is indeed guaranteed ... but it's not in "textual order".确实可以保证静态成员的确定性初始化......但它不是“文本顺序”。 Furthermore, it may not be performed in a completely lazy fashion (meaning only when the static variable is first referenced).此外,它可能不会以完全惰性的方式执行(意味着仅在第一次引用静态变量时)。 However, in your example using integers it wouldn't make a difference.但是,在您使用整数的示例中,它没有区别。

In some cases, it's desirable to get lazy initialization (particularly with expensive Singletons) - in which case you sometimes have to jump through some hoops to get it right.在某些情况下,需要进行延迟初始化(特别是对于昂贵的单例)——在这种情况下,有时您必须跳过一些障碍才能使其正确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM