Comment convertir System.Enum en entier de base?

93

Je voudrais créer une méthode générique pour convertir n'importe quel type dérivé de System.Enum en sa valeur entière correspondante, sans conversion et de préférence sans analyse de chaîne.

Par exemple, ce que je veux, c'est quelque chose comme ceci:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Mais cela ne semble pas fonctionner. Resharper signale que vous ne pouvez pas convertir une expression de type «System.Enum» en type «int».

Maintenant, j'ai trouvé cette solution, mais je préfère avoir quelque chose de plus efficace.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Aucune suggestion?

orj
la source
1
Je crois que c'est le compilateur qui se plaint, pas Resharper.
Kugel
1
Pas nécessairement. J'ai une méthode d'extension sur System.Enum, et parfois Resharper décide de se plaindre: Impossible de convertir le type d'argument d'instance 'Some.Cool.Type.That.Is.An.Enum' en 'System.Enum' alors qu'il EST incontestablement une énumération. Si je compile et exécute le code, cela fonctionne très bien. Si j'arrête ensuite VS, supprime le cache Resharper et le rallume, tout va bien une fois la nouvelle analyse terminée. Pour moi, c'est une sorte de cache snafu. Ça pourrait être la même chose pour lui.
Mir
@Mir J'ai aussi eu une "plainte" de ReSharper à ce sujet. Même solution pour moi. Je ne sais pas pourquoi ces types sont mélangés, mais ce n'est certainement pas le compilateur.
akousmata

Réponses:

134

Si vous ne voulez pas lancer,

Convert.ToInt32()

pourrait faire l'affaire.

La diffusion directe (via (int)enumValue) n'est pas possible. Notez que ce serait aussi « dangereux » car un ENUM peut avoir différents types sous - jacents ( int, long, byte...).

Plus formellement: System.Enumn'a pas de relation d'héritage directe avec Int32(bien que les deux soient ValueTypes), donc le cast explicite ne peut pas être correct dans le système de types

MartinStettner
la source
2
Converter.ToInteger(MyEnum.MyEnumConstant);ne vous donnera aucune erreur ici. Veuillez modifier cette partie.
nawfal
Merci, @nawfal, vous avez raison. J'ai édité la réponse (et appris quelque chose de nouveau :) ...)
MartinStettner
Ne fonctionne pas si le type sous-jacent diffère deint
Alex Zhukovskiy
@YaroslavSivakov cela ne fonctionnera pas par exemple pour longenum.
Alex Zhukovskiy
System.Enumest une classe, donc c'est un type de référence. Les valeurs sont probablement encadrées.
Teejay
42

Je l'ai fait fonctionner en castant sur un objet puis sur un int:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

C'est moche et probablement pas la meilleure façon. Je vais continuer à jouer avec, pour voir si je peux trouver quelque chose de mieux ...

EDIT: J'étais sur le point de publier que Convert.ToInt32 (enumValue) fonctionne également, et j'ai remarqué que MartinStettner m'a battu.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Tester:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

EDIT 2: Dans les commentaires, quelqu'un a dit que cela ne fonctionne qu'en C # 3.0. Je viens de tester ceci dans VS2005 comme ceci et cela a fonctionné:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }
BGratuit
la source
Je vous ai donné +1 mais cette solution ne fonctionnera qu'avec C # 3.0 et supérieur.
Vadim
Je pense que cela ne satisfait que le compilateur. Avez-vous réussi à tester cela au moment de l'exécution? Je n'ai pu transmettre aucune valeur à cette fonction ...
MartinStettner
Parce que c'est une méthode d'extension? Ou est-ce que les énumérations dans les anciennes versions de C # sont différentes?
BGratuit
J'ai ajouté un test à la fin de mon message. J'ai essayé et cela a fonctionné pour moi.
BFree
@BFree: Cela ne semble fonctionner qu'avec des méthodes d'extension. Je l'ai essayé avec une fonction "à l'ancienne" (en C # 2.0) et je n'ai pas réussi à trouver la syntaxe pour lui transmettre une valeur (c'est ce que mon commentaire précédent portait)
MartinStettner
14

Si vous devez convertir une énumération en son type sous-jacent (toutes les énumérations ne sont pas sauvegardées par int), vous pouvez utiliser:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));
Drew Noakes
la source
5
C'est la seule réponse à toute épreuve.
Rudey
Est-ce la cause du déballage de la boxe?
b.ben
@ b.ben oui. Convert.ChangeTypeaccepte objectpour le premier argument et retourne object.
Drew Noakes
Oui, je suis en fait d'accord avec @Rudey à ce sujet, de toute façon, vous devriez faire des tests de performance. La solution de BFree est de loin la plus rapide. Environ huit fois plus vite. Quoi qu'il en soit, nous parlons toujours de tiques.
Vingt
10

Pourquoi avez-vous besoin de réinventer la roue avec une méthode d'aide? Il est parfaitement légal de convertir une enumvaleur en son type sous-jacent.

C'est moins typant, et à mon avis plus lisible, à utiliser ...

int x = (int)DayOfWeek.Tuesday;

