Créer des constructeurs de classe et instancier des objets

Effectué

Outre les propriétés et méthodes de classe, les définitions de classes incluent des constructeurs utilisés pour initialiser de nouveaux objets (instances de classe).

Constructeurs de classe

Un constructeur de classe est une méthode portant le même nom que son type (les méthodes de constructeur utilisent le même nom que la classe).

Il existe deux types de constructeurs de classe :

  • Constructeurs d'instances Les constructeurs d’instance sont utilisés pour créer et initialiser toutes les variables de champ d’instance lorsqu’un objet est créé.
  • Constructeurs statiques. Les constructeurs statiques sont utilisés pour initialiser toutes les données statiques ou pour effectuer une action particulière qui ne doit être effectuée qu’une seule fois. Les constructeurs statiques sont appelés automatiquement avant que la première instance ne soit créée ou que les membres statiques ne soient référencés.

Les constructeurs de classes sont des constructeurs d’instances par défaut.

Syntaxe du constructeur d’instance

Un constructeur d’instance est déclaré à l’aide du même nom que la classe et n’inclut pas de type de retour. La signature de méthode du constructeur peut inclure un modificateur d’accès facultatif, le nom de la méthode et sa liste de paramètres. Les signatures de méthode d’un constructeur n’incluent pas de type de retour.

L’exemple suivant montre un constructeur simple pour une classe nommée Person:


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }

    // Remaining implementation of Person class.
}

Les classes peuvent avoir plusieurs constructeurs. Lorsqu’une classe a plusieurs constructeurs, les constructeurs prennent généralement différents arguments.

L’exemple suivant montre une classe nommée Person avec deux constructeurs.


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.
        string name = "John Doe";
        Console.WriteLine($"Person created: {name}");

    }

    public Person(string fName, string lName)
    {
        string name = fName + " " + lName;

        Console.WriteLine($"Person created: {name}");
   }

   // Remaining implementation of Person class.
}

Instancier des objets à l’aide de constructeurs de classe

Lorsqu’un objet est instancié à l’aide du mot clé new, le runtime .NET appelle le constructeur d’instance associé dans la définition de classe et alloue de la mémoire pour l’objet.

Dans l’extrait de code suivant, la classe Person définit un constructeur d’instance simple. La classe Program inclut une méthode Main qui utilise l’opérateur new pour créer une instance de Person nommée person1. Le runtime appelle le constructeur Person immédiatement après l’allocation de la mémoire pour le nouvel objet.


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
    }
}

Constructeurs avec et sans paramètres

Un constructeur qui ne prend aucun paramètre n’est appelé constructeur sans paramètre. Le runtime appelle le constructeur sans paramètre lorsqu’un objet est instancié à l’aide de l’opérateur new et qu’aucun argument n’est fourni au constructeur.

Remarque

Sauf si la classe est statique, les classes sans constructeurs reçoivent un constructeur public sans paramètre par le compilateur C# afin d’activer l’instanciation de classe.

Les classes définissent souvent des constructeurs qui prennent des paramètres. Les constructeurs qui prennent des paramètres doivent être appelés à l’aide de l’opérateur new ou d’une instruction de base. Les classes peuvent définir un ou plusieurs constructeurs.

L’extrait de code suivant montre une classe nommée Person avec trois constructeurs :


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine("An instance of the Person class is being instantiated without name or age parameters.");
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using a name ({name}) parameter.");
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using name ({name}) and age ({age}) parameters.");
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Jane Doe");
        Person person3 = new Person("John Doe", 30);
    }
}

Classes sans constructeurs

Si une classe n’a aucun constructeur d’instance explicite, C# fournit un constructeur sans paramètre que vous pouvez utiliser pour instancier une instance de cette classe, comme l’illustre l’exemple suivant :


public class Person
{
    public int age;
    public string name = "unknown";
}

class Example
{
    static void Main()
    {
        var person = new Person();
        Console.WriteLine($"Name: {person.name}, Age: {person.age}");
        // Output:  Name: unknown, Age: 0
    }
}

Ce constructeur initialise les champs et propriétés d’instance en fonction des initialiseurs correspondants. Si un champ ou une propriété n’a pas d’initialiseur, sa valeur est définie sur la valeur par défaut du type de champ ou de propriété. Si vous déclarez au moins un constructeur d’instance dans une classe, C# ne fournit pas de constructeur sans paramètre.

Initialiser des données de classe à l’aide de paramètres de constructeur

Les paramètres passés à un constructeur sont locaux au constructeur. Les paramètres sont souvent utilisés pour initialiser les champs de données d’une classe.

L’extrait de code suivant montre une classe nommée Person avec des constructeurs qui initialisent les champs personName et personAge :


public class Person
{
    public string personName;
    public string personAge;

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = "unknown";
        personAge = "unknown";
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = "unknown";
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Jane Doe");
        Person person3 = new Person("John Doe", 30);

        Console.WriteLine($"Person 1 Name: {person1.personName} Age: {person1.personAge}");
        Console.WriteLine($"Person 2 Name: {person2.personName} Age: {person2.personAge}");
        Console.WriteLine($"Person 3 Name: {person3.personName} Age: {person3.personAge}");
    }
}

Dans l’exemple précédent, la classe Person est définie avec trois constructeurs. Le premier constructeur initialise les champs personName et personAge sur "unknown". Le deuxième constructeur initialise le champ personName à la valeur passée dans le paramètre name et le champ personAge à "unknown". Le troisième constructeur initialise les champs personName et personAge aux valeurs passées dans les paramètres name et age, respectivement.

Étant donné que les champs sont publics, ils sont accessibles directement à partir de la méthode Main. Lorsque le code s’exécute, la sortie suivante est générée :


