Cast Int to Generic Enum in C#
C#GenericsCastingEnumsC# Problem Overview
Similar to https://stackoverflow.com/questions/29482/cast-int-to-enum-in-c-sharp but my enum is a Generic Type parameter. What is the best way to handle this?
Example:
private T ConvertEnum<T>(int i) where T : struct, IConvertible
{
return (T)i;
}
Generates compiler error Cannot convert type 'int' to 'T'
Full code is as follows, where value can contain the int, or 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;
}
C# Solutions
Solution 1 - C#
The simplest way I have found is to force the compiler's hand by adding a cast to object
.
return (T)(object)i.Value;
Solution 2 - C#
You should be able to use Enum.Parse
for this:
return (T)Enum.Parse(typeof(T), i.Value.ToString(), true);
This article talks about parsing generic enums for extenstion methods:
Solution 3 - C#
Here's a very fast solution that abuses the fact that the runtime creates multiple instances of static generic classes. Unleash your inner optimization demons!
This really shines when you're reading Enums from a stream in a generic fashion. Combine with an outer class that also caches the enum's underlying type and a BitConverter to unleash the 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);
}
Results on Core i7-3740QM with optimizations enabled:
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
Solution 4 - C#
In .NET core it is now possible to use System.Runtime.CompilerServices.Unsafe code like this:
return Unsafe.As<int, TEnum>(ref int32);
Solution 5 - C#
Alternatively, if you can get a enum not as a generic type, but as Type, then simply use
Enum.ToObject
https://msdn.microsoft.com/en-us/library/system.enum.toobject(v=vs.110).aspx
Solution 6 - C#
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);
}
}
Solution 7 - C#
((T[])Enum.GetValues(typeof(T))) can be used to build a dictionary / lookup table from int to the Enum type Overall I prefer Ramon's cast using "Unsafe.As" because enums are such a thin veneer over integers that it doesnt seem worth building castles around the pretense (not that the thinness is a bad thing). Notice the Enum type contsraint from c# 7.3. (Basic thing is that we can cast the array of T under the generic enum constraint)
(this would be a comment if I had rep)
public static TEnum IntToEnum<TEnum>(int i)
where TEnum : Enum
{
Array array = Enum.GetValues(typeof(TEnum));
int[] intValues = (int[])array;
TEnum[] enumValues = (TEnum[])array;
var b = intValues.Zip(enumValues);
//Consider saving the dictionary to avoid recreating each time
var c = b.ToDictionary<(int n, TEnum e), int, TEnum>(p => p.n, p => p.e);
return c[i];//KeyNotFoundException possible here
}
Should work after the horrible error pointed out by @trinalbadger587 (thanks.. https://dotnetfiddle.net/1oYWjD )
Solution 8 - C#
This is a new answer because it is a different take. Ancient question, but I was doing this yesterday... so
Similar to @Ramon-de-Klein and using the dotnet-fiddle example from @trinalbadger587
Rather terse and opaque, but sometimes thats ok. Note that it needs the right underlying value type if the enum is stored in a byte or 16 bit ushort
//Int to Enum using the hot span newness - but without unsafe{}
Span<int> XS = stackalloc int[] { 100 };
Console.WriteLine(MemoryMarshal.Cast<int, Bla>(XS)[0]);
//Int to Enum using good old arrays
Console.WriteLine(((Bla[])(Array)(new int[] { 100 }))[0]);
//Enum to Int
Console.WriteLine(((int[])(Array)(new Bla[] { Bla.B }))[0]);
enum Bla
{
A = 0,
B = 100
}