Получить значения 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?
Построение типа будет рабочим, но неприемлемым для меня, поскольку оно будет слишком сложным.
Ответы
Ответ 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);
( "объект" является фиктивным типом, так что вы не будете работать с открытым типом)
Или сделайте то, что предложил Джон Скит.