6

在我研究在 C# 中构建单例的最佳方法期间,我偶然发现了以下文章,其中简要提到了 C++

“C++ 规范在静态变量的初始化顺序上留下了一些歧义。”

我最终调查了这个问题,发现了 thisthis。基本上重点(据我所知)是 C++ 中静态变量的初始化顺序是未定义的。好吧,我想到目前为止还不错,但是后来我想了解这篇文章稍后所做的以下陈述

“幸运的是,.NET Framework 通过对变量初始化的处理解决了这种歧义。”

所以我找到了他们说的这个页面

类的静态字段变量初始化器对应于一系列赋值,它们按照它们在类声明中出现的文本顺序执行。

并举个例子

using System;
class Test
{
   static void Main() {
      Console.WriteLine("{0} {1}", B.Y, A.X);
   }
   public static int F(string s) {
      Console.WriteLine(s);
      return 1;
   }
}
class A
{
   static A() {}
   public static int X = Test.F("Init A");
}
class B
{
   static B() {}
   public static int Y = Test.F("Init B");
}

the output must be: 
Init B 
Init A
1 1

“因为静态构造函数何时执行的规则(如第 10.11 节所定义)规定 B 的静态构造函数(以及因此 B 的静态字段初始值设定项)必须在 A 的静态构造函数和字段初始值设定项之前运行。”

但是我感到困惑的是,我的理解是这些示例中静态变量的初始化顺序将基于第一次调用类中的方法或字段的时间,而这又基于代码块的执行顺序(本例从左到右)。IE:完全独立于类声明的位置或顺序。然而,根据我对那篇文章的解释,它说它是这些类的声明顺序的结果,我的测试没有支持?

有人可以为我澄清这一点(以及这篇文章试图提出的观点),或许可以提供一个更好的例子来说明所描述的行为?

4

2 回答 2

8

类的静态字段变量初始化器对应于一系列赋值,它们按照它们在类声明中出现的文本顺序执行。

这意味着在同一个类中,静态字段按照源代码中出现的顺序进行初始化。例如:

class A
{
   public static int X = Test.F("Init A.X");
   public static int Y = Test.F("Init A.Y");
}

当需要初始化静态字段时,X保证在Y.

“因为静态构造函数何时执行的规则(如第 10.11 节中所定义)规定 B 的静态构造函数(以及因此 B 的静态字段初始值设定项)必须在 A 的静态构造函数和字段初始值设定项之前运行。”

这意味着当访问这些类的表达式出现时,每个类的静态构造函数和成员初始化将按评估顺序运行¹。源代码中类定义的相对出现顺序没有任何作用,即使它们出现在同一个源文件中(他们肯定没有义务这样做)。例如:

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

假设既A没有也B没有静态初始化,求值顺序保证 的所有字段B将在 的任何字段之前初始化A。每个类的字段将按照第一条规则指定的顺序进行初始化。


¹出于本次讨论的目的,我忽略了beforefieldinit.

于 2012-07-09T09:55:37.813 回答
3

在 C++ 中,具有静态存储持续时间的变量在单个翻译单元中的初始化顺序是此类变量的定义发生的顺序。未指定具有静态存储持续时间的变量的初始化顺序在不同的翻译单元中是什么。

也就是说,C++ 标准确实提供了与您引用的内容类似的保证,将类中的声明顺序替换为定义此类变量的单个翻译单元中的定义顺序。但这不是重要的区别。

虽然在 C++ 中这是唯一的保证,但在 C# 中还有额外的保证,即所有静态成员都将在第一次使用类之前被初始化。这意味着,如果您的程序依赖于A(考虑不同程序集中的每种类型,这是最坏的情况),它将开始初始化 中的所有静态字段A,如果A反过来依赖于B这些静态初始化中的任何一个,则初始化的B静态成员将在那里触发。

与 C++ 相比,在静态初始化[*]期间,假定所有其他具有静态持续时间的变量都已初始化。这是主要区别:C++ 假定它们已初始化,C# 确保它们在使用之前。


[*]从技术上讲,有问题的情况可能是标准中的动态初始化。在每个翻译单元内初始化具有静态存储持续时间的变量是一个两步过程,其中在第一轮静态初始化中将变量设置为固定的常量表达式,然后在第二轮称为动态初始化中,所有具有静态存储的变量,其初始化程序不是常量表达式被初始化。

于 2012-07-09T11:10:33.550 回答