Utilisation de propriétés (Guide de programmation C#)

Les propriétés allient des caractéristiques des champs et des méthodes. Pour l’utilisateur d’un objet, une propriété s’apparente à un champ. Pour accéder à celle-ci, il doit utiliser la même syntaxe. Pour l’implémenteur d’une classe, une propriété est constituée d’un ou deux blocs de code, représentant un accesseur get et/ou un accesseur set. Le bloc de code correspondant à l’accesseur get est exécuté à la lecture de la propriété ; le bloc de code correspondant à l’accesseur set est exécuté au moment où une nouvelle valeur est assignée à la propriété. Une propriété sans accesseur set est considérée comme étant en lecture seule. Une propriété sans accesseur get est considérée comme étant en écriture seule. Une propriété qui possède les deux accesseurs est en lecture-écriture. En C# 9 et versions ultérieures, vous pouvez utiliser un init accesseur au lieu d’un set accesseur pour rendre la propriété en lecture seule.

Contrairement aux champs, les propriétés ne sont pas classifiées en tant que variables. Par conséquent, vous ne pouvez pas passer une propriété en tant que paramètre ref ou out.

Les propriétés ont diverses utilisations : elles peuvent valider les données avant d’autoriser une modification ; elles peuvent exposer de manière transparente les données d’une classe quand celles-ci sont effectivement extraites d’une autre source, telle qu’une base de données ; elles peuvent effectuer une action quand des données sont modifiées, comme déclencher un événement ou modifier la valeur d’autres champs.

Les propriétés sont déclarées dans le bloc de classe en spécifiant le niveau d’accès du champ, suivi du type de la propriété, du nom de la propriété et d’un bloc de code qui déclare un accesseur get et/ou un accesseur set. Par exemple :

public class Date
{
    private int _month = 7;  // Backing store

    public int Month
    {
        get => _month;
        set
        {
            if ((value > 0) && (value < 13))
            {
                _month = value;
            }
        }
    }
}

Dans cet exemple, Month est déclaré en tant que propriété pour permettre à l’accesseur set de vérifier que la valeur définie de Month se trouve bien entre 1 et 12. La propriété Month utilise un champ privé pour assurer le suivi de la valeur réelle. L’emplacement réel des données d’une propriété est souvent appelé « magasin de stockage » de la propriété. Il est courant que les propriétés utilisent des champs privés comme magasin de stockage. Le champ est marqué comme étant privé pour garantir qu’il ne peut être modifié qu’en appelant la propriété. Pour plus d’informations sur les restrictions d’accès public et privé, consultez Modificateurs d’accès.

Les propriétés implémentées automatiquement fournissent une syntaxe simplifiée pour des déclarations de propriété simples. Pour plus d’informations, consultez Propriétés implémentées automatiquement.

Accesseur get

Le corps de l’accesseur get ressemble à celui d’une méthode. Il doit retourner une valeur du type de la propriété. L’exécution de l’accesseur get revient à lire la valeur du champ. Par exemple, quand vous retournez la variable privée de l’accesseur get et que les optimisations sont activées, l’appel à la méthode de l’accesseur get est intégré (inline) par le compilateur, si bien il n’existe aucune surcharge d’appel de méthode. Cependant, une méthode d’accesseur get virtuelle ne peut pas être inline, car le compilateur ne sait pas au moment de la compilation quelle méthode peut être effectivement appelée au moment de l’exécution. Voici un exemple d’accesseur get qui retourne la valeur d’un champ privé _name :

class Person
{
    private string _name;  // the name field
    public string Name => _name;     // the Name property
}

Quand vous faites référence à la propriété (pas en tant que cible d’une assignation), l’accesseur get est appelé pour lire la valeur de la propriété. Par exemple :

Person person = new Person();
//...

System.Console.Write(person.Name);  // the get accessor is invoked here

L’accesseur get doit se terminer dans une instruction return ou throw et le contrôle ne peut pas déborder du corps de l’accesseur.

Modifier l’état de l’objet en utilisant l’accesseur get n’est pas un style de programmation approprié. Par exemple, l’accesseur suivant s’accompagne d’un effet secondaire, à savoir que l’état de l’objet est modifié chaque fois que le champ _number fait l’objet d’un accès.

private int _number;
public int Number => _number++;	// Don't do this

L’accesseur get peut être utilisé pour retourner la valeur du champ ou pour la calculer et la retourner. Par exemple :

class Employee
{
    private string _name;
    public string Name => _name != null ? _name : "NA";
}

Dans le segment de code précédent, si vous n’attribuez pas de valeur à la Name propriété, elle retourne la valeur NA.

Accesseur set

L’accesseur set ressemble à une méthode dont le type de retour est void. Il utilise un paramètre implicite nommé value, dont le type est celui de la propriété. Dans l’exemple ci-dessous, un accesseur set est ajouté à la propriété Name :

class Person
{
    private string _name;  // the name field
    public string Name    // the Name property
    {
        get => _name;
        set => _name = value;
    }
}

Quand vous assignez une valeur à la propriété, l’accesseur set est appelé en utilisant un argument qui fournit la nouvelle valeur. Par exemple :

Person person = new Person();
person.Name = "Joe";  // the set accessor is invoked here

System.Console.Write(person.Name);  // the get accessor is invoked here

Il s’agit d’une erreur d’utiliser le nom de paramètre implicite, value, pour une déclaration de variable locale dans un accesseur set.

Accesseur init

Le code permettant de créer un init accesseur est identique au code pour créer un set accesseur, sauf que vous utilisez le init mot clé au lieu de set. La différence est que l’accesseur init ne peut être utilisé que dans le constructeur ou à l’aide d’un initialiseur d’objet.

Remarques

Les propriétés peuvent être marquées comme public, internalprotected internalprivateprotectedou .private protected Ces modificateurs d’accès définissent comment les utilisateurs de la classe peuvent accéder à la propriété. Les accesseurs get et set d’une même propriété peuvent avoir des modificateurs d’accès différents. Par exemple, l’accesseur get peut être public pour autoriser l’accès en lecture seule en dehors du type, tandis que l’accesseur set peut être private ou protected. Pour plus d’informations, consultez Modificateurs d’accès.

Une propriété peut être déclarée en tant que propriété statique à l’aide du mot clé static. La propriété devient ainsi accessible à tout moment aux appelants, même s’il n’existe aucune instance de la classe. Pour plus d’informations, consultez la page Classes statiques et membres de classes statiques.

Une propriété peut être marquée comme étant une propriété virtuelle à l’aide du mot clé virtual. Cela permet aux classes dérivées de substituer le comportement de la propriété à l’aide du mot clé override. Pour plus d'informations sur ces options, consultez Héritage.

Un propriété qui se substitue à une propriété virtuelle peut aussi être sealed, ce qui signifie que pour les classes dérivées, elle n’est plus virtuelle. Enfin, une propriété peut être déclarée comme étant abstract. Cela signifie qu’il n’existe aucune implémentation dans la classe et que les classes dérivées doivent écrire leur propre implémentation. Pour plus d’informations sur ces options, consultez Classes abstract et sealed et membres de classe.

Notes

Il s’agit d’une erreur d’utiliser un modificateur virtual, abstract ou override sur un accesseur de propriété static.

Exemples

Cet exemple illustre les propriétés d’instance, statiques et en lecture seule. Il accepte le nom de l’employé à partir du clavier, incrémente NumberOfEmployees de 1 et affiche le nom et le numéro de l’employé.

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:
}

