Propriétés

Références

L'actualité

Librairie

L'information

Introduction

Une propriété est un membre qui fournit un mécanisme flexible pour la lecture, l'écriture ou le calcul de la valeur d'un champ privé. Les propriétés peuvent être utilisées comme s'il s'agissait de membres de données publics, mais ce sont en fait des méthodes spéciales appelées accesseurs. Elles permettent aux données d'être facilement accessibles tout en soutenant quand même la sécurité et la flexibilité des méthodes.

Vue d'ensemble des propriétés

  • Les propriétés permettent à une classe d'exposer un moyen public d'obtenir et de définir des valeurs, tout en masquant le code d'implémentation ou de vérification.
  • Un accesseur de propriété get est utilisé pour retourner la valeur de la propriété et un accesseur de propriété set est utilisé pour affecter une nouvelle valeur. Ces accesseurs peuvent avoir différents niveaux d'accès.
  • Le mot clé value est utilisé pour définir la valeur assignée par l'accesseur set.
  • Les propriétés peuvent être en lecture-écriture (elles ont un accesseur get et un accesseur set), en lecture seule (elles ont un accesseur get, mais pas d'accesseur set) ou en écriture seule (elles ont un accesseur set, mais pas d'accesseur get). Les propriétés en écriture seule sont rares et sont généralement utilisées pour restreindre l'accès aux données sensibles.
  • Les propriétés simples qui ne nécessitent pas de code d'accesseur personnalisé peuvent être implémentées en tant que définitions de corps d'expression ou en tant que propriétés implémentées automatiquement.
Propriétés avec des champs de stockage

Un modèle de base pour l'implémentation d'une propriété consiste à utiliser un champ de stockage privé pour définir et extraire la valeur de propriété. L'accesseur get retourne la valeur du champ privé et l'accesseur set peut effectuer une validation des données avant d'assigner une valeur au champ privé. Les deux accesseurs peuvent également effectuer des opérations de conversion ou de calcul sur les données avant de stocker ou retourner les données. L'exemple suivant illustre ce modèle. Dans cet exemple, la classe TimePeriod représente un intervalle de temps. La classe stocke l'intervalle de temps en secondes, en interne, dans un champ privé nommé _seconds. L'utilisateur peut éventuellement spécifier l'intervalle de temps en heures à l'aide de la propriété en lecture-écriture Hours. Les deux accesseurs de propriété get et set effectuent ensuite la conversion nécessaire des heures en secondes. De plus, l'accesseur set valide les données et lève une exception ArgumentOutOfRangeException si le nombre d'heures n'est pas valide.

Définitions de corps d'expression

Les accesseurs de propriété sont souvent des instructions sur une ligne qui ne font qu'assigner ou retourner le résultat d'une expression. Vous pouvez implémenter ces propriétés en tant que membres expression-bodied. Les définitions de corps d'expression se composent du symbole => suivi de l'expression à assigner à la propriété ou à récupérer de la propriété. à partir de C# 6, les propriétés en lecture seule peuvent implémenter l'accesseur get en tant que membre expression-bodied. Dans ce cas, le mot clé d'accesseur get et le mot clé return ne sont pas utilisés. L'exemple suivant implémente la propriété en lecture seule Name en tant que membre expression-bodied.

À compter de C# 7.0, les accesseurs get et set peuvent être implémentés comme membres expression-bodied. Dans ce cas, les mots clés get et set doivent être spécifiés. L'exemple suivant illustre l'utilisation de définitions de corps d'expression pour les deux accesseurs. Notez que le mot clé return n'est pas utilisé avec l'accesseur get.

Propriétés implémentées automatiquement

Dans certains cas, les accesseurs de propriété get et set ne font qu'assigner une valeur à un champ de stockage, ou récupérer une valeur d'un champ de stockage, sans inclure de logique supplémentaire. En utilisant des propriétés implémentées automatiquement, vous simplifiez votre code, tout en laissant le compilateur C# fournir le champ de stockage de manière transparente. Si une propriété a les accesseurs get et set, tous deux doivent être implémentés automatiquement. Vous définissez une propriété implémentée automatiquement à l'aide des mots clés get et set sans fournir d'implémentation. L'exemple suivant est identique à l'exemple précédent, sauf qu'il utilise les propriétés implémentées automatiquement Name et Price. Notez que l'exemple supprime également le constructeur paramétrable pour que les objets SaleItem soient initialisés avec un appel au constructeur par défaut et un initialiseur d'objet.


Utilisation de propriétés

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.

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 :

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 private.

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 qu'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 :

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 :

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.

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

Dans le segment de code précédent, si vous n'assignez pas de valeur à la propriété Name, 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 :

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

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.

Notes

Les propriétés peuvent être marquées comme étant public, private, protected, internal, protected internal ou 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. 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.

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.

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.

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

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é.

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

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 :

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

