Nearly all of the current answers use Convert
or cast to object before the target type, which results in boxing and unboxing operations. This causes heap allocations and may not be acceptable for hot paths.
Since the majority of these answers were written, System.Runtime.CompilerServices.Unsafe was introduced, enabling low-level manipulation of pointers.
In combination with generics, the Unsafe
class allows us to change the underlying type of the System.Enum
parameter safely, with zero allocations, and with performance that is nearly indistinguishable from an empty method duration:
public long GetEnumValue<T>(T enumInstance) where T : unmanaged, Enum
{
var size = Unsafe.SizeOf<T>();
if (size == Unsafe.SizeOf<byte>())
{
return Unsafe.As<T, byte>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<short>())
{
return Unsafe.As<T, short>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<int>())
{
return Unsafe.As<T, int>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<long>())
{
return Unsafe.As<T, long>(ref enumInstance);
}
return -1; // or throw if you prefer
}
If it is preferable to always return an int, you can do so, although if the backing field of the enum exceeds int.MaxValue
, it will overflow:
public int GetEnumValue<T>(T enumInstance) where T : unmanaged, Enum
{
var size = Unsafe.SizeOf<T>();
if (size == Unsafe.SizeOf<byte>())
{
return Unsafe.As<T, byte>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<short>())
{
return Unsafe.As<T, short>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<int>())
{
return Unsafe.As<T, int>(ref enumInstance);
}
else if (size == Unsafe.SizeOf<long>())
{
return Unsafe.As<T, int>(ref enumInstance);
}
return -1; // or throw if you prefer
}
If the following were used:
public enum EnumLong : long
{
First,
Second,
Third = (long)(int.MaxValue) + 1
}
var val = GetEnumValue(EnumLong.Third);
-2147483648
would be returned due to an overflow. It is extremely unlikely that developers would create enums with such large flag values, but is always a possibility.
get(int foo)
you can define it asget(Question foo)
then do your casting inside the method, the you can call your method asQuestions.Get(Question.Role)
– Yon