Constructeurs statiques (Guide de programmation C#)

Un constructeur statique est utilisé pour initialiser toutes les données statiques ou effectuer une action particulière qui ne doit être effectuée qu’une seule fois. Il est automatiquement appelé avant la création de la première instance ou le référencement d’un membre statique. Un constructeur statique est appelé au plus une fois.

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    static SimpleClass()
    {
        baseline = DateTime.Now.Ticks;
    }
}

Plusieurs actions font partie de l’initialisation statique. Ces actions ont lieu dans l’ordre suivant :

  1. Les champs statiques sont définis sur 0. Le runtime effectue généralement cette initialisation.
  2. Les initialiseurs de champs statiques s’exécutent. Initialiseurs de champs statiques dans l’exécution de type le plus dérivé.
  3. Les initialiseurs de champs statiques de type de base s’exécutent. Initialiseurs de champs statiques commençant par la base directe via chaque type de base vers System.Object.
  4. Tout constructeur statique s’exécute. Tous les constructeurs statiques, de la classe de base ultime de Object.Object pour chaque classe de base par le biais de l’exécution de type. L’ordre d’exécution du constructeur statique n’est pas spécifié. Toutefois, tous les constructeurs statiques de la hiérarchie s’exécutent avant la création d’instances.

Important

Il existe une exception importante à la règle qu’un constructeur statique s’exécute avant la création d’une instance. Si un initialiseur de champ statique crée une instance du type, cet initialiseur s’exécute (y compris tout appel à un constructeur d’instance) avant l’exécution du constructeur statique. Ceci est le plus courant dans le modèle singleton, comme illustré dans l’exemple suivant :

public class Singleton
{
    // Static field initializer calls instance constructor.
    private static Singleton instance = new Singleton();

    private Singleton()
    { 
        Console.WriteLine("Executes before static constructor.");
    }

    static Singleton()
    { 
        Console.WriteLine("Executes after instance constructor.");
    }

    public static Singleton Instance => instance;
}

Un initialiseur de module peut être une alternative au constructeur statique. Pour plus d’informations, consultez la spécification des initialiseurs de module.

Notes

Les constructeurs statiques ont les propriétés suivantes :

  • Un constructeur statique n’utilise ni modificateurs d’accès ni paramètres.
  • Une classe ou struct ne peut pas avoir de constructeur statique.
  • Les constructeurs statiques ne peuvent pas être hérités ou surchargés.
  • Un constructeur statique ne peut pas être appelé directement et est uniquement destiné à être appelé par le Common Language Runtime (CLR). Il est appelé automatiquement.
  • L’utilisateur n’a aucun contrôle sur le moment d’exécution du constructeur statique dans le programme.
  • Un constructeur statique est automatiquement appelé. Il initialise la classe avant la création de la première instance ou les membres statiques déclarés dans cette classe (et non ses classes de base) sont référencés. Un constructeur statique s’exécute avant un constructeur d’instance. Si les initialiseurs de variable de champ statique sont présents dans la classe du constructeur statique, ils sont exécutés dans l’ordre textuel dans lequel ils apparaissent dans la déclaration de classe. Les initialiseurs s’exécutent immédiatement avant le constructeur statique.
  • Si vous ne fournissez pas de constructeur statique pour initialiser les champs statiques, tous les champs statiques sont initialisés à leur valeur par défaut, conformément au Valeurs par défaut des types C#.
  • Si un constructeur statique lève une exception, le runtime ne l’appelle pas une deuxième fois et le type reste non initialisé pour la durée de vie du domaine d’application. En règle générale, une exception TypeInitializationException est levée lorsqu’un constructeur statique ne parvient pas à instancier un type ou quand une exception non gérée se produit dans un constructeur statique. Pour les constructeurs statiques qui ne sont pas définis explicitement dans le code source, la résolution des problèmes peut nécessiter l’inspection du code en langage intermédiaire (IL).
  • La présence d’un constructeur statique empêche l’ajout de l’attribut de type BeforeFieldInit. Cela limite l’optimisation du runtime.
  • Un champ déclaré en tant que static readonly ne peut être attribué que dans le cadre de sa déclaration ou dans un constructeur statique. Si un constructeur statique explicite n’est pas obligatoire, initialisez les champs statiques lors de la déclaration plutôt que via un constructeur statique pour une meilleure optimisation du runtime.
  • Le runtime appelle un constructeur statique au maximum une fois dans un seul domaine d’application. Cet appel est effectué dans une région verrouillée en fonction du type spécifique de la classe. Aucun mécanisme de verrouillage supplémentaire n’est nécessaire dans le corps d’un constructeur statique. Pour éviter le risque d’interblocages, ne bloquez pas le thread actuel dans les constructeurs statiques et les initialiseurs. Par exemple, n’attendez pas les tâches, les threads, attendez les descripteurs ou les événements, n’acquérez pas de verrous et n’exécutez pas d’opérations parallèles bloquantes telles que des boucles parallèles, Parallel.Invoke et des requêtes LINQ parallèles.

