Cast Int to Generic Enum in C#
Asked Answered
A

9

128

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;
}
Aurist answered 30/4, 2012 at 16:26 Comment(3)
#2745820 - might help?Fiesole
Last answer on #1332239, 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.Chill
Something that doesn't box: c-sharp-non-boxing-conversion-of-generic-enum-to-intBarrage
I
170

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

return (T)(object)i.Value;
Impoverish answered 30/4, 2012 at 16:30 Comment(6)
If you don't like boxing: c-sharp-non-boxing-conversion-of-generic-enum-to-intBarrage
We are casting enum to int, not the contrary as in the So question you link. Also, that question has no solution.Meredi
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/iPEzttM4Herwig
I feel kind of evil doing this, but hey, it worksLillalillard
This will throw an exception if the enum's underlying type is not int (i.e. byte or long enums, even uint).Toscana
(T)(object)(int)i.Value worked for meOctroi
E
27

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:

Equanimous answered 30/4, 2012 at 16:34 Comment(2)
@Guvante: I think I converted the value to a string in my example. Do you foresee this causing an issue?Equanimous
FYI: ALso consider Enum.IsDefined after the parse.Unstrained
I
20

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
Immorality answered 10/10, 2014 at 0:2 Comment(2)
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.Marcimarcia
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.Maurreen
L
16

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

return Unsafe.As<int, TEnum>(ref int32);
Loudhailer answered 14/1, 2020 at 12:15 Comment(0)
S
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

Sinful answered 21/6, 2018 at 8:22 Comment(0)
W
4
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);
        }
    }
Wapiti answered 7/4, 2017 at 11:19 Comment(1)
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, EnumKarinkarina
A
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;
Antipope answered 27/6, 2022 at 10:58 Comment(0)
T
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 )

Tegucigalpa answered 19/7, 2021 at 20:38 Comment(4)
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]);Tegucigalpa
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];Tegucigalpa
You can see this answer on .NET fiddle: dotnetfiddle.net/Nrc2oLImpunity
Thinks about performance broHartzel
T
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
}
Tegucigalpa answered 20/7, 2021 at 15:59 Comment(1)
Where generics ?Hartzel

© 2022 - 2024 — McMap. All rights reserved.