Podobnie jak rzutowanie int na wyliczenie w C #, ale moje wyliczenie jest parametrem typu ogólnego. Jak najlepiej sobie z tym poradzić?
Przykład:
private T ConvertEnum<T>(int i) where T : struct, IConvertible
{
return (T)i;
}
Generuje błąd kompilatora Cannot convert type 'int' to 'T'
Pełny kod jest następujący, gdzie value może zawierać int lub 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;
}
Odpowiedzi:
Najprostszym sposobem, jaki znalazłem, jest wymuszenie ręki kompilatora, dodając rzut do
object
.return (T)(object)i.Value;
źródło
Powinieneś być w stanie użyć
Enum.Parse
do tego:return (T)Enum.Parse(typeof(T), i.Value.ToString(), true);
W tym artykule omówiono analizowanie ogólnych wyliczeń pod kątem metod rozszerzania:
źródło
Oto bardzo szybkie rozwiązanie, które wykorzystuje fakt, że środowisko wykonawcze tworzy wiele instancji statycznych klas ogólnych. Uwolnij swoje wewnętrzne demony optymalizacji!
To naprawdę świeci, gdy czytasz wyliczenia ze strumienia w ogólny sposób. Połącz z zewnętrzną klasą, która również buforuje podstawowy typ wyliczenia i BitConverter, aby uwolnić niesamowite.
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); }
Wyniki dla Core i7-3740QM z włączonymi optymalizacjami:
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
źródło
Expression.ConvertChecked
zamiast tego, aby przepełnienie liczbowe zakresu typu wyliczenia skutkowało rozszerzeniemOverflowException
.Alternatywnie, jeśli możesz uzyskać wyliczenie nie jako typ ogólny, ale jako typ, po prostu użyj
https://msdn.microsoft.com/en-us/library/system.enum.toobject(v=vs.110).aspx
źródło
W .NET core można teraz używać kodu System.Runtime.CompilerServices.Unsafe :
return Unsafe.As<int, TEnum>(ref int32);
źródło
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); } }
źródło