c#静态构造函数,又称类构造函数。它是属于类的,不属于任何一个类的实例。它具有以下特点:
1、以static定义的,无访问修饰符,无返回类型,无参数的构造函数,如:
- class test
- {
- /// <summary>
- /// 静态构造函数/类构造函数
- /// </summary>
- static test()
- {
- }
- }
2、不能通过代码显式的调用静态构造函数
3、静态构造函数在类的静态成员第一次访问或第一个类实例创建之前由系统调用
4、静态构造函数最多运行一次
5、静态构造函数须显式定义,若不定义,则不存在默认静态构造函数:此特点后面将详细说明
6、一个类中最多只能定义一个静态构造函数
7、静态构造函数主要完成的是静态数据成员的初始化工作,若定义静态数据成员的时候进行初始化,同时在静态构造函数中进行了初始化,则以静态构造函数的初始化为准。
- C#代码:
- class Test
- {
- public static string x = "a";
- static Test()
- {
- x = "b";
- }
- }
- IL代码:
- .class private auto ansi Test
- extends [mscorlib]System.Object
- {
- .method private hidebysig specialname rtspecialname static void .cctor() cil managed
- {
- .maxstack 8
- L_0000: ldstr "a"
- L_0005: stsfld string StaticTest.Test::x
- L_000a: nop
- L_000b: ldstr "b"
- L_0010: stsfld string StaticTest.Test::x
- L_0015: nop
- L_0016: ret
- }
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .field public static string x
- }
从上面的C#转换为IL代码可以看出,x被赋值了两次,一次是a,一次是b,但是最后起作用的是b。
对于静态构造函数,很多的介绍中都认为存在默认的静态构造函数,我个人认为这是不准确的。因为很多人都认为静态构造函数和转换的IL代码中的.cctor方法是对应的。其实不然,.cctor可以说是静态成员初始化器或是类初始化器,但并不是所有的类在转化成IL代码的时候都会生成.cctor方法;而静态构造函数会影响.cctor生成的代码,同时会通过取消IL代码中的"beforefieldinit"而影响.cctor方法的调用时机。下面就分别从几个类生成的IL代码情况,对问题进行说明。
1、无静态构造函数、静态成员的空类:有beforefieldinit标记,无.cctor函数
- C#代码:
- class Test
- {
- }
- 转化的IL代码:
- .class private auto ansi beforefieldinit Test
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- }
2、无静态构造函数、有静态成员但声明时未进行初始化:与1的结果一样有beforefieldinit标记,无.cctor函数
- C#代码:
- class Test
- {
- public static string x;
- }
- 转化的IL代码:
- .class private auto ansi beforefieldinit Test
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .field public static string x
- }
3、无静态构造函数、有静态成员并已初始化:有beforefieldinit标记、有.cctor函数
- C#代码:
- class Test
- {
- public static string x="a";
- }
- 转化的IL代码:
- .class private auto ansi beforefieldinit Test
- extends [mscorlib]System.Object
- {
- .method private hidebysig specialname rtspecialname static void .cctor() cil managed
- {
- .maxstack 8
- L_0000: ldstr "a"
- L_0005: stsfld string StaticTest.Test::x
- L_000a: ret
- }
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .field public static string x
- }
4、有静态构造函数、无静态成员的类:无beforefieldinit标记、有.cctor函数
- C#代码:
- class Test
- {
- //public static string x="a";
- static Test()
- {
- }
- }
- 转化的IL代码:
- .class private auto ansi Test
- extends [mscorlib]System.Object
- {
- .method private hidebysig specialname rtspecialname static void .cctor() cil managed
- {
- .maxstack 8
- L_0000: nop
- L_0001: ret
- }
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- }
通过上面的分析,可以了解:无静态构造函数的时候,类生成的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