Introduzione alle classi

Tipi di riferimento

Un tipo definito come class è un tipo riferimento. In fase di esecuzione, quando si dichiara una variabile di un tipo riferimento, la variabile contiene il valore null fino a quando non si crea in modo esplicito un'istanza della classe usando l'operatore new o fino a quando non le viene assegnato un oggetto di tipo compatibile, creato altrove, come illustrato nell'esempio seguente:

//Declaring an object of type MyClass.
MyClass mc = new MyClass();

//Declaring another object of the same type, assigning it the value of the first object.
MyClass mc2 = mc;

Quando viene creato l'oggetto, una quantità di memoria sufficiente viene allocata nell'heap gestito per l'oggetto specifico e la variabile mantiene solo un riferimento al percorso dell'oggetto. La memoria usata da un oggetto viene recuperata dalla funzionalità di gestione automatica della memoria del CLR, nota come Garbage Collection. Per altre informazioni sulla Garbage Collection, vedere Gestione automatica della memoria e Garbage Collection.

Dichiarazione di classi

Le classi vengono dichiarate usando la parola chiave class seguita da un identificatore univoco, come illustrato nell'esempio seguente:

//[access modifier] - [class] - [identifier]
public class Customer
{
   // Fields, properties, methods and events go here...
}

Un modificatore di accesso facoltativo precede la parola chiave class. L'accesso predefinito per un tipo class è internal. Poiché in questo caso viene usata la parola chiave public, chiunque può creare istanze di questa classe. Il nome della classe segue la parola chiave class. Il nome della classe deve essere un nome di identificatore C# valido. Il resto della definizione è il corpo della classe, in cui vengono definiti il comportamento e i dati. I campi, le proprietà, i metodi e gli eventi in una classe vengono collettivamente definiti membri della classe.

Creazione di oggetti

Anche se vengono talvolta usati in modo intercambiabile, una classe e un oggetto sono elementi diversi. Una classe definisce un tipo di oggetto, ma non è un oggetto in sé. Un oggetto è un'entità concreta ed è basato su una classe. Talvolta si fa riferimento all'oggetto come istanza di una classe.

È possibile creare oggetti usando la parola chiave new seguita dal nome della classe, nel modo seguente:

Customer object1 = new Customer();

Quando viene creata un'istanza di una classe, viene passato al programmatore un riferimento all'oggetto. Nell'esempio precedente, object1 è un riferimento a un oggetto basato su Customer. Questo riferimento si riferisce al nuovo oggetto, ma non contiene i dati dell'oggetto stesso. Infatti, è possibile creare un riferimento all'oggetto senza creare un oggetto:

Customer object2;

Non è consigliabile creare riferimenti a oggetti che non fanno riferimento a un oggetto, perché il tentativo di accedere a un oggetto tramite un riferimento di questo tipo avrà esito negativo in fase di esecuzione. Un riferimento può fare riferimento a un oggetto, creando un nuovo oggetto oppure assegnandolo a un oggetto esistente, come illustrato di seguito:

Customer object3 = new Customer();
Customer object4 = object3;

Questo codice crea due riferimenti a oggetti che fanno entrambi riferimento allo stesso oggetto. Tutte le modifiche effettuate all'oggetto tramite object3 si riflettono tuttavia nei successivi usi di object4. Poiché gli oggetti che si basano su classi vengono indicati tramite riferimenti, le classi sono note come tipi di riferimento.

Costruttori e inizializzazione

Nelle sezioni precedenti è stata introdotta la sintassi per dichiarare un tipo di classe e creare un'istanza di tale tipo. Quando si crea un'istanza di un tipo, assicurarsi di garantire che i relativi campi e proprietà vengano inizializzati con valori utili. Esistono diversi modi per inizializzare i valori:

  • Accettare i valori predefiniti
  • Inizializzatori di campo
  • Parametri del costruttore
  • Inizializzatori di oggetto

Ogni tipo .NET ha un valore predefinito. In genere, questo valore è 0 per i tipi di numero e null per tutti i tipi di riferimento. È possibile fare affidamento su tale valore predefinito quando è ragionevole nella propria app.

Quando l'impostazione predefinita di .NET non è il valore corretto, è possibile impostare un valore iniziale usando un inizializzatore di campo:

public class Container
{
    // Initialize capacity field to a default value of 10:
    private int _capacity = 10;
}

È possibile richiedere ai chiamanti di fornire un valore iniziale definendo un costruttore responsabile dell'impostazione del valore iniziale:

public class Container
{
    private int _capacity;

    public Container(int capacity) => _capacity = capacity;
}

A partire da C# 12, è possibile definire un costruttore primario come parte della dichiarazione di classe:

public class Container(int capacity)
{
    private int _capacity = capacity;
}

L'aggiunta di parametri al nome della classe definisce il costruttore primario. Tali parametri sono disponibili nel corpo della classe, che include i relativi membri. È possibile usarli per inizializzare i campi o in qualsiasi altra posizione in cui sono necessari.

È anche possibile usare il modificatore required in una proprietà e consentire ai chiamanti di usare un inizializzatore di oggetto per impostare il valore iniziale della proprietà:

public class Person
{
    public required string LastName { get; set; }
    public required string FirstName { get; set; }
}

L'aggiunta della parola chiave required impone ai chiamanti di impostare tali proprietà come parte di un'espressione new:

var p1 = new Person(); // Error! Required properties not set
var p2 = new Person() { FirstName = "Grace", LastName = "Hopper" };

Ereditarietà delle classi

Le classi supportano completamente l'ereditarietà, una caratteristica fondamentale nella programmazione orientata a oggetti. Quando si crea una classe, è possibile ereditare da qualsiasi altra classe non definita come sealed. Altre classi possono ereditare dalla classe ed eseguire l'override dei metodi virtuali della classe. Inoltre, è possibile implementare una o più interfacce.

L'ereditarietà si ottiene usando una derivazione, vale a dire che una classe viene dichiarata usando una classe di base da cui eredita dati e comportamento. Una classe di base viene specificata tramite l'aggiunta di due punti e il nome della classe di base dopo il nome della classe derivata, nel modo seguente:

public class Manager : Employee
{
    // Employee fields, properties, methods and events are inherited
    // New Manager fields, properties, methods and events go here...
}

Quando una dichiarazione di classe include una classe base, eredita tutti i membri della classe base, tranne i costruttori. Per altre informazioni, vedere Ereditarietà.

Una classe in C# può ereditare direttamente solo da una classe di base. Tuttavia, poiché una classe di base può ereditare da un'altra classe, una classe può ereditare indirettamente più classi di base. Inoltre, una classe può implementare direttamente una o più interfacce. Per altre informazioni, vedere Interfacce.

Una classe può essere dichiarata come abstract. Una classe astratta contiene metodi astratti che hanno una definizione di firma, ma senza implementazione. Non è possibile creare un'istanza di classi astratte. Le classi astratte possono essere usate solo tramite classi derivate che implementano i metodi astratti. Al contrario, una classe sealed non consente ad altre classi di derivare da essa. Per altre informazioni, vedere Classi e membri delle classi astratte e sealed.

Le definizioni di classe possono essere suddivise tra file di origine diversa. Per altre informazioni, vedere Classi e metodi parziali.

Specifiche del linguaggio C#

Per altre informazioni, vedere la specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.