我正在编写一个“弱事件工厂” - 将任何Delegate转换为具有相同签名的新委托,但在目标上实现WeakReference的代码。我正在使用MSIL来避免调用Delegate.CreateDelegate(性能显示速度很慢)。
只要底层方法(原始委托的 Method )被宣告为公共,弱引用委托就完全可以正常工作。只要使用私有或匿名方法,MSIL就会在运行时使用 MethodAccessException 进行炸弹。
使用已编译的表达式树,我已经能够调用私有方法,因此必须能够动态发出调用私有方法的MSIL。 ......那么以下是什么问题?
// var target = this.Target
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, targetPropGetter);
il.Emit(OpCodes.Stloc, ilTarget);
// if(target != null)
// {
il.Emit(OpCodes.Ldloc, ilTarget);
il.Emit(OpCodes.Brfalse_S, ilIsNullLabel);
// Method( @target, parm1, parm2 ...);
il.Emit(OpCodes.Ldloc, ilTarget); // this = Target
short argIndex = 1;
foreach (var parm in delgParams) // push all other args
il.Emit(OpCodes.Ldarg, argIndex++);
il.Emit(OpCodes.Callvirt, delegat.Method); // <-- Bombs if method is private
il.Emit(OpCodes.Ret);
// }
il.MarkLabel(ilIsNullLabel);
那么调用私人会员的秘诀是什么?反射可以做到,表达式树可以做到......为什么上面的代码失败了?
任何有兴趣看到工作代码的人都可以转到codeplex - 这里给出的答案有助于实现WeakDelegate功能。
答案 0 :(得分:5)
您是将IL插入DynamicMethod
还是插入动态程序集中的方法?据我了解,无法从动态程序集中跳过可见性检查,但您可以在使用DynamicMethod
时跳过它们(请参阅here)。
答案 1 :(得分:5)
解决方案(针对我的特定问题)是使用委托而不是直接方法调用。您可以轻松构造一个开放委托并将其传递给IL代码,然后当IL代码调用委托的Invoke方法时,JIT接受该模式为合法,并允许调用私有方法。
就像我说的,这是一个解决方案(很高兴允许运行时生成的代码调用私有方法),尽管它仍然没有解释像Expression Trees和Reflection这样的技术如何设法调用私有方法。
答案 2 :(得分:-1)
使用Call,而不是Callvirt。
[编辑:不作为一般性建议,但专门用于解决此问题]
Callvirt用于调用虚方法,其中目标地址也取决于实例的确切类型。当你使用弱引用时,这不起作用。
另一方面,私有方法的目标可以在编译时确定。因此,使用Call调用它是合适的。