Conversión de Int a Genérico Enum en C #


84

Similar a Cast int to enum en C # pero mi enum es un parámetro de tipo genérico. ¿Cuál es la mejor manera de manejar esto?

Ejemplo:

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

Genera error del compilador Cannot convert type 'int' to 'T'

El código completo es el siguiente, donde el valor puede contener 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;
}


La última respuesta en stackoverflow.com/questions/1331739/… está más cerca de lo que desea. Sin embargo, todavía no es inteligente. Tiendo a usar la reflexión para esto, puedes hacer que el código sea mucho más fuerte. Struct no es lo suficientemente retrictivo como para hacer que valga la pena jugar con genéricos en mi opinión.
Tony Hopkinson

Respuestas:


120

La forma más sencilla que he encontrado es forzar la mano del compilador agregando un molde a object.

return (T)(object)i.Value;


5
Estamos lanzando enum a int, no al contrario, como en el enlace So question you. Además, esa pregunta no tiene solución.
MatteoSp

También puede asignar una matriz estática con los valores de enumeración y luego simplemente pasar el índice para recuperar la enumeración correcta. Esto evita tener que hacer cualquier tipo de casting. Ejemplo (solo las líneas 11, 14 y 34 son relevantes para este concepto): pastebin.com/iPEzttM4
Krythic


16

Aquí hay una solución muy rápida que abusa del hecho de que el tiempo de ejecución crea múltiples instancias de clases genéricas estáticas. ¡Desata tus demonios internos de optimización!

Esto realmente brilla cuando estás leyendo Enums de una transmisión de forma genérica. Combine con una clase externa que también almacena en caché el tipo subyacente de la enumeración y un BitConverter para dar rienda suelta a lo impresionante.

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

Resultados en Core i7-3740QM con optimizaciones habilitadas:

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
Esto es muy lindo, gracias. Puede que te guste usarExpression.ConvertChecked embargo, es posible que desee en lugar, de modo que el desbordamiento numérico del rango del tipo de enumeración dé como resultado un OverflowException.
Drew Noakes

Su kilometraje puede variar, ejecuté el código en try.dot.net (blazor) y allí el EnumConverter <T> es mucho más lento que las alternativas. Lanzar al objeto primero fue aproximadamente 6 veces más lento que un lanzamiento directo, pero aún mucho mejor que las otras opciones.
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);
        }
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.