2015-04-15 59 views
1

早些時候我posted a question關於靜態初始化和不同的調試之間輸出和釋放相同的代碼版本。我所確定的發佈版本產生不同的輸出(實際上它產生沒有輸出)從調試生成因爲DebuggableAttribute設置允許的JIT優化器,以消除在Release版本的輸出。靜態初始化是不應該運行

我越是想這雖然,它越我的錯誤。我走的更遠之前,讓我告訴從我原來發布的代碼,並在調試版本產生的輸出:

using System; 

class Test { 
    static int value = 0; 
    static int a = Initialize("Assigning a"); 
    static int b = Initialize("Assigning b"); 
    static String name = "Fred"; 
    static int c = Initialize("Assigning c"); 

    static int Initialize(String mssg) { 
     ++value; 
     Console.WriteLine("In Initialize() :: {0}, name={1}, returning {2}", mssg, name, value); 
     return value; 
    } // Initialize() 

    static void Main() { 
    }// Main() 
} // class Test 

從這個代碼(當與調試版本上運行)的輸出是這樣的:

In Initialize() :: Assigning a, name=, returning 1 
In Initialize() :: Assigning b, name=, returning 2 
In Initialize() :: Assigning c, name=Fred, returning 3 

我瞭解得很清楚,抖動能夠優化掉的輸出,我明白爲什麼會這樣做,所以我不要求任何人來解決之間的調試和發佈版本的差異。

什麼是我的錯誤是爲什麼任何輸出應該出現在第一位。這個類沒有靜態c'tor(這會強制靜態初始化器運行),沒有從類外部引用靜態字段,並且類永遠不會實例化。我要說的是,這個代碼生成的輸出不應該首先生成,至少從我對語言規範的理解來看,甚至不是由Debug生成的。

我一直在鑽研的C#規格的片,我無法找到任何東西,上面寫着靜態初始化應如下所示的代碼運行,無論它的編譯調試或釋放。

任何人都可以解釋爲什麼這代碼應該永遠產生輸出,適用什麼語言規範的一部分?

謝謝。

+0

嘗試在'Main'方法中放置一個斷點,並使用附加的調試器運行代碼。當它打破時,你期望五個領域有什麼價值?在調試和發佈模式下嘗試。還要注意,C#編譯器在技術上將所有初始化器(在聲明中寫入的賦值)移動到靜態類型初始化器。因此,即使C#代碼中沒有靜態構造函數,CLR也會將其視爲靜態方法'.cctor'。但是,CLR可以自由運行或不運行(不參考字段)。在釋放模式下,嘗試一個版本,在'Main'中寫出'name'。 –

回答

5

行爲被定義爲依賴於實現的:

10.4.5.1 Static field initialization

如果靜態構造(第10.11節)在類存在,靜態字段初始化的執行執行靜態之前立即發生構造函數。 否則,靜態字段初始化程序將在首次使用該類的靜態字段之前的實現相關時間執行。

我猜測它使得調試更容易,以儘早初始化它們。

+0

您的報價將符合當前規範的10.5.5.1部分。在我的代碼示例中,沒有靜態c'tor,因此不適用。另外,也沒有使用靜態字段。換句話說,沒有任何靜態字段被引用,所以不需要運行靜態初始化程序。 –

+5

沒有*需要*,但沒有要求*不*運行它們。任何時候「在第一次使用靜態字段之前」都是允許的。由於沒有使用靜態字段,所以任何時候都可以。 – Blorgbeard