Microsoft shows how to create a dynamic class here:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder(v=vs.71).aspx
This defines a custom object, where they define a constructor and a method. I have a class defined, is there a way to emit the class I have already written instead of trying to write it as the example shows?
Thanks FacticiusVir, it's nearly complete. However it doesn't seem to be quite there, 'Countries.USA is not supported by the language'
Full code including FacticiusVir's answer:
class DynamicEnums
{
public static void Main()
{
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName("DynamicEnums");
AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) });
List<Type> types = new List<Type>();
foreach(ReferenceType rt in GetTypes())
{
TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);
ConstructorBuilder staticConstructorBuilder = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
ILGenerator staticConstructorILGenerator = staticConstructorBuilder.GetILGenerator();
foreach (Reference r in GetReferences(rt.ID))
{
string name;
if (rt.Name == "Countries")
name = r.Abbreviation.Trim();
else if (rt.Name == "PermanentFundDividends")
name = "Year" + r.Abbreviation.Trim();
else
name = NameFix(r.Name);
// Create a public, static, readonly field to store the
// named ReferenceObject.
FieldBuilder referenceObjectField = tb.DefineField(name, typeof(ReferenceObject), FieldAttributes.Static | FieldAttributes.Public | FieldAttributes.InitOnly);
// Add code to the static constructor to populate the
// ReferenceObject field:
// Load the ReferenceObject's ID value onto the stack as a
// literal 4-byte integer (Int32).
staticConstructorILGenerator.Emit(OpCodes.Ldc_I4, r.ID);
// Create a reference to a new ReferenceObject on the stack
// by calling the ReferenceObject(int32 pValue) reference
// we created earlier.
staticConstructorILGenerator.Emit(OpCodes.Newobj, referenceObjectConstructor);
// Store the ReferenceObject reference to the static
// ReferenceObject field.
staticConstructorILGenerator.Emit(OpCodes.Stsfld, referenceObjectField);
}
staticConstructorILGenerator.Emit(OpCodes.Ret);
types.Add(tb.CreateType());
}
try
{
ab.Save(aName.Name + ".dll");
}
catch (Exception)
{
Console.WriteLine("Could not save .dll, file must already be loaded.");
}
foreach (Type t in types)
{
foreach (FieldInfo o in t.GetFields())
{
Console.WriteLine("{0}.{1} = {2}", t, o.Name, "Later"); //Don't know how to get Value doing it this way
}
Console.WriteLine();
//Console.ReadKey();
}
Console.WriteLine();
Console.WriteLine("Dynamic Enums Built Successfully.");
//Console.ReadKey();
}
public static List<ReferenceType> GetTypes()
{
List<ReferenceType> referenceTypes = new List<ReferenceType>();
referenceTypes.Add(new ReferenceType { ID = 1, Name = "Countries" });
return referenceTypes;
}
public static List<Reference> GetReferences(int typeID)
{
List<Reference> references = new List<Reference>();
references.Add(new Reference { ID = 120, Abbreviation = "USA" });
return references;
}
public struct ReferenceType
{
public int ID;
public string Name;
}
public struct Reference
{
public int ID;
public int TypeID;
public string Abbreviation;
public string Name;
}
public static string NameFix(string name)
{
//Strip all non alphanumeric characters
string r = Regex.Replace(name, @"[^\w]", "");
//Enums cannot begin with a number
if (Regex.IsMatch(r, @"^\d"))
r = "N" + r;
return r;
}
}
public class ReferenceObject
{
private readonly int value;
public ReferenceObject(int pValue)
{
value = pValue;
}
public override string ToString()
{
return value.ToString();
}
public int Value()
{
return value;
}
public int ID()
{
return value;
}
#region == Operator
public static bool operator ==(int objLeft, ReferenceObject objRight)
{
return objLeft == objRight.value;
}
public static bool operator ==(ReferenceObject objLeft, int objRight)
{
return objLeft.value == objRight;
}
public static bool operator ==(string objLeft, ReferenceObject objRight)
{
return objLeft == objRight.value.ToString();
}
public static bool operator ==(ReferenceObject objLeft, string objRight)
{
return objLeft.value.ToString() == objRight;
}
#endregion
#region != Operator
public static bool operator !=(int objLeft, ReferenceObject objRight)
{
return objLeft != objRight.value;
}
public static bool operator !=(ReferenceObject objLeft, int objRight)
{
return objLeft.value != objRight;
}
public static bool operator !=(string objLeft, ReferenceObject objRight)
{
return objLeft != objRight.value.ToString();
}
public static bool operator !=(ReferenceObject objLeft, string objRight)
{
return objLeft.value.ToString() != objRight;
}
#endregion
public override bool Equals(object obj)
{
if ((obj is ReferenceObject))
return value == ((ReferenceObject)obj).value;
if ((obj is int))
return value == (int)obj;
if ((obj is string))
return value.ToString() == (string)obj;
return false;
}
public override int GetHashCode()
{
return value;
}
}
Alright, I've assumed that Reference & ReferenceType look something like this:
and that the class you're trying to generate will looking something like this:
What you need to do is created a set of fields (I've made these static and readonly, which is a good practice if you're trying to imitate enumerations) and then populate them from a static constructor, e.g.:
---- Edit ----
To access the values of the fields in the generated DLL, you've got a few options. The first is to run this code, take a copy of the "Dynamic Enums.dll" file it generates and reference that directly from whatever other project includes your runtime code; i.e. you have a project that executes at build time to produce the DLL (as above) and a second, separate project that references the DLL and does the run-time work of your application. The advantage of this is that you can refer to the generated classes directly in code (e.g.
SomeMethod(Countries.USA)
orif(someVariable == Countries.CAN)
), while the downside is you must either work the code above into your build process, or remember to regenerate your DLLs whenever the source database changes. If this is what you're looking for, I'd recommend looking at dedicated code-generation tools like T4, which is built into Visual Studio.The option you seem to be going for above is to directly access the dynamic assembly that you generated while it is still held in memory. To do this, you must mark the assembly as runnable as well as savable:
In truth, you could just mark it as
AssemblyBuilderAccess.Run
but I'm assuming you still want to save the output.You can then use the FieldInfo.GetValue(object obj) method to get the static value: