靜態建構函式 (C# 程式設計手冊)
靜態建構函式可用來初始化任何 靜態 數據,或執行只需要執行一次的特定動作。 在建立第一個執行個體或參考任何靜態成員之前,會自動進行呼叫。 靜態建構函式最多會呼叫一次。
class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;
// Static constructor is called at most one time, before any
// instance constructor is invoked or member is accessed.
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}
有數個動作是靜態初始化的一部分。 這些動作會依下列順序進行:
- 靜態欄位設定為 0。 通常由執行階段完成。
- 靜態欄位初始化運算式執行。 在大部分衍生型別中執行的靜態字段初始化表達式。
- 基底類型靜態欄位初始化表達式執行。 靜態欄位初始化表示式,從直接基底開始,透過每個基底類型到 System.Object。
- 基底靜態建構函式會執行。 任何靜態建構函式,從 Object.Object 每個基類開始到直接基類。
- 靜態建構函式會執行。 型別的靜態建構函式會執行。
模組初始化表達式可以是靜態建構函式的替代方案。 如需詳細資訊,請參閱 模組初始化表達式的規格。
備註
靜態建構函式具有下列屬性:
- 靜態建構函式不會採用存取修飾詞或具有參數。
- 類別或結構只能有一個靜態建構函式。
- 靜態建構函式無法繼承或多載。
- 您無法直接呼叫靜態建構函式,且它是設計成只由通用語言執行平台 (CLR) 呼叫的。 系統會自動呼叫它。
- 使用者無法控制在程式中執行靜態建構函式的時間。
- 會自動呼叫靜態建構函式。 它會在建立第一個實例之前初始化 類別 ,或參考該類別中宣告的任何靜態成員(而非其基類)。 靜態建構函式會在實例建構函式之前執行。 如果靜態欄位變數初始化表達式存在於靜態建構函式的類別中,則會以出現在類別宣告中的文字順序執行。 初始化表達式會在靜態建構函式執行之前立即執行。
- 如果您未提供靜態建構函式來初始化靜態欄位,則所有靜態欄位都會初始化為其預設值,如 C# 類型的預設值所列出。
- 如果靜態建構函式擲回例外狀況,運行時間不會第二次叫用它,而且類型在應用程式域的存留期內會保持未初始化。 在多數情況下,當靜態建構函示無法具現化型別時,或靜態建構函式內出現未處理的例外狀況時,會擲回 TypeInitializationException 例外狀況。 針對未在原始碼中明確定義的靜態建構函式,疑難解答可能需要檢查中繼語言 (IL) 程序代碼。
- 靜態建構函式的存在可能阻止 BeforeFieldInit 型別屬性的新增。 這會限制執行階段最佳化。
- 宣告為
static readonly
的欄位,只能指派為其宣告的一部分,或在靜態建構函式中指派。 當不需要明確的靜態建構函式時,請在宣告時初始化靜態字段,而不是透過靜態建構函式,以取得更佳的運行時間優化。 - 運行時間在單一應用程式域中呼叫靜態建構函式不超過一次。 該呼叫會根據 類別的特定類型,在鎖定區域中進行。 靜態建構函式主體中不需要額外的鎖定機制。 為了避免死結的風險,請勿封鎖靜態建構函式和初始化表達式中的目前線程。 例如,不要等候工作、線程、等候句柄或事件、不取得鎖定,也不會執行封鎖平行作業,例如平行迴圈
Parallel.Invoke
和平行 LINQ 查詢。
注意
雖然無法直接存取明確靜態建構函式,但應記錄其存在,以協助對初始化例外狀況進行疑難排解。
使用方式
- 靜態建構函式的一般用法為:當類別正在使用記錄檔,且建構函式用來將項目寫入這個檔案時。
- 如果建構函式可以呼叫
LoadLibrary
方法,靜態建構函式在建立 unmanaged 程式碼的包裝函式類別時也很有用。 - 靜態建構函式也是對無法透過類型參數條件約束在編譯階段檢查的類型參數強制執行運行時間檢查的便利位置。
範例
在此範例中,Bus
類別具有靜態建構函式。 建立 Bus
的第一個執行個體 (bus1
) 時,即會叫用靜態建構函式來初始化類別。 範例輸出可確認即使建立了兩個 Bus
執行個體,靜態建構函式也只執行一次,而且它是在執行個體建構函式執行之前執行。
public class Bus
{
// Static variable used by all Bus instances.
// Represents the time the first bus of the day starts its route.
protected static readonly DateTime globalStartTime;
// Property for the number of each bus.
protected int RouteNumber { get; set; }
// Static constructor to initialize the static variable.
// It is invoked before the first instance constructor is run.
static Bus()
{
globalStartTime = DateTime.Now;
// The following statement produces the first line of output,
// and the line occurs only once.
Console.WriteLine("Static constructor sets global start time to {0}",
globalStartTime.ToLongTimeString());
}
// Instance constructor.
public Bus(int routeNum)
{
RouteNumber = routeNum;
Console.WriteLine("Bus #{0} is created.", RouteNumber);
}
// Instance method.
public void Drive()
{
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
// For demonstration purposes we treat milliseconds as minutes to simulate
// actual bus times. Do not do this in your actual bus schedule program!
Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
this.RouteNumber,
elapsedTime.Milliseconds,
globalStartTime.ToShortTimeString());
}
}
class TestBus
{
static void Main()
{
// The creation of this instance activates the static constructor.
Bus bus1 = new Bus(71);
// Create a second bus.
Bus bus2 = new Bus(72);
// Send bus1 on its way.
bus1.Drive();
// Wait for bus2 to warm up.
System.Threading.Thread.Sleep(25);
// Send bus2 on its way.
bus2.Drive();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Sample output:
Static constructor sets global start time to 3:57:08 PM.
Bus #71 is created.
Bus #72 is created.
71 is starting its route 6.00 minutes after global start time 3:57 PM.
72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/
C# 語言規格
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應