Constructeurs d’instances (Guide de programmation C#)

Vous déclarez un constructeur d’instance pour spécifier le code qui est exécuté lorsque vous créez une instance d’un type avec l’expression new . Pour initialiser une classe statique ou des variables statiques dans une classe non statique, vous pouvez définir un constructeur statique.

Comme le montre l’exemple suivant, vous pouvez déclarer plusieurs constructeurs d’instances dans un type :

class Coords
{
    public Coords()
        : this(0, 0)
    {  }

    public Coords(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }

    public override string ToString() => $"({X},{Y})";
}

class Example
{
    static void Main()
    {
        var p1 = new Coords();
        Console.WriteLine($"Coords #1 at {p1}");
        // Output: Coords #1 at (0,0)

        var p2 = new Coords(5, 3);
        Console.WriteLine($"Coords #2 at {p2}");
        // Output: Coords #2 at (5,3)
    }
}

Dans l’exemple précédent, le premier constructeur sans paramètre appelle le deuxième constructeur avec les deux arguments égaux à 0. Pour ce faire, utilisez le mot clé this.

Lorsque vous déclarez un constructeur d’instance dans une classe dérivée, vous pouvez appeler un constructeur d’une classe de base. Pour ce faire, utilisez le mot clé base, comme le montre l’exemple suivant :

abstract class Shape
{
    public const double pi = Math.PI;
    protected double x, y;

    public Shape(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public abstract double Area();
}

class Circle : Shape
{
    public Circle(double radius)
        : base(radius, 0)
    {  }

    public override double Area() => pi * x * x;
}

class Cylinder : Circle
{
    public Cylinder(double radius, double height)
        : base(radius)
    {
        y = height;
    }

    public override double Area() => (2 * base.Area()) + (2 * pi * x * y);
}

class Example
{
    static void Main()
    {
        double radius = 2.5;
        double height = 3.0;

        var ring = new Circle(radius);
        Console.WriteLine($"Area of the circle = {ring.Area():F2}");
        // Output: Area of the circle = 19.63
        
        var tube = new Cylinder(radius, height);
        Console.WriteLine($"Area of the cylinder = {tube.Area():F2}");
        // Output: Area of the cylinder = 86.39
    }
}

Constructeurs sans paramètre

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 le montre 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 de l’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 du champ ou de la propriété. Si vous déclarez au moins un constructeur d’instance dans une classe, C# ne fournit pas de constructeur sans paramètre.

Un type structure fournit toujours un constructeur sans paramètre. Le constructeur sans paramètre est soit un constructeur sans paramètre implicite qui produit la valeur par défaut d’un type, soit un constructeur sans paramètre déclaré explicitement. Pour plus d’informations, consultez la section Initialisation de struct et valeurs par défaut de l’article Types de structure.

Constructeurs principaux

À compter de C# 12, vous pouvez déclarer un constructeur principal dans les classes et les structs. Vous placez tous les paramètres entre parenthèses en suivant le nom de type :

public class NamedItem(string name)
{
    public string Name => name;
}

Les paramètres d’un constructeur principal sont dans l’étendue dans le corps entier du type déclarant. Ils peuvent initialiser des propriétés ou des champs. Ils peuvent être utilisés comme variables dans des méthodes ou des fonctions locales. Ils peuvent être passés à un constructeur de base.

Un constructeur principal indique que ces paramètres sont nécessaires pour n’importe quelle instance du type. Tout constructeur écrit explicitement doit utiliser la syntaxe d’initialiseur this(...) pour appeler le constructeur principal. Cela garantit que les paramètres du constructeur principal sont définitivement attribués par tous les constructeurs. Pour tout type class, y compris les types record class, le constructeur sans paramètre implicite n’est pas émis lorsqu’un constructeur principal est présent. Pour tout type struct, y compris les types record struct, le constructeur sans paramètre implicite est toujours émis et initialise toujours tous les champs, y compris les paramètres du constructeur principal, au modèle 0 bits. Si vous écrivez un constructeur explicite sans paramètre, il doit appeler le constructeur principal. Dans ce cas, vous pouvez spécifier une valeur différente pour les paramètres du constructeur principal. Le code suivant montre des exemples de constructeurs principaux.

// name isn't captured in Widget.
// width, height, and depth are captured as private fields
public class Widget(string name, int width, int height, int depth) : NamedItem(name)
{
    public Widget() : this("N/A", 1,1,1) {} // unnamed unit cube

    public int WidthInCM => width;
    public int HeightInCM => height;
    public int DepthInCM => depth;

    public int Volume => width * height * depth;
}

Vous pouvez ajouter des attributs à la méthode synthétisée du constructeur principal en spécifiant la cible method: sur l’attribut :

[method: MyAttribute]
public class TaggedWidget(string name)
{
   // details elided
}

Si vous ne spécifiez pas la cible method, l’attribut est disposé sur la classe plutôt que sur la méthode.

Dans les types class et struct, les paramètres du constructeur principal sont disponibles n’importe où dans le corps du type. Ils peuvent être utilisés en tant que champs membres. Lorsqu’un paramètre de constructeur principal est utilisé, le compilateur capture le paramètre de constructeur dans un champ privé avec un nom généré par le compilateur. Si un paramètre de constructeur principal n’est pas utilisé dans le corps du type, aucun champ privé n’est capturé. Cette règle empêche l’allocation accidentelle de deux copies d’un paramètre de constructeur principal passé à un constructeur de base.

Si le type inclut le modificateur record, le compilateur synthétise plutôt une propriété publique portant le même nom que le paramètre du constructeur principal. Pour les types record class, si un paramètre de constructeur principal utilise le même nom qu’un constructeur principal de base, cette propriété est une propriété publique du type de base record class. Il n’est pas dupliqué dans le type dérivé record class. Ces propriétés ne sont pas générées pour les types non record.

Voir aussi