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

C#中静态类初始化的顺序是确定的吗?,c#,static,deterministic,C#,Static,Deterministic,我已经做了一些搜索,我认为以下代码可以保证生成输出: B.X = 7 B.X = 0 A.X = 1 A = 1, B = 0 我已经运行了很多次,并且总是在代码部分上面得到输出;我只是想确认它会改变吗?即使在文本上,类A和类B被重新排列 是否保证第一次使用静态对象将触发其静态成员的初始化,然后实例化其静态构造函数?对于该程序,在main中使用A.X将触发A.X的初始化,后者依次初始化B.X,然后B(),完成A.X的初始化后,将进入A()。最后,Main()。。。但它不是“文本顺序”。此

我已经做了一些搜索,我认为以下代码可以保证生成输出:

B.X = 7

B.X = 0

A.X = 1

A = 1, B = 0
我已经运行了很多次,并且总是在代码部分上面得到输出;我只是想确认它会改变吗?即使在文本上,类
A
和类
B
被重新排列


是否保证第一次使用静态对象将触发其静态成员的初始化,然后实例化其静态构造函数?对于该程序,在main中使用
A.X
将触发
A.X
的初始化,后者依次初始化
B.X
,然后
B()
,完成
A.X
的初始化后,将进入
A()
。最后,
Main()。。。但它不是“文本顺序”。此外,它可能不会以完全惰性的方式执行(这意味着只有在第一次引用静态变量时)。然而,在您使用整数的示例中,这并没有什么区别


在某些情况下,需要进行延迟初始化(特别是使用昂贵的单例),在这种情况下,您有时必须正确地进行初始化。

在C#规范中,有四种不同的规则参与了这一保证,这是针对C#的。NET运行时所做的唯一保证是在使用类型之前开始类型初始化

  • 在类型初始值设定项运行之前,静态字段是零初始化的
  • 静态字段初始值设定项紧跟在静态构造函数之前运行
  • 在第一个实例构造函数调用或第一个静态成员引用时调用静态构造函数
  • 函数参数按从左到右的顺序求值
依赖这一点是一个非常糟糕的想法,因为它可能会让任何阅读您的代码的人感到困惑,特别是如果他们熟悉语法类似的语言,而这些语言不能满足上述四个条件


请注意,Porges的评论与我最初的声明(基于.NET行为)有关,即保证太弱,无法保证观察到的行为。Porges认为担保足够有力是正确的,但事实上,担保所涉及的链条远比他建议的复杂。

直接来自ECMA-334:

17.4.5.1:“如果类中存在静态构造函数(§17.11),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始值设定项将在该类静态字段首次使用之前的依赖于实现的时间执行。”

以及:

17.11:静态构造函数的执行由第一个 在应用程序域中发生的以下事件之一:

  • 将创建该类的一个实例
  • 将引用该类的任何静态成员
如果类包含开始执行的主方法(§10.1),则该类的静态构造函数 在调用Main方法之前执行。如果类包含任何带有初始值设定项的静态字段,则 初始值设定项在执行静态构造函数之前立即按文本顺序执行(§17.4.5)

因此,顺序是:

  • 使用了
    A.X
    ,因此调用了
    static A()
  • A.X
    需要初始化,但它使用
    B.X
    ,因此调用了
    static B()
  • B.X
    需要初始化,并且初始化为7<代码>B.X=7
  • B
    的所有静态字段都已初始化,因此调用
    static B()
    X
    被打印(“7”),然后它被设置为
    A.X
    A
    已经开始初始化,因此我们得到了
    A.X
    的值,这是默认值(“初始化类时,该类中的所有静态字段首先初始化为其默认值”)<代码>B.X=0
    ,并打印(“0”)
  • 初始化
    B
    ,将
    A.X
    的值设置为
    B.X+1
    <代码>A.X=1
  • A
    的所有静态字段都已初始化,因此调用
    static A()
    <代码>A.X被打印(“1”)
  • 回到
    Main
    ,将打印
    A.X
    B.X
    的值(“1”、“0”)
  • 它实际上在标准中对此进行了评论:

    17.4.5:可以在默认值状态下观察带有变量初始值设定项的静态字段。然而,这是强烈反对的风格问题


    您可能有兴趣知道,甚至可以在字段的默认初始化和变量初始化之间为字段赋值

    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);
    }
    
    输出

    0-默认初始化
    3-分配
    4-变量初始化
    
    如果Main()首先使用了B.X,那么输出值将是7、8、8、A=8、B=8。对于您问题中给出的特定代码,Porges的答案是正确的。对于标题中更一般的问题——“C#中静态类初始化的顺序是确定性的吗?”——答案是这取决于:如果涉及的静态类型都有静态构造函数,那么初始化顺序是确定性的;如果没有,则不相关:由于初始化
    A
    B
    之间的循环引用,惰性确实会影响输出。实际上,静态字段初始化器是按文本顺序执行的:stackoverflow.com/A/3681278/616827Not true。阅读17.4.5.1:“[如果类中存在静态构造函数(§17.11),则在执行该静态构造函数之前立即执行静态字段初始值设定项
    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);
    }