ProprietàProperties

Le proprietà sono elementi fondamentali in C#.Properties are first class citizens in C#. Il linguaggio definisce la sintassi che consente agli sviluppatori di scrivere codice che esprime in modo preciso la finalità della progettazione.The language defines syntax that enables developers to write code that accurately expresses their design intent.

Quando viene eseguito l'accesso, le proprietà si comportano come i campi.Properties behave like fields when they are accessed. Tuttavia, a differenza dei campi, le proprietà vengono implementate con funzioni di accesso che definiscono le istruzioni eseguite al momento dell'accesso e dell'assegnazione della proprietà.However, unlike fields, properties are implemented with accessors that define the statements executed when a property is accessed or assigned.

Sintassi delle proprietàProperty syntax

La sintassi delle proprietà è un'estensione naturale dei campi.The syntax for properties is a natural extension to fields. Un campo definisce una posizione di archiviazione:A field defines a storage location:

public class Person
{
    public string FirstName;
    // remaining implementation removed from listing
}

La definizione di una proprietà contiene le dichiarazioni di una funzione di accesso get e set che recupera e assegna il valore della proprietà:A property definition contains declarations for a get and set accessor that retrieves and assigns the value of that property:

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

    // remaining implementation removed from listing
}

La sintassi illustrata sopra è la sintassi della proprietà automatica.The syntax shown above is the auto property syntax. Il compilatore genera la posizione di archiviazione per il campo che esegue il backup della proprietà.The compiler generates the storage location for the field that backs up the property. Il compilatore implementa anche il corpo delle funzioni di accesso get e set.The compiler also implements the body of the get and set accessors.

In alcuni casi è necessario inizializzare una proprietà con un valore diverso da quello predefinito per il suo tipo.Sometimes, you need to initialize a property to a value other than the default for its type. In C# questa operazione è possibile impostando un valore dopo la parentesi graffa chiusa della proprietà.C# enables that by setting a value after the closing brace for the property. Per la proprietà FirstName è preferibile usare come valore iniziale una stringa vuota anziché null.You may prefer the initial value for the FirstName property to be the empty string rather than null. Ecco come eseguire questa operazione:You would specify that as shown below:

public class Person
{
    public string FirstName { get; set; } = string.Empty;

    // remaining implementation removed from listing
}

L'inizializzazione specifica è particolarmente utile per le proprietà di sola lettura, come si vedrà più avanti in questo articolo.Specific initialization is most useful for read-only properties, as you'll see later in this article.

È anche possibile definire l'archiviazione manualmente, come illustrato di seguito:You can also define the storage yourself, as shown below:

public class Person
{
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
    private string firstName;
    // remaining implementation removed from listing
}

Se l'implementazione di una proprietà corrisponde a un'espressione singola, è possibile usare membri con corpo di espressione per il getter o il setter:When a property implementation is a single expression, you can use expression-bodied members for the getter or setter:

public class Person
{
    public string FirstName
    {
        get => firstName;
        set => firstName = value;
    }
    private string firstName;
    // remaining implementation removed from listing
}

Questa sintassi semplificata verrà usata ovunque applicabile in questo articolo.This simplified syntax will be used where applicable throughout this article.

La definizione della proprietà illustrata sopra è una proprietà di lettura/scrittura.The property definition shown above is a read-write property. Si noti la parola chiave value nella funzione di accesso impostata.Notice the keyword value in the set accessor. La funzione di accesso set ha sempre un singolo parametro denominato value.The set accessor always has a single parameter named value. La funzione di accesso get deve restituire un valore che è convertibile nel tipo della proprietà (in questo esempio string).The get accessor must return a value that is convertible to the type of the property (string in this example).

Queste sono le nozioni di basi sulla sintassi.That's the basics of the syntax. Esistono numerose varianti che supportano un'ampia gamma di termini di progettazione diversi.There are many different variations that support a variety of different design idioms. Di seguito sono descritte le opzioni di sintassi per ogni termine.Let's explore, and learn the syntax options for each.

ScenariScenarios

Gli esempi precedenti hanno illustrato uno dei casi più semplici di definizione delle proprietà, ovvero una proprietà di lettura/scrittura senza convalida.The examples above showed one of the simplest cases of property definition: a read-write property with no validation. Scrivendo il codice desiderato nelle funzioni di accesso get e set è possibile creare scenari diversi.By writing the code you want in the get and set accessors, you can create many different scenarios.

ConvalidaValidation

