85 votes

Comment convertir un enum générique en int ?

J'ai une petite méthode qui ressemble à ceci :

public void SetOptions<T>() where T : Enum
{
    int i = 0;
    foreach (T obj in Enum.GetValues(typeof(T)))
    {
        if (i == 0)
            DefaultOption = new ListItem(obj.Description(), obj.ToString());
        i++;
        DropDownList.Items.Add(new ListItem(obj.Description(), obj.ToString()));
    }
}

En fait, j'alimente une liste déroulante à partir d'une énumération. Description() est en fait une méthode d'extension pour les enums, donc T est définitivement un enum .

Cependant, je veux lancer obj comme vous le feriez pour n'importe quel enum à son index, comme ceci (int)obj mais je reçois une erreur disant que je ne peux pas convertir T en int. Existe-t-il un moyen de le faire ?

70voto

NtFreX Points 3818

Vous pouvez également utiliser la valeur object d'abord, puis à int .

C# 7.3 et supérieur

Avec la Enum contrainte générique.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => (int)(object)value;

En dessous de C# 7.3

Sans la Enum contrainte générique.

public static int EnumToInt<TValue>(this TValue value)  where TValue : struct, IConvertible
{
    if(!typeof(TValue).IsEnum)
    {
        throw new ArgumentException(nameof(value));
    }

    return (int)(object)value;
}

Si votre enum hérite d'autres types, par exemple de byte la conversion en int lancera un InvalidCastException .

Vous pouvez vérifier si le type de base de l'énumération est un entier.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
{
    if (!typeof(int).IsAssignableFrom(Enum.GetUnderlyingType(typeof(TValue))))
        throw new ArgumentException(nameof(TValue));

    return (int)(object)value;
}

Ou vous si vous utilisez Convert.ToInt32 il utilisera le IConvertible de int32 pour convertir les types incompatibles.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => Convert.ToInt32(value);

Sachez toutefois que la conversion uint a int et les paires signé/non signé peuvent entraîner des comportements non souhaités. (Encadrement à IConvertible et la conversion est moins performante que le simple déballage).


Je recommande de créer une méthode pour chaque type de base d'énumération afin de s'assurer que le résultat correct est renvoyé.

57voto

petchirajan Points 3922

Essayez ceci,

public void SetOptions<T>()
{
    Type genericType = typeof(T);
    if (genericType.IsEnum)
    {
        foreach (T obj in Enum.GetValues(genericType))
        {
            Enum test = Enum.Parse(typeof(T), obj.ToString()) as Enum;
            int x = Convert.ToInt32(test); // x is the integer value of enum
                        ..........
                        ..........
        }
    }
}

7voto

ForeverZer0 Points 1856

Si vous ciblez .NET Core, vous pouvez utiliser Unsafe.As<TFrom, TTo> dans le System.Runtime.CompilerServices comme expliqué sur le site MSDN . L'avantage est qu'il n'y aura pas de mise en boîte, ce qui est le seul véritable coût de performance dans les autres réponses.

private static int EnumToInt<TEnum>(TEnum enumValue) where TEnum : Enum
{
    return Unsafe.As<TEnum, int>(enumValue);
}

Notez que cette approche souffre du même problème de sécurité que les autres réponses existantes : il n'y a pas de garantie que l'énumération donnée est compatible avec les règles de l'Union européenne. int ce qui est probablement la même raison pour laquelle cette fonctionnalité n'est pas intégrée. Si vous utilisez cette approche en interne et que vous pouvez être sûr que tout enum qui lui est passé est d'un type compatible, il s'agit probablement de l'approche la plus efficace.

Ici est un lien vers une question sur la page GitHub de dotnet où cette question a été soulevée, et certains des développeurs ont élaboré un peu sur cette approche si vous souhaitez en savoir plus.

7voto

scott Points 473

Voici ma solution pour C# 7.3 et plus. Elle ne correspond pas exactement à la question de l'OP, mais elle est probablement utile pour les personnes qui la trouvent sur Google. Le principal avantage par rapport aux autres réponses est qu'il renvoie un ulong, ce qui signifie que n'importe quel type d'enum autorisé peut s'y intégrer. J'ai également créé un comparaison du code machine pour cette réponse et quelques autres. Oui, je m'ennuyais et j'avais envie d'un peu d'optimisation prématurée.

private static unsafe ulong EnumAsUInt64<T>(T item) where T : unmanaged, Enum
{
    ulong x;
    if (sizeof(T) == 1)
        x = *(byte*)(&item);
    else if (sizeof(T) == 2)
        x = *(ushort*)(&item);
    else if (sizeof(T) == 4)
        x = *(uint*)(&item);
    else if (sizeof(T) == 8)
        x = *(ulong*)(&item);
    else
        throw new ArgumentException("Argument is not a usual enum type; it is not 1, 2, 4, or 8 bytes in length.");
    return x;
}

5voto

Alex Joukovsky Points 675

Celui-ci fonctionne avec n'importe quel type de sous-jacent

Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()))

Par exemple, si vous souhaitez ajouter une valeur à SqlCommand qui convertit les enums en 0 et vous devez explicitement le convertir en un type correspondant. Mais nous pouvons écrire l'extension suivante :

public static void AddEnum(this SqlParameterCollection parameters, string parameterName, Enum value)
{
    parameters.AddWithValue(parameterName, Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())));
}

Qui fait tout pour nous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X