使用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
相关产品推荐
相关产品推荐

