Reputation: 840
I have searched google and have gotten bits and pieces of this puzzle I am trying to create, yet there are still missing pieces that I cannot figure out:
I am trying to create, and fully use, a dynamic Enumeration Type.
This is what I have and is the basis for this discussion. It is the NON dynamic way of doing things. This is what I am trying to mimic:
// 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 );
and here is how I do this dynamically. I can currently get to step 3, but even then it is ugly code. Can anyone get #4 for me, or help me with this whole situation?
// 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.
Upvotes: 1
Views: 10058
Reputation: 116711
I'm not really sure that this is such a good idea, but if this architecture is finalized, here's what you can do.
Once your run time type has been created, you can pass it to any of the static methods defined for the Enum
class, including
Enum.GetNames(runtmeType)
- gets an array of the defined names of your enum.Enum.GetValues(runtimeType)
- gets an array of the defined member values of your enum.Enum.Parse(runtmeType, string)
- converts the string representation of the name or value of an enum of the specified type to the equivalent member of the type.Enum.ToObject(runtmeType, /*some integer type*/)
- converts an integer to the equivalent enumeration member.All members of enumerations inherit from the Enum
class, so if you want to write code in advance to deal with enums of your run-time type, you can write nonspecific methods that take an Enum
as an argument. (Of course, you lose some compile-time type checking if you do this.) For instance, you can do Enum myEnum = Enum.Parse(myAnimalType, "Pig")
, and then pass the returned myEnum
to any method you want to write for it. And an Enum
itself has a few useful instance methods, e.g. methods to convert it to various types of integer.
Next, if you happen to have a generic method that takes an enum as one of its input arguments, you can invoke it with MakeGenericMethod
. For instance, if you have the following method and class:
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;
}
}
you can call it with
MethodInfo method = typeof(EnumHelper).GetMethod("ToUInt64");
MethodInfo generic = method.MakeGenericMethod(myEnum.GetType());
UInt64 myValue = (UInt64)generic.Invoke(null, new object [] { myEnum } );
Similarly, you can create instances of generic classes based on your run time type with MakeGenericType
. For instance, a dictionary of enums to strings would look like this:
Type dictType = typeof(Dictionary<,>).MakeGenericType(runtimeType, typeof(string));
var dict = Activator.CreateInstance(dictType);
You can combine techniques 3 and 4 to write most of your code in advance. Say you know you are going to create a run-time type of animals, for which you want to perform certain actions. You can create a non-generic top level interface that returns Enum
instances:
public interface IEnumProcessor
{
IList<string> GetAllValues();
bool ProcessInput(string userValue);
Enum GetCurrentValue();
}
then refine that with a generic interface:
public interface IEnumProcessor<TEnum> : IEnumProcessor where TEnum : struct, IConvertible, IComparable, IFormattable
new TEnum GetCurrentValue();
void AddSelectedValue(TEnum value);
}
and finally create a generic class with the actual code:
public class EnumProcessor<T> where TEnum : struct, IConvertible, IComparable, IFormattable
{
}
You can create instances of this processor with MakeGenericType
, call methods with MakeGenericMethod
, then pass it to other code you write as an IEnumProcessor if that code does not need to know the specific type of enum.
Finally, note that, since the underlying type of an enum can be an s/byte, u/short, u/int or u/long, with this architecture you will never be able to have more than 64 instances of your run time type. If you want more you need a different architecture, at the minimum defining your enum as a bit field with the [Flags]
attribute and then defining your enum values as composites, which might have unforeseen (by me) consequences.
Upvotes: 1
Reputation: 29895
Step 3 and 4 can be done using the Enum.Parse() method:
var theAnimal = Enum.Parse(typeof(MyAnimals), "Pig");
No Dynamics necessary.
Upvotes: 0