2012-04-30 3 views
50

Как и для Cast int to enum in C#, но мое перечисление является параметром Generic Type. Что такое best способ справиться с этим?Включение Int в Generic Enum в C#

Пример:

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

Формирует ошибка компиляции Cannot convert type 'int' to 'T'

Полный код выглядит следующим образом, где значение может содержать INT, или нуль.

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 - может помочь ? – Sunny

+0

Последний ответ на http://stackoverflow.com/questions/1331739/enum-type-constraints-in-c-sharp ближе к тому, что вы хотите. Тем не менее, это все еще не умно. Я склонен использовать рефлексию для этого, вы можете сделать код намного сильнее. Struct не является достаточно доступным для того, чтобы пообщаться с дженериками, на мой взгляд. –

+0

Что-то не в коробке: [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

ответ

76

Самый простой способ я нашел, чтобы заставить руку компилятора, добавив к актерскому object.

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

Если вам не нравится бокс: [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

Мы перебрасываем enum в int, а не наоборот, как в вопросе So, который вы связываете. Кроме того, этот вопрос не имеет решения. – MatteoSp

+0

Вы также можете просто выделить статический массив с значениями перечисления, а затем просто перейти в индекс, чтобы получить правильное перечисление. Это экономит необходимость делать любые кастинга. Пример. Для этой концепции применимы только строки 11,14 и 34): https://pastebin.com/iPEzttM4 – Krythic

11

Вы должны быть в состоянии использовать Enum.Parse для этого:

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

Эта статья говорит о разборе родовых перечислений методов extenstion:

+0

@Guvante: Я думаю, что я преобразовал значение в строку в моем примере. Вы предвидите это, вызывая проблему? –

14

Вот очень быстрое решение, которое злоупотребляет фактом что среда выполнения создает несколько экземпляров статических родовых классов. Развяжите свои внутренние демоны оптимизации!

Это действительно сияет, когда вы читаете Enums из потока в общем виде. Комбинируйте с внешним классом, который также кэширует базовый тип перечисления и битконвертер, чтобы развязать удивительный.

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

Результатов на ядро ​​i7-3740QM с поддержкой оптимизацией:

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

Это действительно приятно, спасибо. Вместо этого вы можете использовать 'Expression.ConvertChecked', так что числовое переполнение диапазона типа enum приводит к' 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); 
     } 
    } 
Смежные вопросы