2012-04-30 22 views
50

Simile a Cast int to enum in C# ma il mio enum è un parametro di tipo generico. Qual è il modo migliore per gestire questo ?Cast Int to Enum generico in C#

Esempio:

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

genera l'errore di compilatore Cannot convert type 'int' to 'T'

codice completa è come segue, dove valore può contenere l'int o nullo.

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; 
} 
+0

http://stackoverflow.com/questions/2745320/enum-tryparse-with-flags-attribute - potrebbero aiutare ? – Sunny

+0

Ultima risposta su http://stackoverflow.com/questions/1331739/enum-type-constraints-in-c-sharp, è più vicino a ciò che desideri. Comunque non è ancora intelligente. Tendo a usare la riflessione per questo, puoi rendere il codice molto più forte. La Struct non è abbastanza restrittiva per fare confusione con i generici, a mio avviso. –

+0

Qualcosa che non box: [c-sharp-non-boxing-conversion-of-generic-enum-to-int] (http://stackoverflow.com/questions/1189144/c-sharp-non-boxing- conversion-of-generic-enum-to-int) – nawfal

risposta

76

Il modo più semplice che ho trovato è quello di forzare la mano del compilatore con l'aggiunta di un cast di object.

return (T)(object)i.Value; 
+10

Se non ti piace il pugilato: [c-sharp-non-boxing-conversion-of-generic-enum-to-int] (http://stackoverflow.com)/questions/1189144/c-sharp-non-boxing-conversion-of-generic-enum-to-int) – nawfal

+2

Stiamo lanciando enum a int, non il contrario come nella domanda So che si collega. Inoltre, questa domanda non ha soluzione. – MatteoSp

+0

Si potrebbe anche solo allocare un array statico con i valori enum e quindi passare semplicemente l'indice per recuperare l'enum corretto. Questo evita di dover fare qualsiasi tipo di casting. Esempio (Solo la riga 11,14 e 34 sono rilevanti per questo concetto): https://pastebin.com/iPEzttM4 – Krythic

11

Si dovrebbe essere in grado di utilizzare Enum.Parse per questo:

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

Questo articolo parla di analisi enumerazioni generici per i metodi extenstion:

+0

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

14

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

Questo brilla davvero quando leggi Enums da un flusso in modo generico. Combina con una classe esterna che memorizza nella cache anche il tipo sottostante all'enumerazione e un BitConverter per scatenare l'impressionante.

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 sul Core i7-3740QM con ottimizzazioni abilitati:

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 
+0

Questo è davvero carino, grazie. Si potrebbe preferire utilizzare 'Expression.ConvertChecked', tuttavia, in modo che l'overflow numerico dell'intervallo del tipo enum risulti in un 'OverflowException'. –

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); 
     } 
    }