Menu

Enum en C# : valeurs, ToString, Parse, Flags et parcours

Comment fonctionnent les enums en C# : déclarer des constantes nommées, valeurs entières sous-jacentes et casts, convertir un enum en chaîne et une chaîne en enum avec Parse et TryParse, lister toutes les valeurs, [Flags] avec les opérateurs bit à bit et HasFlag, faire un switch sur un enum, et gérer les valeurs non définies.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un enum (énumération) est un type dont les valeurs forment un ensemble fixe de constantes nommées : statuts de commande, jours de la semaine, niveaux de log. En dessous, chaque nom est un entier, mais le système de types empêche de confondre un OrderStatus avec un simple int ou avec un autre enum.

Déclarer et utiliser un enum

Listez les noms des membres entre accolades. Par défaut, le premier vaut 0 et chacun des suivants vaut un de plus :

Sortie :

Paid
On its way
True
2

L'enum est un vrai type : une méthode qui prend un OrderStatus ne peut pas être appelée par erreur avec 3 ou avec un LogLevel. Les enums sont des types valeur, ils ne valent donc jamais null et se comparent par valeur avec ==.

Valeurs explicites et type sous-jacent

Vous pouvez affecter les nombres vous-même. C'est important dès que le nombre sort de votre programme (une colonne de base de données, un statut HTTP, un format de fichier), car une renumérotation casse alors les données stockées :

Sortie :

404
Created
418
1
Byte

Deux choses à remarquer. Caster un int vers un enum n'échoue jamais : (HttpStatus)418 est une valeur valide qui n'a simplement pas de nom, et elle s'affiche sous forme de nombre. Et le type sous-jacent peut être n'importe quel type entier (byte, short, long, ...), ce qui ne compte que pour du code sensible au stockage ; int est la valeur par défaut et presque toujours le bon choix.

Quand vous ajoutez des membres plus tard, ajoutez-les à la fin ou donnez-leur des valeurs explicites. Insérer Refunded entre Paid et Shipped change silencieusement le numéro de chaque membre qui suit.

Enum vers chaîne

ToString() renvoie le nom du membre, qui est aussi ce qu'utilisent Console.WriteLine et l'interpolation de chaînes. Des chaînes de format changent la sortie :

Sortie :

Warning
2
00000002
[Warning]
Error
Error
Needs attention

Les noms de membres sont des identifiants, ils ne peuvent donc pas contenir d'espaces et ne sont pas traduits. Pour le texte montré aux utilisateurs, associez les valeurs vous-même, comme le fait Label, ou avec un Dictionary<LogLevel, string>. Certaines bases de code placent un attribut [Description("Needs attention")] sur chaque membre et le lisent par réflexion ; la page sur la réflexion et les attributs montre comment fonctionne cette recherche.

Chaîne vers enum : Parse et TryParse

Enum.Parse reconvertit un nom en valeur et lève ArgumentException si rien ne correspond. Enum.TryParse renvoie false à la place, ce qu'il vous faut pour toute entrée que vous ne contrôlez pas :

Sortie :

Large
Medium
Parse threw ArgumentException for Huge
small  parsed=True  value=Small  defined=True
XL     parsed=False value=Small  defined=True
2      parsed=True  value=Large  defined=True
7      parsed=True  value=7      defined=False

Les deux dernières lignes sont le piège. Les deux méthodes acceptent des chaînes numériques, donc "7" s'analyse avec succès en un Size sans nom. Et un TryParse qui échoue met le résultat à 0, qui est ici la valeur d'apparence valide Small. Quand le texte vient d'une query string, d'un fichier de configuration ou d'un formulaire, vérifiez toujours à la fois la valeur de retour et Enum.IsDefined :

if (Enum.TryParse(input, true, out Size size) && Enum.IsDefined(typeof(Size), size))
{
    // safe to use size
}

.NET Core 2.0 et plus ajoutent un Enum.Parse<Size>("Large") générique qui ne demande pas de cast.

Lister toutes les valeurs

Enum.GetValues renvoie chaque membre, triés par valeur numérique (comparée comme non signée, donc les membres négatifs viennent en dernier) ; Enum.GetNames renvoie leurs noms. C'est ainsi qu'on remplit une liste déroulante ou qu'on valide contre toutes les options :

Sortie :

Free       0 EUR/month
Starter    9 EUR/month
Pro       29 EUR/month
Team      99 EUR/month
Free | Starter | Pro | Team
3 paid plans

Enum.GetValues(typeof(Plan)) renvoie un simple Array, d'où le Cast<Plan>() avant LINQ. Sur .NET 5 et plus, Enum.GetValues<Plan>() renvoie directement un Plan[] typé.

Flags : combiner des valeurs

Certains enums décrivent un ensemble d'options plutôt qu'un seul choix : permissions de fichier, jours d'ouverture d'un magasin, canaux de notification. Donnez à chaque membre son propre bit (1, 2, 4, 8, ...), ajoutez None = 0 et marquez l'enum [Flags]. Les valeurs se combinent alors avec | :

Sortie :

Read, Share
Editor, Share
True
False
Editor
3
Read, Delete
True

Ce que fait chaque opérateur : | active des bits, & ~X les efface, ^ les inverse, et (value & X) != 0 ou value.HasFlag(X) les teste. HasFlag(X) signifie « tous les bits de X sont activés », donc HasFlag(None) est vrai pour toute valeur, et HasFlag(Editor) exige à la fois Read et Write.