È possibile scrivere codice nella funzione di accesso set per assicurarsi che i valori rappresentati da una proprietà siano sempre validi.You can write code in the set accessor to ensure that the values represented by a property are always valid. Ad esempio, si supponga che una regola per la classe Person preveda che il nome non può essere vuoto o uno spazio vuoto.For example, suppose one rule for the Person class is that the name cannot be blank or white space. Sarà necessario scrivere:You would write that as follows:

public class Person
{
    public string FirstName
    {
        get => firstName;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("First name must not be blank");
            firstName = value;
        }
    }
    private string firstName;
    // remaining implementation removed from listing
}

L'esempio precedente può essere semplificato usando un' throw espressione come parte della convalida del setter di proprietà:The preceding example can be simplified by using a throw expression as part of the property setter validation:

public class Person
{
    public string FirstName
    {
        get => firstName;
        set => firstName = (!string.IsNullOrWhiteSpace(value)) ? value : throw new ArgumentException("First name must not be blank");
    }
    private string firstName;
    // remaining implementation removed from listing
}

L'esempio precedente applica la regola che prevede che il nome non può essere vuoto o uno spazio vuoto.The example above enforces the rule that the first name must not be blank or white space. Se lo sviluppatore scriveIf a developer writes

hero.FirstName = "";

L'assegnazione genera ArgumentException.That assignment throws an ArgumentException. Poiché la funzione di accesso di un insieme di proprietà deve avere un tipo restituito void, gli errori vengono segnalati nella funzione di accesso dell'insieme generando un'eccezione.Because a property set accessor must have a void return type, you report errors in the set accessor by throwing an exception.

La stessa sintassi può essere estesa a ogni elemento necessario nello scenario.You can extend this same syntax to anything needed in your scenario. È possibile controllare le relazioni tra le diverse proprietà o eseguire la convalida in base a qualsiasi condizione esterna.You can check the relationships between different properties, or validate against any external conditions. Tutte le istruzioni C# valide sono valide nella funzione di accesso di una proprietà.Any valid C# statements are valid in a property accessor.

Sola letturaRead-only

Le definizioni di proprietà descritte fino a questo punto si riferiscono a proprietà di lettura/scrittura con funzioni di accesso pubbliche.Up to this point, all the property definitions you have seen are read/write properties with public accessors. Non si tratta tuttavia dell'unica accessibilità valida per le proprietà.That's not the only valid accessibility for properties. È possibile creare proprietà di sola lettura o assegnare un'accessibilità diversa alle funzioni di accesso set e get.You can create read-only properties, or give different accessibility to the set and get accessors. Si supponga che la classe Person debba consentire soltanto la modifica del valore della proprietà FirstName da altri metodi della classe.Suppose that your Person class should only enable changing the value of the FirstName property from other methods in that class. È possibile assegnare alla funzione di accesso set l'accessibilità private anziché public:You could give the set accessor private accessibility instead of public:

public class Person
{
    public string FirstName { get; private set; }

    // remaining implementation removed from listing
}

L'accesso alla proprietà FirstName potrà quindi essere eseguito da qualsiasi codice, ma la proprietà potrà essere assegnata soltanto da altro codice della classe Person.Now, the FirstName property can be accessed from any code, but it can only be assigned from other code in the Person class.

È possibile aggiungere qualsiasi modificatore di accesso restrittivo alle funzioni di accesso set o get.You can add any restrictive access modifier to either the set or get accessors. Il modificatore di accesso inserito nella singola funzione di accesso deve essere più restrittivo del modificatore di accesso della definizione della proprietà.Any access modifier you place on the individual accessor must be more limited than the access modifier on the property definition. Il codice precedente è valido poiché la proprietà FirstName è public e la funzione di accesso set è private.The above is legal because the FirstName property is public, but the set accessor is private. Non è possibile dichiarare una proprietà private con una funzione di accesso public.You could not declare a private property with a public accessor. Le dichiarazioni di proprietà possono anche essere dichiarate protected, internal, protected internal o anche private.Property declarations can also be declared protected, internal, protected internal, or, even private.

È anche consentito inserire il modificatore più restrittivo nella funzione di accesso get.It is also legal to place the more restrictive modifier on the get accessor. Ad esempio, è possibile avere una proprietà public e limitare la funzione di accesso get a private.For example, you could have a public property, but restrict the get accessor to private. Questo scenario viene usato raramente.That scenario is rarely done in practice.

È anche possibile limitare le modifiche a una proprietà, in modo che possa essere impostata solo in un costruttore o in un inizializzatore di proprietà.You can also restrict modifications to a property so that it can only be set in a constructor or a property initializer. È possibile modificare in tal senso la classe Person come segue:You can modify the Person class so as follows:

public class Person
{
    public Person(string firstName) => this.FirstName = firstName;

    public string FirstName { get; }

    // remaining implementation removed from listing
}

Questa funzionalità viene in genere usata per l'inizializzazione delle raccolte esposte come proprietà di sola lettura:This feature is most commonly used for initializing collections that are exposed as read-only properties:

public class Measurements
{
    public ICollection<DataPoint> points { get; } = new List<DataPoint>();
}

Proprietà calcolateComputed properties

Una proprietà non si limita a restituire il valore di un campo membro.A property does not need to simply return the value of a member field. È possibile creare proprietà che restituiscono un valore calcolato.You can create properties that return a computed value. Espandere l'oggetto Person per restituire il nome completo, calcolato concatenando il nome e il cognome:Let's expand the Person object to return the full name, computed by concatenating the first and last names:

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

    public string LastName { get; set; }

    public string FullName { get { return $"{FirstName} {LastName}"; } }
}

L'esempio precedente usa la funzionalità di interpolazione delle stringhe per creare la stringa formattata per il nome completo.The example above uses the string interpolation feature to create the formatted string for the full name.

È anche possibile usare un membro con corpo di espressione che consente di creare la proprietà calcolata FullName in modo più conciso:You can also use an expression-bodied member, which provides a more succinct way to create the computed FullName property:

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

    public string LastName { get; set; }

    public string FullName => $"{FirstName} {LastName}";
}

I membri con corpo di espressione usano la sintassi delle espressioni lambda per definire i metodi che contengono una singola espressione.Expression-bodied members use the lambda expression syntax to define methods that contain a single expression. In questo caso, l'espressione restituisce il nome completo per l'oggetto person.Here, that expression returns the full name for the person object.

Proprietà con valutazione memorizzata nella cacheCached evaluated properties

È possibile unire il concetto di proprietà calcolata al concetto di archiviazione e creare una proprietà con valutazione memorizzata nella cache.You can mix the concept of a computed property with storage and create a cached evaluated property. Ad esempio, è possibile aggiornare la proprietà FullName in modo che venga eseguita soltanto la formattazione della stringa al primo accesso:For example, you could update the FullName property so that the string formatting only happened the first time it was accessed:

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

    public string LastName { get; set; }

    private string fullName;
    public string FullName
    {
        get
        {
            if (fullName == null)
                fullName = $"{FirstName} {LastName}";
            return fullName;
        }
    }
}

Il codice riportato sopra tuttavia contiene un bug.The above code contains a bug though. Se il codice aggiorna il valore della proprietà FirstName o LastName, il campo fullName valutato in precedenza non è valido.If code updates the value of either the FirstName or LastName property, the previously evaluated fullName field is invalid. Modificare le funzioni di accesso set della proprietà FirstName e LastName in modo che il campo fullName venga calcolato nuovamente:You modify the set accessors of the FirstName and LastName property so that the fullName field is calculated again:

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 == null)
                fullName = $"{FirstName} {LastName}";
            return fullName;
        }
    }
}

La versione finale valuta la proprietà FullName solo quando necessario.This final version evaluates the FullName property only when needed. Se è valida, viene usata la versione calcolata in precedenza.If the previously calculated version is valid, it's used. Se un'altra modifica dello stato annulla la validità della versione calcolata in precedenza, la versione verrà ricalcolata.If another state change invalidates the previously calculated version, it will be recalculated. Non è necessario che gli sviluppatori che usano questa classe siano a conoscenza dei dettagli dell'implementazione.Developers that use this class do not need to know the details of the implementation. Nessuna di queste modifiche interne ha effetto sull'uso dell'oggetto Person.None of these internal changes affect the use of the Person object. Questo è il motivo principale dell'uso delle proprietà per l'esposizione dei membri dati di un oggetto.That's the key reason for using Properties to expose data members of an object.

Collegamento di attributi a proprietà implementate automaticamenteAttaching attributes to auto-implemented properties

