代码之家  ›  专栏  ›  技术社区  ›  Mark

如何从MSIL调用私有方法?

  •  6
  • Mark  · 技术社区  · 14 年前

    我正在编写一个“弱事件工厂”(weak event factory)——代码将任何委托转换为具有相同签名的新委托,但在目标上实现weakreference。我正在使用MSIL来避免调用delegate.createDelegate(其性能显示很慢)。

    弱参考代表工作得很好 只要 基础方法 方法 原来的代表),被宣布为公众。一旦使用私有或匿名方法,运行时的msil将使用 方法访问异常 .

    使用已编译的表达式树,我能够调用私有方法,因此必须能够动态地发出调用私有方法的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)…因为动作源于mscorlib,所以jit似乎非常乐意 调用私有方法。尝试使用自己的委托,并且JIT会像直接向目标发出调用或callvirt一样进行吐出。

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

    3 回复  |  直到 14 年前
        1
  •  5
  •   kvb    14 年前

    你把你的IL插入 DynamicMethod 或者进入动态装配的方法中?据我所知,无法从动态程序集中跳过可见性检查,但使用 动态方法 (见 here )

        2
  •  5
  •   Mark    14 年前

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

    正如我所说,这是一个解决方案(它很高兴地允许运行时生成的代码调用私有方法),但它仍然无法解释表达式树和反射等技术如何调用私有方法。

        3
  •  -1
  •   Richard Flamsholt    7 年前

    使用call,而不是callvirt。

    [编辑:不是一般性建议,而是专门针对这个问题]

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

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