2012-07-09 13 views
5

Durante mi investigación sobre la mejor manera de construir un Singleton en C# me encontré con el siguiente article donde hay una breve mención que en C++ambigüedad en orden de inicialización de variables estáticas

"La especificación C++ dejó algunas ambigüedad alrededor de la inicialización orden de las variables estáticas. "

terminé mirando a la pregunta y se encontró this y this. Donde básicamente el punto (hasta donde yo entiendo) es que el orden de inicialización de las variables estáticas en C++ no está definido. Ok supongo que hasta ahora tan bueno, pero luego quería entender la siguiente declaración de que el artículo posterior hace

"Afortunadamente, el .NET Framework resuelve esta ambigüedad a través de su manejo de inicialización de variables".

así que encontré this página donde dicen

campo estático inicializadores de variables de una clase corresponden a una secuencia de tareas que se ejecutan en el orden textual en el que aparecen en la clase declaración.

y dar el ejemplo de

using System; 
class Test 
{ 
    static void Main() { 
     Console.WriteLine("{0} {1}", B.Y, A.X); 
    } 
    public static int F(string s) { 
     Console.WriteLine(s); 
     return 1; 
    } 
} 
class A 
{ 
    static A() {} 
    public static int X = Test.F("Init A"); 
} 
class B 
{ 
    static B() {} 
    public static int Y = Test.F("Init B"); 
} 

the output must be: 
Init B 
Init A 
1 1 

"Debido a que las reglas para cuando constructores estáticos ejecutar (como se define en Sección 10.11) proporcionan que constructor estático de B (y por tanto inicializadores campo estático de B) debe ejecutarse antes del constructor estático de A y inicializadores de campo. "

Pero dónde estoy confundido es que mi entendimiento es que el orden de inicialización de variables estáticas en estos ejemplos se basaría en cuando se invocó por primera vez un método o campo dentro de la clase, que es a su vez basado en la ejecución orden del bloque de código (este caso de izquierda a derecha). IE: completamente independiente de dónde - o el orden - de la declaración de clase. Sin embargo, según mi interpretación de ese artículo, ¿es como resultado del orden de declaración de esas clases, que mis pruebas no respaldan?

¿Podría alguien aclarar esto (y el punto que el artículo está tratando de hacer) para mí y tal vez proporcionar un mejor ejemplo que analfabe el comportamiento descrito?

Respuesta

7

campo estático inicializadores de variables de una clase corresponden a una secuencia de asignaciones que se ejecutan en el orden textual en el que aparecen en la declaración de clase.

Esto significa que dentro de la misma clase, los campos estáticos se inicializan en orden de aparición en el código fuente.Por ejemplo:

class A 
{ 
    public static int X = Test.F("Init A.X"); 
    public static int Y = Test.F("Init A.Y"); 
} 

Cuando es hora de que los campos estáticos que pueden inicializar, X está garantizado que ser inicializado antes de Y.

"Debido a que las reglas para cuando constructores estáticos ejecutar (como se define en Sección 10.11) proporcionan que constructor estático de B (y por tanto inicializadores campo estático de B) se debe ejecutar antes de constructor estático de A y inicializadores de campo."

Esto significa que el constructor estático y la inicialización del miembro para cada clase se ejecutarán en el orden de evaluación cuando aparezcan las expresiones que acceden a estas clases¹¹. El orden relativo de aparición de las definiciones de clase en el código fuente no juega ningún papel, incluso si aparecen en el mismo archivo fuente (que ciertamente no están obligados a hacer). Por ejemplo:

static void Main() { 
    Console.WriteLine("{0} {1}", B.Y, A.X); 
} 

Suponiendo que ni A ni B ya se ha inicializado de forma estática, el orden de las garantías de evaluación que todos los campos de la B se iniciará antes de cualquier campo de la A. Los campos de cada clase se inicializarán en el orden especificado por la primera regla.


¹ para los fines de esta discusión que estoy haciendo caso omiso de la existencia de beforefieldinit.

+0

Muchas gracias por la respuesta extremadamente concisa. –

3

En C++, el orden de inicialización de variables con duración de almacenamiento estático en una sola unidad de traducción es el orden en el que se producen las definiciones de dichas variables. No se especifica qué orden de inicialización de variables tiene una duración de almacenamiento estática en diferentes unidades de traducción.

Es decir, el estándar C++ ofrece una garantía similar a la que se cita, sustituyendo el orden de declaración en la clase por el orden de definición en la unidad de traducción única que define dichas variables. Pero esa no es la diferencia importante.

Mientras que en C++ es la única garantía, en C# existe la garantía adicional de que todos los miembros estáticos se inicializarán antes del primer uso de la clase. Esto significa que, si su programa depende de A (considere cada tipo en un ensamblaje diferente que es el peor caso), comenzará la inicialización de todos los campos estáticos en A, si A a su vez depende de B para cualquiera de esas inicializaciones estáticas , entonces la inicialización de los miembros estáticos B se activará allí.

contraste que con C++, en donde durante la inicialización estática [*], todas las otras variables con la duración estática son supone a ser inicializado. Esta es la principal diferencia: C++ supone que están inicializados, C# asegura que están antes de ese uso.


[*] Técnicamente el caso de que esto es problemático podría ser dinámica de inicialización en la norma.La inicialización de variables con duración de almacenamiento estático dentro de cada unidad de traducción es un proceso de dos pasos, donde durante la primera pasada la inicialización estática establece las variables en una expresión de constante fija, y luego en un segundo pase dinámica inicialización todas las variables con el almacenamiento estático cuyo inicializador no es una expresión constante se inicializa.

+0

+1 y nitpick: Técnicamente, si un tipo está decorado con 'beforefieldinit', entonces no se requiere que CLR inicialice los miembros estáticos hasta que realmente se tenga acceso a ellos (por ejemplo, puede llamar a los métodos estáticos todo lo que desee). Prácticamente, inicializa los campos incluso antes de acceder a la clase. – Jon