Person 1 Name: unknown Age: unknown
Person 2 Name: Jane Doe Age: unknown
Person 3 Name: John Doe Age: 30

Définitions de corps d’expression

Si un constructeur peut être implémenté en tant qu’instruction unique, vous pouvez utiliser une définition de corps d’expression pour affecter un paramètre à un membre de classe lorsque le constructeur est implémenté.

Par exemple, le constructeur suivant initialise le champ modelName avec la valeur passée au paramètre model :


public class Car
{
    public string modelName;

    public Car(string model) => modelName = model;
    
}

La classe Car a un champ public unique, modelName, qui est de type string. Le champ modelName est destiné à stocker le nom du modèle de voiture.

La classe Car inclut également un constructeur qui accepte un paramètre de chaîne unique nommé model. Le constructeur utilise une définition de corps d’expression (indiquée par la syntaxe =>) pour initialiser le champ modelName avec la valeur passée au paramètre model. Cela signifie que lorsqu’un nouvel objet Car est instancié, le champ modelName est défini sur la valeur fournie comme argument du constructeur.

Comme le terme expression implique, le côté droit de l’opérateur => est une expression et n’est pas limité à une instruction d’assignation simple. L’expression peut être n’importe quelle expression C# valide qui retourne une valeur.

L’extrait de code suivant montre comment implémenter une définition de corps d’expression qui effectue un calcul simple :


public class Employee
{
    public int Salary;

    public Employee() { }

    public Employee(int annualSalary) => Salary = annualSalary;

    public Employee(int weeklySalary, int numberOfWeeks) => Salary = weeklySalary * numberOfWeeks;
}

Cette classe peut être créée à l’aide de l’une des instructions suivantes :


Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);

Constructeurs statiques

Un constructeur statique est utilisé pour initialiser toutes les données statiques ou pour effectuer une action particulière qui ne doit être effectuée qu’une seule fois. Elle est appelée automatiquement avant que la première instance soit créée ou que les membres statiques soient référencés. Un constructeur statique est appelé au plus une fois.

L’extrait de code suivant montre une version mise à jour de la classe Person qui implémente des champs statiques et un constructeur statique :


public class Person
{
    public string personName;
    public string personAge;

    // Static field
    public static string defaultName;
    public static string defaultAge;

    // Static constructor
    static Person()
    {
        // Static field initialization
        defaultName = "unknown";
        defaultAge = "unknown";
    }

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = defaultName;
        personAge = defaultAge;
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = defaultAge;
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

La classe Person mise à jour a deux champs d’instance, personName et personAge, qui sont tous deux de type string. Ces champs stockent respectivement le nom et l’âge d’une personne.

La classe définit également deux champs statiques, defaultName et defaultAge, également de type string. Les champs statiques sont partagés entre toutes les instances de la classe et sont initialisés une seule fois. Dans ce cas, les champs statiques sont utilisés pour fournir des valeurs par défaut pour les champs personName et personAge.

Le constructeur statique static Person() est responsable de l’initialisation des champs statiques. Il définit defaultName sur « inconnu » et defaultAge sur "unknown". Le constructeur statique est appelé automatiquement avant que toutes les instances de la classe soient créées ou que des membres statiques soient accessibles.

La classe Person comprend trois constructeurs d’instances :

Le constructeur sans paramètre public Person() initialise les champs personName et personAge avec les valeurs des champs statiques defaultName et defaultAge. Cela signifie que si aucun argument n’est fourni lors de la création d’un objet Person, les valeurs par défaut « inconnues » sont utilisées pour le nom et l’âge.

Le constructeur public Person(string name) prend un paramètre unique, name, et initialise le champ personName avec cette valeur. Le champ personAge est initialisé avec la valeur du champ statique defaultAge. Ce constructeur permet la création d’un objet Person avec un nom spécifié tout en utilisant l’âge par défaut.

Le constructeur public Person(string name, int age) prend deux paramètres, name et age. Il initialise le champ personName avec la valeur du paramètre name et le champ personAge avec la représentation sous forme de chaîne du paramètre d’âge. Ce constructeur permet la création d’un objet Person avec un name spécifié et un age.

Propriétés des constructeurs statiques

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

  • Un constructeur statique ne prend pas de modificateurs d’accès ni n’a de paramètres.

  • Une classe ne peut avoir qu’un seul 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 destiné uniquement à être appelé par le Common Language Runtime (CLR). Elle est appelée automatiquement.

  • L’utilisateur n’a aucun contrôle sur le moment où le constructeur statique est exécuté dans le programme.

  • Un constructeur statique est appelé automatiquement. 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 s’exécutent 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 des champs statiques, tous les champs statiques sont initialisés à leur valeur par défaut.

  • Si un constructeur statique lève une exception, le runtime ne l’appelle pas une seconde fois et le type reste non initialisé pendant la durée de vie du domaine d’application. Le plus souvent, une exception TypeInitializationException est levée lorsqu’un constructeur statique ne peut pas instancier un type ou pour une exception non gérée se produisant dans un constructeur statique. Pour les constructeurs statiques qui ne sont pas explicitement définis dans le code source, la résolution des problèmes peut nécessiter une inspection du code il (Intermediate Language).

  • 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. Lorsqu’un constructeur statique explicite n’est pas obligatoire, initialisez des champs statiques à la déclaration plutôt qu’à l’aide d’un constructeur statique pour une meilleure optimisation du runtime.

  • Le runtime appelle un constructeur statique pas plus d’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.

Remarque

Bien qu’il ne soit pas directement accessible, la présence d’un constructeur statique explicite doit être documentée pour faciliter la résolution des exceptions d’initialisation.