47

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

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

4 回答 4

40

这两者之间有一个细微的区别,可以在 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    
}
于 2010-05-03T21:46:45.153 回答
18

beforefieldinit属性指示初始化如何发生的。

在显式静态构造函数初始化的情况下,静态成员的初始化发生在访问类型的那一刻。在类 A 的例子中,初始化只会在第一次引用connectionString时发生,而在类 B 的情况下,初始化将在第一次引用类型 B 时发生,不一定访问connectionString

只有 C# (.NET 4.0) 为我们提供了对如何初始化静态成员的控制。对于 VB.NET,只有非 beforefieldinit方法是可能的,而对于 C++/CLI,只有beforefieldinit机制是可能的。

于 2012-03-16T20:07:30.087 回答
7

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

于 2010-05-03T21:41:15.447 回答
1

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

来自https://rules.sonarsource.com/csharp/RSPEC-3963

当静态构造函数除了初始化静态字段之外没有其他用途时,它会带来不必要的性能成本,因为编译器会在每个静态方法或实例构造函数调用之前生成检查。

相反,强烈建议使用内联初始化。

于 2019-03-05T17:16:13.647 回答