статический инициализатор поля только для чтения vs статическая инициализация конструктора

Ниже приведены два разных способа инициализации статических полей только для чтения. Есть ли разница между этими двумя подходами? Если да, то когда одно должно быть предпочтительнее другого?

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

3 ответов


существует одно тонкое различие между этими двумя, которое можно увидеть в коде IL-размещение явного статического конструктора говорит компилятору C# не отмечать тип как beforefieldinit. Beforefieldinit влияет на запуск инициализатора типа, и знание об этом полезно при написании ленивые синглеты в C#, например.

вкратце разница заключается в следующем:

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

во всех других аспектах они одинаковы. Выход от рефлектора:

Класс:

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

на beforefieldinit атрибут указывает, как происходит инициализация.

в случае явной инициализации статического конструктора инициализация статического члена происходит в момент обращения к типу. В примере, приведенном в случае класса A, инициализация произойдет только тогда, когда connectionString сначала упоминается, тогда как в случае инициализации класса B произойдет первый раз, когда класс типа B упоминается, а не обязательно доступ connectionString.

только C# (.NET 4.0 ) предоставляет нам контроль над тем, как статические члены могут быть инициализированы. С VB.NET только non beforefieldinit метод возможен, тогда как с C++ / CLI только beforefieldinit механизм возможен.


Они по существу одинаковы, но если у вас есть и назначение только для чтения статическому полю и конструктор статического типа, Сначала выполняется назначение только для чтения.