Trasmetti Int a Enum generico in C #


86

Simile a Cast int per enumerare in C # ma il mio enum è un parametro di tipo generico. Qual è il modo migliore per gestirlo?

Esempio:

private T ConvertEnum<T>(int i) where T : struct, IConvertible
{
    return (T)i;
}

Genera un errore del compilatore Cannot convert type 'int' to 'T'

Il codice completo è il seguente, dove value può contenere int o null.

private int? TryParseInt(string value)
{
    var i = 0;
    if (!int.TryParse(value, out i))
    {
        return null;
    }
    return i;
}

private T? TryParseEnum<T>(string value) where T : struct, IConvertible
{
    var i = TryParseInt(value);
    if (!i.HasValue)
    {
        return null;
    }

    return (T)i.Value;
}


L'ultima risposta su stackoverflow.com/questions/1331739/… , è più vicina a ciò che desideri. Tuttavia non è ancora intelligente. Tendo a usare la riflessione per questo, puoi rendere il codice molto più forte. Struct non è abbastanza retrittivo per rendere utile, secondo me, scherzare con i generici.
Tony Hopkinson

Risposte:


121

Il modo più semplice che ho trovato è forzare la mano del compilatore aggiungendo un cast a object.

return (T)(object)i.Value;


5
Stiamo eseguendo il casting di enum su int, non il contrario come nel link So question you. Inoltre, quella domanda non ha soluzione.
MatteoSp

Potresti anche allocare un array statico con i valori enum e quindi passare l'indice per recuperare l'enum corretto. Ciò evita di dover eseguire qualsiasi tipo di casting. Esempio (solo le righe 11, 14 e 34 sono rilevanti per questo concetto): pastebin.com/iPEzttM4
Krythic

21

Dovresti essere in grado di utilizzare Enum.Parseper questo:

return (T)Enum.Parse(typeof(T), i.Value.ToString(), true);

Questo articolo parla dell'analisi di enumerazioni generiche per i metodi di estensione:


@Guvante: penso di aver convertito il valore in una stringa nel mio esempio. Prevedi che questo causi un problema?
James Johnson

16

Ecco una soluzione molto veloce che abusa del fatto che il runtime crea più istanze di classi generiche statiche. Scatena i tuoi demoni dell'ottimizzazione interiore!

Questo brilla davvero quando stai leggendo Enums da uno stream in modo generico. Combina con una classe esterna che memorizza anche nella cache il tipo sottostante dell'enumerazione e un BitConverter per scatenare il fantastico.

void Main() 
{
    Console.WriteLine("Cast (reference): {0}", (TestEnum)5);
    Console.WriteLine("EnumConverter: {0}", EnumConverter<TestEnum>.Convert(5));
    Console.WriteLine("Enum.ToObject: {0}", Enum.ToObject(typeof(TestEnum), 5));

    int iterations = 1000 * 1000 * 100;
    Measure(iterations, "Cast (reference)", () => { var t = (TestEnum)5; });
    Measure(iterations, "EnumConverter", () => EnumConverter<TestEnum>.Convert(5));
    Measure(iterations, "Enum.ToObject", () => Enum.ToObject(typeof(TestEnum), 5));
}

static class EnumConverter<TEnum> where TEnum : struct, IConvertible
{
    public static readonly Func<long, TEnum> Convert = GenerateConverter();

    static Func<long, TEnum> GenerateConverter()
    {
        var parameter = Expression.Parameter(typeof(long));
        var dynamicMethod = Expression.Lambda<Func<long, TEnum>>(
            Expression.Convert(parameter, typeof(TEnum)),
            parameter);
        return dynamicMethod.Compile();
    }
}

enum TestEnum 
{
    Value = 5
}

static void Measure(int repetitions, string what, Action action)
{
    action();

    var total = Stopwatch.StartNew();
    for (int i = 0; i < repetitions; i++)
    {
        action();
    }
    Console.WriteLine("{0}: {1}", what, total.Elapsed);
}

Risultati su Core i7-3740QM con ottimizzazioni abilitate:

Cast (reference): Value
EnumConverter: Value
Enum.ToObject: Value
Cast (reference): 00:00:00.3175615
EnumConverter: 00:00:00.4335949
Enum.ToObject: 00:00:14.3396366

2
È davvero carino, grazie. Ti potrebbe piacere usare Expression.ConvertCheckedinvece, però, in modo che l'overflow numerico dell'intervallo del tipo enum si traduca in un file OverflowException.
Drew Noakes

Il tuo chilometraggio potrebbe variare, ho eseguito il codice su try.dot.net (blazor) e lì EnumConverter <T> è molto più lento delle alternative. Il lancio sull'oggetto per primo è stato circa 6 volte più lento di un cast diretto, ma comunque molto meglio delle altre opzioni.
Herman



0
public static class Extensions
    {
        public static T ToEnum<T>(this int param)
        {
            var info = typeof(T);
            if (info.IsEnum)
            {
                T result = (T)Enum.Parse(typeof(T), param.ToString(), true);
                return result;
            }

            return default(T);
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.