Rendre obligatoire l'initialisation de propriétés
Introduction
Lorsqu'une classe possède plusieurs propriétés, nous souhaitons parfois que certaines d'entre elles soient indispensables au bon fonctionnement des objets qu'elle permet de créer.
Exemple sur une classe
Voici une première implémentation de la classe Customer :
public class Customer
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Rien ne nous empêche d'instancier cette classe avec le constructeur par défaut, généré automatiquement par le compilateur, puisqu'aucun constructeur n'a été explicitement implémenté.
L'objet customer est créé et ses propriétés sont initialisées avec une chaîne de caractères vide. Cette situation peut entraîner des erreurs ou nécessiter des vérifications supplémentaires, dans le code lors de la manipulation de ces propriétés.
Depuis la version 11 du langage C#, le mot-clé required permet d'indiquer qu'une propriété doit obligatoirement être initialisée, lors de la création d'un objet. Par exemple, décidons que les propriétés FirstName et LastName doivent être valorisées :
public class Customer
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
public string? Email { get; set; }
}
Si nous conservons cette écriture pour instancier la classe Customer :
Une erreur apparaît lors de la compilation :
Required member 'Customer.FirstName' must be set in the object initializer.
En effet, le compilateur s'attend à ce que les propriétés définies avec le mot clé required soient initialisées lors de l'instanciation :
Ainsi, le code compile.
Remarque importante :
Initialisée ne signifie pas nécessairement valorisée avec une donnée valide. Si vous spécifiez la valeur
nullet que le type du paramètre est de type référence nullable, alors le compilateur n'indiquera aucune erreur ou avertissement.
Impacts sur les constructeurs
Lorsqu'un constructeur initialise lui-même les propriétés obligatoires, il est possible d'indiquer au compilateur que ce constructeur satisfait les contraintes grâce à l'attribut SetsRequiredMembers :
public class Customer
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
[SetsRequiredMembers]
public Customer(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
Sans l'utilisation de cette métadonnée, le compilateur peut signaler que les propriétés requises (marquées avec required) ne sont pas initialisées. Et dans ce cas, nous devrions implémenter :
Et cette implémentation est une mauvaise pratique par la duplication des données, d'où l'utilité de SetsRequiredMembers.
required remplace-t-il les constructeurs ?
La réponse est : non. Ils ne jouent pas les mêmes rôles. L'utilisation de required garantit qu'une propriété est initialisée, alors qu'un constructeur permet d'imposer des règles métier et des validations, et indiquer comment valoriser les propriétés. Ils ne s'opposent pas et sont même complémentaires.