Condividi tramite


Costruttori di istanze (guida per programmatori C#)

Si dichiara un costruttore di istanza per specificare il codice eseguito quando si crea una nuova istanza di un tipo con l'espressione new . Per inizializzare una classe statica o variabili statiche in una classe non statica, definire un costruttore statico.

Come illustrato nell'esempio seguente, è possibile dichiarare diversi costruttori di istanza in un solo tipo:

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

Nell'esempio precedente, il primo costruttore senza parametri chiama il secondo costruttore con entrambi gli argomenti uguali a 0. Per farlo, usare la parola chiave this.

Quando si dichiara un costruttore di istanza in una classe derivata, è possibile chiamare un costruttore di una classe base. A tale scopo, usare la parola chiave base, come illustrato nell'esempio seguente:

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

Costruttori senza parametri

Se una classe non dispone di costruttori di istanze espliciti, C# fornisce un costruttore senza parametri che è possibile usare per creare un'istanza di tale classe, come illustrato nell'esempio seguente:

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

Tale costruttore inizializza i campi e le proprietà dell'istanza in base agli inizializzatori corrispondenti. Se un campo o una proprietà non dispongono di inizializzatore, il relativo valore viene impostato sul valore predefinito del tipo del campo o della proprietà. Se si dichiara almeno un costruttore di istanza in una classe, C# non fornisce un costruttore senza parametri.

Un tipo struttura fornisce sempre un costruttore senza parametri. Il costruttore senza parametri è implicito e produce il valore predefinito di un tipo o un costruttore senza parametri dichiarato in modo esplicito. Per altre informazioni, vedere la sezione Inizializzazione Struct e valori predefiniti dell'articolo Tipi di struttura.

Costruttori primari

A partire da C# 12, è possibile dichiarare un costruttore primario in classi e struct. Tutti i parametri vengono inseriti tra parentesi dopo il nome del tipo:

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

I parametri di un costruttore primario sono inclusi nell'ambito nell'intero corpo del tipo dichiarante. Possono inizializzare proprietà o campi. Possono essere usati come variabili nei metodi o nelle funzioni locali. Possono essere passati a un costruttore di base.

Un costruttore primario indica che questi parametri sono necessari per qualsiasi istanza del tipo. Qualsiasi costruttore scritto in modo esplicito deve usare la sintassi dell'inizializzatore this(...) per richiamare il costruttore primario. Ciò garantisce che i parametri del costruttore primario siano sicuramente assegnati da tutti i costruttori. Per qualsiasi tipo class, inclusi i tipi record class, il costruttore implicito senza parametri non viene generato quando è presente un costruttore primario. Per qualsiasi tipo struct, inclusi i tipi record struct, il costruttore implicito senza parametri viene sempre generato e inizializza sempre tutti i campi, inclusi i parametri del costruttore primario, al modello a 0 bit. Se si scrive un costruttore esplicito senza parametri, deve richiamare il costruttore primario. In tal caso, è possibile specificare un valore diverso per i parametri del costruttore primario. Nel codice riportato di seguito vengono illustrati esempi di costruttori primari.

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

È possibile aggiungere attributi al metodo del costruttore primario sintetizzato specificando la destinazione method: nell'attributo:

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

Se non si specifica la destinazione method, l'attributo viene inserito nella classe anziché nel metodo.

In class e struct i parametri del costruttore primario sono disponibili in qualsiasi punto del corpo del tipo. Il parametro può essere implementato come campo privato acquisito. Se gli unici riferimenti a un parametro sono inizializzatori e chiamate al costruttore, tale parametro non viene acquisito in un campo privato. Se viene usato in altri membri del tipo, il compilatore acquisirà il parametro in un campo privato.

Se il tipo include il modificatore record, il compilatore sintetizza invece una proprietà pubblica con lo stesso nome del parametro del costruttore primario. Per i tipi record class, se un parametro del costruttore primario utilizza lo stesso nome di un costruttore primario di base, tale proprietà è una proprietà pubblica del tipo record class di base. Non viene duplicato nel tipo derivato record class. Queste proprietà non vengono generate per i tipi non record.

Vedi anche