C#运行时生成类时如何动态构造调用基类的构造函数
你可以通过ConstructorBuilder配合IL生成完成构造函数的定义与基类构造调用,完整实现如下:
完整实现代码
var newTypeName = "SomeReader"; var assemblyName = new AssemblyName("DynamicReaders"); var dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var dynamicModule = dynamicAssembly.DefineDynamicModule("Main"); var dynamicType = dynamicModule.DefineType( newTypeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, typeof(MyServiceBase)); // 注意:获取基类构造函数时参数类型要和基类定义完全匹配,否则basector会为null var basector = typeof(MyServiceBase).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(ILogger), typeof(IOptions<MyReaderOptions>), typeof(Extractor), typeof(IResolveSourceSystem), typeof(IAcceptLogs) }, null); if (basector == null) { throw new InvalidOperationException("未找到匹配的基类构造函数,请检查参数类型是否匹配"); } // 1. 定义子类构造函数的参数类型 Type[] ctorParamTypes = new Type[] { // 第一个参数为泛型ILogger<当前生成的子类>,和你期望的生成结构一致 typeof(ILogger<>).MakeGenericType(dynamicType), typeof(IOptions<MyReaderOptions>), typeof(Extractor), typeof(IResolveSourceSystem), typeof(IAcceptLogs) }; // 2. 创建构造函数Builder ConstructorBuilder ctorBuilder = dynamicType.DefineConstructor( MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, ctorParamTypes); // 3. 生成构造函数的IL逻辑 ILGenerator il = ctorBuilder.GetILGenerator(); // 加载隐式参数this到栈上 il.Emit(OpCodes.Ldarg_0); // 依次加载构造函数的5个入参(实例方法参数索引从1开始,0为this) il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.Emit(OpCodes.Ldarg_S, 4); il.Emit(OpCodes.Ldarg_S, 5); // 调用基类构造函数,将栈上的参数传递给基类 il.Emit(OpCodes.Call, basector); // 构造函数执行返回 il.Emit(OpCodes.Ret); // 完成类型创建 var type = dynamicType.CreateType();
关键说明
- 你原代码中获取
basector时传入的参数类型和基类定义存在不匹配问题(比如基类是IOptions<MyReaderOptions>你写为IOptions<ReaderOptions>,Extractor写为JsonExtractor等),需要修正为和基类构造参数完全一致的类型才能正确拿到基类构造函数。 - 生成的IL逻辑和手写C#构造函数的编译结果完全一致,
ILogger<T>继承自ILogger,所以可以直接传入基类构造函数,不需要额外转换。
内容的提问来源于stack exchange,提问作者Mulciber Coder
相关产品推荐
相关产品推荐

