Cast Int to Generic Enum in C#

37,805

Solution 1

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

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

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

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

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

Share:
37,805

Related videos on Youtube

cariseldon
Author by

cariseldon

Sr. Software Developer @ Nudge.ai Mostly work with C#, Java, JavaScript, Bash

Updated on July 27, 2021

Comments

  • cariseldon
    cariseldon almost 3 years

    Similar to Cast int to enum in C# 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;
    }
    
  • James Johnson
    James Johnson about 12 years
    @Guvante: I think I converted the value to a string in my example. Do you foresee this causing an issue?
  • nawfal
    nawfal almost 11 years
  • MatteoSp
    MatteoSp about 9 years
    We are casting enum to int, not the contrary as in the So question you link. Also, that question has no solution.
  • Drew Noakes
    Drew Noakes about 9 years
    This is really nice, thanks. You might like to use Expression.ConvertChecked instead though, so that numeric overflow of the enum type's range results in an OverflowException.
  • Krythic
    Krythic almost 7 years
    You could also just allocate a static array with the enum values, and then just pass in the index to retrieve the correct enum. This saves having to do any kind of casting. Example(Only line 11,14, and 34 are relevant to this concept): pastebin.com/iPEzttM4
  • Herman
    Herman almost 5 years
    Your mileage might vary, I ran the code on try.dot.net (blazor) and there the EnumConverter<T> is much slower than the alternatives. Casting to object first was about 6 times slower than a direct cast, but still far better than the other options.
  • Dunno
    Dunno about 3 years
    I feel kind of evil doing this, but hey, it works
  • Oscar Abraham
    Oscar Abraham almost 3 years
    This will throw an exception if the enum's underlying type is not int (i.e. byte or long enums, even uint).
  • Xela.Trawets
    Xela.Trawets almost 3 years
    And of course the same thing can be done without using linq, replacing the .Zip, .ToDictionary lines with Dictionary<int, TEnum> c = new Dictionary<int, TEnum>(array.Length); for (int j = 0; j < array.Length; j++) c.Add(intValues[j], enumValues[j]);
  • Xela.Trawets
    Xela.Trawets almost 3 years
    Also same Array cast can be used to set a generic enum to an arbitrary integer value; TEnum enumValue = ((TEnum[])(Array)(new int[] { -1 }))[0];
  • trinalbadger587
    trinalbadger587 almost 3 years
    You can see this answer on .NET fiddle: dotnetfiddle.net/Nrc2oL
  • Mike Christiansen
    Mike Christiansen about 2 years
    You can add some constraints on there. Makes the if (info.IsEnum) check unnecessary. public static T ToEnum<T>(this int param) where T : struct, Enum