Приведение Int к общему перечислению в C#


похоже на приведение int к enum в C#, но мое перечисление является параметром универсального типа. Что такое лучшие способ справиться с этим?

пример:

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

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

полный код выглядит следующим образом, где значение может содержать int или 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;
}
5 57

5 ответов:

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

return (T)(object)i.Value;

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

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

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

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

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

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

альтернативно, если вы можете получить перечисление не как общий тип, А как тип, то просто используйте

Enum.ToObject

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

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