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

Jes*_*ess 36 c# static deterministic

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

B.X = 7

B.X = 0

A.X = 1

A = 1, B = 0
Run Code Online (Sandbox Code Playgroud)
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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经运行了很多次,总是得到代码部分之上的输出; 我只是想验证它会改变吗?即使文本,课程A和班级B重新安排?

是否保证第一次使用静态对象会触发其静态成员的初始化,然后实例化其静态构造函数?对于这个程序,A.X在main中使用将触发A.X初始化B.X,然后初始化,然后B()在完成初始化之后A.X,将继续进行A().最后,Main()将输出A.X和BX`.

por*_*ges 43

直接来自ECMA-334:

17.4.5.1: " 如果类中存在静态构造函数(第17.11节),则在执行静态构造函数之前立即执行静态字段初始化程序.否则,静态字段初始化程序在执行相关时间之前执行.首次使用该类的静态字段."

和:

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

  • 创建了一个类的实例.
  • 引用该类的任何静态成员.

如果一个类包含执行开始的Main方法(第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.XB.X被打印("1","0").

它实际上在标准中对此进行了评论:

17.4.5: 可以在默认值状态下观察具有可变初始值设定项的静态字段.然而,作为一种风格问题,强烈建议不要这样做.


Ben*_*igt 6

C#规范中约有四个不同的规则涉及到这种保证,它特定于C#..NET运行时唯一的保证是在使用类型之前开始类型初始化.

  • 在类型初始化程序运行之前,静态字段为零初始化.
  • 静态字段初始化程序在静态构造函数之前运行.
  • 在第一个实例构造函数调用或第一个静态成员引用中调用静态构造函数.
  • 该函数参数按从左到右的顺序进行评估.

依赖于这是一个非常糟糕的主意,因为它可能会让任何阅读代码的人感到困惑,特别是如果他们熟悉具有类似语法但没有完成上述所有四项保证的语言.

请注意,Porges注释与我的初始声明(基于.NET行为)相关,即保证太弱而无法确保观察到的行为.Porges是正确的,保证足够强大,但实际上涉及的复杂链比他建议的要复杂得多.

  • 不对.阅读17.4.5.1:"[如果类中存在静态构造函数(第17.11节),则在执行该静态构造函数之前立即执行静态字段初始化程序.]否则,静态字段初始化程序将在与实现相关的时间执行在第一次使用该类的静态字段之前." 如果您没有静态构造函数,则静态字段初始化仅是实现定义的. (3认同)

Kyl*_*ney 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);
}
Run Code Online (Sandbox Code Playgroud)

输出

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);
}
Run Code Online (Sandbox Code Playgroud)