C# Reflection Emit调用泛型接口实现类动态方法时触发无效程序异常
问题分析与解决方案
核心问题:栈不平衡引发无效程序异常
你遇到的问题根源在于返回值处理不匹配:
- 当接口方法为
void时,你的IL代码调用Invoke后直接执行Ret,但如果Invoke方法返回object(常见拦截方法签名),栈上会遗留未处理的返回值,CLR会判定栈状态非法,抛出异常。 - 有返回值的方法刚好能匹配
Invoke的返回值(后续隐式转换),因此可以正常运行。
另外,MethodBase.GetCurrentMethod()获取的是动态生成的代理方法,而非原始接口方法,这会导致拦截逻辑拿到错误的方法元数据,后续处理泛型参数时也可能出现问题。
具体修改步骤
1. 区分处理void和非void方法的返回值
调用Invoke后,根据原方法的返回类型决定后续操作:
- 若原方法返回
void,使用OpCodes.Pop指令移除栈上的返回值 - 若原方法有返回值,将
Invoke返回的object拆箱/转换为原返回类型
2. 直接使用原始接口方法的MethodInfo
不在IL中通过GetCurrentMethod()获取方法,而是直接将原始interfaceMethod的MethodInfo作为常量传入IL,避免拿到错误的方法实例。
3. 修正泛型参数的类型处理
确保泛型参数的约束和类型替换完全正确,特别是值类型装箱时使用正确的泛型参数类型。
修改后的代码示例
var invokeMethod = typeof(ServiceInterceptor).GetMethod(nameof(Invoke), BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("Could not get Invoke method"); var interfaceMethods = GetAllInterfaceMethods(interfaceType); var getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Static | BindingFlags.Public) ?? throw new InvalidOperationException("Could not get GetTypeFromHandle method"); foreach (var interfaceMethod in interfaceMethods) { var interfaceMethodTypeBuilder = serviceInterceptorTypeBuilder.DefineMethod( interfaceMethod.Name, interfaceMethod.Attributes & ~MethodAttributes.Abstract, interfaceMethod.CallingConvention); builtMethods.Add(interfaceMethodTypeBuilder); // 处理泛型参数 var interfaceMethodGenericArguments = interfaceMethod.GetGenericArguments(); var interfaceMethodGenericParametersMap = new Dictionary<Type, GeneraticParameterTypeWithInitialization>(); if (serviceInterceptorGenericParametersMap != null && serviceInterceptorGenericParametersMap.Any()) { foreach (var parameterMap in serviceInterceptorGenericParametersMap) { interfaceMethodGenericParametersMap[parameterMap.Key] = parameterMap.Value; } } if (interfaceMethodGenericArguments.Any()) { var genericMethodParameters = interfaceMethodTypeBuilder.DefineGenericParameters( interfaceMethodGenericArguments.Select(arg => arg.Name).ToArray()); for (int i = 0; i < genericMethodParameters.Length; i++) { interfaceMethodGenericParametersMap[interfaceMethodGenericArguments[i]] = new GeneraticParameterTypeWithInitialization { GenericTypeParameterBuilder = genericMethodParameters[i] }; } foreach (var interfaceMethodGenericArgument in interfaceMethodGenericArguments) { foreach (var constraint in interfaceMethodGenericArgument.GetGenericParameterConstraints()) { var replacedConstraint = ReplaceGenericArgumentsAndConstraintsFromType(constraint, interfaceMethodGenericParametersMap); if (replacedConstraint.IsInterface) { interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetInterfaceConstraints(replacedConstraint); continue; } interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetBaseTypeConstraint(replacedConstraint); interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetGenericParameterAttributes( constraint.GenericParameterAttributes & ~GenericParameterAttributes.Covariant & ~GenericParameterAttributes.Contravariant); } } } // 设置方法参数和返回类型 var interfaceParameters = interfaceMethod.GetParameters(); var interfaceMethodParameters = interfaceParameters.Select(x => ReplaceGenericArgumentsFromType(x.ParameterType, serviceInterceptorGenericParametersMap)).ToArray(); interfaceMethodTypeBuilder.SetParameters(interfaceMethodParameters); var returnType = ReplaceGenericArgumentsFromType(interfaceMethod.ReturnType, serviceInterceptorGenericParametersMap); interfaceMethodTypeBuilder.SetReturnType(returnType); var ilGenerator = interfaceMethodTypeBuilder.GetILGenerator(); var localOjectParamList = ilGenerator.DeclareLocal(typeof(object[])); var localMethodInfo = ilGenerator.DeclareLocal(typeof(MethodInfo)); // 直接加载原始接口方法的MethodInfo(替代GetCurrentMethod) ilGenerator.Emit(OpCodes.Ldtoken, interfaceMethod); ilGenerator.Emit(OpCodes.Call, typeof(MethodBase).GetMethod(nameof(MethodBase.GetMethodFromHandle), BindingFlags.Static | BindingFlags.Public)!); ilGenerator.Emit(OpCodes.Castclass, typeof(MethodInfo)); ilGenerator.Emit(OpCodes.Stloc, localMethodInfo); // 处理参数数组 if (interfaceParameters.Any()) { ilGenerator.Emit(OpCodes.Ldc_I4, interfaceParameters.Length); ilGenerator.Emit(OpCodes.Newarr, typeof(object)); ilGenerator.Emit(OpCodes.Stloc, localOjectParamList); for (short parameterIndex = 0; parameterIndex < interfaceParameters.Length; parameterIndex++) { var paramType = interfaceMethodParameters[parameterIndex]; ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList); ilGenerator.Emit(OpCodes.Ldc_I4, parameterIndex); ilGenerator.Emit(OpCodes.Ldarg, (short)(parameterIndex + 1)); // 值类型装箱,引用类型直接存储 if (paramType.IsValueType && paramType != typeof(void)) { ilGenerator.Emit(OpCodes.Box, paramType); } ilGenerator.Emit(OpCodes.Stelem_Ref); } ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldloc, localMethodInfo); ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList); } else { ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldloc, localMethodInfo); ilGenerator.Emit(OpCodes.Ldnull); } // 调用拦截方法 ilGenerator.Emit(OpCodes.Callvirt, invokeMethod); // 处理返回值 if (returnType == typeof(void)) { // void方法弹出Invoke的返回值,保证栈平衡 ilGenerator.Emit(OpCodes.Pop); } else { // 非void方法将object转换为原返回类型 if (returnType.IsValueType) { ilGenerator.Emit(OpCodes.Unbox_Any, returnType); } else { ilGenerator.Emit(OpCodes.Castclass, returnType); } } ilGenerator.Emit(OpCodes.Ret); }
关键修改点说明
- 替换
GetCurrentMethod逻辑:通过Ldtoken和GetMethodFromHandle获取原始接口方法的MethodInfo,确保拦截逻辑拿到正确的方法元数据。 - 返回值分支处理:
- 针对
void方法,调用Pop移除Invoke的返回值,保证栈状态合法 - 针对非
void方法,根据返回类型执行拆箱或类型转换,匹配原方法的返回值要求
- 针对
- 简化参数装箱逻辑:直接判断参数类型是否为值类型,减少多余的局部变量和分支跳转,降低IL代码复杂度。
内容的提问来源于stack exchange,提问作者Alecu
相关产品推荐
相关产品推荐

