inicializador de campo estático de solo lectura vs inicialización de constructor estático


A continuación se muestran dos formas diferentes de inicializar campos de lectura estáticos. ¿Hay alguna diferencia entre los dos enfoques? En caso afirmativo, ¿cuándo se debe preferir uno sobre el otro?

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;
    }
}
Author: CZoellner, 2010-05-04

3 answers

Hay una sutil diferencia entre estos dos, que se puede ver en el código IL - poner un constructor estático explícito le dice al compilador de C# que no marque el tipo como beforefieldinit. El beforefieldinit afecta cuando se ejecuta el inicializador de tipos y saber esto es útil cuando se escriben singletons perezosos en C#, por ejemplo.

En resumen, la diferencia es esta:

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

En todos los demás aspectos son los mismos. Salida del reflector:

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

Clase 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    
}
 32
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-23 14:31:14

El atributo beforefieldinit indica cómo ocurre la inicialización.

En el caso de una inicialización explícita del constructor estático, la inicialización del miembro estático ocurre en el momento en que se accede al tipo. En el ejemplo dado en el caso de la clase A, la inicialización ocurrirá solo cuando ConnectionString se refiere por primera vez, mientras que en el caso de la inicialización de clase B ocurrirá la primera vez que se refiere el tipo clase B, no necesariamente accediendo ConnectionString.

Solo C# (. NET 4.0 ) nos proporciona control sobre cómo se pueden inicializar los miembros estáticos. Con VB.NET solo es posible el método non beforefieldinit mientras que con C++/CLI solo es posible el mecanismo beforefieldinit.

 15
Author: Swami PR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-23 13:01:30

Son esencialmente los mismos, pero si tiene tanto una asignación de solo lectura a un campo estático y un constructor de tipo estático, la asignación de solo lectura se produce primero.

 7
Author: Paul Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-03 21:41:15