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

使用TypeBuilder构建含自引用属性的运行时类型遇困境

运行时类型生成器的循环引用类型创建问题

我正在用TypeBuilder构建运行时类型生成器,传入属性名称和类型列表生成类型的场景能正常运行,但现在要实现类似以下的循环引用结构:

public class Person
{
    public string Name { get; set; }
    public Person Child { get; set; }
}

现在卡在了“先有鸡还是先有蛋”的问题上——不知道怎么让TypeBuilder把还没创建出来的类型作为属性类型,同时想了解CLR是怎么处理这种情况的。


当前实现代码

FieldDescription类

public class FieldDescription
{
    public string FieldName { get; set; }
    public Type FieldType { get; set; }
}

CustomTypeBuilder类

public static class CustomTypeBuilder
{
    public static Type CompileResultType(List<FieldDescription> fields, string TypeName)
    {
        TypeBuilder tb = GetTypeBuilder(TypeName);
       
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        foreach (var field in fields)
            CreateProperty(tb, field);

        Type objectType = tb.CreateType();
        return objectType;
    }

    private static TypeBuilder GetTypeBuilder(string typeSignature)
    {
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null);

        FieldBuilder fieldBuilder = tb.DefineField("_ToStringValue", typeof(string), FieldAttributes.Private);
        return tb;
    }

    private static void CreateProperty(TypeBuilder tb, FieldDescription field)
    {
        string propertyName = field.FieldName;
        Type propertyType = field.FieldType ?? tb;
       
        FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);

        MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual, propertyType, Type.EmptyTypes);
      
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, fieldBuilder);
        getIl.Emit(OpCodes.Ret);

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig | MethodAttributes.Virtual,
              null, new[] { propertyType });

        ILGenerator setIl = setPropMthdBldr.GetILGenerator();
        Label modifyProperty = setIl.DefineLabel();
        Label exitSet = setIl.DefineLabel();

        setIl.MarkLabel(modifyProperty);
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, fieldBuilder);

        setIl.Emit(OpCodes.Nop);
        setIl.MarkLabel(exitSet);
        setIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
        propertyBuilder.SetSetMethod(setPropMthdBldr);
    }
}

解决循环引用类型创建的方法

其实TypeBuilder本身就支持这种场景——直接把TypeBuilder实例当作类型参数传入即可,不需要等CreateType()调用完成。因为TypeBuilder继承自Type,CLR在处理动态类型时,会在调用CreateType()时完成所有类型引用的绑定。

修改后的调用示例

要创建Person类这种循环引用类型,只需要在构建FieldDescription时,把Child字段的FieldType设为TypeBuilder实例本身(或留空,利用你代码里的field.FieldType ?? tb逻辑),然后正常调用CompileResultType即可:

// 构建字段描述
var fields = new List<FieldDescription>
{
    new FieldDescription { FieldName = "Name", FieldType = typeof(string) },
    // 循环引用字段留空,让代码自动使用当前TypeBuilder作为类型
    new FieldDescription { FieldName = "Child", FieldType = null }
};

// 生成类型
Type personType = CustomTypeBuilder.CompileResultType(fields, "Person");

你的代码里已经写了Type propertyType = field.FieldType ?? tb;,这部分逻辑是正确的,只要在传入字段描述时,把循环引用的字段类型留空,就会自动使用当前的TypeBuilder作为属性类型。


CLR如何处理这种情况

CLR在处理动态类型的循环引用时,采用了延迟绑定的机制:

  1. 当你用TypeBuilder定义字段、属性或方法时,CLR不会立即解析所有类型引用,而是记录下TypeBuilder的引用。
  2. 当你调用TypeBuilder.CreateType()时,CLR会一次性完成所有类型的验证和绑定,包括处理循环引用的情况——此时TypeBuilder已经可以转化为正式的Type实例,所有指向它的引用都会被替换为实际的类型。

这种机制和编译静态代码时的处理逻辑类似:编译器在编译类时,会先记录类的存在,处理完所有类的定义后再完成交叉引用的绑定,CLR对动态类型的处理也是同理。

内容的提问来源于stack exchange,提问作者jekcom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:45:36