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.