静态只读字段初始化程序与静态构造函数初始化

时间:2010-05-03 21:37:21

标签: c# c#-3.0 c#-2.0 initializer static-constructor

以下是初始化静态只读字段的两种不同方法。两种方法之间有区别吗?如果是的话,什么时候应该优先于另一个呢?

class A
{
    private static readonly string connectionString =
        WebConfigurationManager.ConnectionStrings["SomeConnection"].ConnectionString;
}

class B
{
    private static readonly string connectionString;

    static B()
    {
        connectionString =
            WebConfigurationManager.ConnectionStrings["SomeConnection"].ConnectionString;
    }
}

4 个答案:

答案 0 :(得分:33)

这两者之间有一个细微的区别,可以在IL代码中看到 - 将显式静态构造函数告知C#编译器不要将类型标记为beforefieldinit。 beforefieldinit会影响何时运行类型初始值设定项,例如在编写lazy singletons in C#时知道这一点很有用。

简而言之,区别在于:

.class private auto ansi beforefieldinit A
.class private auto ansi B

在所有其他方面,它们是相同的。反射器的输出:

A类:

.class private auto ansi beforefieldinit A
    extends [mscorlib]System.Object
{
    .method private hidebysig specialname rtspecialname static void .cctor() cil managed
    {
        .maxstack 8
        L_0000: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, class Connection> WebConfigurationManager::ConnectionStrings
        L_0005: ldstr "SomeConnection"
        L_000a: callvirt instance !1 [mscorlib]System.Collections.Generic.Dictionary`2<string, class Connection>::get_Item(!0)
        L_000f: ldfld string Connection::ConnectionString
        L_0014: stsfld string A::connectionString
        L_0019: 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 private static initonly string connectionString
} 

B组:

.class private auto ansi B
    extends [mscorlib]System.Object
{
    .method private hidebysig specialname rtspecialname static void .cctor() cil managed
    {
        .maxstack 8
        L_0000: nop 
        L_0001: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, class Connection> WebConfigurationManager::ConnectionStrings
        L_0006: ldstr "SomeConnection"
        L_000b: callvirt instance !1 [mscorlib]System.Collections.Generic.Dictionary`2<string, class Connection>::get_Item(!0)
        L_0010: ldfld string Connection::ConnectionString
        L_0015: stsfld string B::connectionString
        L_001a: 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 private static initonly string connectionString    
}

答案 1 :(得分:15)

beforefieldinit 属性指示初始化的发生方式。

在显式静态构造函数初始化的情况下,静态成员的初始化在访问类型时发生。在A类情况下给出的示例中,初始化仅在首次引用 connectionString 时发生,而在B类情况下,初始化将在第一次引用类型B时发生,不一定是访问的的connectionString

只有C#(.NET 4.0)使我们能够控制如何初始化静态成员。使用VB.NET只能使用 non beforefieldinit 方法,而使用C ++ / CLI时,只能使用 beforefieldinit 机制。

答案 2 :(得分:7)

它们本质上是相同的,但是如果你碰巧两者对静态字段的只读赋值和静态类型构造函数,那么只读赋值首先发生。

答案 3 :(得分:0)

我必须补充一点,在存在显式构造函数(非beforefieldinit版本)的情况下,访问静态成员的速度相对较慢。