【问题标题】:Passing enum as function parameter, handle it as int, and return enum将 enum 作为函数参数传递,将其作为 int 处理,并返回 enum
【发布时间】:2018-10-18 03:32:32
【问题描述】:

说明

我有一个名为HandleEnum 的函数,它接受enum 作为参数e,我需要将eenum 转换为int,并根据它的int 值进行一些操作。最后,我需要将其转换回enum 并返回。 (这个要求可能看起来有点奇怪,因为它只是我实际问题的一个抽象)。

方法一:通用

我尝试使用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. 为了满足我的要求,哪种方法更好或都不是好方法?

谢谢。

【问题讨论】:

  • 我想知道为什么method2不起作用,实际上哪种方法更好。
  • 第一种方法会更快,而且有效。第二个速度较慢并且不起作用。我建议第一种方法更好。
  • 我建议您的设计可能有问题。有更好(和更安全)的方法可以在枚举中找到下一个值。这条指令会发生什么:HandleEnumViaGeneric(Day.day4)?有了这个:HandleEnumViaGeneric(new MyObject())?还有这个:enum Days { first = 0, second = 2 }; HandleEnumViaGeneric(Days.first)?你可以考虑another implementation
  • @Spotted 这只是对实际问题的抽象。将枚举转换为 int 时会做更复杂的事情。
  • @Caesium 我知道这个例子被简化了,但是潜在的问题仍然存在:将枚举转换为一个脆弱的操作。

标签: c# enums generic-programming system.reflection


【解决方案1】:

方法 2 不起作用,因为您使用的是 CreateInstance(Type, Object[]),其中参数必须与构造函数匹配,并且您的对象是枚举。我建议您采用第一种方法,因为我认为不可能实施第二种方法。

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

简而言之,Activator.CreateInstance 仅适用于具有构造函数的对象。

【讨论】:

  • 是的,我也不认为反射会起作用,但我发现使用 Enum.ToObject(e.GetType(), x) 会避免使用泛型。
【解决方案2】:

关于第一个问题,试试这个:

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

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

关于第二个问题,假设抛出异常,因为你不能为枚举实现参数构造函数

【讨论】:

    【解决方案3】:

    你可以这样做:

    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);
           }
         }
    }
    

    更多:How do I cast generic enum to int?

    【讨论】:

      【解决方案4】:
      static object HandleEnumViaReflection(object e)
      {
          int x = (int) e + 1;
          return Activator.CreateInstance(e.GetType(), x);
      }
      

      这不起作用——但它可以。事实上,如果在运行时找不到类型,我们可以创建它。

      如果您必须使用反射或其他间接方式,那么您可以使用 ModuleBuilder 实例和 IEnumerable&lt;KeyValuePair&lt;string,int&gt;&gt; 定义自己的枚举。下面我们传入 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 到第 4 行的Color.Blue。此枚举未在第 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;
      }
      

      其他一切都没有改变。

      【讨论】:

      • 其实反射是可选的,我只是想尝试找到适合我需求的最佳解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 2010-09-21
      • 2021-02-08
      • 1970-01-01
      相关资源
      最近更新 更多