6

我正在编写一个“弱事件工厂” - 将任何 Delegate 转换为具有相同签名的新委托的代码,但在目标上实现 WeakReference。我正在使用 MSIL 来避免对 Delegate.CreateDelegate 的调用(其性能已被证明很慢)。

只要底层方法(原始委托的方法)被声明为公共的,弱引用委托就可以完美地工作。一旦使用私有或匿名方法,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);

那么调用私有成员的秘诀是什么?反射可以做到,表达式树可以做到……为什么上面的代码会失败?


编辑:非常感谢所有在这里提供答案的人。事实证明,在我的上下文中始终有效的唯一解决方案是使用泛型委托(Action)......因为 Action 源自 mscorlib,JIT 似乎非常乐意让它调用私有方法。尝试使用您自己的委托和 JIT pukes,就像您直接向目标发出 Call 或 Callvirt 一样。

任何有兴趣查看工作代码的人都可以前往codeplex - 这里给出的答案有助于实现 WeakDelegate 功能。

4

3 回答 3

5

您是否将 IL 插入DynamicMethod到动态程序集中的方法或方法中?据我了解,无法从动态程序集中跳过可见性检查,但您可以在使用 a 时跳过它们DynamicMethod(请参见此处)。

于 2010-10-27T13:36:55.670 回答
5

解决方案(针对我的特定问题)是使用委托而不是直接方法调用。您可以轻松地构造一个开放委托并将其传递给 IL 代码,然后当 IL 代码调用委托的 Invoke 方法时,JIT 接受该模式为合法并允许调用私有方法。

就像我说的,这是一个解决方案(它很高兴允许运行时生成的代码调用私有方法),尽管它仍然没有解释像表达式树和反射这样的技术是如何设法调用私有方法的。

于 2010-10-28T08:37:51.243 回答
-1

使用 Call,而不是 Callvirt。

[编辑:不是一般建议,而是专门解决这个问题]

Callvirt 用于调用虚拟方法,其中目标地址还取决于实例的确切类型。当您使用弱引用时,这不起作用。

另一方面,私有方法的目标可以在编译时确定。因此,使用 Call 调用它是合适的。

于 2010-10-27T00:57:30.810 回答