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

使用Reflection.Emit时如何为未创建完成的动态类型创建委托

问题描述

我正在使用System.Reflection.Emit实现Google Protocol消息相关功能。

场景截图

我需要为动态生成的类型初始化静态字段,目标结构如下:

class MessageA
{
   static MessageParser<MessageA> Parser = new MessageParser<MessageA>(()=>new MessageA())
}

当前MessageA类型尚未完成创建,我尝试获取对应Func泛型委托的构造函数时,抛出System.NotSupportedException异常,提示信息为Specified method is not supported.,最小复现代码如下:

using System.Reflection;
using System.Reflection.Emit;

Console.WriteLine("Hello, World!");
var assembleBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Dynamic"), AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assembleBuilder.DefineDynamicModule("Dynamic.dll");
var typeA = moduleBuilder.DefineType("classA");
typeA.DefineDefaultConstructor(MethodAttributes.Public);

typeof(Func<>).MakeGenericType(typeA).GetConstructors();

请问C#编译器是如何处理这类场景的,该如何实现上述需求?


原因说明

异常的核心原因是TypeBuilder是未完成加载的动态类型占位符,不是运行时已加载的真实类型。直接将TypeBuilder作为泛型参数调用MakeGenericType得到的泛型类型属于未闭合的构造类型,这类类型不支持直接调用GetConstructors()、GetMethods()这类反射查询接口,会直接抛出NotSupportedException。

C#编译器处理这类自引用静态初始化逻辑时,不会走「先创建类型再反射获取成员」的路径,而是在IL生成阶段直接通过元数据token引用对应成员,不需要等类型完全构造完成再做反射查询。


实现方案

不要在类型创建阶段直接对未闭合的泛型类型调用反射查询接口,使用TypeBuilder提供的专用元数据解析API即可,具体步骤如下:

  • 先定义动态类型的默认构造函数、静态字段等基础成员
  • 生成静态构造函数(.cctor)的IL逻辑
  • 生成Lambda表达式对应的静态工厂方法,逻辑为返回目标类型的新实例
  • 用TypeBuilder.GetConstructor方法获取未闭合泛型类型对应的构造函数元数据,这个API专门为动态生成场景设计,可以识别TypeBuilder作为泛型参数的类型
  • 生成IL完成委托实例构造、MessageParser实例构造、静态字段赋值逻辑
  • 所有成员引用配置完成后,再调用CreateType()完成动态类型的创建

可直接运行的修正代码:

using System.Reflection;
using System.Reflection.Emit;

var assembleBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Dynamic"), AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assembleBuilder.DefineDynamicModule("Dynamic.dll");
var typeA = moduleBuilder.DefineType("classA");
// 定义默认实例构造函数
var defaultCtor = typeA.DefineDefaultConstructor(MethodAttributes.Public);

// 定义静态Parser字段
var funcGenericDef = typeof(Func<>);
var messageParserGenericDef = typeof(MessageParser<>);
var parserField = typeA.DefineField(
    "Parser", 
    messageParserGenericDef.MakeGenericType(typeA), 
    FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly);

// 定义静态构造函数
var cctorBuilder = typeA.DefineConstructor(
    MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
    CallingConventions.Standard, 
    Type.EmptyTypes);
var il = cctorBuilder.GetILGenerator();

// 生成 ()=>new classA() 对应的静态工厂方法
var factoryMethod = typeA.DefineMethod(
    "CreateInstance",
    MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig,
    typeA,
    Type.EmptyTypes);
var factoryIl = factoryMethod.GetILGenerator();
factoryIl.Emit(OpCodes.Newobj, defaultCtor);
factoryIl.Emit(OpCodes.Ret);

// 获取Func<classA>的构造函数元数据
var funcType = funcGenericDef.MakeGenericType(typeA);
var funcCtor = TypeBuilder.GetConstructor(
    funcType, 
    typeof(Func<>).GetConstructors().First(ci => 
        ci.GetParameters().Length == 2 
        && ci.GetParameters()[0].ParameterType == typeof(object) 
        && ci.GetParameters()[1].ParameterType == typeof(IntPtr))
);
// 生成Func委托实例
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldftn, factoryMethod);
il.Emit(OpCodes.Newobj, funcCtor);

// 获取MessageParser<classA>的构造函数元数据
var parserType = messageParserGenericDef.MakeGenericType(typeA);
var parserCtor = TypeBuilder.GetConstructor(
    parserType,
    messageParserGenericDef.GetConstructors().First(ci => 
        ci.GetParameters().Length == 1 
        && ci.GetParameters()[0].ParameterType.IsGenericType 
        && ci.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Func<>))
);
// 生成MessageParser实例并赋值给静态字段
il.Emit(OpCodes.Newobj, parserCtor);
il.Emit(OpCodes.Stsfld, parserField);
il.Emit(OpCodes.Ret);

// 所有元数据配置完成后再创建类型
var createdType = typeA.CreateType();

// 验证生成结果
var parserInstance = createdType.GetField("Parser", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
Console.WriteLine(parserInstance?.GetType()); // 输出 MessageParser`1[classA]

注意事项

  • 所有涉及TypeBuilder作为泛型参数的构造类型,要获取其构造函数、方法、字段等元数据,必须使用TypeBuilder的静态方法(GetConstructor/GetMethod/GetField),不能直接在MakeGenericType返回的对象上调用反射查询方法。
  • 不需要等CreateType()调用完成再生成内部IL逻辑,所有成员引用都是元数据级别的关联,运行时加载类型时会自动解析到正确的成员。
  • 示例中生成的静态工厂方法+ldftn指令构造委托的逻辑,和C#编译器编译同类Lambda表达式生成的IL完全一致,不需要额外引入表达式树生成逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:27:39