动态创建和使用枚举类型



我已经在谷歌上搜索过了,已经得到了我试图创造的这个谜题的点点滴滴,但仍然有缺失的部分我无法弄清楚:

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

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

// 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.

我不确定这是不是一个好主意,但是如果这个体系结构最终确定,您可以这样做。

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

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

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

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

    你可以用

    MethodInfo method = typeof(EnumHelper).GetMethod("ToUInt64");
    MethodInfo generic = method.MakeGenericMethod(myEnum.GetType());
    UInt64 myValue = (UInt64)generic.Invoke(null, new object [] { myEnum } );
    
  4. 类似地,您可以使用MakeGenericType基于您的运行时类型创建泛型类的实例。例如,从枚举到字符串的字典如下所示:

    Type dictType = typeof(Dictionary<,>).MakeGenericType(runtimeType, typeof(string));
    var dict = Activator.CreateInstance(dictType);
    
  5. 您可以结合技术3和4来提前编写大部分代码。假设您知道要创建一种运行时类型的动物,并希望对其执行某些操作。您可以创建一个返回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创建这个处理器的实例,用MakeGenericMethod调用方法,然后把它作为一个IEnumProcessor传递给你写的其他代码,如果这些代码不需要知道枚举的具体类型。

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

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

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

相关内容

  • 没有找到相关文章

最新更新