2012-07-09 113 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