2012-07-09 118 views
5

的初始化命令在我的研究,以建立一个Singleton在C#中我碰到下面article那里是一个简单提一下,在C++歧义静态变量

“的C++规范留下了一些偶然的最佳方式围绕初始化 静态变量的顺序模糊不清。“

我最终调查了这个问题,发现thisthis。基本上这个点(据我所知)是C++中静态变量的初始化顺序是未定义的。好吧,我想到目前为止好,但后来我想明白下面的语句是,本文的后面,使

“幸运的是,.NET框架通过其 处理变量的初始化的解决了这个模棱两可。”

所以我发现this页面,他们说那里的一类

的静态字段变量初始化对应分配的 序列在文本顺序 执行它们出现在类宣言。

并给出

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:完全独立于类声明的位置或顺序。然而,根据我对这篇文章的解释,它说这是由于我的测试没有备份的那些类的声明顺序的结果?

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

回答

7

在静磁场用变量初始化对应于指派的 序列被在文本顺序在它们出现在类声明 执行。

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

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的静态构造和 字段初始运行”。

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

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

假设未A也不B已经静态初始化,评估保证级顺序的B所有字段将成为A任何领域之前进行初始化。每个类的字段将按照第一条规则指定的顺序进行初始化。


¹此讨论我忽略的beforefieldinit存在的目的。

+0

非常感谢你的非常简洁的答案。 – 2012-07-11 03:45:52

3

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

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

虽然在C++中是唯一的保证,但在C#中还有一个额外保证,即所有静态成员都将在首次使用该类之前进行初始化。这意味着,如果你的程序依赖于A(考虑在最坏情况下的不同汇编中的每种类型),它将开始初始化A中的所有静态字段,如果A依次取决于B用于任何这些静态初始化,那么将会在那里触发B静态成员的初始化。

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


[*]技术上其中这是有问题的可能是在标准动态初始化的情况。的与每个转换单元内部静态存储持续时间的变量初始化是一个两步过程,其中在第一遍期间静态初始化设置变量固定常量表达式,后来在称为动态初始化所有的变量的第二通静态存储其初始化是不是一个常量表达式被初始化。

+0

+1和鸡蛋里挑骨头:从技术上讲,如果一个类装饰有'beforefieldinit'那么CLR不需要直到他们实际访问初始化静态成员(例如,你可以调用静态方法所有你想要的)。实际上,它初始化被访问的类甚至在各个领域。 – Jon 2012-07-09 11:28:43