Ниже приведены два разных способа инициализации статических полей readonly. Есть ли разница между двумя подходами? Если да, когда нужно быть предпочтительнее другого?статический инициализатор поля readonly vs инициализация статического конструктора
ответ
Существует одно тонкое различие между этими двумя, которое можно увидеть в коде 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
}
Они, по существу, то же самое, но если вы будете иметь как только для чтения отнесение к статическому полю и конструктора статического типа, назначение только для чтения происходит первое.
beforefieldinit атрибут указывает, как инициализация происходит.
В случае явной инициализации статического конструктора инициализация статического члена происходит в момент обращения к типу. В примере, приведенном в случае класса A, инициализация будет происходить только тогда, когда ссылается на stringString, тогда как в случае инициализации класса B произойдет первый раз, когда относится тип класса B, не обязательно обращаясь к connectionString.
Только C# (.NET 4.0) предоставляет нам возможность контролировать, как статические члены могут быть инициализированы. С VB.NET возможен только метод non beforefieldinit, тогда как с C++/CLI возможен только механизм beforefieldinit.