A partire da C# 7.3 è possibile collegare gli attributi campo al campo sottostante generato dal compilatore nelle proprietà implementate automaticamente.Beginning with C# 7.3, field attributes can be attached to the compiler generated backing field in auto-implemented properties. Si consideri ad esempio una revisione della classe Person che aggiunge una proprietà Id al valore intero univoco.For example, consider a revision to the Person class that adds a unique integer Id property. È possibile scrivere la Id proprietà usando una proprietà implementata automaticamente, ma la progettazione non chiama per la persistenza della Id Proprietà.You write the Id property using an auto-implemented property, but your design does not call for persisting the Id property. La classe NonSerializedAttribute può essere collegata soltanto a campi, non a proprietà.The NonSerializedAttribute can only be attached to fields, not properties. È possibile collegare la classe NonSerializedAttribute al campo sottostante per la proprietà Id usando l'identificatore field: dell'attributo, come illustrato nell'esempio seguente:You can attach the NonSerializedAttribute to the backing field for the Id property by using the field: specifier on the attribute, as shown in the following example:

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

    public string LastName { get; set; }

    [field:NonSerialized]
    public int Id { get; set; }

    public string FullName => $"{FirstName} {LastName}";
}

Questa tecnica funziona con qualsiasi attributo collegato al campo sottostante nella proprietà implementate automaticamente.This technique works for any attribute you attach to the backing field on the auto-implemented property.

Implementazione di NotifyPropertyChangedImplementing INotifyPropertyChanged

Uno scenario finale in cui è necessario scrivere codice nella funzione di accesso di una proprietà è quello finalizzato al supporto dell'interfaccia INotifyPropertyChanged usata per inviare ai client di data binding la notifica della modifica di un valore.A final scenario where you need to write code in a property accessor is to support the INotifyPropertyChanged interface used to notify data binding clients that a value has changed. Quando viene modificato il valore di una proprietà, l'oggetto genera l'evento INotifyPropertyChanged.PropertyChanged per indicare la modifica.When the value of a property changes, the object raises the INotifyPropertyChanged.PropertyChanged event to indicate the change. Le librerie di data binding aggiornano a loro volta gli elementi visualizzati in base alla modifica.The data binding libraries, in turn, update display elements based on that change. Il codice seguente illustra come implementare INotifyPropertyChanged per la proprietà FirstName della classe person.The code below shows how you would implement INotifyPropertyChanged for the FirstName property of this person class.

public class Person : INotifyPropertyChanged
{
    public string FirstName
    {
        get => firstName;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("First name must not be blank");
            if (value != firstName)
            {
                PropertyChanged?.Invoke(this,
                    new PropertyChangedEventArgs(nameof(FirstName)));
            }
            firstName = value;
        }
    }
    private string firstName;

    public event PropertyChangedEventHandler PropertyChanged;
    // remaining implementation removed from listing
}

L'operatore ?. è chiamato operatore condizionale Null.The ?. operator is called the null conditional operator. L'operatore cerca un riferimento Null prima di eseguire la valutazione della parte destra dell'operatore.It checks for a null reference before evaluating the right side of the operator. Se non vengono trovati sottoscrittori dell'evento PropertyChanged, il codice che genera l'evento non viene eseguito.The end result is that if there are no subscribers to the PropertyChanged event, the code to raise the event doesn't execute. In questo caso, verrà generato NullReferenceException senza eseguire il controllo.It would throw a NullReferenceException without this check in that case. Per altre informazioni, vedere events.For more information, see events. Questo esempio usa anche il nuovo operatore nameof per convertire il simbolo del nome della proprietà nella rappresentazione di testo.This example also uses the new nameof operator to convert from the property name symbol to its text representation. L'uso di nameof può ridurre gli errori nel caso in cui il nome della proprietà sia stato digitato erroneamente.Using nameof can reduce errors where you have mistyped the name of the property.

L'implementazione di INotifyPropertyChanged è quindi un esempio di caso in cui è possibile scrivere codice nelle funzioni di accesso per supportare gli scenari necessari.Again, implementing INotifyPropertyChanged is an example of a case where you can write code in your accessors to support the scenarios you need.

RiepilogoSumming up

Le proprietà sono una forma di campi intelligenti in una classe o un oggetto.Properties are a form of smart fields in a class or object. All'esterno dell'oggetto, vengono visualizzate come campi dell'oggetto.From outside the object, they appear like fields in the object. Tuttavia, le proprietà possono essere implementate usando l'intera gamma di funzionalità di C#.However, properties can be implemented using the full palette of C# functionality. È possibile specificare la convalida, un'accessibilità diversa, la valutazione lazy o tutti i requisiti necessari negli scenari.You can provide validation, different accessibility, lazy evaluation, or any requirements your scenarios need.