Orden de constructores / inicializadores estáticos en C #

Mientras trabajaba en una aplicación de C #, me di cuenta de que en varios lugares, los inicializadores estáticos tienen dependencias entre sí como esto:

static private List a = new List() { 0 }; static private List b = new List() { a[0] }; 

Sin hacer nada especial que funcionara. ¿Eso es sólo suerte? ¿C # tiene reglas para resolver esto?

Edit: (re: Panos) En un archivo ¿el orden léxico parece ser el rey? ¿Qué pasa con los archivos?

Al mirar probé una dependencia cíclica como esta:

 static private List a = new List() { b[0] }; static private List b = new List() { a[0] }; 

y el progtwig no funcionó igual (la demanda de prueba falló en todos los ámbitos y no busqué más).

Parece depender de la secuencia de líneas. Este código funciona:

 static private List a = new List() { 1 }; static private List b = new List() { a[0] }; 

Si bien este código no funciona (lanza una NullReferenceException )

 static private List a = new List() { b[0] }; static private List b = new List() { 1 }; 

Entonces, obviamente no existen reglas para la dependencia cíclica. Es curioso sin embargo que el comstackdor no se queje …


EDITAR – ¿Qué está pasando “a través de archivos”? Si declaramos estas dos clases:

 public class A { public static List a = new List() { Bb[0] }; } public class B { public static List b = new List() { Aa[0] }; } 

e intenta acceder a ellos con este código:

 try { Console.WriteLine(Bb); } catch (Exception e) { Console.WriteLine(e.InnerException.Message.); } try { Console.WriteLine(Aa); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); } try { Console.WriteLine(Bb); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); } 

estamos obteniendo esta salida:

 The type initializer for 'A' threw an exception. Object reference not set to an instance of an object. The type initializer for 'A' threw an exception. 

Por lo tanto, la inicialización de B provoca una excepción en el constructor estático A y deja el campo a con el valor predeterminado (nulo). Como a es null , b tampoco puede inicializarse correctamente.

Si no tenemos dependencias cíclicas, todo funciona bien.


EDIT: En caso de que no haya leído los comentarios, Jon Skeet proporciona una lectura muy interesante: Las diferencias entre los constructores estáticos y los inicializadores de tipo .

Vea la sección 10.4 de la especificación de C # para las reglas aquí:

cuando se inicializa una clase, todos los campos estáticos de esa clase se inicializan primero a sus valores predeterminados, y luego los inicializadores de campo estáticos se ejecutan en orden textual. Del mismo modo, cuando se crea una instancia de una clase, todos los campos de instancia en esa instancia se inicializan primero a sus valores predeterminados, y luego los inicializadores de campo de instancia se ejecutan en orden textual. Es posible observar campos estáticos con inicializadores variables en su estado de valor predeterminado. Sin embargo, esto es fuertemente desaconsejado como una cuestión de estilo.

En otras palabras, en su ejemplo, ‘b’ se inicializa a su estado predeterminado (nulo) y, por lo tanto, la referencia a él en el inicializador de ‘a’ es legal pero daría lugar a una NullReferenceException.

Estas reglas son diferentes a las de Java (consulte la sección 8.3.2.3 de JLS para las reglas de Java sobre referencias futuras , que son más restrictivas).

Personalmente me gustaría deshacerme de los inicializadores estáticos ya que no están claros y agregar un constructor estático para inicializar estas variables.

 static private List a; static private List b; static SomeClass() { a = new List() { 0 }; b = new List() { a[0] }; } 

Entonces no tienes que adivinar qué está pasando y estás siendo claro en tus intenciones.

Sí, tuviste suerte. C # parece ejecutar el código en el orden en que aparece en la clase.

 static private List a = new List() { 0 }; static private List b = new List() { a[0] }; 

Funcionará pero …

 static private List b = new List() { a[0] }; static private List a = new List() { 0 }; 

Fallará.

Recomendaría poner todas sus dependencias en un solo lugar, el constructor estático es el lugar para esto.

 static MyClass() { a = new List() { 0 }; b = new List() { a[0] }; }