能否修改MethodInfo的DeclaringType?调用匿名方法遇实例类型错误
这个问题的核心是匿名方法的归属类型与Controller实例不兼容:你在Descriptor里创建的匿名Func<string>,会被编译器偷偷编译成Descriptor类的私有嵌套匿名类型中的方法,它的DeclaringType是那个嵌套类型,而Controller尝试用自身实例去调用这个属于Descriptor相关类型的方法,自然会触发实例类型验证失败的ArgumentException。
因为你无法修改Controller类,这里有两个可行的解决思路:
方案1:使用静态方法替代匿名方法
静态方法不依赖任何实例,调用时不会触发实例类型匹配检查,是最直接的解决方案:
// 在Descriptor类中定义静态方法 public static string GetFoo() { return "foo"; } // 获取对应的MethodInfo var mx = typeof(Descriptor).GetMethod(nameof(GetFoo), BindingFlags.Public | BindingFlags.Static);
把这个mx传给Controller的构造函数,调用时就不会再出现实例类型不匹配的问题了。
方案2:用DynamicMethod生成无归属类型的方法
如果不想在Descriptor中显式定义静态方法,可以用System.Reflection.Emit.DynamicMethod创建一个不关联到任何具体类的方法,这样Expression验证时不会检查实例类型:
using System.Reflection.Emit; // 在Descriptor中创建DynamicMethod var dynamicMethod = new DynamicMethod( name: "DynamicGetFoo", returnType: typeof(string), parameterTypes: Type.EmptyTypes, module: typeof(Descriptor).Module); // 编写IL代码返回"foo" ILGenerator il = dynamicMethod.GetILGenerator(); il.Emit(OpCodes.Ldstr, "foo"); il.Emit(OpCodes.Ret); // 获取MethodInfo var mx = dynamicMethod.GetMethodInfo();
DynamicMethod生成的方法不属于任何类,因此Controller调用时不会触发实例类型验证逻辑,完美绕过报错。
为什么原来的匿名方法不行?
编译器会把匿名方法包装成包含它的类(这里是Descriptor)的私有嵌套类中的实例方法,当Controller尝试用自身实例调用这个方法时,System.Linq.Expressions.Expression.ValidateCallInstanceType会检查实例类型是否是方法DeclaringType的子类或实现类,显然Controller和那个匿名嵌套类型完全无关,所以直接抛出异常。
内容的提问来源于stack exchange,提问作者WoF_Angel

