c#靜態構造函數

  c#靜態構造函數,又稱類構造函數。它是屬於類的,不屬於任何一個類的實例。它具有以下特點:
1、以static定義的,無訪問修飾符,無返回類型,無參數的構造函數,如: 

Code:
  1. class test   
  2.         {   
  3.             /// <summary>   
  4.             /// 靜態構造函數/類構造函數   
  5.             /// </summary>   
  6.             static test()   
  7.             {   
  8.             }   
  9.         }  

2、不能通過代碼顯式的調用靜態構造函數
3、靜態構造函數在類的靜態成員第一次訪問或第一個類實例創建之前由系統調用
4、靜態構造函數最多運行一次
5、靜態構造函數須顯式定義,若不定義,則不存在默認靜態構造函數:此特點後面將詳細說明
6、一個類中最多隻能定義一個靜態構造函數
7、靜態構造函數主要完成的是靜態數據成員的初始化工作,若定義靜態數據成員的時候進行初始化,同時在靜態構造函數中進行了初始化,則以靜態構造函數的初始化爲準。 

Code:
  1. C#代碼:
  2. class Test   
  3.     {   
  4.         public static string x = "a";   
  5.   
  6.         static Test()   
  7.         {   
  8.             x = "b";   
  9.         }           
  10.     }   
  11.   
  12. IL代碼:   
  13. .class private auto ansi Test   
  14.     extends [mscorlib]System.Object   
  15. {   
  16.     .method private hidebysig specialname rtspecialname static void .cctor() cil managed   
  17.     {   
  18.         .maxstack 8   
  19.         L_0000: ldstr "a"  
  20.         L_0005: stsfld string StaticTest.Test::x   
  21.         L_000a: nop    
  22.         L_000b: ldstr "b"  
  23.         L_0010: stsfld string StaticTest.Test::x   
  24.         L_0015: nop    
  25.         L_0016: ret    
  26.     }   
  27.   
  28.     .method public hidebysig specialname rtspecialname instance void .ctor() cil managed   
  29.     {   
  30.         .maxstack 8   
  31.         L_0000: ldarg.0    
  32.         L_0001: call instance void [mscorlib]System.Object::.ctor()   
  33.         L_0006: ret    
  34.     }   
  35.   
  36.   
  37.     .field public static string x   
  38.   
  39. }   
  40.   
  41.     

從上面的C#轉換爲IL代碼可以看出,x被賦值了兩次,一次是a,一次是b,但是最後起作用的是b。


對於靜態構造函數,很多的介紹中都認爲存在默認的靜態構造函數,我個人認爲這是不準確的。因爲很多人都認爲靜態構造函數和轉換的IL代碼中的.cctor方法是對應的。其實不然,.cctor可以說是靜態成員初始化器或是類初始化器,但並不是所有的類在轉化成IL代碼的時候都會生成.cctor方法;而靜態構造函數會影響.cctor生成的代碼,同時會通過取消IL代碼中的"beforefieldinit"而影響.cctor方法的調用時機。下面就分別從幾個類生成的IL代碼情況,對問題進行說明。
1、無靜態構造函數、靜態成員的空類:有beforefieldinit標記,無.cctor函數 

Code:
  1. C#代碼:   
  2. class Test   
  3.     {   
  4.     }   
  5. 轉化的IL代碼:   
  6. .class private auto ansi beforefieldinit Test   
  7.     extends [mscorlib]System.Object   
  8. {   
  9.     .method public hidebysig specialname rtspecialname instance void .ctor() cil managed   
  10.     {   
  11.         .maxstack 8   
  12.         L_0000: ldarg.0    
  13.         L_0001: call instance void [mscorlib]System.Object::.ctor()   
  14.         L_0006: ret    
  15.     }   
  16. }   

2、無靜態構造函數、有靜態成員但聲明時未進行初始化:與1的結果一樣有beforefieldinit標記,無.cctor函數 

