Condividi tramite


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ò avere init come modificatore di accesso. Tale funzione di accesso set può essere chiamata solo da un inizializzatore di oggetto o dai costruttori del tipo. È più restrittivo rispetto private alla funzione di accesso set.
  • Una proprietà implementata automaticamente può dichiarare una get funzione di accesso senza una set funzione di accesso. In tal caso, il compilatore consente di chiamare la funzione di accesso set solo dai costruttori del tipo. È più restrittivo rispetto alla funzione di accesso init nella funzione di accesso set.

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 o init.
  • Le proprietà possono essere di lettura/scrittura con entrambe le funzione di accesso get e set, di sola lettura con la funzione di accesso get e senza la funzione di accesso set o di sola scrittura con la funzione di accesso set e senza la funzione di accesso get. 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#.

Vedi anche