Dans cet exemple, deux classes, Cube et Square, implémentent une classe abstract, /code>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.


Propriétés de l'interface

Des propriétés peuvent être déclarées dans une interface. L'exemple ci-dessous porte sur un accesseur de propriété d'interface :

L'accesseur d'une propriété d'interface n'a pas de corps. Par conséquent, les accesseurs visent à indiquer si la propriété est en lecture-écriture, en lecture seule ou en écriture seule.

Exemple

Dans cet exemple, l'interface IEmployee a une propriété en lecture-écriture, Name, et une propriété en lecture seule, Counter. La classe Employee implémente l'interface IEmployee et utilise ces deux propriétés. Le programme lit le nom d'un nouvel employé et le nombre actuel d'employés et affiche le nom de l'employé et le nombre d'employés calculé.

Vous pouvez utiliser le nom qualifié complet de la propriété, qui fait référence à l'interface dans laquelle le membre est déclaré, par exemple :

Ceci s'appelle une implémentation d'interface explicite. Par exemple, si la classe Employee implémente deux interfaces, ICitizen et IEmployee, et que les deux interfaces ont la même propriété Name, l'implémentation de membre d'interface explicite est nécessaire. Autrement dit, la déclaration de propriété suivante :

implémente la propriété Name dans l'interface IEmployee, alors que la déclaration suivante :

implémente la propriété Name dans l'interface ICitizen.


Restriction d'accessibilité de l'accesseur

Les parties get et set d'une propriété ou d'un indexeur sont appelées accesseurs. Par défaut, ces accesseurs ont la visibilité ou le niveau d'accès de la propriété ou de l'indexeur auquel ils appartiennent. Toutefois, il peut parfois s'avérer utile de restreindre l'accès à l'un de ces accesseurs. En général, cela implique de restreindre l'accessibilité de l'accesseur set, tout en gardant l'accesseur get publiquement accessible, par exemple :

Dans cet exemple, une propriété appelée Name définit un accesseur get et set. L'accesseur get reçoit le niveau d'accessibilité de la propriété elle-même, public dans le cas présent, alors que l'accesseur set est restreint explicitement par l'application du modificateur d'accès protected à l'accesseur lui-même.

Restrictions sur les modificateurs d'accès sur les accesseurs

L'utilisation de modificateurs d'accesseurs sur les propriétés ou les indexeurs est soumise aux conditions suivantes :

  • Vous ne pouvez pas utiliser de modificateurs d'accesseur sur une interface ou sur une implémentation de membre d'interface explicite.
  • Vous pouvez utiliser les modificateurs d'accesseur uniquement si la propriété ou l'indexeur dispose à la fois d'accesseurs set et get. Dans ce cas, le modificateur est autorisé uniquement sur l'un des deux accesseurs.
  • Si la propriété ou l'indexeur possède un modificateur override, le modificateur d'accesseur doit correspondre à l'accesseur de l'accesseur substitué, le cas échant.
  • Le niveau d'accessibilité sur l'accesseur doit être plus restrictif que le niveau d'accessibilité sur la propriété ou l'indexeur eux-mêmes.

Modificateurs d'accès sur les accesseurs de substitution

Quand vous substituez une propriété ou un indexeur, les accesseurs remplacés doivent être accessibles au code de substitution. Par ailleurs, l'accessibilité de la propriété/l'indexeur et de ses accesseurs doivent correspondre à la propriété/l'indexeur et à ses accesseurs substitués, par exemple :

Implémentation des interfaces

Quand vous utilisez un accesseur pour implémenter une interface, l'accesseur peut ne pas avoir de modificateur d'accès. Toutefois, si vous implémentez l'interface à l'aide d'un accesseur, tel que get, l'autre accesseur peut avoir un modificateur d'accès, comme dans l'exemple suivant :

Domaine d'accessibilité de l'accesseur

Si vous utilisez un modificateur d'accès sur l'accesseur, le domaine d'accessibilité de l'accesseur est déterminé par ce modificateur.

Si vous n'avez pas utilisé un modificateur d'accès sur l'accesseur, le domaine d'accessibilité de l'accesseur est déterminé par le niveau d'accessibilité de la propriété ou de l'indexeur.

Exemple

L'exemple suivant contient trois classes, BaseClass, DerivedClass et MainClass. Il y a deux propriétés sur BaseClass, Name et Id sur les deux classes. L'exemple montre comment la propriété Id sur DerivedClass peut être masquée par la propriété Id sur BaseClass quand vous utilisez un modificateur d'accès restrictif tel que protected ou private. Par conséquent, quand vous affectez des valeurs à cette propriété, la propriété sur la classe BaseClass est appelée à la place. Le remplacement du modificateur d'accès par public rend la propriété accessible.

