【问题标题】:static readonly field initializer vs static constructor initialization静态只读字段初始化与静态构造函数初始化
【发布时间】:2011-02-15 05:25:12
【问题描述】:

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

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

【问题讨论】:

    标签: c# c#-3.0 c#-2.0 initializer static-constructor


    【解决方案1】:

    这两者之间有一个细微的区别,这可以在 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    
    }
    

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • “在A类的例子中,初始化只会在第一次引用connectionString时发生” - 我相信这可以概括为“只有在第一次引用 A 类的任何静态字段时才会进行初始化”
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-06
          • 2014-03-22
          • 2012-01-07
          • 1970-01-01
          相关资源
          最近更新 更多