Limitare l'accesso alle proprietà
Una classe può specificare il modo in cui ogni membro è accessibile al codice all'esterno della classe . I metodi e le variabili che non devono essere usati dall'esterno della classe o dell'assembly possono essere nascosti per limitare il potenziale di errori di codifica o exploit dannosi.
Alcuni metodi e proprietà devono essere chiamati o accessibili dal codice all'esterno di una classe, noti come codice client. Altri metodi e proprietà possono essere utilizzati solo nella classe stessa. È importante limitare l'accessibilità del codice in modo che solo il codice client previsto possa raggiungerlo. Specificare il modo in cui i tipi e i relativi membri sono accessibili al codice client usando i modificatori di accesso seguenti:
-
public: il tipo o il membro è accessibile da qualsiasi altro codice nello stesso assembly o da un altro assembly che vi fa riferimento. -
protected: il tipo o il membro è accessibile solo dal codice nella stessa classe o in una classe derivata. -
internal: il tipo o il membro è accessibile da qualsiasi codice nello stesso assembly, ma non da un altro assembly. -
protected internal: il tipo o il membro è accessibile da qualsiasi codice nello stesso assembly o da qualsiasi classe derivata in un altro assembly. -
private: il tipo o il membro è accessibile solo dal codice nella stessa classe o struct. -
private protected: il tipo o il membro è accessibile solo dal codice nello stesso assembly e solo dal codice nella stessa classe o da una classe derivata.
Ai membri della classe viene assegnato private l'accesso per impostazione predefinita.
Modificatori di accesso per proprietà e funzioni di accesso
Fino a questo punto del modulo, ci siamo concentrati sulle proprietà che includono sia get che set funzioni di accesso. Queste proprietà vengono definite proprietà di lettura/scrittura. Oltre alle proprietà di lettura/scrittura, è possibile creare proprietà di sola lettura o offrire accessibilità diversa alle funzioni di accesso set e get.
Per impostazione predefinita, le funzioni di accesso get e set hanno lo stesso livello di accessibilità della proprietà a cui appartengono. Tuttavia, è possibile limitare l'accessibilità della funzione di accesso get o set. I modificatori di accesso sono utili quando si vuole limitare l'accesso alla proprietà stessa, ma consentono comunque l'accesso al valore della proprietà tramite una funzione di accesso get.
Si supponga di avere una classe Person che deve abilitare solo la modifica del valore della proprietà FirstName da altri metodi nella classe . È possibile concedere l'accessibilità privata della funzione di accesso set anziché interna o pubblica:
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 solo dal codice nella classe Person.
È possibile aggiungere qualsiasi modificatore di accesso restrittivo alle funzioni di accesso set o get. Un modificatore di accesso in una singola funzione di accesso deve essere più restrittivo rispetto all'accesso della proprietà. Il codice precedente è valido perché la proprietà FirstName è public, ma la funzione di accesso set è private. Non è possibile dichiarare una proprietà private con una funzione di accesso public. Le dichiarazioni di proprietà possono essere dichiarate anche protected, internal, protected internalo anche private.
Esistono due modificatori di accesso speciali per le funzioni di accesso set:
- Una funzione di accesso
setpuò avereinitcome modificatore di accesso. Talesetfunzione di accesso può essere chiamata solo da un inizializzatore di oggetto o dai costruttori del tipo. È più restrittivo diprivatenella funzione di accessoset. - Una proprietà implementata automaticamente può dichiarare una funzione di accesso
getsenza una funzione di accessoset. In tal caso, il compilatore consente di chiamare la funzione di accessosetsolo dai costruttori del tipo. È più restrittivo rispetto alla funzione di accessoinitnella funzione di accessoset.
Considerare gli aggiornamenti seguenti alla classe Person:
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 gli inizializzatori di oggetto per assegnare un valore alla proprietà . Per supportare gli inizializzatori, è possibile impostare la funzione di accesso set come funzione di accesso 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.
Limitare l'accessibilità della funzione di accesso
In alcuni casi, è utile limitare l'accesso alla funzione di accesso get o set. In genere, si limita l'accessibilità della funzione di accesso set, mantenendo accessibile pubblicamente la funzione di accesso get.
Per esempio:
private string _name = "Hello";
public string Name
{
get
{
return _name;
}
protected set
{
_name = value;
}
}
In questo esempio, una proprietà denominata Name definisce una funzione di accesso get e set. La funzione di accesso get riceve il livello di accessibilità della proprietà stessa, public in questo caso, mentre la funzione di accesso set è limitata in modo esplicito applicando il modificatore di accesso protected alla funzione di accesso stessa.
Restrizioni sui modificatori di accesso nelle funzioni di accesso
L'uso dei modificatori delle funzioni di accesso per proprietà o indicizzatori è soggetto a queste condizioni:
- È possibile usare i modificatori delle funzioni di accesso solo se la proprietà dispone di funzioni di accesso sia
setche diget. In questo caso, il modificatore è consentito solo in una delle due funzioni di accesso. - Il livello di accessibilità della funzione di accesso deve essere più restrittivo rispetto al livello di accessibilità della proprietà o dell'indicizzatore stesso.
Proprietà statiche di sola lettura
Nell'esempio seguente viene illustrato l'uso di proprietà statiche e di istanza in una classe .
La classe Employee ha una proprietà statica denominata Counter che tiene traccia del numero di istanze di classe create. La proprietà Counter è di sola lettura, ovvero è accessibile dall'esterno della classe, ma non modificata. La proprietà Counter è anche static, ovvero si accede al valore Counter usando la classe Employee, non le istanze employee1 o employee2 della classe .
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:
}
public class Program
{
static void Main(string[] args)
{
// Create an initial instance of the Employee class
Employee employee1 = new Employee { Name = "NameOne" };
// Display the name and counter values
Console.WriteLine($"Employee 1: Name = {employee1.Name}, Counter = {Employee.Counter}");
// Create a second instance of the Employee class
Employee employee2 = new Employee { Name = "NameTwo" };
// Display the name and counter values
Console.WriteLine($"Employee 2: Name = {employee2.Name}, Counter = {Employee.Counter}");
employee2.Name = "NameThree";
// Display the name and counter values
Console.WriteLine($"Employee 2: Name = {employee2.Name}, Counter = {Employee.Counter}");
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
La classe Employee ha i membri seguenti:
-
NumberOfEmployees: campo statico pubblico che tiene traccia del numero totale di istanze di Employee create. Essendo statico, viene condiviso tra tutte le istanze della classe Employee. -
_counter: campo statico privato usato per assegnare un numero univoco a ogni istanza dipendente. Viene anche condiviso tra tutte le istanze. -
_name: campo di istanza privata in cui è archiviato il nome del dipendente. È specifico di ogni istanza della classe Employee. -
Name: proprietà dell'istanza di lettura/scrittura pubblica che fornisce l'accesso al campo _name. La funzione di accessogetrestituisce il valore di_namee la funzione di accessosetassegna un nuovo valore a_name. -
Counter: proprietà statica di sola lettura pubblica che fornisce l'accesso al campo_counter. La funzione di accessogetrestituisce il valore di_counter. La proprietàCounterè associata alla classe anziché agli oggetti perché è definita come proprietà statica.
I due campi statici vengono inizializzati quando la classe viene caricata per la prima volta in memoria, prima della creazione di qualsiasi istanza della classe. In C# i campi statici vengono inizializzati con i relativi valori predefiniti se non vengono inizializzati in modo esplicito. Per int campi, il valore predefinito è 0.
Proprietà statiche e campi appartengono alla classe stessa, non a un'istanza specifica della classe . Tutte le istanze della classe condividono le stesse proprietà statiche e gli stessi campi.
Il costruttore incrementa il campo statico NumberOfEmployees di 1 e assegna il nuovo valore al campo statico _counter. Questo approccio garantisce che ogni istanza di Employee ottenga un numero univoco.
Funzioni di accesso private
Si consideri il codice seguente che implementa le proprietà di lettura/scrittura:
class Person
{
private string _name = "N/A";
private int _age = 0;
// Declare a Name property of type string:
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
// Declare an Age property of type int:
public int Age
{
get
{
return _age;
}
set
{
_age = value;
}
}
}
class TestPerson
{
static void Main()
{
// Create a new Person object:
Person person = new Person();
// Print out the name and the age associated with the person:
Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");
// Set some values on the person object:
person.Name = "NameOne";
person.Age = 99;
Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");
// Increment the Age property:
person.Age += 1;
Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Person details - Name = N/A, Age = 0
Person details - Name = NameOne, Age = 99
Person details - Name = NameOne, Age = 100
*/
In questo esempio la classe Person include proprietà Name e Age. Le proprietà sono pubbliche e includono get e set funzioni di accesso. Le funzioni di accesso pubbliche consentono a qualsiasi oggetto di leggere e scrivere queste proprietà.
A volte è escludere una delle funzioni di accesso. È possibile omettere la funzione di accesso set per rendere la proprietà di sola lettura:
public string Name
{
get
{
return _name;
}
}
In alternativa, è possibile esporre pubblicamente una funzione di accesso, ma rendere l'altra privata o protetta.
public string Name
{
get
{
return _name;
}
private set
{
_name = value;
}
}