Proprietà (Guida per programmatori C#)
Una proprietà è un membro che fornisce un meccanismo flessibile per leggere, scrivere o calcolare il valore di un campo dati. Le proprietà vengono visualizzate come membri dati pubblici, ma vengono implementate come metodi speciali denominati funzioni di accesso. Questa funzione consente ai chiamanti di accedere facilmente ai dati e di alzare di livello la sicurezza e la flessibilità dei dati. La sintassi delle proprietà è un'estensione naturale dei campi. Un campo definisce una posizione di archiviazione:
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
Proprietà implementate automaticamente
La definizione di una proprietà contiene le dichiarazioni di una funzione di accesso get
e set
che recupera e assegna il valore della proprietà:
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
L'esempio precedente mostra una proprietà implementata automaticamente. Il compilatore genera un campo sottostante nascosto per la proprietà . Il compilatore implementa anche il corpo delle funzioni di accesso get
e set
. Tutti gli attributi vengono applicati alla proprietà implementata automaticamente. È possibile applicare l'attributo al campo sottostante generato dal compilatore specificando il tag field:
sull'attributo .
È possibile inizializzare una proprietà su un valore diverso da quello predefinito impostando un valore dopo la parentesi graffa di chiusura per la proprietà . Per la proprietà FirstName
, è preferibile usare come valore iniziale una stringa vuota anziché null
. È necessario specificarlo, come illustrato nel codice seguente:
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
Controllo di accesso
Gli esempi precedenti hanno mostrato proprietà di lettura/scrittura. È possibile anche creare proprietà di sola lettura o assegnare un'accessibilità diversa alle funzioni di accesso set e get. Si supponga che la classe Person
debba consentire soltanto la modifica del valore della proprietà FirstName
da altri metodi della classe. È possibile assegnare alla funzione di accesso set l'accessibilità private
anziché public
:
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
La proprietà FirstName
può essere letta da qualsiasi codice, ma può essere assegnata soltanto dal codice della classe Person
.
È possibile aggiungere qualsiasi modificatore di accesso restrittivo alle funzioni di accesso set o get. Un modificatore di accesso su una singola funzione di accesso deve essere più restrittivo rispetto all'accesso della proprietà. Il codice precedente è valido poiché la proprietà FirstName
è public
e la funzione di accesso set è private
. Non è possibile dichiarare una proprietà private
con una funzione di accesso public
. Le dichiarazioni di proprietà possono anche essere dichiarate protected
, internal
, protected internal
o anche private
.
Esistono due modificatori di accesso speciali per le funzioni di accesso set
:
- Una funzione di accesso
set
può avereinit
come modificatore di accesso. Tale funzione di accessoset
può essere chiamata solo da un inizializzatore di oggetto o dai costruttori del tipo. È più restrittivo rispettoprivate
alla funzione di accessoset
. - Una proprietà implementata automaticamente può dichiarare una
get
funzione di accesso senza unaset
funzione di accesso. In tal caso, il compilatore consente di chiamare la funzione di accessoset
solo dai costruttori del tipo. È più restrittivo rispetto alla funzione di accessoinit
nella funzione di accessoset
.
Modificare la classe Person
, come indicato di seguito:
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
L'esempio precedente richiede ai chiamanti di usare il costruttore che include il parametro FirstName
. I chiamanti non possono usare inizializzatori di oggetti per assegnare un valore alla proprietà. Per supportare gli inizializzatori, è possibile sostituire la funzione di accesso set
con init
, come illustrato nel codice seguente:
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
Questi modificatori vengono spesso usati con il modificatore required
per forzare l'inizializzazione corretta.
Proprietà obbligatorie
Nell'esempio precedente, un chiamante può creare un Person
utilizzando il costruttore predefinito, senza impostare la proprietà FirstName
. La proprietà ha modificato il tipo in una stringa nullable. A partire da C# 11, è possibile richiedere ai chiamanti di impostare una proprietà:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
Il codice precedente apporta due modifiche alla classe Person
. In primo luogo, la dichiarazione di proprietà FirstName
include il modificatore required
. Questo significa che qualsiasi codice che crea un nuovo Person
deve impostare questa proprietà utilizzando un inizializzatore di oggetto. In secondo luogo, il costruttore che accetta un parametro firstName
ha l'attributo System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute. Quest'ultimo informa il compilatore che questo costruttore imposta tutti i membri required
. I chiamanti che usano questo costruttore non sono necessari per impostare le proprietà required
con un inizializzatore di oggetto.
Importante
Non confondere un tipo required
con uno che non ammette i valori Null. Una proprietà required
può essere impostata su null
o default
. Se il tipo non ammette i valori Null, come string
in questi esempi, il compilatore genera un avviso.
var aPerson = new Person("John");
aPerson = new Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
Definizioni del corpo dell'espressione
Le funzioni di accesso alle proprietà sono spesso costituite da istruzioni a riga singola. Le funzioni di accesso assegnano o restituiscono il risultato di un'espressione. È possibile implementare queste proprietà come membri con corpo di espressione. Le definizioni del corpo dell'espressione sono costituite dal token =>
seguito dall'espressione per l'assegnazione o il recupero dalla proprietà.
Le proprietà di sola lettura possono implementare la funzione di accesso get
come membro con corpo di espressione. L'esempio seguente implementa la proprietà Name
di sola lettura come membro con corpo di espressione:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
La proprietà Name
è una proprietà di calcolo. Non esiste alcun campo sottostante per Name
. La proprietà lo calcola ogni volta.
Proprietà con campi sottostanti
È possibile unire il concetto di proprietà calcolata al concetto di campo privato e creare una proprietà con valutazione memorizzata nella cache. Ad esempio, aggiornare la proprietà FullName
in modo che la formattazione della stringa avvenga al primo accesso:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Questa implementazione funziona perché le proprietà FirstName
e LastName
sono di sola lettura. Le persone possono cambiare il nome. L'aggiornamento delle proprietà FirstName
e LastName
per consentire le funzioni di accesso set
richiede di invalidare qualsiasi valore memorizzato nella cache per fullName
. Modificare le funzioni di accesso set
della proprietà FirstName
e LastName
in modo che il campo fullName
venga calcolato nuovamente:
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
La versione finale valuta la proprietà FullName
solo quando necessario. Se valida, viene usata la versione calcolata in precedenza. In caso contrario, il calcolo aggiorna il valore memorizzato nella cache. Non è necessario che gli sviluppatori che usano questa classe siano a conoscenza dei dettagli dell'implementazione. Nessuna di queste modifiche interne ha effetto sull'uso dell'oggetto Person.
A partire da C# 13, è possibile creare proprietà partial
nelle classi partial
. La dichiarazione di implementazione per una partial
proprietà non può essere una proprietà implementata automaticamente. Una proprietà implementata automaticamente usa la stessa sintassi di una dichiarazione di proprietà parziale dichiarante.
Proprietà
Le proprietà sono una forma di campi intelligenti in una classe o un oggetto. All'esterno dell'oggetto, vengono visualizzate come campi dell'oggetto. Tuttavia, le proprietà possono essere implementate usando l'intera gamma di funzionalità di C#. È possibile specificare la convalida, un'accessibilità diversa, la valutazione lazy o tutti i requisiti necessari negli scenari.
- Le proprietà semplici che non richiedono codice della funzione di accesso personalizzato possono essere implementate come definizioni del corpo dell'espressione o come proprietà implementate automaticamente.
- Le proprietà consentono a una classe di esporre un modo pubblico per ottenere e impostare i valori, nascondendo però il codice di implementazione o di verifica.
- Una funzione di accesso della proprietà get viene usata per restituire il valore della proprietà, mentre una funzione di accesso della proprietà set viene usata per assegnare un nuovo valore. Una funzione di accesso alle proprietà init viene usata per assegnare un nuovo valore solo durante la costruzione di oggetti. Queste funzioni di accesso possono avere diversi livelli di accesso. Per altre informazioni, vedere Limitazione dell'accessibilità delle funzioni di accesso.
- La parola chiave value viene usata per definire il valore assegnato dalla funzione di accesso
set
oinit
. - Le proprietà possono essere di lettura/scrittura con entrambe le funzione di accesso
get
eset
, di sola lettura con la funzione di accessoget
e senza la funzione di accessoset
o di sola scrittura con la funzione di accessoset
e senza la funzione di accessoget
. Le proprietà di sola scrittura sono rare.
Specifiche del linguaggio C#
Per altre informazioni, vedere Tipi integrali in Specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.