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
0un 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],IsDefinedrenvoiefalsepour les combinaisons qui n'ont pas de nom (Read | Share), donc vérifiez plutôt les bits :(value & ~Permissions.All) == 0avec un membreAllqui couvre tous les bits.
Erreurs courantes
- Se fier à
TryParseseul. Les chaînes numériques s'analysent, et un échec donne0. AjoutezEnum.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
defaultdans 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.