Condividi tramite


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 modificatore new 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
*/

Vedi anche