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

时间:2010-09-09 22:36:58

标签: c# static deterministic

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

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

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

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

4 个答案:

答案 0 :(得分:36)

直接来自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.XA已经开始初始化,因此我们得到A.X的值,这是默认值(“当初始化类时,该类中的所有静态字段首先被初始化为其默认值” ); B.X = 0,并打印(“0”)。
  • 完成初始化BA.X的值设置为B.X+1A.X = 1
  • 初始化A的所有静态字段,因此调用static A()A.X已打印(“1”)。
  • 返回MainA.XB.X的值将被打印(“1”,“0”)。

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

  

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

答案 1 :(得分:6)

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

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

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

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

答案 2 :(得分:4)

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

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 - Default initialization
3 - Assignment
4 - Variable initialization

答案 3 :(得分:-2)

确实保证静态成员的确定性初始化...但它不是“文本顺序”。此外,它可能不会以完全懒惰的方式执行(意味着仅当静态变量是首先参考)。但是,在使用整数的示例中,它不会产生任何影响。

在某些情况下,需要进行延迟初始化(特别是使用昂贵的单身人士) - 在这种情况下,您有时必须jump through some hoops才能使其正确。