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é apparaît comme 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 un ou deux blocs de code, représentant un accesseur get
et/ou un accesseur set
ou init
. 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
ou init
est exécuté au moment où une valeur est attribué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. Vous pouvez utiliser un accesseur init
au lieu d’un accesseur set
pour permettre à la propriété d’être définie dans le cadre de l’initialisation de l’objet, mais autrement la rendre accessible 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
.
Il existe de nombreuses utilisations pour les propriétés :
- Elles peuvent valider les données avant d’autoriser une modification.
- Elles peuvent exposer de manière transparente des données dans une classe où ces données sont extraites à partir d’une autre source, telle qu’une base de données.
- Elles peuvent agir lorsque les données sont modifiées, par exemple en déclenchant un événement ou en modifiant 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é. Le compilateur C# et le compilateur Juste-à-temps (JIT) détectent les modèles courants de mise en œuvre de l’accesseur get
et optimisent ces modèles. Par exemple, un accesseur get
qui retourne un champ sans effectuer de calcul est probablement optimisé pour une lecture mémoire de ce champ. Les propriétés implémentées automatiquement suivent ce modèle et bénéficient de ces optimisations. Cependant, une méthode d’accesseur get
virtuelle ne peut pas être intégrée car le compilateur ne sait pas, au moment de la compilation, quelle méthode pourrait être appelée au moment de l’exécution. Voici un exemple d’accesseur get
qui retourne la valeur d’un champ privé _name
:
class Employee
{
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 :
var employee= new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
L’accesseur get
doit être un membre à corps d’expression, ou se terminer par une instruction return ou throw, et le contrôle ne peut pas circuler hors du corps de l’accesseur.
Avertissement
Modifier l’état de l’objet en utilisant l’accesseur get
n’est pas un style de programmation approprié.
L’accesseur get
peut être utilisé pour retourner la valeur du champ ou pour la calculer et la retourner. Par exemple :
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
Dans l’exemple précédent, si vous n’attribuez pas de valeur à la propriété Name
, la valeur NA
est retournée.
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é. Le compilateur et le compilateur JIT reconnaissent également des modèles courants pour un accesseur set
ou init
. Ces modèles courants sont optimisés, en écrivant directement dans la mémoire pour le champ de stockage. Dans l’exemple ci-dessous, un accesseur set
est ajouté à la propriété Name
:
class Student
{
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 :
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
Utiliser le nom de paramètre implicite, value
, pour une déclaration de variable locale dans un accesseur set
, est une erreur.
Accesseur init
Le code permettant de créer un accesseur init
est identique au code utilisé pour créer un accesseur set
, sauf que vous utilisez le mot clé init
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 é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, le get
peut être public
pour autoriser l’accès en lecture seule en dehors du type, tandis que le 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
. Les propriétés statiques sont accessibles à 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. Les propriétés virtuelles permettent 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.
Une 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. Les propriétés abstraites ne définissent pas d’implémentation dans la classe, et 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:
}
Exemple de propriété masquée
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 :
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
*/
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 modificateurnew
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
*/