Cours complet pour apprendre à programmer en D


précédentsommairesuivant

35. Les énumérations (enum)

enum est la fonctionnalité qui permet de définir des valeurs constantes nommées.

35-1. Les effets des constantes magiques sur la qualité du code

Le code suivant apparaît dans les solutions des exercices du chapitre sur les entiers et les opérations arithmétiquesNombres entiers et opérations arithmétiques :

 
Sélectionnez
if (operation == 1) {
      resultat = premier + second;
 
} else if (operation == 2) {
      resultat = premier - second;
 
} else if (operation == 3) {
      resultat = premier * second;
 
} else if (operation == 4) {
      resultat = premier / second;
}

Les littéraux entiers 1, 2, 3 et 4 dans ce bout de code sont appelés constantes magiques. Il n'est pas facile de déterminer à quoi correspond chacun de ces littéraux dans le programme. Le code de chaque bloc doit être examiné pour voir que 1 désigne l'addition, 2 la soustraction, etc. Cette tâche est relativement aisée dans cet exemple parce que chaque bloc ne contient qu'une ligne. Il serait considérablement plus difficile de déchiffrer le sens des constantes magiques dans la plupart des autres programmes.

Les constantes magiques doivent être évitées parce qu'elles limitent les deux qualités les plus importantes des programmes : lisibilité et maintenabilité.

enum permet de donner des noms à de telles constantes et donc de rendre le code plus lisible et maintenable. Chaque condition est immédiatement compréhensible quand les valeurs énumérées suivantes sont utilisées :

 
Sélectionnez
if (operation == Operation.ajouter) {
      resultat = premier + second;
 
} else if (operation == Operation.soustraire) {
      resultat = premier - second;
 
} else if (operation == Operation.multiplier) {
      resultat = premier * second;
 
} else if (operation == Operation.diviser) {
      resultat = premier / second;
}

Le type énuméré Operation ci-dessus qui rend inutile le recours aux constantes magiques 1, 2, 3 et 4 peut être défini comme ceci :

 
Sélectionnez
enum Operation { ajouter = 1, soustraire, multiplier, diviser }

35-2. La syntaxe d'enum

La forme la plus simple de la définition d'une énumération est la suivante :

 
Sélectionnez
enum NomDuType { NomDeValeur_1, NomDeValeur_2, /* etc. */ }

Il est parfois nécessaire de spécifier également le vrai type (le type de base) des valeurs :

 
Sélectionnez
enum NomDuType : type_de_base { NomDeValeur_1, NomDeValeur_2, /* etc. */ }

Nous verrons comment ceci peut être utilisé dans la section suivante.

NomDuType définit la signification commune des valeurs. Toutes les valeurs d'un type énuméré sont listées entre accolades. Voici quelques exemples :

 
Sélectionnez
enum PileOuFace { pile, face }
enum Couleur { pique, cœur, carreau, trèfle }
enum Voyageur { ordinaire, enfant, etudiant, senior }

Chaque ensemble de valeur fait alors partie d'un type distinct. Par exemple, pile et face deviennent des valeurs du type PileOuFace. Le nouveau type peut être utilisé comme n'importe quel type lors de la définition de variables :

 
Sélectionnez
PileOuFace resultat;         // initialisé à la valeur par défaut
auto tq = PileOuFace.pile;   // type inféré

Comme nous l'avons vu dans les codes précédents, les valeurs des types énumérés sont toujours spécifiées au moyen du nom de leur type :

 
Sélectionnez
if (resultat == PileOuFace.pile) {
   // ...
}

35-3. Valeurs réelles et types de base

