我們知道在C#語言中建立一個類型的執行個體前,就應該初始化該類型的所有靜態成員變數。C#語言為我們提供了靜態初始化器和靜態建構函式。其中,靜態建構函式是一個特殊的建構函式,將在其他所有方法執行前以及變數或屬性被第一次訪問之前將自動調用靜態建構函式,且僅執行一次。我們可以通過使用靜態建構函式來初始化靜態變數、實現單例模式或者執行類在可用之前的所有操作。但是不能夠使用執行個體建構函式專門的私人函數或者其他什麼方式來初始化靜態變數。
靜態類成員變數也有和執行個體成員類似的初始化器文法,如果只是需要為某個靜態成員分配空間,可以直接使用初始化器文法,但是如果需要使用一些更複雜的邏輯來初始化靜態成員變數那就應該直接使用靜態建構函式。
1.單例模式中的靜態建構函式
在C#中實現單例模式是靜態建構函式的一個常見情境。只需要將執行個體建構函式聲明為私人,然後添加一個初始化器即可:
1 public class Singleton 2 { 3 /// <summary> 4 /// 靜態成員變數,添加 readonly 關鍵字 5 /// </summary> 6 private static readonly Singleton theOneAndOnly = new Singleton(); 7 8 /// <summary> 9 /// 唯讀靜態屬性10 /// </summary>11 public static Singleton TheOnly12 {13 get { return theOneAndOnly; }14 }15 16 /// <summary>17 /// 私人的執行個體建構函式18 /// </summary>19 private Singleton()20 {21 }22 }
編譯器產生的程式碼類似於下面:
1 public class Singleton 2 { 3 /// <summary> 4 /// 靜態成員變數,添加 readonly 關鍵字 5 /// </summary> 6 private static readonly Singleton theOneAndOnly; 7 8 /// <summary> 9 /// 唯讀靜態屬性10 /// </summary>11 public static Singleton TheOnly12 {13 get { return theOneAndOnly; }14 }15 16 /// <summary>17 /// 靜態建構函式18 /// </summary>19 static Singleton()20 {21 theOneAndOnly = new Singleton();22 }23 24 /// <summary>25 /// 私人的執行個體建構函式26 /// </summary>27 private Singleton()28 {29 }30 }
和執行個體初始化器類似,靜態初始化器在任何靜態建構函式之前執行。而且,靜態初始化器在調用基類的靜態建構函式之前執行。
2.關於靜態建構函式
靜態建構函式用於初始化任何待用資料,在建立第一個執行個體或者引用任何靜態成員之前,靜態建構函式將會被CLR調用來初始化類,且僅調用一次。
靜態建構函式具有如下特點:
- 靜態建構函式既沒有存取修飾詞也不接受任何參數
- 在建立第一個執行個體或者引用任何靜態成員之前,靜態建構函式將會被CLR調用來初始化類
- 不能直接調用靜態建構函式,並且在程式中不受控制
- 如果靜態建構函式引發異常,CLR將不會嘗試再次調用該建構函式,在Application 領域(AppDomain)內,類型將保持未初始化,這會導致該類及其衍生類別建立的類型沒有得到完全初始化
3.靜態初始化器&靜態建構函式
通過前面關於靜態建構函式的特點,我們知道:如果靜態建構函式引發異常,在該次Application 領域(AppDomain)內CLR將不會嘗試再次調用該建構函式,這會導致該類及其衍生類別建立的類型沒有得到完全初始化。
如果直接只用靜態初始化器文法:編譯器會添直接將靜態成員初始化代碼加入到靜態建構函式中,並且沒有任何異常處理。所以在使用靜態初始化器時,我們無法捕獲並處理異常。然而在我們可以直接在靜態建構函式中添加try/catch代碼塊了進行異常處理。如下:
1 public class Singleton 2 { 3 /// <summary> 4 /// 靜態成員變數,添加 readonly 關鍵字 5 /// </summary> 6 private static readonly Singleton theOneAndOnly; 7 8 /// <summary> 9 /// 唯讀靜態屬性10 /// </summary>11 public static Singleton TheOnly12 {13 get { return theOneAndOnly; }14 }15 16 /// <summary>17 /// 直接在靜態建構函式中進行異常處理18 /// </summary>19 static Singleton()20 {21 try22 {23 theOneAndOnly = new Singleton();24 }25 catch26 {27 //......28 }29 }30 31 /// <summary>32 /// 私人的執行個體建構函式33 /// </summary>34 private Singleton()35 {36 }37 }
小節:
靜態初始化器和靜態建構函式是初始化靜態成員的最佳選擇,容易理解並且不易出錯。在其他語言中,這兩個特性也是專門用來方便初始化靜態成員而提供的。如果你需要在你的代碼初始化靜態成員的代碼中進行異常處理是可以直接使用靜態建構函式,在建構函式中添加異常處理代碼;如果是只需要對靜態成員進行空間的分配那麼直接使用初始化器文法即可——在聲明靜態成員的時候對其進行初始化。
進一步閱讀&參考資料
MSDN:靜態建構函式