You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过代码从多个类动态生成.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:22:57