Вопрос по generics, c#, casting, enums – Приведите Int к Generic Enum в C #

63

Похожий наПриведите int к перечислению в C # но мое перечисление является параметром Generic Type. Чтоbest способ справиться с этим?

Пример:

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

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

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

<code>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;
}
</code>
Последний ответ наstackoverflow.com/questions/1331739/…, ближе к твоему. Это все еще не умно, хотя. Я склонен использовать рефлексию для этого, вы можете сделать код намного сильнее. На мой взгляд, структура не достаточно медлительна, чтобы возиться с дженериками. Tony Hopkinson
stackoverflow.com/questions/2745320/… - может помочь? Sunny
Что-то, что не упаковано:c-sharp-non-boxing-conversion-of-generic-enum-to-int nawfal

Ваш Ответ

5   ответов
95

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

return (T)(object)i.Value;
Если вам не нравится бокс:c-sharp-non-boxing-conversion-of-generic-enum-to-int
Мы приводим enum к int, а не наоборот, как в вопросе So, который вы связываете. Кроме того, этот вопрос не имеет решения.
Вы также можете просто выделить статический массив со значениями перечисления, а затем просто передать индекс, чтобы получить правильное перечисление. Это избавляет от необходимости делать любые виды кастинга. Пример (только строки 11, 14 и 34 имеют отношение к этой концепции):pastebin.com/iPEzttM4
15

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

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

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

Результаты на Core 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
Это действительно мило, спасибо. Вы могли бы использоватьExpression.ConvertChecked хотя вместо этого, так что числовое переполнение диапазона типа enum приводит кOverflowException.
15

Enum.Parse за это:

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

В этой статье рассказывается о разборе обобщенных перечислений для методов расширения:

Generic Enum Parsing with Extension Methods
@Guvante: я думаю, что я преобразовал значение в строку в моем примере. Вы предвидите, что это вызывает проблему?
1

если вы можете получить перечисление не как универсальный тип, а как Тип, тогда просто используйте

Enum.ToObject

https://msdn.microsoft.com/en-us/library/system.enum.toobject(v=vs.110).aspx

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

Похожие вопросы