以下は、静的読み取り専用フィールドを初期化する2つの異なる方法です。 2つのアプローチに違いはありますか?はいの場合、どちらが他方よりも優先されるべきですか?
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;
}
}
これら2つの間に微妙な違いが1つあります。これは、ILコードに見られます。明示的な静的コンストラクターを配置すると、C#コンパイラーに型を beforefieldinit としてマークしないように指示します。 beforefieldinitは、型初期化子が実行されるタイミングに影響します。これを知っていると、たとえば、 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の場合の例では、初期化はconnectionStringが最初に参照されたときにのみ発生しますが、クラスBの場合、初期化はタイプクラスBが最初に参照されたときに発生し、必ずしもアクセスする必要はありません。 connectionString。
C#(。NET 4.0)のみが、静的メンバーの初期化方法を制御できます。 VB.NETではnon beforefieldinitメソッドのみが可能ですが、C++/CLIではbeforefieldinitメカニズムのみが可能です。
それらは基本的に同じですが、両方静的フィールドへの読み取り専用割り当ておよび静的型コンストラクターがある場合、読み取り専用割り当てが最初に発生します。
明示的なコンストラクターが存在する場合(beforefieldinitバージョン以外)、静的メンバーへのアクセスは比較的遅くなることを追加する必要があります。