在 C# 中将 int 强制转换为泛型枚举

本文关键字:转换 泛型 枚举 中将 int | 更新日期: 2023-09-27 18:32:40

类似于 C# 中的 Cast int 到枚举,但我的枚举是一个泛型类型参数。 处理此问题的最佳方法是什么?

例:

private T ConvertEnum<T>(int i) where T : struct, IConvertible
{
    return (T)i;
}

生成编译器错误Cannot convert type 'int' to 'T'

完整代码如下所示,其中值可以包含 int 或 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# 中将 int 强制转换为泛型枚举

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

return (T)(object)i.Value;

您应该能够为此使用 Enum.Parse

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

在.NET core中,现在可以像这样使用System.Runtime.CompilerServices.Unsafe代码:

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

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

Enum.ToObject

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

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

从 c# 7.3 开始,这种不安全的转换可以在 .net 框架中使用,假设基础类型是 int。

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

((T[])Enum.GetValues(typeof(T)))可用于构建从int到Enum类型的字典/查找表总的来说,我更喜欢拉蒙使用"Unsafe.As"的演员阵容,因为枚举在整数上是如此薄,以至于似乎不值得围绕伪装建造城堡(并不是说薄是一件坏事)。请注意 c# 7.3 中的枚举类型。(基本的事情是我们可以在通用枚举约束下强制转换 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 位 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
}