static object HandleEnumViaReflection(object e)
{
int x = (int) e + 1;
return Activator.CreateInstance(e.GetType(), x);
}
Это не работает - но могло бы.Фактически мы можем создать тип, если он не может быть найден во время выполнения.
Если вы должны использовать отражение или другие косвенные средства, вы можете определить свое собственное перечисление с экземпляром ModuleBuilder и IEnumerable<KeyValuePair<string,int>>
.Ниже мы переходим к IEnumerable только потому, что он упрощает тело метода.
static object HandleEnumViaOtherMeans(object e, List<KeyValuePair<string,int>> colors)
{
int x = (int) e + 1;
var arbitraryName = new AssemblyName(Guid.NewGuid().Replace("-", string.Empty));
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(arbitraryName, AssemblyBuilderAccess.Run);
var builder = assemblyBuilder.DefineDynamicModule(arbitraryName.Name);
var colorEnum = builder.DefineEnum("Color", TypeAttributes.Public, typeof(int));
foreach(var color in colors)
{
colorEnum.DefineLiteral(color.Key,color.Value);
}
var complete = colorEnum.CreateType();
return Enum.ToObject(complete, x);
}
Как правило, это не очень хороший способ достижения вашей цели, но он может быть полезен, когда цветовая палитра заранее неизвестна (т. е. во время проектирования или сборки).
Однако в этом случае большинство извлекает генерацию типов в отдельный статический метод, чтобы, среди прочего, мы могли избежать перестройки отсутствующего типа.
public class ColorHandler
{
static ColorHandler()
{
CurrentColors = new List<KeyValuePair<string,int>>();
}
public static List<KeyValuePair<string,int>> CurrentColors { get; set; }
private static Type _colorType;
public static Type ColorType => _colorType ?? (_colorType = DefineColor());
private static DefineColor()
{
var arbitraryName = new AssemblyName(Guid.NewGuid().Replace("-", string.Empty));
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(arbitraryName, AssemblyBuilderAccess.Run);
var builder = assemblyBuilder.DefineDynamicModule(arbitraryName.Name);
var colorEnum = builder.DefineEnum("Color", TypeAttributes.Public, typeof(int));
foreach(var color in CurrentColors)
{
colorEnum.DefineLiteral(color.Key,color.Value);
}
return colorEnum.CreateType();
}
public static object HandleEnum(object e)
{
int x = (int) e + 1;
return Enum.ToObject(ColorType, x);
}
}
Теперь мы почти полный круг.Особенно, если у нас есть универсальный класс и мы все еще хотим использовать Activator.CreateInstance () нетривиальным способом.
public class ColorConsumer<C> where C : struct
{
public C InstanceColor { get; set; }
public ColorConsumer(dynamic color)
{
InstanceColor = color;
}
//we can move the HandleEnum method here from ColorHandler
public object HandleEnum()
{
int x = (int) InstanceColor + 1;
return Enum.ToObject(typeof(C), x);
}
}
Теперь мы можем обновить класс ColorHandler.
public class ColorHandler
{
static ColorHandler()
{
CurrentColors = new List<KeyValuePair<string,int>>();
}
private static List<KeyValuePair<string,int>> _current;
public static List<KeyValuePair<string,int>> CurrentColors
{
get
{
return _current;
}
set
{
if(value != _current && (null != _colorType))
{
_current = Value;
_colorType = DefineColor();
}
}
}
private static Type _colorType;
public static Type ColorType => _colorType ?? (_colorType = DefineColor());
private static DefineColor()
{
var arbitraryName = new AssemblyName(Guid.NewGuid().Replace("-", string.Empty));
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(arbitraryName, AssemblyBuilderAccess.Run);
var builder = assemblyBuilder.DefineDynamicModule(arbitraryName.Name);
var colorEnum = builder.DefineEnum("Color", TypeAttributes.Public, typeof(int));
foreach(var color in CurrentColors)
{
colorEnum.DefineLiteral(color.Key,color.Value);
}
return colorEnum.CreateType();
}
public static object HandleEnumViaReflection(object e)
{
var openType = typeof(ColorConsumer<>);
var typeToActivate = openType.MakeGenericType(new Type[]{ ColorType });
var consumer = Activator.CreateInstance(typeToActivate, new object[]{ e });
return consumer.HandleEnum();
}
}
Теперь у нас есть как общие параметры, так и вызов Activator.CreateInstance ().
QEF.Давайте посмотрим, как это выглядит.
var e = anInstanceOfSomeEnumeration;
ColorHandler.CurrentColors.Add(new KeyValuePair<string,int>("Red", 0));
ColorHandler.CurrentColors.Add(new KeyValuePair<string,int>("Blue", 1));
var f = ColorHandler.HandleEnumViaReflection(e);
Если e
является достаточно низким значением любого перечисления, тогда f
установлено в Color.Blue
в строке 4. Это перечисление не определенов строке 3.
Обновление
На самом деле HandleEnum () должен быть вызван в конструкторе.
public ColorConsumer(dynamic color)
{
InstanceColor = HandleEnum(color);
}
// ...
//then we update HandleEnum very slightly
public object HandleEnum(object e)
{
int x = (int) e + 1;
return Enum.ToObject(typeof(C), x);
}
Теперь необходимо обновить HandleEnumViaReflection.
public static object HandleEnumViaReflection(object e)
{
var openType = typeof(ColorConsumer<>);
var typeToActivate = openType.MakeGenericType(new Type[]{ ColorType });
var consumer = Activator.CreateInstance(typeToActivate, new object[]{ e });
return consumer.InstanceColor;
}
Все остальное без изменений.