Remarquez la deuxième ligne : quand une combinaison nommée couvre certains des bits activés, ToString l'utilise, donc Read | Write | Share s'affiche Editor, Share. Gardez-le en tête avant d'analyser la sortie de ToString avec autre chose qu'Enum.Parse.

L'attribut ne change pas l'arithmétique. Il change le formatage : sans [Flags], Read | Share s'affiche 9, car aucun membre n'a cette valeur à lui seul. Avec lui, ToString et Parse fonctionnent tous deux avec la forme séparée par des virgules. Les membres doivent quand même être des puissances de deux ; écrire Read, Write, Delete avec la numérotation par défaut (0, 1, 2) fait que Write | Delete vaut 3, une valeur dénuée de sens.

Faire un switch sur un enum

switch est la façon naturelle d'agir selon un enum. Incluez une branche default, car une variable enum peut contenir des valeurs sans nom :

switch (status)
{
    case OrderStatus.Pending:
    case OrderStatus.Paid:
        return "Preparing";
    case OrderStatus.Shipped:
        return "On the way";
    case OrderStatus.Delivered:
        return "Delivered";
    default:
        return "Unknown";
}

Depuis C# 8, une expression switch est plus courte. Sans branche _, le compilateur émet un avertissement : CS8509 quand un membre nommé manque, et CS8524 quand chaque nom est traité mais que les valeurs sans nom comme (OrderStatus)7 ne le sont pas :

string text = status switch
{
    OrderStatus.Pending or OrderStatus.Paid => "Preparing",   // 'or' pattern: C# 9
    OrderStatus.Shipped => "On the way",
    OrderStatus.Delivered => "Delivered",
    OrderStatus.Cancelled => "Cancelled",
    _ => throw new ArgumentOutOfRangeException(nameof(status)),
};

Valeurs par défaut et valeurs non définies

La valeur par défaut de tout enum est 0, qu'un membre ait cette valeur ou non. Les champs, les éléments de tableau et un TryParse qui échoue la produisent tous. Concevez en conséquence :

  • Faites de 0 un membre « non défini » qui a un sens (None, Unknown) plutôt qu'un vrai choix. Sinon, un champ non initialisé se lit silencieusement comme la première vraie option.
  • Validez les nombres venant de l'extérieur avec Enum.IsDefined. Pour les enums [Flags], IsDefined renvoie false pour les combinaisons qui n'ont pas de nom (Read | Share), donc vérifiez plutôt les bits : (value & ~Permissions.All) == 0 avec un membre All qui couvre tous les bits.

Erreurs courantes

  • Se fier à TryParse seul. Les chaînes numériques s'analysent, et un échec donne 0. Ajoutez Enum.IsDefined.
  • Compter sur la numérotation implicite pour des valeurs stockées. Insérer un membre renumérote ceux qui suivent. Donnez des valeurs explicites à tout enum persisté.
  • Des Flags sans puissances de deux. La numérotation par défaut (0, 1, 2, 3) fait se chevaucher les bits. Utilisez 1, 2, 4, 8, ou 1 << n.
  • Montrer ToString() aux utilisateurs. Les noms de membres sont des identifiants de code. Associez les valeurs à un texte d'affichage.
  • Pas de default dans un switch. Un enum peut contenir des valeurs en dehors de ses membres nommés.

Questions fréquentes

Comment convertir un enum en chaîne en C# ?

Appelez ToString() : OrderStatus.Shipped.ToString() renvoie "Shipped", et l'interpolation de chaînes fait de même. ToString("D") donne plutôt le nombre. Pour un nom connu à la compilation, nameof(OrderStatus.Shipped) est une constante. Pour un texte destiné aux utilisateurs, avec des espaces ou des traductions, associez vous-même les valeurs à des chaînes (un switch ou un dictionnaire) plutôt que de compter sur le nom du membre.

Comment convertir une chaîne en enum en C# ?

Utilisez Enum.TryParse<OrderStatus>(text, true, out var status), qui renvoie false au lieu de lever une exception quand le texte ne correspond à aucun membre (le true le rend insensible à la casse). Enum.Parse(typeof(OrderStatus), text) lève ArgumentException sur une entrée invalide. Les deux acceptent aussi des chaînes numériques comme "42", donc vérifiez le résultat avec Enum.IsDefined quand l'entrée vient des utilisateurs.

Comment convertir entre un enum et un int en C# ?

Castez dans un sens ou dans l'autre : int code = (int)OrderStatus.Paid; et var status = (OrderStatus)2;. Le cast depuis un int n'échoue jamais, même pour des nombres sans membre correspondant ; le résultat est une valeur d'enum qui s'affiche sous forme de nombre. Validez avec Enum.IsDefined(typeof(OrderStatus), value) quand le nombre vient de l'extérieur.

Comment parcourir toutes les valeurs d'un enum en C# ?

foreach (OrderStatus s in Enum.GetValues(typeof(OrderStatus))) visite chaque membre dans l'ordre de leurs valeurs numériques. Depuis .NET 5, il existe une version générique, Enum.GetValues<OrderStatus>(), qui ne demande pas de cast. Enum.GetNames(typeof(OrderStatus)) renvoie les noms sous forme de chaînes.

À quoi sert [Flags] sur un enum C# ?

Il marque un enum dont les valeurs sont des bits destinés à être combinés avec |, comme Read | Write. Donnez à chaque membre une puissance de deux (1, 2, 4, 8) et un None = 0. L'attribut fait afficher les combinaisons par ToString() sous la forme "Read, Write" et permet à Enum.Parse de relire ce format. Testez un bit avec HasFlag ou (value & Permissions.Write) != 0.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER