4. Les énumérations
Après cette quête, tu sauras utiliser des énumérations
Objectifs
- Savoir créer une énumération
- Savoir utiliser une énumération
Introduction
Jusqu'à présent, tu as pu découvrir plusieurs concept de Programmation Orientée Objet, et plusieurs outils permettant de représenter des notions comme les classes et les interfaces.
Chaque notion comporte son propre mot clé (public class Plane, public interface IFlyableInterface), mais ce ne sont pas les seules !
Ainsi, une classe sert principalement à définir la structure d'un objet (attributs/méthodes), et l'interface permet de définir un comportement avec un ensemble de méthodes.
Réfléchissons maintenant à un cas pratique en particulier. Dans ma classe User, en plus du nom, du prénom et des autres informations sur l'utilisateur, je souhaite stocker sa civilité (Monsieur, ou Madame donc). Comment répondre à cette problématique ?
Facile chef, j'ajoute un
public string Civilitie { get; set; }dans ma classe !
C'est une solution, effectivement.
Mais rien ne m'empêche d'écrire le code suivant :
User u = new User();
u.Civilitie = "Non binaire";
Dans ce cas, on peut contrôler les nouvelles valeurs dans le setter ?
public class User
{
// ...
private string _civilitie;
public string Civilitie {
get { return _civilitie; }
set {
if (value == "Monsieur" || value == "Madame")
_civilitie = value;
}
}
// ...
}
C'est aussi une solution, mais dans ce cas là je ne peux pas faire ceci à cause de la casse :
User u = new User();
u.Civilitie = "monsieur"
On ajoute ToUpper ou ToLower dans le setter ?
Ca pourrait fonctionner. Mais le jour où on veut autoriser la saisie de la valeur Non binaire, ca commence à devenir compliqué !
La problématique des types primitifs
En utilisant une simple string pour stocker ce type d'informations, tu vas toujours être confronté aux mêmes problématiques :
- Comment faire pour vérifier que la nouvelle valeur fait parti de la liste des valeurs autorisées ?
- Comment comparer facilement des valeurs sans devoir utiliser du
ToLower()ouToUpper()a tour de bras ? - Comment savoir, en tant que développeur, quelles sont les valeurs possibles ?
L'utilisation des structures existantes
Les listes et les dictionnaires sont donc des classes permettant de stocker un ensemble de données. Ces deux structures ont des tailles dynamiques et permettent de manipuler les données qu'elles contiennent.
On pourrait donc imaginer le code suivant :
List<string> possibleCivilities = new List<string> { "Monsieur", "Madame" };
On a ainsi la liste des valeurs possibles. Mais ça ne répond pas au problème de casse.
De plus, rien ne m'empêche de faire ça
possibleCivilities.Add("Non Binaire");
Or je ne veux pas que n'importe quel développeur de l'application rajoute des civilités en cours de route !
Même combat avec un dictionnaire.
Je passe ma liste en lecture-seule, et comme ça, plus possible de modifier les valeurs contenues dedans !
Hmm... même si la liste est en lecture seule, ca veut dire que tu ne pourra pas modifier la référence de la variable (faire = new List...), mais rien ne t'empêche d'y ajouter/supprimer des éléments.
Et même en utilisant une ReadOnlyCollection, ca répondrait à cette problématique, mais c'est loin d'être confortable à utiliser pour le développeur.
Mais alors comment faire pour avoir un ensemble connu et fini de valeurs ?
Sous entendu, un ensemble (ou une collection si tu préfères) dont toutes les valeurs sont connues au démarrage de l'application et qui n'évoluent pas au cours de l'exécution de l'application, et où on ne peut ni en ajouter ni en enlever.
Réponse : avec une énumération !
Les enum
Une enum n'est ni une classe, ni une interface. C'est un autre type d'objet, dédié spécifiquement à notre besoin.
Pour créer une énum, il faut donc faire
public enum ECivilities
{
MONSIEUR,
MADAME
}
Et c'est tout !
Chaque valeur possible de l'énumération est spécifiée à cet endroit.
Par convention, les différentes valeurs sont à mettre entièrement en majuscule. J'aime bien également préfixer les enum avec un E (comme les interfaces préfixées par I).
Avec cette syntaxe, impossible d'ajouter ou enlever une valeur au cours de l'exécution du programme.
Mais alors... comment faire pour utiliser ces valeurs ?
L'utilisation des enum
Une fois l'enum créée dans ton projet, il va falloir modifier la classe User comme suit :
public class User
{
// ...
public ECivilities Civilitie { get; set; }
// ...
}
L'enum ECivilities est donc utilisée ici comme un type de données (tout comme int, double, string, User, etc....)
Ensuite on peut lui affecter une valeur de cette manière :
User user = new User();
user.Civilitie = ECivilities.MONSIEUR;
L'accès aux différentes valeurs de l'enum se fait via une syntaxe qui ressemble un peu à Classe.ProprieteStatique.
La liste des valeurs est fixe et connue à l'avance. Par conséquent le compilateur sait que ECivilities.MONSIEUR est une valeur valide, contrairement à ECivilities.Monsieur ou ECivilities.NONBINAIRE. Il est donc impossible d'écrire du code faux grâce à ce type.
De plus, les valeurs étant connues et détectées par le compilateur, les différents possibilités de l'enum sont visible directement dans l'auto-complétion de ton IDE. Plus besoin de retenir par cœur les différents cas !

