Cast Int zu Generic Enum in C #


84

Ähnlich wie Cast int to enum in C #, aber meine Aufzählung ist ein generischer Typparameter. Was ist der beste Weg, um damit umzugehen?

Beispiel:

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

Erzeugt einen Compilerfehler Cannot convert type 'int' to 'T'

Der vollständige Code lautet wie folgt: Der Wert kann int oder null enthalten.

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


Die letzte Antwort auf stackoverflow.com/questions/1331739/… ist näher an Ihren Wünschen . Es ist aber immer noch nicht klug. Ich neige dazu, Reflexion dafür zu verwenden, Sie können den Code viel stärker machen. Struct ist meiner Meinung nach nicht zurückhaltend genug, um das Herumspielen mit Generika lohnenswert zu machen.
Tony Hopkinson

1
Etwas, das nicht boxt
nawfal

Antworten:


120

Der einfachste Weg, den ich gefunden habe, besteht darin, die Hand des Compilers durch Hinzufügen eines Casts zu erzwingen object.

return (T)(object)i.Value;


5
Wir geben eine Aufzählung zu int ab, nicht das Gegenteil wie in der von Ihnen verlinkten So-Frage. Auch diese Frage hat keine Lösung.
MatteoSp

Sie können auch einfach ein statisches Array mit den Aufzählungswerten zuweisen und dann einfach den Index übergeben, um die richtige Aufzählung abzurufen. Dies erspart jede Art von Casting. Beispiel (Nur 11,14 Zeile und 34 auf dieses Konzept relevant sind): pastebin.com/iPEzttM4
Krythic

20

Sie sollten in der Lage sein, Enum.Parsedies zu verwenden:

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

Dieser Artikel befasst sich mit dem Parsen generischer Aufzählungen für Erweiterungsmethoden:


@Guvante: Ich glaube, ich habe den Wert in meinem Beispiel in einen String konvertiert. Sehen Sie voraus, dass dies ein Problem verursacht?
James Johnson

16

Hier ist eine sehr schnelle Lösung, die die Tatsache missbraucht, dass die Laufzeit mehrere Instanzen statischer generischer Klassen erstellt. Entfessle deine inneren Optimierungsdämonen!

Dies scheint wirklich zu leuchten, wenn Sie Enums aus einem Stream generisch lesen. Kombiniere es mit einer äußeren Klasse, die auch den zugrunde liegenden Typ der Enumeration zwischenspeichert, und einem BitConverter, um das Fantastische zu entfesseln.

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

Ergebnisse auf Core i7-3740QM mit aktivierten Optimierungen:

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
Das ist wirklich schön, danke. Möglicherweise möchten Sie Expression.ConvertCheckedstattdessen verwenden, damit ein numerischer Überlauf des Bereichs des Aufzählungstyps zu einem führt OverflowException.
Drew Noakes

Ihr Kilometerstand kann variieren, ich habe den Code auf try.dot.net (blazor) ausgeführt und dort ist der EnumConverter <T> viel langsamer als die Alternativen. Das erste Casting auf ein Objekt war ungefähr sechsmal langsamer als ein direktes Casting, aber immer noch weitaus besser als die anderen Optionen.
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);
        }
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.