Передав enum в качестве параметра функции, обработайте его как int и верните enum - PullRequest
0 голосов
/ 18 октября 2018

Описание

У меня есть функция с именем HandleEnum, которая принимает enum в качестве параметра e, и мне нужно преобразовать e из enum в int и выполнить некоторые операции на основена его значение int.Наконец, мне нужно преобразовать его обратно в enum и вернуть.(Требование может показаться немного странным, потому что это просто краткое изложение моей реальной проблемы).

Метод 1: универсальный

Я пытался использовать generic, и это действительно помогает:

static object HandleEnumViaGeneric<T>(T e)
{
    int x = (int)(object)e + 1;
    return (T)(object) x;
}

Его можно назвать так:

enum Color { black, red, green };
enum Day { day1, day2, day3, day4 };

static void Main(string[] args)
 {
    Console.WriteLine(HandleEnumViaGeneric(Day.day3).GetType());
    Console.WriteLine(HandleEnumViaGeneric(Color.black).GetType());
 }

Метод 2: отражение

Тем временем я пытался использовать reflection, чтобы сделать то же самое, ноошибка:

static object HandleEnumViaReflection(object e)
{
    int x = (int) e + 1;
    return Activator.CreateInstance(e.GetType(), x);
}

Когда я позвонил: Console.WriteLine(HandleEnumViaReflection(Color.black).GetType());, было сгенерировано исключение: Конструктор типа TestEnum.Color не найден. (TestEnum - мое пространство имен).

Мой вопрос

Вот мой вопрос:

  1. Как может работать второй метод?
  2. Чтобы удовлетворить мои требования, какой метод лучшеили ни один из них не является хорошим методом?

thx.

Ответы [ 4 ]

0 голосов
/ 18 октября 2018

вы можете сделать это:

static object HandleEnumViaGeneric<T>(T e)
{
    var genType = typeof(T);
    if(genType.IsEnum)
    {
       foreach (T object in Enum.GetValues(genType))
       {
           Enum temp =  Enum.Parse(typeof(T), e.ToString()) as Enum;
           return Convert.ToInt32(temp);
       }
     }
}

Подробнее здесь: Как я могу привести общий enum к int?

0 голосов
/ 18 октября 2018

относительно первого вопроса, попробуйте этот:

static T HandleEnumViaGeneric<T>(int id)
{
    if(!typeof(T).IsEnum)
        throw new Exception("");

    var values = Enum.GetValues(typeof(T));  
    return (T)values.GetValue(id);
}

Относительно второго вопроса, предположим, что выдается исключение, потому что вы не можете реализовать параметрический конструктор дляenum

0 голосов
/ 18 октября 2018
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;
}

Все остальное без изменений.

0 голосов
/ 18 октября 2018

Метод 2 не работает, потому что вы используете CreateInstance (Type, Object []) , где параметры должны соответствовать конструктору, а ваш объект - перечисление.Я бы предложил перейти к вашему первому подходу, поскольку я не думаю, что возможно реализовать второй.

// Summary:
//     Creates an instance of the specified type using the constructor that best
//     matches the specified parameters.

Короче говоря, Activator.CreateInstance работает только для объектов с конструкторами.

...