简体   繁体   English

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

[英]static readonly field initializer vs static constructor initialization

Below are two different ways to initialize static readonly fields. 以下是初始化静态只读字段的两种不同方法。 Is there a difference between the two approaches? 两种方法之间有区别吗? If yes, when should one be preferred over the other? 如果是的话,什么时候应该优先于另一个呢?

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;
    }
}

There is one subtle difference between these two, which can be seen in the IL code - putting an explicit static constructor tells the C# compiler not to mark the type as beforefieldinit . 这两者之间有一个细微的区别,可以在IL代码中看到 - 将显式静态构造函数告知C#编译器不要将类型标记为beforefieldinit The beforefieldinit affects when the type initializer is run and knowing about this is useful when writing lazy singletons in C# , for example. beforefieldinit影响何时运行类型初始化程序,例如, 在C#中编写惰性单例时,了解这一点很有用。

In brief the difference is this: 简而言之,区别在于:

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

In all other aspects they are the same. 在所有其他方面,它们是相同的。 Output from Reflector: 反射器的输出:

Class A: 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
} 

Class B: 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    
}

The beforefieldinit attribute indicates how the initialization happens. beforefieldinit属性指示初始化是如何发生的。

In case of an explicit static constructor initialization, the initialization of the static member happens the moment the type is accessed. 在显式静态构造函数初始化的情况下,静态成员的初始化在访问类型时发生。 In the example given in case of class A, the initialization will happen only when connectionString is first referred, whereas in case of class B initialization will happen the first time the type class B is referred, not necessarily accessing connectionString . 在类A的情况下给出的示例中,初始化仅在首次引用connectionString时发生,而在类B的情况下,初始化将在第一次引用类型类B时发生,而不一定访问connectionString

Only C# (.NET 4.0 ) provides us control over how static members can be initialized. 只有C#(.NET 4.0)使我们能够控制如何初始化静态成员。 With VB.NET only the non beforefieldinit method is possible whereas with C++/CLI only the beforefieldinit mechanism is possible. 使用VB.NET只能使用非beforefieldinit方法,而使用C ++ / CLI时,只能使用beforefieldinit机制。

他们基本上是相同的,但如果你碰巧有两个只读分配一个静态字段静态类型构造,只读分配第一次出现。

我必须补充一点,使用显式构造函数(非beforefieldinit版本)访问静态成员相对慢一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM