我們知道在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 TheOnly 12 { 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 TheOnly 12 { 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將不會嘗試再次調用該構造函數,在應用程序作用域(AppDomain)內,類型將保持未初始化,這會導致該類及其派生類創建的類型沒有得到完全初始化
3.靜態初始化器&靜態構造函數
通過前面關於靜態構造函數的特點,我們知道:如果靜態構造函數引發異常,在該次應用程序作用域(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 TheOnly 12 { 13 get { return theOneAndOnly; } 14 } 15 16 /// <summary> 17 /// 直接在靜態構造函數中進行異常處理 18 /// </summary> 19 static Singleton() 20 { 21 try 22 { 23 theOneAndOnly = new Singleton(); 24 } 25 catch 26 { 27 //...... 28 } 29 } 30 31 /// <summary> 32 /// 私有的實例構造函數 33 /// </summary> 34 private Singleton() 35 { 36 } 37 }
小節:
靜態初始化器和靜態構造函數是初始化靜態成員的最佳選擇,容易理解並且不易出錯。在其他語言中,這兩個特性也是專門用來方便初始化靜態成員而提供的。如果你需要在你的代碼初始化靜態成員的代碼中進行異常處理是可以直接使用靜態構造函數,在構造函數中添加異常處理代碼;如果是只需要對靜態成員進行空間的分配那么直接使用初始化器語法即可——在聲明靜態成員的時候對其進行初始化。