在我研究在C#中构建Singleton的最佳方法期间,我偶然发现了以下article,其中简要提及了C ++
“C ++规范在初始化时留下了一些含糊之处 静态变量的顺序。“
我最后查看了这个问题,找到了this和this。基本上(据我所知),C ++中静态变量的初始化顺序是未定义的。好吧我觉得到目前为止一切都那么好,但后来我想理解以下文章后面的文章
“幸运的是,.NET Framework通过它解决了这种模糊性问题 处理变量初始化。“
所以我找到了他们说的this页面
类的静态字段变量初始值设定项对应于a 以文本顺序执行的分配顺序 它们出现在课堂宣言中。
并举出
的例子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的 static field initializers)必须在A的静态构造函数之前运行 字段初始化程序。“
但令我感到困惑的是,我的理解是这些示例中静态变量的初始化顺序将基于何时首次调用类中的方法或字段,而后者又基于执行顺序代码块(这种情况从左到右)。 IE:完全独立于类声明的位置或顺序。然而,根据我对该文章的解释,它说这是由于这些类的声明顺序,我的测试没有备份?
有人可以请我澄清一下(以及文章试图提出的观点),并提供一个更好的例子来说明所描述的行为吗?
答案 0 :(得分:7)
类的静态字段变量初始值设定项对应于a 以文本顺序执行的分配顺序 它们出现在课堂宣言中。
这意味着在同一个类中,静态字段按源代码中的外观顺序初始化。例如:
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的 static field initializers)必须在A的静态构造函数之前运行 字段初始化程序。“
这意味着当访问这些类的表达式出现时,每个类的静态构造函数和成员初始化将按评估顺序运行。源代码中类定义的出现的相对顺序不起任何作用,即使它们出现在同一个源文件中(他们肯定没有义务这样做)。例如:
static void Main() {
Console.WriteLine("{0} {1}", B.Y, A.X);
}
假设A
和B
都没有静态初始化,评估顺序保证B
的所有字段都会在A
的任何字段之前初始化。每个类的字段将按照第一个规则指定的顺序初始化。
beforefieldinit
的存在。
答案 1 :(得分:3)
在C ++中,在单个转换单元中具有静态存储持续时间的变量的初始化顺序是这些变量的定义发生的顺序。未指定具有静态存储持续时间的变量的初始化顺序是跨越不同的转换单元。
也就是说,C ++标准确实为您所引用的内容提供了类似的保证,将类中声明的顺序替换为定义此类变量的单个转换单元中的定义顺序。但这不是重要的区别。
虽然在C ++中这是唯一的保证,但在C#中还可以保证在第一次使用类之前初始化所有静态成员。这意味着,如果您的程序依赖A
(考虑不同程序集中的每种类型,这是最坏的情况),它将开始A
中所有静态字段的初始化,如果{{1}对于任何静态初始化,依赖于A
,然后将在那里触发B
静态成员的初始化。
与C ++相比,在静态初始化 [*] 期间,具有静态持续时间的所有其他变量假定被初始化。这是主要区别:C ++假定它们已初始化,C#确保它们在使用之前。
[*] 从技术上讲,这有问题的情况可能是标准中的动态初始化。在每个转换单元内初始化具有静态存储持续时间的变量是一个两步过程,其中在第一次传递期间 static 初始化将变量设置为固定常量表达式,稍后在第二次传递中称为动态初始化所有具有静态存储的变量,其初始化程序不是常量表达式。