¿Qué hace la bandera beforefieldinit?

82

¿Qué hace la bandera beforefieldinit? Cuando miro el IL de mi clase, veo esta bandera, pero no sé qué está haciendo realmente esta bandera.

Embedd_Khurja
fuente

Respuestas:

132

Vea mi artículo sobre este mismo tema.

Básicamente, beforefieldinitsignifica que "el tipo se puede inicializar en cualquier punto antes de que se haga referencia a cualquier campo estático". En teoría, eso significa que puede inicializarse de forma muy perezosa : si llama a un método estático que no toca ningún campo, el JIT no necesita inicializar el tipo.

En la práctica , significa que la clase se inicializa antes de lo que sería de otra manera; está bien que se inicialice al comienzo del primer método que podría usarla. Compare esto con los tipos que no se les han beforefieldinitaplicado, donde la inicialización del tipo debe ocurrir inmediatamente antes del primer uso real .

Entonces, supongamos que tenemos:

public static void DoSomething(bool which)
{
    if (which)
    {
        FirstType.Foo();
    }
    else
    {
        SecondType.Bar();
    }
}

Si ambos tipos se beforefieldinitles han aplicado (lo que en C # lo hacen de forma predeterminada a menos que el tipo tenga un constructor estático), ambos se inicializarán al comienzo del DoSomethingmétodo (por lo general, no está garantizado). Si no lo tienen beforefieldinit, solo se inicializará uno de ellos, según la bandera.

Por eso es común usar un constructor estático (¡incluso uno vacío!) Al implementar el patrón singleton .

Jon Skeet
fuente
"el tipo se puede inicializar en cualquier punto antes de que se haga referencia a cualquier campo". ¿También es cierto para ejecutar métodos estáticos?
Royi Namir
@RoyiNamir, la especificación CLI dice que si se aplica BeforeFieldInit, "el método de inicialización del tipo se ejecuta en, o en algún momento antes, el primer acceso a cualquier campo estático definido para ese tipo". Si se pierde ese atributo (.ctor estático), entonces "primero acceda a cualquier campo estático o de instancia de ese tipo, o primera invocación de cualquier método estático, de instancia o virtual de ese tipo". Por lo tanto, no es cierto para BeforeFieldInit aplicado a menos que el método estático haga referencia a otro campo estático.
Arman McHitarian
3
Descubrí que hay una penalización de rendimiento por usar constructores estáticos (es decir, clases que no tienen la bandera beforefieldinit). Si llama a miembros estáticos de una determinada clase con frecuencia, parece que el tiempo de ejecución tiene que hacer una comprobación adicional antes de cada llamada para comprobar si el tipo ya se ha inicializado; beforefieldinit evita estos controles. Un par de puntos de referencia fueron aproximadamente un 50% más rápidos con beforefieldinit: codeproject.com/Articles/87991/…
Qwertie
6

Parece que va a cambiar en 4.6

https://github.com/dotnet/coreclr/issues/1193

OmariO
fuente
Genial, ¿significa esto que esperará hasta el último momento para inicializar el campo (independientemente de si lo ha hecho beforefieldinito no)?
James Ko
1
Antes del primer uso, todos los accesos tendrán como prefijo una verificación de inicialización. Después de eso, cuando se modifiquen otros métodos, el código generado accederá al campo directamente. Si es de tipo primitivo, incluso se puede utilizar como constante de tiempo JIT.
OmariO
2
El análisis detallado de Jon sobre los cambios en la inicialización de tipos a partir de .Net 4.0 aquí .
RBT