... plutôt que quelque chose comme ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
LukeH
la source
6
Je veux convertir N'IMPORTE QUELLE valeur d'énumération. Par exemple, j'ai une variété de classes qui ont des champs d'énumération qui sont traités par du code de manière générique. C'est pourquoi un simple cast en int n'est pas approprié.
orj
@orj, je ne suis pas vraiment sûr de ce que tu veux dire. Pouvez-vous donner un exemple montrant l'utilisation dont vous avez besoin?
LukeH
2
Parfois, vous ne pouvez pas convertir directement une énumération, comme dans le cas d'une méthode générique. Étant donné que les méthodes génériques ne peuvent pas être contraintes à des énumérations, vous devez les contraindre à quelque chose comme struct, qui ne peut pas être converti en un int. Dans ce cas, vous pouvez utiliser Convert.ToInt32 (enum) pour le faire.
Daniel T.
J'aime l'idée que la méthode d'extension ToInteger () suit le même format que ToString (). Je pense qu'il est moins lisible de convertir en int d'une part lorsque vous voulez la valeur int et d'utiliser ToString () lorsque nous avons besoin de la valeur de chaîne, en particulier lorsque le code est côte à côte.
Louise Eggleton
En outre, l'utilisation d'une méthode d'extension peut ajouter de la cohérence à votre base de code. Puisque, comme @nawfal l'a souligné, il existe plusieurs façons d'obtenir la valeur int à partir d'une enum, créer une méthode d'extension et appliquer son utilisation signifie que chaque fois que vous obtenez la valeur int d'une enum, vous utilisez la même méthode
Louise Eggleton
4

De ma réponse ici :

Donné ecomme dans:

Enum e = Question.Role;

Ensuite, ces travaux:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Les deux derniers sont vraiment laids. Le premier devrait être plus lisible, bien que le second soit beaucoup plus rapide. Ou peut-être qu'une méthode d'extension est la meilleure, le meilleur des deux mondes.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Vous pouvez maintenant appeler:

e.GetValue<int>(); //or
e.GetIntValue();
nawfal
la source
Êtes-vous sûr que le second est plus rapide? Je suppose que cela encapsulera l'énumération, puis retournera à int?
yoyo
1
@yoyo la evariable a déjà l'instance encadrée de type valeur réelle, c'est-à-dire enum. Lorsque vous écrivez, Enum e = Question.Role;il est déjà encadré. La question est de reconvertir le boxed System.Enumen type int sous-jacent (unboxing). Donc, ici, le déballage n'est que le problème des performances. (int)(object)eest un appel de déballage direct; oui devrait être plus rapide que les autres approches. Voir ça
nawfal
Merci pour l'explication. J'avais l'impression erronée qu'une instance de System.Enum était une valeur sans boîte. Voir aussi - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo
@yoyo hmm oui. La citation pertinente du lien: De tout type enum au type System.Enum.
nawfal
1

Casting from a System.Enumto an intfonctionne très bien pour moi (c'est aussi sur MSDN ). C'est peut-être un bogue Resharper.

jpoh
la source
1
Quelle version du runtime que vous utilisez?
JoshBerke
2
le lien montre le casting de sous-types de System.Enum, pas System.Enumlui-même.
nawfal
Un problème similaire que j'ai eu était un snafu de cache Resharper. La fermeture de VS et la suppression du cache Resharper ont fait disparaître l'erreur, même après une nouvelle analyse complète.
Mir
1

Puisque les Enums sont limités à byte, sbyte, short, ushort, int, uint, long et ulong, nous pouvons faire certaines hypothèses.

Nous pouvons éviter les exceptions lors de la conversion en utilisant le plus grand conteneur disponible. Malheureusement, quel conteneur utiliser n'est pas clair car ulong explosera pour les nombres négatifs et long explosera pour les nombres entre long.MaxValue et ulong.MaxValue. Nous devons basculer entre ces choix en fonction du type sous-jacent.

Bien sûr, vous devez toujours décider quoi faire lorsque le résultat ne rentre pas dans un int. Je pense que le casting est correct, mais il y a encore des pièges:

  1. pour les énumérations basées sur un type avec un espace de champ plus grand que int (long et ulong), il est possible que certaines énumérations évaluent à la même valeur.
  2. la conversion d'un nombre supérieur à int.MaxValue lèvera une exception si vous êtes dans une région cochée.

Voici ma suggestion, je laisserai au lecteur le soin de décider où exposer cette fonction; comme aide ou extension.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}
éisenpony
la source
-1

N'oubliez pas que le type Enum lui-même contient un tas de fonctions d'assistance statiques. Si tout ce que vous voulez faire est de convertir une instance de l'énumération en son type entier correspondant, alors la conversion est probablement le moyen le plus efficace.

Je pense que ReSharper se plaint car Enum n'est pas une énumération d'un type particulier, et les énumérations elles-mêmes dérivent d'un type de valeur scalaire, pas Enum. Si vous avez besoin d'un casting adaptable de manière générique, je dirais que cela pourrait bien vous convenir (notez que le type d'énumération lui-même est également inclus dans le générique:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Cela pourrait alors être utilisé comme ceci:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Je ne peux pas le dire avec certitude, mais le code ci-dessus pourrait même être pris en charge par l'inférence de type C #, permettant ce qui suit:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);
jrista
la source
1
Malheureusement, dans votre exemple, E peut être de n'importe quel type. Les génériques ne me permettent pas d'utiliser Enum comme contrainte de paramètre de type. Je ne peux pas dire: où T: Enum
Vadim
Vous pouvez cependant utiliser where T: struct. Ce n'est pas aussi strict que vous le souhaitez, mais cela supprimerait tout type de référence (ce que je pense que c'est ce que vous essayez de faire.)
jrista
vous pouvez utiliser: if (! typeof (E) .IsEnum) throw new ArgumentException ("E doit être un type énuméré");
diadiora
1
Ce n'est même pas correct à distance, l'assistant statique n'est même pas valide.
Nicholi
1
Pourquoi quelqu'un devrait-il utiliser à la EnumHelpers.Convert<int, StopLight>(StopLight.Red);place de (int)StopLight.Read;? Aussi la question parle System.Enum.
nawfal