c#使用Reflection.Emit将定义好的类输出到动态模块
本文关键字:输出 动态 模块 Reflection 使用 Emit 定义 | 更新日期: 2023-09-27 18:11:27
Microsoft在这里展示了如何创建一个动态类:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder (v = vs.71) . aspx
定义了一个自定义对象,其中定义了一个构造函数和一个方法。我有一个类定义,有没有办法发出类我已经写了,而不是试图写它的例子显示?
谢谢FacticiusVir,它几乎完成了。然而,它似乎并没有完全到位,"国家。"语言"
"不支持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<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;
}
}
好的,我已经假设了Reference &参考类型看起来像这样:
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();
}