Accueil > .Net, C#, Développement > [C#] Les énumérations

[C#] Les énumérations

Normalement, tout le monde connait les énumérations.
Ce n’est pas les listes (héritant de IEnumerable), mais des types utilisant le mot-clef enum.

C’est bien les énumération. C’est bon, mangez-en.
Mais mal utilisé, c’est une VRAIE plaie (j’aurais bien d’autres mots fleuris, mais passons).

Au départ, je voulais juste faire un billet pour montrer un truc qui m’avait réellement agacé, mais finalement, il est un peu plus long !🙂

Voici une énumération de base :

public enum Couleur1
{
    Rouge,
    Vert,
    Bleu
}

Il est aussi possible de l’écrire de cette manière :

public enum Couleur2
{
    Rouge = 1,
    Vert = 2,
    Bleu = 3
}

Entre les deux exemples, il y a cependant un monde de différence.

Déjà, il faut bien comprendre qu’une énumération est, à la base, fonctionne comme un type int.
Ou, en fait, comme un type byte, sbyte, short, ushort, int, uint, long ou ulong. Au choix.
Mais de base, c’est un type int.
Donc, ça veut dire, accessoirement, que l’on peut mettre BEAUCOUP de valeurs dans une énumération.
De -9223372036854775808L à 9223372036854775807L pour un long, par exemple.
18.446.744.073.709.551.615 valeurs, en fait.
Ça laisse un peu de marge pour les évolutions😛

Je m’explique : une énumération, c’est simplement faire correspondre une chaîne de caractères (humainement lisible) à une valeur.
Et c’est cette valeur qui est importante.
Ce qui veut dire que TOUTES les valeurs valides pour le type sous-jacent sont valides pour l’énumération.

Ainsi, comme tout int, la valeur par défaut d’une énumération est 0.
Si l’on écrit cela :

Console.WriteLine(new Couleur1());
Console.WriteLine(new Couleur2());

Le résultat va donc être…
Rouge pour la première ligne.
0 pour la seconde ligne.
Accessoirement, cela veut aussi dire qu’il est possible de caster une énumération en int (ce qui est fait ici).

Pourquoi ?
Si l’on ne spécifie pas de valeur int associée (cas de Couleur1), alors la première valeur va avoir pour valeur 0 et donc être la valeur par défaut.
Ainsi, il est TRES important de spécifier une valeur 0 !

Le corollaire de cela ?
C’est qu’il est possible d’affecter à une énumération une valeur…qui n’est pas dans l’énumération.

Couleur1 c1 = (Couleur1)42;
Console.WriteLine(c1);

C’est bien 42 qui sera affiché…

Il faut donc réellement faire attention à ce que l’on met comme valeur.
Sinon, ça peut vite tourner au drame.

L’exemple type étant un switch.
Il peut être très confortable d’écrire ceci :

switch (c1)
{
    case Couleur1.Rouge:
        //...
        break;
    case Couleur1.Vert:
        //...
        break;
    case Couleur1.Bleu:
        //...
        break;
}

Mais dans ce cas, les valeurs que peut transporter l’énumération ne sont pas prises en compte…
Il faut donc toujours mettre un default (comme le snippet le fait de lui-même).

Autre danger des énumérations :

public enum Couleur3
{
    Rouge = 1,
    Vert = 2,
    Bleu = 3,
    Noir = 4,
    Blanc = 5,
    Violet = Rouge + Bleu, // 1 + 3 = 4
    Jaune = Vert + Bleu // 2 + 3 = 5
}

Il est tout à fait possible d’utiliser cette énumération. D’un point de vue compilateur, ça marche.
Mais…

Couleur3 c3 = (Couleur3)4;
Console.WriteLine(c3);

Quelle couleur, à votre avis ?
Noir.
Pourquoi ?
Parce que c’est la première valeur 4 déclarée, c’est Noir. Premier arrivé, premier servi…

Par contre, sur le switch, c’est une autre histoire :
Enumeration - Switch
Simplement parce que le switch, lui, il fait la résolution… Un int, je vous dis, c’est un int…

Alors, comment faire pour tester si la valeur est dans la plage ?
C’est là que l’on peut s’appuyer sur la classe abstraire Enum :

Console.WriteLine(
    Enum.IsDefined(typeof(Couleur2), 42) 
    ? "C'est dans la plage." 
    : "Hors champs.");

Ce code va gentiment afficher « Hors champs. » car 42 n’est effectivement pas dans les bornes.

Bien sûr, c’est aussi possible de le faire sur les noms :

Couleur2 result;
if (Enum.TryParse("Bleu", out result))
{
    Console.WriteLine("C'est dans la plage." );
}
else
{
    Console.WriteLine("Hors champs.");
}

Après, il y a aussi les énumérations avec l’attribut Flags.
Là, je vais simplement vous rediriger vers deux billets qui sont, à mon sens, plus qu’assez explicatifs :
C# Fundamentals: Combining Enum Values with Bit-Flags
The Beginner’s Guide to Using Enum Flags

Je rajouterais uniquement les types d’écriture possibles :

// En puissance de 2 sur un système décimal
[Flags]
public enum Cloud
{
    OVH = 0,
    Ikoula = 1,
    Microsoft = 2,
    Google = 4,
    Amazon = 8,
    Dropbox = 16,
    Drive = 32,
    HubiC = 64,
    iCloud = 128
}
// En puissance de 2 sur un système héxadécimal
[Flags]
public enum Cloud2
{
    OVH = 0x0,
    Ikoula = 0x1,
    Microsoft = 0x2,
    Google = 0x4,
    Amazon = 0x8,
    Dropbox = 0x10,
    Drive = 0x20,
    HubiC = 0x40,
    iCloud = 0x80
}
// Probablement la plus simple à se rappeler
[Flags]
public enum Cloud3
{
    OVH = 0,
    Ikoula = 1 << 0,
    Microsoft = 1 << 1,
    Google = 1 << 2,
    Amazon = 1 << 3,
    Dropbox = 1 << 4,
    Drive = 1 << 5,
    HubiC = 1 << 6,
    iCloud = 1 << 7,
}
Catégories :.Net, C#, Développement
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :