Cast Int to Generic Enum in C#

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;
}
40558 次浏览

我发现的最简单的方法是通过向 object添加强制转换来强制编译器执行。

return (T)(object)i.Value;

You should be able to use Enum.Parse for this:

return (T)Enum.Parse(typeof(T), i.Value.ToString(), true);

本文讨论如何为扩展方法解析泛型枚举:

下面是一个非常快速的解决方案,它利用了运行时创建静态泛型类的多个实例这一事实。释放你内心最优化的恶魔!

当您以通用的方式从流中读取 Enums 时,这非常有用。 结合一个外部类,也缓存枚举的基础类型和一个 BitConverter 来释放可怕的。

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);
}

支持优化的 Core i7-3740QM 的结果:

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
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);
}
}

或者,如果可以将枚举作为 Type 而不是泛型类型获取,那么只需使用

Enum.ToObject

Https://msdn.microsoft.com/en-us/library/system.enum.toobject(v=vs.110).aspx

在.NET core 中,现在可以像下面这样使用 系统。运行时。编译器服务。不安全代码:

return Unsafe.As<int, TEnum>(ref int32);

((T []) Enum.GetValue (typeof (T)))可用于构建从 int 到 Enum 类型的字典/查找表 总的来说,我更喜欢 Ramon 的演员阵容使用“ Unsafe. As”,因为 enum 是如此薄的一层,以至于似乎不值得围绕着伪装建造城堡(并不是说薄是一件坏事)。注意 c # 7.3中的 Enum 类型约束。(基本的事情是,我们可以在通用枚举约束下强制转换 T 的数组)

(这将是一个评论,如果我有代表)

    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
}

在@trinalbadger587指出可怕的错误后应该可以工作 (谢谢. . https://dotnetfiddle.net/1oYWjD)

这是一个新的答案,因为它是一个不同的采取。古老的问题,但我昨天这样做... 所以

类似于@Ramon-de-Klein,并使用@trinalbadger587中的 dotnet-fiddle 示例

相当简洁和不透明,但有时也可以。 注意,如果枚举存储在一个字节或16位 usshort 中,则需要正确的底层值类型

        //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
}

这个不安全的转换可以在.net 框架中使用,因为 c # 7.3,假设底层类型是 int。

unsafe static TEnum Int2EnumUnsafe<TEnum>(int i) where TEnum : unmanaged, Enum
=> *(TEnum*)&i;