Creare costruttori di classi e creare istanze di oggetti

Completato

Oltre alle proprietà e ai metodi di classe, le definizioni di classe includono costruttori usati per inizializzare nuovi oggetti (istanze di classe).

Costruttori di classi

Un costruttore di classe è un metodo con lo stesso nome del tipo (i metodi del costruttore usano lo stesso nome della classe).

Esistono due tipi di costruttori di classi:

  • Costruttori di istanze. I costruttori di istanze vengono usati per creare e inizializzare le variabili di campo dell'istanza quando viene creato un oggetto.
  • Costruttori statici. I costruttori statici vengono usati per inizializzare dati statici o per eseguire una determinata azione che deve essere eseguita una sola volta. I costruttori statici vengono chiamati automaticamente prima della creazione della prima istanza o di tutti i membri statici a cui viene fatto riferimento.

I costruttori di classi sono costruttori di istanze per impostazione predefinita.

Sintassi dei costruttori di istanze

Un costruttore di istanze viene dichiarato usando lo stesso nome della classe e non include un tipo restituito. La firma del metodo del costruttore può includere un modificatore di accesso facoltativo, il nome del metodo e il relativo elenco di parametri. Le firme del metodo di un costruttore non includono un tipo restituito.

L'esempio seguente mostra un costruttore semplice per una classe denominata Person:


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

    }

    // Remaining implementation of Person class.
}

Le classi possono avere più costruttori. Quando una classe ha più di un costruttore, generalmente i costruttori accettano argomenti diversi.

L'esempio seguente mostra una classe denominata Person con due costruttori.


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

Creare un'istanza di oggetti usando costruttori di classi

Quando viene creata un'istanza di un oggetto usando la parola chiave new, il runtime .NET chiama il costruttore di istanza associato nella definizione della classe e alloca la memoria per l'oggetto.

Nel frammento di codice seguente, la classe Person definisce un costruttore di istanze semplice. La classe Program include un metodo Main che usa l'operatore new per creare un'istanza di Person denominata person1. Il runtime richiama il costruttore Person immediatamente dopo l'allocazione della memoria per il nuovo oggetto.


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

Costruttori con e senza parametri

Un costruttore che non accetta parametri viene chiamato costruttore senza parametri. Il runtime richiama il costruttore senza parametri quando viene creata un'istanza di un oggetto usando l'operatore new e nessun argomento viene fornito al costruttore.

Annotazioni

A meno che la classe non sia statica, alle classi senza costruttori viene assegnato un costruttore pubblico senza parametri dal compilatore C# per abilitare la creazione di istanze della classe.

Spesso le classi definiscono costruttori che accettano parametri. I costruttori che accettano parametri devono essere chiamati usando l'operatore new o un'istruzione base. Le classi possono definire uno o più costruttori.

Il frammento di codice seguente mostra una classe denominata Person con tre costruttori:


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

Classi senza costruttori

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

Questo costruttore inizializza i campi e le proprietà dell'istanza in base agli inizializzatori corrispondenti. Se un campo o una proprietà non dispone 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.

Inizializzare i dati della classe usando i parametri del costruttore

I parametri passati a un costruttore sono locali al costruttore. I parametri vengono spesso usati per inizializzare i campi dati di una classe.

Il frammento di codice seguente mostra una classe denominata Person con costruttori che inizializzano i campi personName e 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}");
    }
}

Nell'esempio precedente la classe Person viene definita con tre costruttori. Il primo costruttore inizializza i campi personName e personAge per "unknown". Il secondo costruttore inizializza il campo personName sul valore passato nel parametro name e il campo personAge per "unknown". Il terzo costruttore inizializza rispettivamente i campi personName e personAge ai valori passati rispettivamente nei parametri name e age.

Poiché i campi sono pubblici, è possibile accedervi direttamente dal metodo Main. Quando viene eseguito il codice, viene generato l'output seguente:


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

Definizioni del corpo dell'espressione

Se un costruttore può essere implementato come singola istruzione, è possibile usare una definizione del corpo dell'espressione per assegnare un parametro a un membro di classe quando viene implementato il costruttore.

Ad esempio, il costruttore seguente inizializza il campo modelName con il valore passato al parametro model:


public class Car
{
    public string modelName;

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

La classe Car ha un singolo campo pubblico, modelName, che è di tipo string. Il campo modelName è destinato a memorizzare il nome del modello di auto.

La classe Car include anche un costruttore che accetta un singolo parametro stringa denominato model. Il costruttore usa una definizione del corpo dell'espressione (indicata dalla sintassi =>) per inizializzare il campo modelName con il valore passato al parametro model. Ciò significa che quando viene creata un'istanza di un nuovo oggetto Car, il campo modelName viene impostato sul valore fornito come argomento per il costruttore.

Come suggerisce il termine espressione, il lato destro dell'operatore => è un'espressione e non è limitato a una semplice istruzione di assegnazione. L'espressione può essere qualsiasi espressione C# valida che restituisce un valore.

Il frammento di codice seguente illustra come implementare una definizione del corpo dell'espressione che esegue un calcolo semplice:


public class Employee
{
    public int Salary;

