在 C# 中将 Int 转换为通用枚举

Posted

技术标签:

【中文标题】在 C# 中将 Int 转换为通用枚举【英文标题】:Cast Int to Generic Enum in C# 【发布时间】:2012-04-30 16:26:45 【问题描述】:

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

例子:

private T ConvertEnum<T>(int i) where T : struct, IConvertible

    return (T)i;

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

完整代码如下,其中value可以是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;

【问题讨论】:

***.com/questions/2745320/… - 可能有帮助吗? ***.com/questions/1331739/… 上的最后一个答案更接近你想要的。虽然它仍然不聪明。我倾向于为此使用反射,您可以使代码更强大。在我看来,Struct 的限制性不足以让使用泛型变得值得。 没有装箱的东西:c-sharp-non-boxing-conversion-of-generic-enum-to-int 【参考方案1】:

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

return (T)(object)i.Value;

【讨论】:

如果你不喜欢拳击:c-sharp-non-boxing-conversion-of-generic-enum-to-int 我们将枚举转换为 int,而不是与您链接的 So 问题中相反。另外,这个问题没有答案。 您也可以只分配一个带有枚举值的静态数组,然后传入索引以检索正确的枚举。这样就不必进行任何类型的铸造。示例(只有第 11,14 和 34 行与此概念相关):pastebin.com/iPEzttM4 我觉得这样做有点邪恶,但是嘿,它有效 如果枚举的底层类型不是 int(即字节或长枚举,甚至是 uint),这将引发异常。【参考方案2】:

您应该可以为此使用Enum.Parse

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

这篇文章讨论了为扩展方法解析泛型枚举:

Generic Enum Parsing with Extension Methods

【讨论】:

@Guvante:我想我在示例中将值转换为字符串。您预见到这会导致问题吗?【参考方案3】:

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

当您以通用方式从流中读取枚举时,这真的很出色。 结合一个还缓存枚举的底层类型的外部类和一​​个 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

【讨论】:

这真的很好,谢谢。不过,您可能想改用Expression.ConvertChecked,以便枚举类型范围的数字溢出导致OverflowException 您的里程可能会有所不同,我在 try.dot.net (blazor) 上运行了代码,并且 EnumConverter 比替代方案慢得多。首先投射到对象比直接投射慢大约 6 倍,但仍然比其他选项好得多。【参考方案4】:

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

Enum.ToObject

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:
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);
        
    

【讨论】:

【参考方案7】:

((T[])Enum.GetValues(typeof(T))) 可用于构建从int到Enum类型的字典/查找表 总的来说,我更喜欢 Ramon 使用“Unsafe.As”的演员阵容,因为枚举是整数的一层薄薄的外衣,似乎不值得在假装周围建造城堡(并不是说薄是一件坏事)。请注意 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)

【讨论】:

当然,同样的事情可以在不使用 linq 的情况下完成,将 .Zip、.ToDictionary 行替换为 Dictionary c = new Dictionary(array.Length) ; for (int j = 0; j 同样的 Array cast 也可用于将通用枚举设置为任意整数值; TEnum enumValue = ((TEnum[])(Array)(new int[] -1 ))[0]; 你可以在 .NET fiddle 上看到这个答案:dotnetfiddle.net/Nrc2oL【参考方案8】:

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

类似于@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

【讨论】:

以上是关于在 C# 中将 Int 转换为通用枚举的主要内容,如果未能解决你的问题,请参考以下文章

如何将 int 转换为枚举?

在 C# 中将通用列表转换为数据集 [关闭]

在 C# 中将 DataTable 转换为通用列表

在 C# 中将 char 转换为 int

在 C# 中将 double 转换为 int

在 C# 中将 int[] 转换为 byte[]