Instanzkonstruktoren (C#-Programmierleitfaden)

Sie deklarieren einen Instanzkonstruktor, um den Code anzugeben, der ausgeführt wird, wenn Sie eine neue Instanz eines Typs mit dem new-Ausdruck erstellen. Um eine statische Klasse oder statische Variablen in einer nicht statischen Klasse zu initialisieren, können Sie einen statischen Konstruktor definieren.

Wie im folgenden Beispiel gezeigt, können Sie mehrere Instanzkonstruktoren in einem Typ deklarieren:

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)
    }
}

Im vorherigen Beispiel ruft der erste parameterlose Konstruktor den zweiten Konstruktor auf, wobei beide Argumente gleich 0 sind. Verwenden Sie dazu das Schlüsselwort this.

Wenn Sie einen Instanzkonstruktor in einer abgeleiteten Klasse deklarieren, können Sie einen Konstruktor einer Basisklasse aufrufen. Verwenden Sie dazu das Schlüsselwort base, wie im folgenden Beispiel gezeigt:

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
    }
}

Parameterlose Konstruktoren

Wenn eine Klasse keine expliziten Instanzkonstruktoren hat, stellt C# einen parameterlosen Konstruktor bereit, mit dem Sie eine Instanz dieser Klasse instanziieren können. Dies wird im folgenden Beispiel gezeigt:

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
    }
}

Dieser Konstruktor initialisiert Instanzfelder und -eigenschaften gemäß den entsprechenden Initialisierern. Wenn ein Feld oder eine Eigenschaft keinen Initialisierer hat, wird der Wert auf den Standardwert des Feld- oder Eigenschaftstyps festgelegt. Wenn Sie mindestens einen Instanzkonstruktor in einer Klasse deklarieren, stellt C# keinen parameterlosen Konstruktor bereit.

Ein Struktur-Typ stellt einen parameterlosen Konstruktor bereit. Der parameterlose Konstruktor und höher ist dies entweder ein impliziter parameterloser Konstruktor, der den Standardwert eines Typs erzeugt, oder ein explizit deklarierter parameterloser Konstruktor. Weitere Informationen finden Sie im Abschnitt zu Strukturinitialisierung und Standardwerten des Artikels Strukturtypen.

Primäre Konstruktoren

Ab C# 12 können Sie einen primären Konstruktor in Klassen und Strukturen deklarieren. Sie platzieren alle Parameter nach dem Typnamen in Klammern:

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

Die Parameter für einen primären Konstruktor befinden sich im gesamten Textkörper des deklarierenden Typs. Sie können Eigenschaften oder Felder initialisieren. Sie können als Variablen in Methoden oder lokalen Funktionen verwendet werden. Sie können an einen Basiskonstruktor übergeben werden.

Ein primärer Konstruktor gibt an, dass diese Parameter für alle Instanzen eines Typs erforderlich sind. Jeder explizit geschriebene Konstruktor muss die this(...)-Initialisierersyntax verwenden, um den primären Konstruktor aufzurufen. Dadurch wird sichergestellt, dass die Parameter des primären Konstruktors definitiv von allen Konstruktoren zugewiesen werden. Für jeden class-Typ, einschließlich record class-Typen, wird der implizite parameterlose Konstruktor nicht ausgegeben, wenn ein primärer Konstruktor vorhanden ist. Für jeden struct-Typ, einschließlich record struct-Typen, wird der implizite parameterlose Konstruktor immer ausgegeben, und er initialisiert immer alle Felder, einschließlich der Parameter des primären Konstruktors, mit dem 0-Bit-Muster. Wenn Sie einen expliziten parameterlosen Konstruktor schreiben, muss er den primären Konstruktor aufrufen. In diesem Fall können Sie einen anderen Wert für die Parameter des primären Konstruktors angeben. Der folgende Code zeigt Beispiele für primäre Konstruktoren.

// 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;
}

Sie können der synthetisierten primären Konstruktormethode Attribute hinzufügen, indem Sie das method: Ziel für das Attribut angeben:

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

Wenn Sie das method Ziel nicht angeben, wird das Attribut anstelle der Methode auf der Klasse platziert.

In den class- und struct-Typen sind die Parameter des primären Konstruktors überall im Textkörper des Typs verfügbar. Sie können als Memberfelder verwendet werden. Wenn ein Parameter eines primären Konstruktors verwendet wird, erfasst der Compiler den Konstruktorparameter in einem privaten Feld mit einem vom Compiler generierten Namen. Wenn im Textkörper des Typs kein Parameter eines primären Konstruktors verwendet wird, wird kein privates Feld erfasst. Diese Regel verhindert, dass versehentlich zwei Kopien eines primären Konstruktorparameters zugewiesen werden, der an einen Basiskonstruktor übergeben wird.

Wenn der Typ den record-Modifizierer enthält, synthetisiert der Compiler stattdessen eine öffentliche Eigenschaft mit demselben Namen wie der Parameter des primären Konstruktors. Wenn ein Parameter eines primären Konstruktors für record class-Typen denselben Namen wie ein primärer Basiskonstruktor verwendet, ist diese Eigenschaft eine öffentliche Eigenschaft des record class-Basistyps. Sie wird nicht im abgeleiteten record class-Typ dupliziert. Diese Eigenschaften werden nicht für Nicht-record-Typen generiert.

Weitere Informationen