动态创建和使用枚举类型

时间:2014-08-05 21:10:51

标签: c# dynamic reflection enums

我已经搜索了谷歌并且已经得到了我想要创建的这个谜题的点点滴滴,但仍然缺少一些我无法弄清楚的部分:

我正在尝试创建并充分使用动态枚举类型。

这就是我所拥有的,也是本次讨论的基础。这是非常动态的做事方式。这就是我想模仿的东西:

// 1) Create the enum and classes
public enum MyAnimals { Cat, Dog, Pig };

public abstract class Animal { internal MyAnimals _myType; 
                               public MyAnimals myType {get{return _myType;}} }
public class Cat : Animal { public Cat(){_myType = MyAnimals.Cat;} }
public class Dog : Animal { public Dog(){_myType = MyAnimals.Dog;} }
public class Pig : Animal { public Pig(){_myType = MyAnimals.Pig;} }

// 2) Instantiate a class and a variable using the enum
Dog aDog = new Dog(); // aDog.myType is 'Dog'
MyAnimals theAnimal; // Will default to 'Cat'

// 3) Change the variable to another enum
aDog.myType = MyAnimals.Cat; // Error, cant change Type!  Good!
theAnimal = MyAnimals.Pig;

// 4) Use the variable in a method call
public void Method( MyAnimals animal ) { ... }

Method( aDog.myType );
Method( theAnimal );

以下是我动态执行此操作的方法。我现在可以进入第3步,但即便如此,它仍然是丑陋的代码。任何人都可以为我获得#4,或者帮我解决这个问题吗?

// 1) Create the enum  and classes
public static Type MyAnimals;

public static dynamic getAnimal(string name)
{
    dynamic theAnimal = Activator.CreateInstance(MyAnimals); // Will default to 'Cat'
    FieldInfo fi = MyAnimals.GetField(name);
    int iEnum = (int)fi.GetValue(MyAnimals);
    return Enum.ToObject(MyAnimals, iEnum);
}
public abstract class Animal { internal dynamic _myType; 
                               public dynamic myType { get { return _myType; } } }
public class Cat : Animal { public Cat() { _myType = getAnimal("Cat"); } }
public class Dog : Animal { public Dog() { _myType = getAnimal("Dog"); } }
public class Pig : Animal { public Pig() { _myType = getAnimal("Pig"); } }

// Get the current application domain for the current thread.
AppDomain currentDomain = AppDomain.CurrentDomain;

// Create a dynamic assembly in the current application domain
AssemblyName aName = new AssemblyName("TempAssembly");
AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);

EnumBuilder eb = mb.DefineEnum("MyAnimalType", TypeAttributes.Public, typeof(int));

var types = new List<Type>();
int Count = 0;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    types.AddRange(assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(Animal))));

foreach (var type in types)
    eb.DefineLiteral(type.Name, Count++);

// Create the type
MyAnimals = eb.CreateType();

// 2) Instantiate a variable using the enum
Cat c = new Cat();
dynamic theAnimal = getAnimal("Pig");

// 3) Change the vairable to another enum
c.myType = getAnimal("Dog"); // Error, again, good
theAnimal = getAnimal("Dog");

// 4) Use the variable in a method call
public void Method( MyAnimals animal ) // Compile error: 'MyAnimals' is a field but used like a type.

2 个答案:

答案 0 :(得分:1)

我不确定这是一个好主意,但如果这个架构已经完成,那么这就是你能做的。

  1. 创建运行时类型后,可以将其传递给为Enum类定义的任何静态方法,包括

    1. Enum.GetNames(runtmeType) - 获取枚举的已定义名称数组。
    2. Enum.GetValues(runtimeType) - 获取枚举的已定义成员值数组。
    3. Enum.Parse(runtmeType, string) - 将指定类型的枚举的名称或值的字符串表示形式转换为该类型的等效成员。
    4. Enum.ToObject(runtmeType, /*some integer type*/) - 将整数转换为等效的枚举成员。
  2. 枚举的所有成员都继承自Enum类,因此如果您想提前编写代码来处理运行时类型的枚举,则可以编写非{{}的非特定方法1}}作为一个论点。 (当然,如果执行此操作,则会丢失一些编译时类型检查。)例如,您可以执行Enum,然后将返回的Enum myEnum = Enum.Parse(myAnimalType, "Pig")传递给您要为其编写的任何方法。 myEnum本身有一些有用的实例方法,例如methods to convert it to various types of integer

  3. 接下来,如果您碰巧有一个将枚举作为其输入参数之一的泛型方法,则可以使用MakeGenericMethod调用它。例如,如果您有以下方法和类:

    Enum

    你可以用

    来调用它
    public static class EnumHelper
    {
        public static ulong ToUInt64<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable
        {
            // Silently convert the value to UInt64 from the other base 
            // types for enum without throwing an exception.
            // This is need since the Convert functions do overflow checks.
            TypeCode typeCode = value.GetTypeCode();
            ulong result;
    
            switch (typeCode)
            {
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                    unchecked
                    {
                        result = (UInt64)value.ToInt64(CultureInfo.InvariantCulture);
                    }
                    break;
    
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Boolean:
                case TypeCode.Char:
                    unchecked
                    {
                        result = value.ToUInt64(CultureInfo.InvariantCulture);
                    }
                    break;
    
                default:
                    throw new InvalidOperationException();
            }
            return result;
        }
    }
    
  4. 同样,您可以使用MakeGenericType基于运行时类型创建泛型类的实例。例如,枚举到字符串的字典将如下所示:

    MethodInfo method = typeof(EnumHelper).GetMethod("ToUInt64");
    MethodInfo generic = method.MakeGenericMethod(myEnum.GetType());
    UInt64 myValue = (UInt64)generic.Invoke(null, new object [] { myEnum } );
    
  5. 您可以结合使用技巧3和4来提前编写大部分代码。假设您知道要创建一个运行时类型的动物,您想要执行某些动作。您可以创建一个返回Type dictType = typeof(Dictionary<,>).MakeGenericType(runtimeType, typeof(string)); var dict = Activator.CreateInstance(dictType); 个实例的非通用顶级接口:

    Enum

    然后用通用接口改进它:

    public interface IEnumProcessor 
    {
         IList<string> GetAllValues();
    
         bool ProcessInput(string userValue);
    
         Enum GetCurrentValue();
    }
    

    最后用实际代码创建一个泛型类:

    public interface IEnumProcessor<TEnum> : IEnumProcessor where TEnum : struct, IConvertible, IComparable, IFormattable
    
         new TEnum GetCurrentValue();
    
         void AddSelectedValue(TEnum value);
    }
    

    您可以使用public class EnumProcessor<T> where TEnum : struct, IConvertible, IComparable, IFormattable { } 创建此处理器的实例,使用MakeGenericType调用方法,然后将其传递给您编写为IEnumProcessor的其他代码(如果该代码不需要知道枚举的特定类型)

  6. 最后,请注意,由于枚举的基础类型可以是s / byte,u / short,u / int或u / long,使用此架构,您将永远不能超过64运行时类型的实例。如果你想要更多,你需要一个不同的架构,至少将你的枚举定义为具有[Flags]属性的位字段,然后将你的枚举值定义为复合,这可能会产生无法预料的(由我)后果。

答案 1 :(得分:0)

步骤3和4可以使用Enum.Parse()方法完成:

var theAnimal = Enum.Parse(typeof(MyAnimals), "Pig");

无需动力学。