Comment un int
être jeté à un enum
en C #?
Comment un int
être jeté à un enum
en C #?
D'une chaîne:
YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);
// the foo.ToString().Contains(",") check is necessary for enumerations marked with an [Flags] attribute
if (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))
throw new InvalidOperationException($"{yourString} is not an underlying value of the YourEnum enumeration.")
D'un int:
YourEnum foo = (YourEnum)yourInt;
Mettre à jour:
De numéro, vous pouvez également
YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum) , yourInt);
Juste le jeter:
MyEnum e = (MyEnum)3;
Vous pouvez vérifier si c'est dans la gamme en utilisant Enum.IsDefined:
if (Enum.IsDefined(typeof(MyEnum), 3)) { ... }
Vous pouvez également utiliser une méthode d'extension au lieu d'une seule ligne:
public static T ToEnum<T>(this string enumString)
{
return (T) Enum.Parse(typeof (T), enumString);
}
Usage:
Color colorEnum = "Red".ToEnum<Color>();
OU
string color = "Red";
var colorEnum = color.ToEnum<Color>();
Je pense que pour avoir une réponse complète, les gens doivent savoir comment fonctionnent les enums en interne .NET.
Comment ça fonctionne
Une énumération dans .NET est une structure qui mappe un ensemble de valeurs (champs) à un type de base (la valeur par défaut est int
). Cependant, vous pouvez réellement choisir le type intégral auquel votre énumération correspond:
public enum Foo : short
Dans ce cas, l'énumération est mappée au short
type de données, ce qui signifie qu'il sera stocké dans la mémoire comme un court et se comportera comme un court lorsque vous lancez et l'utilisez.
Si vous le regardez d'un point de vue IL, une enum (normal, int) ressemble à ceci:
.class public auto ansi serializable sealed BarFlag extends System.Enum
{
.custom instance void System.FlagsAttribute::.ctor()
.custom instance void ComVisibleAttribute::.ctor(bool) = { bool(true) }
.field public static literal valuetype BarFlag AllFlags = int32(0x3fff)
.field public static literal valuetype BarFlag Foo1 = int32(1)
.field public static literal valuetype BarFlag Foo2 = int32(0x2000)
// and so on for all flags or enum values
.field public specialname rtspecialname int32 value__
}
Ce qui devrait attirer votre attention ici, c'est que le value__
est stocké séparément des valeurs enum. Dans le cas de l'enum Foo
ci-dessus, le type de value__
est int16. Cela signifie essentiellement que vous pouvez stocker ce que vous voulez dans une énumération, tant que les types correspondent.
À ce stade, je voudrais souligner que System.Enum
est un type de valeur, ce qui signifie essentiellement que BarFlag
prendra 4 octets en mémoire et Foo
prendra 2 - par ex. la taille du type sous-jacent (c'est en fait plus compliqué que ça, mais bon ...).
La réponse
Donc, si vous avez un nombre entier que vous voulez mapper à une énumération, le moteur d'exécution doit seulement faire 2 choses: copier les 4 octets et le nommer quelque chose d'autre (le nom de l'énumération). La copie est implicite parce que les données sont stockées en tant que type de valeur - cela signifie essentiellement que si vous utilisez du code non géré, vous pouvez simplement échanger des énumérations et des entiers sans copier de données.
Pour le rendre sûr, je pense que c'est une bonne pratique savoir que les types sous-jacents sont les mêmes ou implicitement convertibles et pour s'assurer que les valeurs enum existent (elles ne sont pas vérifiées par défaut!).
Pour voir comment cela fonctionne, essayez le code suivant:
public enum MyEnum : int
{
Foo = 1,
Bar = 2,
Mek = 5
}
static void Main(string[] args)
{
var e1 = (MyEnum)5;
var e2 = (MyEnum)6;
Console.WriteLine("{0} {1}", e1, e2);
Console.ReadLine();
}
Notez que le casting vers e2
fonctionne aussi! Du point de vue du compilateur ci-dessus, cela a du sens: le value__
champ est simplement rempli avec 5 ou 6 et quand Console.WriteLine
appels ToString()
, le nom de e1
est résolu alors que le nom de e2
n'est pas.
Si ce n'est pas ce que vous vouliez, utilisez Enum.IsDefined(typeof(MyEnum), 6)
pour vérifier si la valeur que vous lancez correspond à une énumération définie.
Notez également que je suis explicite sur le type sous-jacent de l'énumération, même si le compilateur vérifie réellement cela. Je fais cela pour m'assurer de ne pas avoir de surprises sur la route. Pour voir ces surprises en action, vous pouvez utiliser le code suivant (en fait, j'ai vu cela se produire beaucoup dans le code de la base de données):
public enum MyEnum : short
{
Mek = 5
}
static void Main(string[] args)
{
var e1 = (MyEnum)32769; // will not compile, out of bounds for a short
object o = 5;
var e2 = (MyEnum)o; // will throw at runtime, because o is of type int
Console.WriteLine("{0} {1}", e1, e2);
Console.ReadLine();
}
Prenons l'exemple suivant:
int one = 1;
MyEnum e = (MyEnum)one;
J'utilise ce morceau de code pour lancer int à mon énumération:
if (typeof(YourEnum).IsEnumDefined(valueToCast)) return (YourEnum)valueToCast;
else { //handle it here, if its not defined }
Je trouve que c'est la meilleure solution.
Voici une belle classe d'utilité pour Enums
public static class EnumHelper
{
public static int[] ToIntArray<T>(T[] value)
{
int[] result = new int[value.Length];
for (int i = 0; i < value.Length; i++)
result[i] = Convert.ToInt32(value[i]);
return result;
}
public static T[] FromIntArray<T>(int[] value)
{
T[] result = new T[value.Length];
for (int i = 0; i < value.Length; i++)
result[i] = (T)Enum.ToObject(typeof(T),value[i]);
return result;
}
internal static T Parse<T>(string value, T defaultValue)
{
if (Enum.IsDefined(typeof(T), value))
return (T) Enum.Parse(typeof (T), value);
int num;
if(int.TryParse(value,out num))
{
if (Enum.IsDefined(typeof(T), num))
return (T)Enum.ToObject(typeof(T), num);
}
return defaultValue;
}
}
Si vous êtes prêt pour le 4.0 .NET Cadre, il y a un nouveau Enum.TryParse () fonction qui est très utile et joue bien avec l'attribut [Flags]. Voir Enum.TryParse, méthode (String, TEnum%)
Pour les valeurs numériques, cela est plus sûr car il retournera un objet, peu importe quoi:
public static class EnumEx
{
static public bool TryConvert<T>(int value, out T result)
{
result = default(T);
bool success = Enum.IsDefined(typeof(T), value);
if (success)
{
result = (T)Enum.ToObject(typeof(T), value);
}
return success;
}
}