C#使用Reflection.Emit将定义的类输出到动态模块

Microsoft在此处演示了如何创建动态类:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder(v=vs.71).aspx

这定义了一个自定义对象,它定义了一个构造函数和一个方法。 我有一个类定义,有没有办法发出我已经编写的类而不是尝试编写它,如示例所示?

谢谢FacticiusVir,它已接近完成。 然而它似乎并不存在,’Countries.USA不支持该语言’

完整代码包括FacticiusVir的答案:

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 types = new List(); 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 GetTypes() { List referenceTypes = new List(); referenceTypes.Add(new ReferenceType { ID = 1, Name = "Countries" }); return referenceTypes; } public static List GetReferences(int typeID) { List references = new List(); 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; } } 

好吧,我假设Reference&ReferenceType看起来像这样:

 public class ReferenceType { public string Name { get; set; } public int ID { get; set; } } public class Reference { public string Abbreviation { get; set; } public int ID { get; set; } } 

并且您尝试生成的类将看起来像这样:

 public static class Countries { public static readonly ReferenceObject USA = new ReferenceObject(120); public static readonly ReferenceObject CAN = new ReferenceObject(13); //... } 

你需要做的是创建一组字段(我已经使这些静态和只读,如果你试图模仿枚举这是一个很好的做法),然后从静态构造函数填充它们,例如:

 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"); // Store a handle to the ReferenceObject(int32 pValue) // constructor. ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) }); foreach (ReferenceType rt in GetTypes()) { TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public); // Define a static constructor to populate the ReferenceObject // fields. 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 = r.Abbreviation.Trim(); // 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); } // Finish the static constructor. staticConstructorILGenerator.Emit(OpCodes.Ret); tb.CreateType(); } ab.Save(aName.Name + ".dll"); 

—-编辑—-

要访问生成的DLL中的字段值,您有几个选项。 第一个是运行此代码,获取它生成的“Dynamic Enums.dll”文件的副本,并直接从包含运行时代码的任何其他项目引用该文件; 即你有一个项目在构建时执行以生成DLL(如上所述)和第二个单独的项目,它引用DLL并执行应用程序的运行时工作。 这样做的好处是你可以直接在代码中引用生成的类(例如SomeMethod(Countries.USA)if(someVariable == Countries.CAN) ),而缺点是你必须将上面的代码放到你的构建中进程,或者记住在源数据库发生更改时重新生成DLL。 如果您正在寻找这个,我建议您查看专用的代码生成工具,如T4,它内置于Visual Studio中。

您似乎在上面选择的选项是直接访问您在内存中保留时生成的动态程序集。 为此,您必须将程序集标记为可运行且可保存:

 AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); 

事实上,你可以将它标记为AssemblyBuilderAccess.Run但我假设你仍然想保存输出。

然后,您可以使用FieldInfo.GetValue(object obj)方法获取静态值:

  foreach (Type t in types) { foreach (FieldInfo o in t.GetFields()) { // As this is a static field no instance of type 't' is // required to get the field value, so just pass null ReferenceObject value = o.GetValue(null) as ReferenceObject; Console.WriteLine("{0}.{1} = {2}", t, o.Name, value); } Console.WriteLine(); }