Подтвердить что ты не робот

Получить значения enum через отражение от вложенного перечисления в родовом классе

Мне нужно распечатать значения enum и их соответствующие значения для подделки из определенных типов, которые я узнаю через отражение. Это прекрасно работает большую часть времени. Однако, если перечисление объявлено в родовом типе, 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
    }
}

Или протестируйте его здесь

Является ли это желаемым поведением и как я могу работать arround?

Построение типа будет рабочим, но неприемлемым для меня, поскольку оно будет слишком сложным.

4b9b3361

Ответ 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 когда-либо фиксируется, чтобы предотвратить появление таких странных значений, ваш код не сломается.

Ответ 2

Это ожидаемое поведение. Открытые общие типы не могут существовать во время выполнения, поэтому ничто не может жить внутри них. Единственный способ сделать это - сначала закрыть родительский тип любым типом, а затем использовать его для отображения перечисления:

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

Ответ 3

Foo - это то, что называется открытым типом (тип A, который не полностью определен, потому что он имеет общий в нем) И массив открытого типа недопустим, вы можете имитировать его, выполняя

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

И поскольку GetValues ​​из Enum зависит от создания массива, он терпит неудачу. Вместо этого вы можете сделать

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

( "объект" является фиктивным типом, так что вы не будете работать с открытым типом) Или сделайте то, что предложил Джон Скит.