L'ordine di inizializzazione della classe statica in C# è deterministico?

L'ordine di inizializzazione della classe statica in C# è deterministico?

Direttamente da ECMA-334:

E:

Quindi l'ordine è:

  • A.X usato, quindi static A() chiamato.
  • A.X deve essere inizializzato, ma utilizza B.X , quindi static B() chiamato.
  • B.X deve essere inizializzato e viene inizializzato su 7. B.X = 7
  • Tutti i campi statici di B sono inizializzati, quindi static B() è chiamato. X viene stampato ("7"), quindi viene impostato su A.X . A ha già iniziato l'inizializzazione, quindi otteniamo il valore di A.X , che è il valore predefinito ("quando una classe viene inizializzata, tutti i campi statici in quella classe vengono prima inizializzati al loro valore predefinito"); B.X = 0 , e viene stampato ("0").
  • Completata l'inizializzazione di B e il valore di A.X è impostato su B.X+1 . A.X = 1 .
  • Tutti i campi statici di A sono inizializzati, quindi static A() è chiamato. A.X viene stampato ("1").
  • Di nuovo in Main , i valori di A.X e B.X vengono stampati ("1", "0").

In realtà lo commenta nello standard:


Per rendere questa garanzia sono coinvolte circa quattro regole diverse nella specifica C#, ed è specifica per C#. L'unica garanzia fornita dal runtime .NET è che l'inizializzazione del tipo inizi prima che il tipo venga utilizzato.

  • I campi statici vengono inizializzati da zero finché non viene eseguito l'inizializzatore del tipo.
  • Gli inizializzatori di campi statici vengono eseguiti immediatamente prima del costruttore statico.
  • Che i costruttori statici vengono chiamati alla chiamata del costruttore di prima istanza o al riferimento del primo membro statico.
  • Gli argomenti della funzione vengono valutati in ordine da sinistra a destra.

Fare affidamento su questa è una pessima idea perché è probabile che confonda chiunque legga il tuo codice, soprattutto se ha familiarità con linguaggi con una sintassi simile che non forniscono tutte e quattro le garanzie di cui sopra.

Si noti che il commento di Porges era correlato alla mia affermazione iniziale (basata sul comportamento .NET) che le garanzie sono troppo deboli per assicurare il comportamento osservato. Porges ha ragione sul fatto che le garanzie sono abbastanza forti, ma in realtà è coinvolta una catena molto più complessa di quanto suggerisce.


Potrebbe interessarti sapere che è anche possibile assegnare valori a un campo tra l'inizializzazione predefinita e l'inizializzazione della variabile.

private static int b = Foo();
private static int a = 4;

private static int Foo()
{
    Console.WriteLine("{0} - Default initialization", a);
    a = 3;
    Console.WriteLine("{0} - Assignment", a);
    return 0;
}

public static void Main()
{
    Console.WriteLine("{0} - Variable initialization", a);
}

uscite

0 - Default initialization
3 - Assignment
4 - Variable initialization