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

时间:2017-04-26 10:22:59

标签: c# generics reflection enums

我需要从我通过反射获得的某些类型中打印出枚举值及其相应的下划线值。这在大多数情况下都可以正常工作。 但是,如果枚举是在泛型类型中声明的,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

这是理想的行为吗?我如何工作?

构建一个类型对我来说是一个可行的但是不可接受的,因为它会变得太复杂。

3 个答案:

答案 0 :(得分:44)

  

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

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

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

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被修复以防止产生这种奇怪的值,你的代码就不会破坏。

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

Foo是所谓的开放类型(A类型未完全定义,因为它有一个通用的) 并且不允许打开类型的数组, 你可以通过

来模拟它
Array.CreateInstance(typeof(Foo<>), 2)

由于Enum的GetValues依赖于创建数组,因此它失败了。 你可以改为

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

(“对象”是虚拟类型,因此您不能使用开放类型) 或者做Jon Skeet的建议。