[英]C# - Static initializer is not called when field is accessed from other class' static constructor
[英]Static field is initialized later when the class has a static constructor
通過運行這個簡單的代碼:
class Program
{
class MyClassWithStatic
{
public static int Number = SomeService.GetData();
static MyClassWithStatic()
{
Console.WriteLine("static ctor runs");
}
}
class SomeService
{
public static int GetData()
{
Console.WriteLine("GetDataRuns");
return 42;
}
}
static void Main(string[] args)
{
InitService();
var value = MyClassWithStatic.Number;
Console.WriteLine(value);
}
private static void InitService()
{
Console.WriteLine("InitServiceRuns");
}
}
我機器上的輸出是這樣的:
InitServiceRuns
GetDataRuns
靜態ctor運行
42
意思是首先調用InitService方法,然后初始化MyClassWithStatic的靜態字段,然后調用靜態構造函數(實際上通過在ILSpy和IlDasm中查看這個,我們可以看到靜態字段的初始化發生在執行者)
這一點沒有什么有意思的,一切都有意義,但是當我刪除MyClassWithStatic的靜態構造函數時(所以MyClassWithStatic變成了這個,其他一切都像以前一樣)
class MyClassWithStatic
{
public static int Number = SomeService.GetData();
}
輸出是這樣的:
GetDataRuns
InitServiceRuns
42
這意味着通過刪除靜態構造函數,可以更早地初始化靜態字段。 由於初始化是靜態構造函數的一部分(我通過使用ildasm查看它來告訴它),效果基本上是先前調用靜態構造函數。
所以這是一個問題:
有人可以解釋這種行為嗎? 這可能是什么原因?
在調用靜態構造函數時是否還有其他可以改變的東西? (例如,附加一個分析器或在IIS中運行它等)(我比較了調試,發布模式,x86,x64和所有顯示相同的行為)
一些一般的東西:
- 這是在.NET 4.6控制台應用程序中。 我也切換到.NET 2(應該運行不同的clr,行為是相同的,它沒有任何區別)
- 我也嘗試使用.NET內核:無論是否使用cctor,首先調用InitService方法。
- 現在我完全清楚這個頁面 :
用戶無法控制何時在程序中執行靜態構造函數。
而且我也知道在靜態構造函數中有許多你不應該做的事情。 但不幸的是,我必須處理一個代碼,其中這部分超出了我的控制范圍,我所描述的差異產生了巨大的差異。 (而且我還經歷了許多與C#cctor相關的SO問題..)
(和問題nr3 :)所以我描述的整個事情不是有點問題嗎?
與靜態構造一個類將不被標有beforefieldinit
標志,它允許運行時在稍后的時間將其初始化(換句話說, MyClassWithStatic.Number
將被初始化時,第一參考/接入MyClassWithStatic
)
在本文中獲取戰利品以獲取更多信息。
有人可以解釋這種行為嗎? 這可能是什么原因?
@JonSkeet 在深度C#中有一個關於靜態字段和靜態構造函數的段落。 這是一個片段:
C#規范聲明:
類的靜態構造函數在給定的應用程序域中最多執行一次。 靜態構造函數的執行由應用程序域中發生的以下第一個事件觸發:
- 創建了一個類的實例。
- 引用該類的任何靜態成員。
CLI規范(ECMA 335)在第8.9.5節中說明:
類型可以具有類型初始化方法或不具有類型初始化方法。 可以將類型指定為具有用於其類型初始化方法的松弛語義(為了方便起見,我們稱之為松弛語義BeforeFieldInit):.
- 如果標記為BeforeFieldInit,則類型的初始化方法在首次訪問為該類型定義的任何靜態字段時或之前執行。
- 如果未標記為BeforeFieldInit,則執行該類型的初始化方法((at(即,由...觸發):首先訪問該類型的任何靜態或實例字段,或者首先調用該類型的任何靜態,實例或虛擬方法))
這將向您展示當一個類型沒有beforefieldinit
標志時,運行時可以在任意時間內調用它,因為它是在第一次訪問定義的任何靜態字段之前,這正是您所要求的看到。
在調用靜態構造函數時是否還有其他可以改變的東西?
唯一的事情是在你的類型上創建一個靜態類型構造函數。 其他人,你無法控制它的調用。
所以我描述的整個事情不是有點問題嗎?
有問題的問題是什么? 只要你知道自己在做什么,我就沒有問題。 CLI規范使得對類型初始化程序的保證非常清楚。 因此,如果你遵循這些指導原則,就不應該含糊不清。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.