2010-05-03 17 views

Respuesta

31

Hay una diferencia sutil entre estos dos, que se puede ver en el código IL: al colocar un constructor estático explícito, el compilador C# no debe marcar el tipo como beforefieldinit. El campo anterior afecta cuando se ejecuta el inicializador de tipo y conocer esto es útil al escribir lazy singletons in C#, por ejemplo.

En resumen, la diferencia es la siguiente:

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

En todos los demás aspectos que son los mismos. La 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  
} 
5

Ellos son esencialmente los mismos, pero si le sucede que tiene tanto una asignación de sólo lectura a un campo estático y un constructor de tipo estático, la asignación de sólo lectura que ocurra primero.

13

El beforefieldinit atributo 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á cuando se refiera por primera vez connectionString, mientras que en el caso de la clase B la inicialización ocurrirá la primera vez que se refiere la clase de tipo B, no necesariamente accediendo a connectionString.

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

Cuestiones relacionadas