Les valeurs des types énumérés sont normalement implémentées, en interne, par des valeurs int. Autrement dit, même si elles apparaissent comme des valeurs nommées comme pile et face dans le code, elles sont en réalité des valeurs int. (Note : il est possible de choisir un autre type que int lorsque c'est nécessaire.)

Sauf dans le cas où c'est explicitement indiqué par le programmeur, les valeurs int commencent par 0 et sont incrémentées de 1 pour chaque valeur énumérée. Par exemple, les deux valeurs de PileOuFace ont les valeurs 0 et 1 :

 
Sélectionnez
writeln("pile vaut 0 : ", (PileOuFace.pile == 0));
writeln("face vaut 1 : ", (PileOuFace.face == 1));

La sortie :

 
Sélectionnez
pile vaut 0 : true
face vaut 1 : true

Il est possible de réinitialiser manuellement les valeurs à n'importe quel endroit. Cela a été le cas quand on a donné la valeur 1 à Operation.ajouter. L'exemple suivant réinitialise les valeurs deux fois :

 
Sélectionnez
enum Test { a, b, c, ç = 100, d, e, f = 222, g, ğ }
writefln("%d %d %d", Test.b, Test.ç, Test.ğ);

La sortie :

 
Sélectionnez
0

Si int n'est pas adapté en tant que type de base des valeurs énumérées, le type de base peut être indiqué de façon explicite après le nom de l'énumération :

 
Sélectionnez
enum ConstanteNaturelle : double { pi = 3.14, e = 2.72 }
enum UnitéDeTempérature : string { C = "Celsius", F = "Fahrenheit" }

35-4. Des valeurs énumérées sans type énuméré

Nous avons vu qu'il était important d'éviter les constantes magiques et qu'il valait mieux se servir des énumérations.

Cependant, parfois, il peut ne pas être naturel d'utiliser les noms des types énumérés pour simplement utiliser des constantes nommées. Supposons que l'on ait besoin de représenter le nombre de secondes par jour. Il ne devrait pas être nécessaire de définir également un type énuméré pour cette constante. Tout ce dont on a besoin est une valeur constante à laquelle on peut se référer par son nom. Dans de tels cas, le type de l'énumération et les accolades ne sont pas écrits :

 
Sélectionnez
enum secondesParJour = 60 * 60 * 24;

Le type de la valeur peut être spécifié explicitement, ce qui est nécessaire si le type ne peut pas être inféré depuis la valeur :

 
Sélectionnez
enum int secondesParJour = 60 * 60 * 24;

Comme il n'y a pas de type énuméré auquel se référer, de telles constantes nommées peuvent être simplement utilisées dans le code par leur nom :

 
Sélectionnez
nombreTotalDeSecondes = nombreDeJours * secondesParJour;

enum peut également être utilisé pour définir des constantes nommées d'autres types. Par exemple, le type de la constante suivante est string :

 
Sélectionnez
enum nomDeFichier = "list.txt";

35-5. Propriétés

Les propriétés .min et .max sont les valeurs minimale et maximale du type énuméré. Quand les valeurs du type énuméré sont consécutives, elles peuvent être itérées dans une boucle for entre ces limites :

 
Sélectionnez
enum Couleur { pique, cœur, carreau, trèfle }
 
for (auto couleur = Couleur.min; couleur <= Couleur.max; ++couleur) {
   writefln("%s : %d", couleur, couleur);
}

Les indicateurs de format "%s" et "%d" produisent des sorties différentes :

 
Sélectionnez
pique : 0
cœur : 1
carreau : 2
trèfle : 3

Notez qu'une boucle foreach sur cet intervalle ne considérerait pas la valeur .max :

 
Sélectionnez
foreach (couleur; Couleur.min .. Couleur.max) {
   writefln("%s : %d", couleur, couleur);
}

La sortie :

 
Sélectionnez
pique : 0
cœur : 1
carreau : 2
    ← il manque le trèfle

35-6. Conversion depuis le type de base

Comme nous l'avons vu dans la sortie formatée ci-dessus, une valeur énumérée peut être automatiquement convertie vers son type de base (par ex. vers int). La conversion inverse n'est pas automatique :

 
Sélectionnez
Couleur couleur = 1;   //  ERREUR de compilation

La raison à cela est d'éviter de se retrouver avec des valeurs énumérées invalides :

 
Sélectionnez
couleur = 100;         //  cela serait une valeur énumérée invalide

Les valeurs que l'on sait pouvoir correspondre à des valeurs énumérées valides d'un type énuméré particulier peuvent quand même être converties vers ce type par un cast explicite :

 
Sélectionnez
couleur = cast(Couleur)1;   // devient cœur

Il est à la charge du programmeur de s'assurer de la validité des valeurs quand un cast explicite est utilisé. Nous verrons les conversions de type et les casts dans des chapitres ultérieurs.

35-7. Exercice

Modifiez le programme de calculatrice des exercices du chapitre sur les entiers et les opérations arithmétiquesNombres entiers et opérations arithmétiques en faisant choisir à l'utilisateur l'opération arithmétique dans un menu.

Ce programme doit différer du précédent par au moins ces points :

  • utilisez des valeurs énumérées, pas des constantes magiques ;
  • utilisez double à la place d'int ;
  • utilisez une instruction switch à la place de la chaîne « if, else if, else ».

La solutionLes énumérations (enum) - Correction.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+