class TestEmployee
{
    static void Main()
    {
        Employee.NumberOfEmployees = 107;
        Employee e1 = new Employee();
        e1.Name = "Claude Vige";

        System.Console.WriteLine("Employee number: {0}", Employee.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}
/* Output:
    Employee number: 108
    Employee name: Claude Vige
*/

Exemple de propriété masquée

Cet exemple montre comment accéder à une propriété dans une classe de base masquée par une autre propriété portant le même nom dans une classe dérivée :

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
{
    static void Main()
    {
        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
*/

Voici les points importants de l’exemple précédent :

  • La propriété Name de la classe dérivée masque la propriété Name de la classe de base. En pareil cas, le modificateur new est utilisé dans la déclaration de la propriété de la classe dérivée :

    public new string Name
    
  • La conversion de type (Employee) est utilisée pour accéder à la propriété masquée de la classe de base :

    ((Employee)m1).Name = "Mary";
    

    Pour plus d’informations sur le masquage des membres, consultez new, modificateur.

Exemple de propriété de remplacement

Dans cet exemple, deux classes, Cube et Square, implémentent une classe abstract, Shape, et remplacent sa propriété Area abstract. Notez l’utilisation du modificateur override sur les propriétés. Le programme accepte le côté (« side ») comme entrée et calcule les surfaces (« areas ») du carré (« square ») et du cube. De même, il accepte la surface (« area ») comme entrée et calcule le côté (« side ») correspondant du carré (« square ») et du cube.

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

Voir aussi