通过泛型类中嵌套枚举的反射获取枚举值

Posted

技术标签:

【中文标题】通过泛型类中嵌套枚举的反射获取枚举值【英文标题】:Get enum values via reflection from nested enum in generic class 【发布时间】:2017-04-26 10:22:58 【问题描述】:

我需要从我通过反射获取的某些类型中打印出枚举值及其相应的下标值。这在大多数情况下都可以正常工作。 但是,如果枚举是在泛型类型中声明的,Enum.GetValues 会抛出以下异常:

[System.NotSupportedException: Cannot create arrays of open type. ]  
at System.Array.InternalCreate(Void* elementType, Int32 rank, Int32* pLengths, Int32* pLowerBounds)    
at System.Array.CreateInstance(Type elementType, Int32 length)
at System.Array.UnsafeCreateInstance(Type elementType, Int32 length)   
at System.RuntimeType.GetEnumValues()

复制的完整代码:

using System;

public class Program

    public static void Main()
    
       var enumType= typeof(Foo<>.Bar);
       var underlyingType = Enum.GetUnderlyingType(enumType);
       Console.WriteLine(enumType.IsEnum);

       foreach(var value in Enum.GetValues(enumType))
       
           Console.WriteLine("0 = 1", value, Convert.ChangeType(value, underlyingType));
       
    



public class Foo<T>

    public enum Bar
    
        A = 1,
        B = 2
    

或者测试一下here

这是理想的行为吗?我该如何解决?

构造一个类型是一种解决方法,但对我来说是不可接受的,因为它会变得太复杂。

【问题讨论】:

奇怪的是Enum.GetNames(enumType) 工作正常。 【参考方案1】:

构造一个类型是一种解决方法,但对我来说是不可接受的,因为它会变得太复杂。

这是获取正常行为值的唯一方法。

您可以获取开放类型的字段,奇怪的是您可以以这种方式获取枚举值。您应该尽量避免使用这些值,但您可以将它们转换为它们的基础类型。

public static void Main()

   var enumType = typeof(Foo<>.Bar);
   var underlyingType = Enum.GetUnderlyingType(enumType);

   foreach(var field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
   
       var value = field.GetValue(null);
       var underlyingValue = Convert.ChangeType(value, underlyingType);
       Console.WriteLine($"field.Name = underlyingValue");
   

不过,更好的解决方案是使用field.GetRawConstantValue()

public static void Main()

   var enumType = typeof(Foo<>.Bar);

   foreach(var field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
   
       Console.WriteLine($"field.Name = field.GetRawConstantValue()");
   

这样,如果 CLR 被修复以防止生成此类奇怪的值,您的代码就不会中断。

【讨论】:

@CSharpie:我暂时回滚了这个更改,因为你的问题并没有说明你实际上想要做什么。您建议的“解决方案”返回整数值;那仍然不是枚举类型的值。如果这是您需要的,请在问题中明确说明。 @CSharpie:实际上,看起来我们最终会得到一些非常奇怪的值。将编辑。 @CSharpie:也更新了答案。对此要非常小心:您处于非常奇怪的领域。 当调试器无法评估变量时总是一个好兆头。 感觉好多了。【参考方案2】:

这是预期的行为。开放的泛型类型不能在运行时存在,因此它们内部的任何东西也不能存在。 你可以做到这一点的唯一方法是首先用任何类型关闭父类型,然后使用它来反映枚举:

 var enumType = typeof(Foo<object>.Bar);

【讨论】:

我知道这一点。但在这种情况下,这对我来说毫无意义。我还说在我的情况下构造一个类型太复杂了。【参考方案3】:

Foo 是所谓的开放类型(一种没有完全定义的类型,因为它有一个泛型) 并且不允许开放类型的数组, 你可以通过做来模拟它

Array.CreateInstance(typeof(Foo<>), 2)

而且由于 Enum 的 GetValues 依赖于创建数组,所以它失败了。 你可以这样做

var enumType = typeof(Foo<object>.Bar);

("object" 是一个虚拟类型,所以你不会使用开放类型) 或者按照 Jon Skeet 的建议去做。

【讨论】:

我知道这一点。但在这种情况下,这对我来说毫无意义。我还说在我的情况下构造一个类型太复杂了。

以上是关于通过泛型类中嵌套枚举的反射获取枚举值的主要内容,如果未能解决你的问题,请参考以下文章

代码清单3-6 表示一对值泛型类

泛型与枚举

C#关于反射创建泛型类

180530-反射获取泛型类的实际参数

反射获取泛型类泛型方法

泛型类中,获取当前new的对象的 T的真实 类型