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à sembra essere un campo; l'accesso alla proprietà richiede la stessa sintassi. Per l'implementatore di una classe, una proprietà è uno o due blocchi di codice, che rappresentano una get funzione di accesso e/o una set funzione di accesso o init . Il blocco di codice per la get funzione di accesso viene eseguito quando la proprietà viene letta. Il blocco di codice per la set funzione di accesso o init viene eseguito quando alla proprietà viene assegnato un valore. 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 init funzione di accesso anziché una set funzione di accesso per abilitare l'impostazione della proprietà come parte dell'inizializzazione degli oggetti, ma in caso contrario renderla di sola lettura.

A differenza dei campi, le proprietà non vengono classificate come variabili. Pertanto, non è possibile passare una proprietà come ref parametro o out .

Le proprietà hanno molti usi:

  • Possono convalidare i dati prima di consentire una modifica.
  • Possono esporre in modo trasparente i dati in una classe in cui 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à viene spesso definita "archivio di backup" della proprietà. È comune che le proprietà usino 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.

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 modelli comuni per l'implementazione della get funzione di accesso e ottimizzano tali modelli. Ad esempio, una get funzione di accesso che restituisce un campo senza eseguire alcun calcolo è probabilmente ottimizzata per una lettura di memoria di tale campo. Le proprietà implementate automaticamente seguono questo modello e traggono vantaggio da queste ottimizzazioni. Tuttavia, non è possibile inlinizzare un metodo della funzione di accesso virtuale get perché il compilatore non sa in fase di compilazione quale metodo potrebbe effettivamente essere chiamato in fase di esecuzione. Nell'esempio seguente viene illustrata una get funzione di accesso che restituisce il valore di un campo _nameprivato :

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 get funzione di accesso deve essere un membro con corpo di espressione o terminare in un'istruzione return o throw e il controllo non può passare dal corpo della funzione di accesso.

Avviso

Si tratta di uno stile di programmazione non valido per modificare lo stato dell'oggetto usando la get funzione di accesso.

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 Name proprietà , restituisce 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 i modelli comuni per una set funzione di accesso o init . Questi modelli comuni sono 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 set funzione di accesso.

Funzione di accesso init

Il codice per creare una init funzione di accesso è uguale al codice per creare una set funzione di accesso, ad eccezione del fatto che si usa la init parola chiave anziché set. La differenza è che la init funzione di accesso può essere usata solo nel costruttore o usando un inizializzatore di oggetto.

Osservazioni:

Le proprietà possono essere contrassegnate come public, protectedprivate, internal, protected internal, o private protected. Questi modificatori di accesso definiscono in che modo gli utenti della classe possono accedere alla proprietà. Le get funzioni di accesso e set per la stessa proprietà possono avere modificatori di accesso diversi. Ad esempio, get potrebbe essere public consentire l'accesso in sola lettura dall'esterno del tipo e set può essere private o protected. Per altre informazioni, vedere Modificatori di accesso.

Una proprietà può essere dichiarata come proprietà statica usando la static parola chiave . 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 proprietà virtuale usando 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 sealed, specificando che per le classi derivate non è più virtuale. 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 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