L'exemple montre également qu'un modificateur d'accès restrictif, tel que private ou protected, sur l'accesseur set de la propriété Name dans DerivedClass empêche l'accès à l'accesseur et génère une erreur quand vous lui affectez une valeur.


Déclarer et utiliser des propriétés en lecture-écriture

Les propriétés offrent la commodité des membres de données publics sans les risques liés à un accès non protégé, non contrôlé et non vérifié aux données d'un objet. Cela se fait au moyen d'accesseurs, lesquels sont des méthodes spéciales qui affectent et récupèrent des valeurs du membre de données sous-jacent. L'accesseur set permet aux membres de données d'être affectés, et l'accesseur get récupère des valeurs de membres de données.

L'exemple suivant montre une classe Person qui possède deux propriétés : Name(string) et Age(int). Étant donné que les deux propriétés fournissent des accesseurs get et set, elles sont considérées comme des propriétés en lecture / écriture.

Programmation fiable

Dans l'exemple précédent, les propriétés Name et Age sont publiques, et incluent un accesseur get et un accesseur set. Cela permet à n'importe quel objet de lire et d'écrire ces propriétés. Toutefois, il est parfois souhaitable d'exclure l'un des accesseurs. L'omission de l'accesseur set, par exemple, met la propriété en lecture seule :

Vous pouvez également exposer publiquement un accesseur mais rendre l'autre private ou protected.
Une fois les propriétés déclarées, vous pouvez les utiliser comme s'il s'agissait de champs de la classe. Cela permet une syntaxe très naturelle pour obtenir ou définir la valeur d'une propriété, comme dans les instructions suivantes :

Notez qu'une variable value spéciale est disponible dans la méthode set d'une propriété. Cette variable contient la valeur que l'utilisateur a spécifiée, par exemple :

Notez la syntaxe correcte pour incrémenter la propriété Age sur un objet Person :

Si des méthodes set et get distinctes ont été utilisées pour modeler des propriétés, le code équivalent peut avoir la forme suivante :

La méthode ToString est substituée dans cet exemple :

Vous pouvez remarquer que ToString n'est pas utilisée de façon explicite dans le programme. Elle est appelée par défaut par les appels WriteLine.

Propriétés implémentées automatiquement

En C# 3.0 et versions ultérieures, les propriétés implémentées automatiquement rendent la déclaration de propriété plus concise quand aucune logique supplémentaire n'est requise dans les accesseurs de propriété. Elles permettent également au code client de créer des objets. Quand vous déclarez une propriété comme indiqué dans l'exemple suivant, le compilateur crée un champ de stockage privé et anonyme uniquement accessible via les accesseurs get et set de la propriété.

L'exemple suivant montre une classe simple qui a des propriétés implémentées automatiquement :

En C# 6 et versions ultérieures, vous pouvez initialiser des propriétés implémentées automatiquement de la même façon que des champs :

La classe qui est illustrée dans l'exemple précédent est mutable. Le code client peut modifier les valeurs dans les objets après leur création. Dans les classes complexes qui contiennent un comportement significatif (méthodes) ainsi que des données, il est souvent nécessaire d'avoir des propriétés publiques. Toutefois, pour les petites classes ou les petits structs qui encapsulent simplement un ensemble de valeurs (données) et qui ont peu de comportements ou aucun, vous devez rendre les objets immuables en déclarant l'accesseur set comme étant privé (immuables pour les consommateurs) ou en déclarant uniquement un accesseur get (immuables partout sauf dans le constructeur).


Implémenter une classe Lightweight avec des propriétés implémentées automatiquement

Cet exemple montre comment créer une classe légère immuable qui sert uniquement à encapsuler un jeu de propriétés implémentées automatiquement. Utilisez ce type de construction à la place d'un struct quand vous devez utiliser une sémantique de type de référence.

Vous pouvez rendre une propriété immuable de deux manières. Vous pouvez déclarer l'accesseur set comme étant private. La propriété peut uniquement être définie dans le type, mais elle est immuable pour les consommateurs. Vous pouvez autrement déclarer uniquement l'accesseur get, ce qui rend la propriété immuable partout sauf dans le constructeur du type.

Quand vous déclarez un accesseur set privé, vous ne pouvez pas utiliser un initialiseur d'objet pour initialiser la propriété. Vous devez utiliser un constructeur ou une méthode de fabrique.

Exemple

L'exemple suivant montre deux façons d'implémenter une classe immuable qui possède des propriétés implémentées automatiquement. Chaque façon déclare l'une des propriétés avec un set privé et l'autre avec un get uniquement. La première classe utilise un constructeur uniquement pour initialiser les propriétés et la deuxième classe utilise une méthode de fabrique statique qui appelle un constructeur.

Le compilateur crée des champs de stockage pour chaque propriété implémentée automatiquement. Les champs ne sont pas accessibles directement à partir du code source.