La comparaison des enums
Plus besoin de penser à la casse pour comparer les valeurs. Il suffit de faire
if (user.Civilitie == ECivilities.MONSIEUR)
{
// ....
} else if (user.Civilitie == ECivilities.MADAME)
{
// ....
}
else
{
// ...
}
La syntaxe est bien évidemment compatible avec le switch/case
switch (user.Civilitie)
{
case ECivilities.MONSIEUR :
// ...
break;
case ECivilities.MADAME :
// ...
break;
default :
break;
}
Les valeurs des enum
Chaque cas listé dans mon enum ECivilitie possède en réalité une valeur en mémoire. Cette valeur est de type int afin de faciliter et d'optimiser les comparaisons et stockage de valeur.
Ainsi, la syntaxe précédente équivaut à
public enum ECivilities
{
MONSIEUR = 0,
MADAME = 1
}
Il est donc possible de spécifier une valeur numérique arbitraire à chaque cas
public enum ECivilities
{
MONSIEUR = 12,
MADAME = 23
}
L'intérêt ici peut être de s'assurer que si un jour je rajoute NONBINAIRE en haut de la liste, la valeur 0 stockée en base de données, et qui correspond actuellement à MONSIEUR, ne sera pas lue ensuite comme NONBINAIRE puisqu'on aurait ce genre de choses :
public enum ECivilities
{
NONBINAIRE = 0,
MONSIEUR = 1,
MADAME = 2
}
Il est possible d'affecter une valeur de type string aux cas de l'enum avec la syntaxe suivante, mais je ne te recommande pas spécialement son utilisation sans raison particulière :
public enum ECivilities : string
{
MONSIEUR = "Monsieur",
MADAME = "Madame"
}
Affichage d'une enum
Par défaut, si tu affiche la civilité de ton utilisateur dans la console, ou dans une page web, il va afficher MONSIEUR ou MADAME et non la valeur numérique 0 ou 1.
La liste des valeurs possibles
Il peut arriver que tu veuilles avoir la liste de tous les cas possibles d'une énum. Par exemple pour proposer une liste déroulante en HTML afin que l'utilisateur choisisse une valeur.
Pour ça, il faut utiliser la méthode GetNames de la classe Enum.
Exemple
foreach(string s in Enum.GetNames(typeof(ECivilitie)))
Console.WriteLine(s);
Si tu souhaites obtenir tous les cas possible, au format ECvilite et pas string, c'est la fonction GetValues qu'il te faut !
foreach(ECivilite c in Enum.GetValues(typeof(ECivilitie)))
Console.WriteLine(c);
Conclusion
Tu connais maintenant le principe de base d'une énumération, comment en définir une, et comment l'utiliser.
Il est possible de faire encore beaucoup d'autres choses avec les enum, comme par exemple :
- Retrouver l'enum depuis un int (avec un Parse)
- Combiner des enums avec le l'opérateur bit à bit
- etc...
Mais gardons ça pour une autre fois. Retiens que l'énumération répond à ton besoin si il faut :
- Une liste de valeur connue à l'avance
- Une liste de valeur qui ne change pas
- Une aide de l'autocomplétion pour savoir quelles sont les valeurs possibles
- Ne pas se préoccuper de la casse lors des comparaisons
Ce type ne peut donc pas être utilisé à la place d'une List si les valeurs sont vouées à changer au cours de l'execution du programme (la catégorie d'une dépense sur le site de ta banque par exemple).
Challenge
Tu vas créer une énum permettant de représenter les différentes couleurs gérées dans l'application. Il faut pouvoir utiliser ROUGE, BLEU, VERT et JAUNE.
Crée ensuite une classe User avec un nom, un prénom, et une couleur.
Dans la console, instancie une liste de plusieurs utilisateurs avec différentes couleurs, et affiche leurs informations. . Exemple :
Prénom : John
Nom : Doe
Couleur : ROUGE
Affiche ensuite pour chaque couleur, le nombre d'utilisateur correspondant. ROUGE : 2 utilisateurs par exemple.
Critères de validation
- Un lien vers ta solution sur GitHub
- Les informations de l'utilisateur s'affichent correctement
- La liste des couleurs est affichée
- Pour chaque couleur, le nombre d'utilisateur est précisé