Code:
  1. C#代碼:   
  2.  class Test   
  3.     {   
  4.         public static string x;           
  5.     }   
  6. 轉化的IL代碼:   
  7. .class private auto ansi beforefieldinit Test   
  8.     extends [mscorlib]System.Object   
  9. {   
  10.     .method public hidebysig specialname rtspecialname instance void .ctor() cil managed   
  11.     {   
  12.         .maxstack 8   
  13.         L_0000: ldarg.0    
  14.         L_0001: call instance void [mscorlib]System.Object::.ctor()   
  15.         L_0006: ret    
  16.     }   
  17.     .field public static string x   
  18. }   
  19.   
  20.     

3、無靜態構造函數、有靜態成員並已初始化:有beforefieldinit標記、有.cctor函數

Code:
  1. C#代碼:   
  2. class Test   
  3.     {   
  4.         public static string x="a";           
  5.     }   
  6. 轉化的IL代碼:   
  7. .class private auto ansi beforefieldinit Test   
  8.     extends [mscorlib]System.Object   
  9. {   
  10.     .method private hidebysig specialname rtspecialname static void .cctor() cil managed   
  11.     {   
  12.         .maxstack 8   
  13.         L_0000: ldstr "a"  
  14.         L_0005: stsfld string StaticTest.Test::x   
  15.         L_000a: ret    
  16.     }   
  17.     .method public hidebysig specialname rtspecialname instance void .ctor() cil managed   
  18.     {   
  19.         .maxstack 8   
  20.         L_0000: ldarg.0    
  21.         L_0001: call instance void [mscorlib]System.Object::.ctor()   
  22.         L_0006: ret    
  23.     }   
  24.     .field public static string x   
  25. }   
  26.   

4、有靜態構造函數、無靜態成員的類:無beforefieldinit標記、有.cctor函數

Code:
  1. C#代碼:   
  2. class Test   
  3.     {   
  4.         //public static string x="a";   
  5.         static Test()   
  6.         {   
  7.         }   
  8.     }   
  9. 轉化的IL代碼:   
  10. .class private auto ansi Test   
  11.     extends [mscorlib]System.Object   
  12. {   
  13.     .method private hidebysig specialname rtspecialname static void .cctor() cil managed   
  14.     {   
  15.         .maxstack 8   
  16.         L_0000: nop    
  17.         L_0001: ret    
  18.     }   
  19.     .method public hidebysig specialname rtspecialname instance void .ctor() cil managed   
  20.     {   
  21.         .maxstack 8   
  22.         L_0000: ldarg.0    
  23.         L_0001: call instance void [mscorlib]System.Object::.ctor()   
  24.         L_0006: ret    
  25.     }   
  26. }   

通過上面的分析,可以瞭解:無靜態構造函數的時候,類生成的IL代碼中一定有beforefieldinit標記,但不一定有.cctor函數;有靜態構造函數的時候,生成的IL代碼中必定有.cctor函數,類中無beforefieldinit標記;.cctor函數是負責靜態成員的初始化的,如果聲明時和在靜態構造函數中都有初始化,則兩者會合併到.cctor函數中,聲明的初始化在前,靜態構造函數的初始化在後。

beforefieldinit標記主要是用來決定.cctor調用時機。
如果未聲明類的靜態構造函數,則會設置beforefieldinit標記,此時的.cctor函數會交給CLR進行調用,CLR可以選擇程序集加載到第一訪問類型(包括訪問靜態成員和類實例)的這個時間段內的任何時間點調用.cctor函數,此時較難掌握.cctor運行時機。
如果聲明瞭類的靜態構造函數,則將不設置beforefieldinit標記,此時.cctor是在第一次使用類之前調用,可以順利跟蹤具體的時機。
以上兩種方式在速度上存在較大的差別,其中設置了beforefieldinit標記的類,在類型初始化器(.cctor)的運行性能上優於未設置beforefieldinit的類。具體可參考:
blog.csdn.net/liuyimu/archive/2010/04/29/5544222.aspx
www.cnblogs.com/carysun/archive/2009/09/08/beforefieldinit.html
csharpindepth.com/Articles/General/Beforefieldinit.aspx

靜態構造函數在性能上不如未設置靜態構造函數的情況,那爲什麼還是建議大家使用靜態構造函數初始化各類靜態成員呢?個人認爲,主要是通過靜態構造函數,可以很好的把握初始化靜態成員的時機,雖然在性能上有點劣勢,但是因爲只初始化一次,這點性能上的影響微乎其微。可以不予考慮。可參看Effective C#原則13

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章