Utilizzo delle proprietà (Guida per programmatori C#)
Le proprietà combinano gli aspetti sia dei campi che dei metodi. Per l'utente di un oggetto, una proprietà si presenta come un campo; l'accesso alla proprietà richiede la stessa sintassi. Per il responsabile dell'implementazione di una classe, una proprietà è costituita da uno o due blocchi di codice, che rappresentano una funzione di accesso get
e/o una funzione di accesso set
o init
. Il blocco di codice per la funzione di accesso get
viene eseguito al momento della lettura della proprietà, mentre il blocco di codice per la funzione di accesso set
o init
viene eseguito quando si assegna un valore alla proprietà. Una proprietà senza una funzione di accesso set
viene considerata di sola lettura. Una proprietà senza una funzione di accesso get
viene considerata di sola scrittura. Una proprietà con entrambe le funzioni di accesso è di lettura/scrittura. È possibile usare una funzione di accesso init
invece di una funzione di accesso set
per consentire l'impostazione della proprietà durante l'inizializzazione dell'oggetto, ma renderla di sola lettura.
A differenza dei campi, le proprietà non sono classificate come variabili. Non è pertanto possibile passare una proprietà come un parametro ref
o out
.
Le proprietà vengono usate in vari modi:
- Possono convalidare i dati prima di consentire una modifica.
- Possono esporre in modo trasparente i dati in una classe dove tali dati vengono recuperati da un'altra origine, ad esempio un database.
- Possono eseguire un'azione quando i dati vengono modificati, ad esempio la generazione di un evento o la modifica del valore di altri campi.
Le proprietà sono dichiarate nel blocco della classe specificando il livello di accesso del campo, seguito dal tipo della proprietà, seguito dal nome della proprietà, seguito da un blocco di codice che dichiara una funzione di accesso get
e/o una funzione di accesso set
. Ad esempio:
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
In questo esempio, Month
è dichiarato come una proprietà in modo che la funzione di accesso set
possa verificare che il valore Month
sia compreso tra 1 e 12. La proprietà Month
usa un campo privato per tenere traccia del valore effettivo. La posizione reale dei dati di una proprietà è spesso denominata "archivio di backup" della proprietà. Generalmente, le proprietà usano campi privati come archivio di backup. Il campo viene contrassegnato come privato per assicurare che possa essere modificato solo chiamando la proprietà. Per altre informazioni sulle restrizioni di accesso pubblico e privato, vedere Modificatori di accesso. Le proprietà implementate automaticamente forniscono una sintassi semplificata per le dichiarazioni di proprietà semplici. Per altre informazioni, vedere Proprietà implementate automaticamente.
A partire da C# 13, è possibile usare le proprietà supportate dal campo per aggiungere la convalida alla set
funzione di accesso di una proprietà implementata automaticamente, come illustrato nell'esempio seguente:
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
Importante
La field
parola chiave è una funzionalità di anteprima in C# 13. È necessario usare .NET 9 e impostare l'elemento <LangVersion>
su preview
nel file di progetto per usare la field
parola chiave contestuale.
È consigliabile prestare attenzione usando la funzionalità parola field
chiave in una classe con un campo denominato field
. La nuova field
parola chiave ombreggiate un campo denominato field
nell'ambito di una funzione di accesso alle proprietà. È possibile modificare il nome della field
variabile oppure usare il @
token per fare riferimento all'identificatore field
come @field
. Per altre informazioni, leggere la specifica della funzionalità per la field
parola chiave .
Funzione di accesso get
Il corpo della funzione di accesso get
è simile a quello di un metodo. Deve restituire un valore del tipo di proprietà. Il compilatore C# e il compilatore JIT rilevano criteri comuni per l'implementazione della funzione di accesso get
e ottimizzano tali criteri. Ad esempio, una funzione di accesso get
che restituisce un campo senza eseguire alcun calcolo è probabilmente ottimizzata per una lettura in memoria di tale campo. Le proprietà implementate automaticamente seguono questo modello e traggono vantaggio da queste ottimizzazioni. Tuttavia, non è possibile impostare come inline un metodo della funzione di accesso get
virtuale perché il compilatore non può stabilire in fase di compilazione quale metodo potrebbe essere effettivamente chiamato in fase di esecuzione. Nell'esempio seguente viene illustrata una funzione di accesso get
che restituisce il valore di un campo privato _name
:
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
Quando si fa riferimento alla proprietà, tranne che come destinazione di un'assegnazione, viene chiamata la funzione di accesso get
per leggere il valore della proprietà. Ad esempio:
var employee= new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
La funzione di accesso get
deve essere un membro con corpo di espressione oppure terminare con un'istruzione return o throw e il controllo non può passare dal corpo della funzione di accesso.
Avviso
In genere è uno stile di programmazione non valido per modificare lo stato dell'oggetto usando la get
funzione di accesso. Un'eccezione a questa regola è una proprietà valutata differita , in cui il valore di una proprietà viene calcolato solo quando si accede per la prima volta.
La funzione di accesso get
può essere usata per restituire il valore del campo o per calcolarlo e restituirlo. Ad esempio:
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
Nell'esempio precedente, se non si assegna un valore alla proprietà Name
, viene restituito il valore NA
.
Funzione di accesso set
La funzione di accesso set
è simile a un metodo il cui tipo restituito è void. Usa un parametro implicito denominato value
, il cui tipo è il tipo della proprietà. Il compilatore e il compilatore JIT riconoscono anche criteri comuni per una funzione di accesso set
o init
. Questi criteri comuni vengono ottimizzati, scrivendo direttamente la memoria per il campo sottostante. Nell'esempio seguente, viene aggiunta una funzione di accesso set
alla proprietà Name
:
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
Quando si assegna un valore alla proprietà, viene richiamata la funzione di accesso set
tramite un argomento che fornisce il nuovo valore. Ad esempio:
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
È un errore usare il nome del parametro implicito, value
, per una dichiarazione di variabile locale in una funzione di accesso set
.
Funzione di accesso init
Il codice per creare una funzione di accesso init
equivale al codice per creare una funzione di accesso set
, ad eccezione del fatto che si usa la parola chiave init
anziché set
. La differenza è che la funzione di accesso init
può essere usata solo nel costruttore o usando un inizializzatore oggetto.
Osservazioni:
Le proprietà possono essere contrassegnate come public
, private
, protected
, internal
, protected internal
o private protected
. Questi modificatori di accesso definiscono in che modo gli utenti della classe possono accedere alla proprietà. Le funzioni di accesso get
e set
per la stessa proprietà possono avere modificatori di accesso diversi. Ad esempio, get
potrebbe essere public
per consentire l'accesso in sola lettura dall'esterno del tipo, mentre set
potrebbe essere private
o protected
. Per altre informazioni, vedere Modificatori di accesso.
Una proprietà può essere dichiarata come statica tramite la parola chiave static
. Le proprietà statiche sono disponibili per i chiamanti in qualsiasi momento, anche se non esiste alcuna istanza della classe. Per altre informazioni, vedere Classi statiche e membri di classi statiche.
Una proprietà può essere contrassegnata come virtuale tramite la parola chiave virtual. Le proprietà virtuali consentono alle classi derivate di eseguire l'override del comportamento della proprietà usando la parola chiave override. Per altre informazioni su queste opzioni, vedere Ereditarietà.
Una proprietà che esegue l'override di una proprietà virtuale può anche essere contrassegnata come sealed, in modo che non risulti più virtuale per le classi derivate. Infine, una proprietà può essere dichiarata astratta. Le proprietà astratte non definiscono un'implementazione nella classe e le classi derivate devono scrivere la propria implementazione. Per altre informazioni su queste opzioni, vedere Classi e membri delle classi astratte e sealed.
Nota
È un errore usare un modificatore virtual, abstract o override in una funzione di accesso di una proprietà statica.
Esempi
Questo esempio illustra le proprietà di istanza, statiche e di sola lettura. Accetta il nome del dipendente dalla tastiera, incrementa NumberOfEmployees
di 1 e visualizza il nome e il numero del dipendente.
public class Employee
{
public static int NumberOfEmployees;
private static int _counter;
private string _name;
// A read-write instance property:
public string Name
{
get => _name;
set => _name = value;
}
// A read-only static property:
public static int Counter => _counter;
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}
Esempio di proprietà hidden
In questo esempio viene illustrato come accedere a una proprietà in una classe di base che è nascosta da un'altra proprietà con lo stesso nome in una classe derivata:
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Manager : Employee
{
private string _name;
// Notice the use of the new modifier:
public new string Name
{
get => _name;
set => _name = value + ", Manager";
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
// Derived class property.
m1.Name = "John";
// Base class property.
((Employee)m1).Name = "Mary";
System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
}
}
/* Output:
Name in the derived class is: John, Manager
Name in the base class is: Mary
*/
Di seguito sono riportati gli aspetti più importanti relativi all'esempio precedente:
- La proprietà
Name
nella classe derivata nasconde la proprietàName
nella classe di base. In tal caso, viene usato il modificatorenew
nella dichiarazione della proprietà nella classe derivata:public new string Name
- Il cast
(Employee)
viene usato per accedere alla proprietà nascosta nella classe di base:((Employee)m1).Name = "Mary";
Per altre informazioni su come nascondere i membri, vedere Modificatore new.
Esempio di proprietà di override
In questo esempio, due classi, Cube
e Square
, implementano una classe astratta, Shape
, ed eseguono l'override della proprietà astratta Area
. Si noti l'uso del modificatore override nelle proprietà. Il programma accetta il lato come input e calcola le aree per il quadrato e il cubo. Inoltre, accetta l'area come input e calcola il lato corrispondente per il quadrato e il cubo.
abstract class Shape
{
public abstract double Area
{
get;
set;
}
}
class Square : Shape
{
public double side;
//constructor
public Square(double s) => side = s;
public override double Area
{
get => side * side;
set => side = System.Math.Sqrt(value);
}
}
class Cube : Shape
{
public double side;
//constructor
public Cube(double s) => side = s;
public override double Area
{
get => 6 * side * side;
set => side = System.Math.Sqrt(value / 6);
}
}
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s = new Square(side);
Cube c = new Cube(side);
// Display the results:
System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
System.Console.WriteLine();
// Input the area:
System.Console.Write("Enter the area: ");
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine("Side of the square = {0:F2}", s.side);
System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
}
}
/* Example Output:
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
*/