122

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;
}
Community
  • 1
  • 1
csauve
  • 5,904
  • 9
  • 40
  • 50
  • http://stackoverflow.com/questions/2745320/enum-tryparse-with-flags-attribute - might help? – Sunny Apr 30 '12 at 16:30
  • Last answer on http://stackoverflow.com/questions/1331739/enum-type-constraints-in-c-sharp, is closer to what you want. It's still not clever though. I tend to use reflection for this, you can make the code a lot stronger. Struct isn't retrictive enough to make messing about with generics worthwhile in my opinion. – Tony Hopkinson Apr 30 '12 at 16:44
  • 1
    Something that doesn't box: [c-sharp-non-boxing-conversion-of-generic-enum-to-int](http://stackoverflow.com/questions/1189144/c-sharp-non-boxing-conversion-of-generic-enum-to-int) – nawfal Jun 08 '13 at 21:45

9 Answers9

161

The simplest way I have found is to force the compiler's hand by adding a cast to object.

return (T)(object)i.Value;
Guvante
  • 18,775
  • 1
  • 33
  • 64
  • 13
    If you don't like boxing: [c-sharp-non-boxing-conversion-of-generic-enum-to-int](http://stackoverflow.com/questions/1189144/c-sharp-non-boxing-conversion-of-generic-enum-to-int) – nawfal Jun 08 '13 at 21:46
  • 6
    We are casting enum to int, not the contrary as in the So question you link. Also, that question has no solution. – MatteoSp Feb 24 '15 at 17:21
  • 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): https://pastebin.com/iPEzttM4 – Krythic Jul 02 '17 at 16:04
  • I feel kind of evil doing this, but hey, it works – Dunno May 10 '21 at 12:46
  • 2
    This will throw an exception if the enum's underlying type is not int (i.e. byte or long enums, even uint). – Oscar Abraham Jul 05 '21 at 01:03
  • (T)(object)(int)i.Value worked for me – Night94 Oct 21 '22 at 19:12
25

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:

James Johnson
  • 45,496
  • 8
  • 73
  • 110
18

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
Raif Atef
  • 2,878
  • 1
  • 25
  • 31
  • 3
    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`. – Drew Noakes Feb 25 '15 at 14:41
  • Your mileage might vary, I ran the code on try.dot.net (blazor) and there the EnumConverter 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. – Herman Jun 14 '19 at 16:31
13

In .NET core it is now possible to use System.Runtime.CompilerServices.Unsafe code like this:

return Unsafe.As<int, TEnum>(ref int32);
Ramon de Klein
  • 5,172
  • 2
  • 41
  • 64
8

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

Do-do-new
  • 794
  • 8
  • 15
2
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);
        }
    }
  • You can add some constraints on there. Makes the `if (info.IsEnum)` check unnecessary. `public static T ToEnum(this int param) where T : struct, Enum` – Mike Christiansen Mar 23 '22 at 20:46
1

This unsafe conversion can be used in .net framework since c# 7.3, supposing the underlying type is int.

unsafe static TEnum Int2EnumUnsafe<TEnum>(int i) where TEnum : unmanaged, Enum
        => *(TEnum*)&i;
Palle Due
  • 5,929
  • 4
  • 17
  • 32
shingo
  • 18,436
  • 5
  • 23
  • 42
0

((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 )

  • And of course the same thing can be done without using linq, replacing the .Zip, .ToDictionary lines with Dictionary c = new Dictionary(array.Length); for (int j = 0; j < array.Length; j++) c.Add(intValues[j], enumValues[j]); – Xela.Trawets Jul 20 '21 at 00:38
  • 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]; – Xela.Trawets Jul 20 '21 at 04:48
  • You can see this answer on .NET fiddle: https://dotnetfiddle.net/Nrc2oL – trinalbadger587 Jul 20 '21 at 04:53
0

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
}