Notes

S’il n’est pas directement accessible, la présence d’un constructeur statique explicite doit être documentée pour faciliter la résolution des exceptions de l’initialisation.

Utilisation

  • Généralement, on utilise des constructeurs statiques quand la classe utilise un fichier journal et que le constructeur sert à écrire des entrées dans ce fichier.
  • Les constructeurs statiques sont également utiles pour la création de classes wrapper pour du code non managé, quand le constructeur peut appeler la méthode LoadLibrary.
  • Les constructeurs statiques sont également pratiques pour appliquer les contrôles d’exécution sur le paramètre de type qui ne peuvent pas être effectués au moment de la compilation par le biais de contraintes de paramètre.

Exemple

Dans cet exemple, la classe Bus possède un constructeur statique. Quand la première instance de Bus est créée (bus1), le constructeur statique est appelé pour initialiser la classe. L’exemple de sortie vérifie que le constructeur statique s’exécute une seule fois, même si deux instances de Bus sont créées, et qu’il s’exécute avant le constructeur d’instance.

public class Bus
{
    // Static variable used by all Bus instances.
    // Represents the time the first bus of the day starts its route.
    protected static readonly DateTime globalStartTime;

    // Property for the number of each bus.
    protected int RouteNumber { get; set; }

    // Static constructor to initialize the static variable.
    // It is invoked before the first instance constructor is run.
    static Bus()
    {
        globalStartTime = DateTime.Now;

        // The following statement produces the first line of output,
        // and the line occurs only once.
        Console.WriteLine("Static constructor sets global start time to {0}",
            globalStartTime.ToLongTimeString());
    }

    // Instance constructor.
    public Bus(int routeNum)
    {
        RouteNumber = routeNum;
        Console.WriteLine("Bus #{0} is created.", RouteNumber);
    }

    // Instance method.
    public void Drive()
    {
        TimeSpan elapsedTime = DateTime.Now - globalStartTime;

        // For demonstration purposes we treat milliseconds as minutes to simulate
        // actual bus times. Do not do this in your actual bus schedule program!
        Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
                                this.RouteNumber,
                                elapsedTime.Milliseconds,
                                globalStartTime.ToShortTimeString());
    }
}

class TestBus
{
    static void Main()
    {
        // The creation of this instance activates the static constructor.
        Bus bus1 = new Bus(71);

        // Create a second bus.
        Bus bus2 = new Bus(72);

        // Send bus1 on its way.
        bus1.Drive();

        // Wait for bus2 to warm up.
        System.Threading.Thread.Sleep(25);

        // Send bus2 on its way.
        bus2.Drive();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Sample output:
    Static constructor sets global start time to 3:57:08 PM.
    Bus #71 is created.
    Bus #72 is created.
    71 is starting its route 6.00 minutes after global start time 3:57 PM.
    72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/

spécification du langage C#

Pour plus d’informations, voir la section Constructeurs statiques de la spécification du langage C#.

Voir aussi