如何通过代码从多个类动态生成.dll程序集?
解决方案:用Reflection.Emit直接从现有类元数据生成程序集
我之前也被CodeDom的这个限制坑过——非要传源码字符串,手写或者拼接起来特别麻烦,尤其是类里有特性、复杂属性的时候,很容易出错。既然你想直接传入已定义好的类类型(比如MyClass)来生成程序集,完全可以绕开CodeDom,用System.Reflection.Emit这套API来实现,它可以直接基于现有类的元数据,在内存中复刻出类的结构,全程不需要跟源码字符串打交道。
核心思路
通过反射获取目标类的所有信息(包括类特性、属性、属性特性、访问修饰符等),然后用AssemblyBuilder、ModuleBuilder、TypeBuilder这些Emit类,一步步在新程序集里构建出和原类完全一致的类型。
完整代码示例
假设你的原类是这样的:
using System.Runtime.Serialization; [DataContract] public class MyClass { [DataMember(Order = 1)] public bool Valid { get; set; } [DataMember(Order = 2)] public string Name { get; set; } }
下面是动态生成包含这个类的程序集的代码:
using System; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; class Program { static void Main() { // 1. 创建动态程序集和模块 var assemblyName = new AssemblyName("DynamicMyClassAssembly"); // 这里指定RunAndSave表示可以运行也可以保存到磁盘 var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, $"{assemblyName.Name}.dll"); // 2. 获取原类的类型信息,定义新的类型 var originalType = typeof(MyClass); var typeBuilder = moduleBuilder.DefineType( originalType.FullName, originalType.Attributes // 继承原类的访问修饰符等特性 ); // 3. 复制原类上的自定义特性(比如[DataContract]) CopyCustomAttributes(originalType, typeBuilder); // 4. 复制原类的所有公共实例属性(包括特性、get/set方法) foreach (var prop in originalType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { // 定义自动属性的私有 backing 字段 var fieldBuilder = typeBuilder.DefineField( $"<{prop.Name}>k__BackingField", prop.PropertyType, FieldAttributes.Private ); // 定义属性本身 var propBuilder = typeBuilder.DefineProperty( prop.Name, prop.Attributes, prop.PropertyType, null ); // 复制属性上的特性(比如[DataMember(Order = 1)]) CopyCustomAttributes(prop, propBuilder); // 生成get方法 var getMethod = typeBuilder.DefineMethod( $"get_{prop.Name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, prop.PropertyType, Type.EmptyTypes ); var getIl = getMethod.GetILGenerator(); getIl.Emit(OpCodes.Ldarg_0); // 加载this getIl.Emit(OpCodes.Ldfld, fieldBuilder); // 取字段值 getIl.Emit(OpCodes.Ret); // 返回 // 生成set方法 var setMethod = typeBuilder.DefineMethod( $"set_{prop.Name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new[] { prop.PropertyType } ); var setIl = setMethod.GetILGenerator(); setIl.Emit(OpCodes.Ldarg_0); // 加载this setIl.Emit(OpCodes.Ldarg_1); // 加载传入的值 setIl.Emit(OpCodes.Stfld, fieldBuilder); // 设置字段值 setIl.Emit(OpCodes.Ret); // 返回 // 将get/set方法关联到属性 propBuilder.SetGetMethod(getMethod); propBuilder.SetSetMethod(setMethod); } // 5. 完成类型定义,并保存程序集 var dynamicType = typeBuilder.CreateType(); assemblyBuilder.Save($"{assemblyName.Name}.dll"); // 验证:可以从动态程序集中加载类型并创建实例 var loadedType = assemblyBuilder.GetType(originalType.FullName); var instance = Activator.CreateInstance(loadedType); Console.WriteLine($"动态生成的类实例:{instance.GetType().FullName}"); } // 通用方法:复制自定义特性到目标构建器 static void CopyCustomAttributes(MemberInfo source, ICustomAttributeProvider target) { foreach (var attrData in source.GetCustomAttributesData()) { // 构造特性的构造函数参数和命名参数 var constructorArgs = attrData.ConstructorArguments.Select(arg => arg.Value).ToArray(); var namedProps = attrData.NamedArguments.Select(na => na.MemberInfo).Cast<PropertyInfo>().ToArray(); var namedPropValues = attrData.NamedArguments.Select(na => na.TypedValue.Value).ToArray(); var attrBuilder = new CustomAttributeBuilder( attrData.Constructor, constructorArgs, namedProps, namedPropValues ); target.SetCustomAttribute(attrBuilder); } } }
补充说明
- 如果你的原类还有方法、字段、嵌套类型等,都可以用类似的反射+Emit方式复制,只需要遍历对应的
MethodInfo、FieldInfo等,然后用对应的Builder类生成即可。 - 如果你觉得Reflection.Emit的IL编写有点繁琐,也可以考虑使用一些封装好的库来简化动态类型的创建,但原生的Emit是最灵活且不需要额外依赖的选择。
- 如果你一定要用CodeDom,那只能通过反编译工具把已编译的类转成源码字符串,但这个过程会引入额外的复杂度,不如直接用Emit来得直接。
内容的提问来源于stack exchange,提问作者Arno Nym
相关产品推荐
相关产品推荐

