Cast Int to Generic Enum in C#

C#GenericsCastingEnums

C# 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
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncsauveView Question on Stackoverflow
Solution 1 - C#GuvanteView Answer on Stackoverflow
Solution 2 - C#James JohnsonView Answer on Stackoverflow
Solution 3 - C#Raif AtefView Answer on Stackoverflow
Solution 4 - C#Ramon de KleinView Answer on Stackoverflow
Solution 5 - C#Do-do-newView Answer on Stackoverflow
Solution 6 - C#Vladimir KurguzovView Answer on Stackoverflow
Solution 7 - C#Xela.TrawetsView Answer on Stackoverflow
Solution 8 - C#Xela.TrawetsView Answer on Stackoverflow