分享方式:


靜態建構函式 (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;
    }
}

有數個動作屬於靜態初始化的一部分。 這些動作會依下列順序進行:

  1. 靜態欄位設定為 0。 執行階段通常會執行此初始化。
  2. 靜態欄位初始設定式會執行。 最衍生型別中靜態欄位初始設定式會執行。
  3. 基底類型靜態欄位初始設定式會執行。 從直接基底開始,通過每個基底型別到 System.Object 的靜態欄位初始設定式。
  4. 所有靜態建構函式都會執行。 任何靜態建構函式,從最終的基底類別 Object.Object 透過類型執行到每個基底類別。 未指定靜態建構函式執行的順序。 不過,階層中的所有靜態建構函式都會在建立任何執行個體之前執行。

重要

在建立任何執行個體之前,靜態建構函式執行的規則有一個重要的例外狀況。 如果靜態欄位初始設定式建立該類型的執行個體,該初始設定式會在靜態建構函式執行之前執行 (包括對執行個體建構函式的任何呼叫)。 這在單一模式中最常見,如下列範例所示:

public class Singleton
{
    // Static field initializer calls instance constructor.
    private static Singleton instance = new Singleton();

    private Singleton()
    { 
        Console.WriteLine("Executes before static constructor.");
    }

    static Singleton()
    { 
        Console.WriteLine("Executes after instance constructor.");
    }

    public static Singleton Instance => instance;
}

模組初始設定式可以是靜態建構函式的替代方案。 如需詳細資訊,請參閱模組初始設定式的規格 (英文)。

備註

靜態建構函式具有下列屬性:

  • 靜態建構函式不會接受存取修飾詞,也不會包含參數。
  • 類別或結構只能有一個靜態建構函式。
  • 靜態建構函式無法繼承或多載。
  • 您無法直接呼叫靜態建構函式,且其是設計成只由通用語言執行平台 (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# 語言規格

如需詳細資訊,請參閱 C# 語言規格靜態建構函式一節。

另請參閱