    public Employee() { }

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

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

Questa classe può essere creata usando una delle istruzioni seguenti:


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

Costruttori statici

Un costruttore statico viene usato per inizializzare tutti i dati statici o per eseguire una determinata azione che deve essere eseguita una sola volta. Viene chiamato automaticamente prima che ne venga creata la prima istanza o venga fatto riferimento a qualsiasi membro statico. Un costruttore statico viene chiamato al massimo una volta.

Il frammento di codice seguente mostra una versione aggiornata della classe Person che implementa campi statici e un costruttore statico:


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 aggiornata include due campi di istanza, personName e personAge, entrambi di tipo string. Questi campi archiviano rispettivamente il nome e l'età di una persona.

La classe definisce anche due campi statici, defaultName e defaultAge, anche di tipo string. I campi statici vengono condivisi tra tutte le istanze della classe e vengono inizializzati una sola volta. In questo caso, i campi statici vengono usati per fornire valori predefiniti per i campi personName e personAge.

Il costruttore statico static Person() è responsabile dell'inizializzazione dei campi statici. Imposta defaultName su "sconosciuto" e defaultAge su "unknown". Il costruttore statico viene chiamato automaticamente prima della creazione di qualsiasi istanza di classe o membro statico.

La classe Person include tre costruttori di istanza:

Il costruttore senza parametri public Person() inizializza i campi personName e personAge con i valori dei campi statici defaultName e defaultAge. Ciò significa che se non vengono forniti argomenti durante la creazione di un Person oggetto, i valori predefiniti "sconosciuti" vengono usati sia per il nome che per l'età.

Il costruttore public Person(string name) accetta un singolo parametro, name, e inizializza il campo personName con questo valore. Il campo personAge viene inizializzato con il valore del campo statico defaultAge. Questo costruttore consente la creazione di un oggetto Person con un nome specificato durante l'utilizzo dell'età predefinita.

Il costruttore public Person(string name, int age) accetta due parametri, name e age. Inizializza il campo personName con il valore del parametro name e il campo personAge con la rappresentazione di stringa del parametro età. Questo costruttore consente la creazione di un oggetto Person con name e age specificati.

Proprietà dei costruttori statici

I costruttori statici hanno le proprietà seguenti:

  • Un costruttore statico non può avere modificatori di accesso né parametri.

  • Una classe può avere un solo costruttore statico.

  • I costruttori statici non possono essere ereditati o sovraccaricati.

  • Un costruttore statico non può essere chiamato direttamente ed è progettato esclusivamente per essere chiamato da Common Language Runtime (CLR). Viene richiamato automaticamente.

  • L'utente non può controllare in alcun modo il momento in cui il costruttore statico viene eseguito nel programma.

  • Un costruttore statico viene chiamato automaticamente. Inizializza la classe prima della creazione della prima istanza o prima che sia fatto riferimento ai membri statici dichiarati in tale classe (non le relative classi di base). Un costruttore statico viene eseguito prima di un costruttore di istanza. Se gli inizializzatori di variabili di campo statici sono presenti nella classe del costruttore statico, vengono eseguiti nell'ordine testuale in cui vengono visualizzati nella dichiarazione di classe. Gli inizializzatori vengono eseguiti immediatamente prima del costruttore statico.

  • Se non si fornisce un costruttore statico per inizializzare i campi statici, tutti i campi statici vengono inizializzati sul valore predefinito.

  • Se un costruttore statico genera un'eccezione, il runtime non lo richiama una seconda volta e il tipo rimane non inizializzato per la durata del dominio applicazione. Viene più comunemente generata un'eccezione TypeInitializationException quando un costruttore statico non è in grado di creare un'istanza di un tipo o per un'eccezione non gestita che si verifica all'interno di un costruttore statico. Per i costruttori statici che non sono definiti in modo esplicito nel codice sorgente, la risoluzione dei problemi può richiedere l'ispezione del codice del linguaggio intermedio (IL).

  • La presenza di un costruttore statico impedisce l'aggiunta dell'attributo di tipo BeforeFieldInit. Ciò limita l'ottimizzazione in fase di esecuzione.

  • Un campo dichiarato come static readonly può essere assegnato solo come parte della relativa dichiarazione o in un costruttore statico. Quando non è richiesto un costruttore statico esplicito, inizializzare i campi statici in fase di dichiarazione, anziché tramite un costruttore statico per una migliore ottimizzazione in fase di esecuzione.

  • Il runtime chiama un costruttore statico non più di una volta in un singolo dominio applicazione. Tale chiamata viene eseguita in un'area bloccata in base al tipo specifico della classe. Non sono necessari meccanismi di blocco aggiuntivi nel corpo di un costruttore statico.

Annotazioni

Anche se non è direttamente accessibile, la presenza di un costruttore statico esplicito deve essere documentata per facilitare la risoluzione dei problemi relativi alle eccezioni di inizializzazione.