6

私は「弱いイベントファクトリ」を書いています。これは、任意のデリゲートを同じ署名を持つ新しいデリゲートに変換するコードですが、ターゲットに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);

では、プライベートメンバーを呼び出す秘訣は何ですか?リフレクションはそれを行うことができます、式ツリーはそれを行うことができます...なぜ上記のコードは失敗するのですか?


編集:ここで答えを提供してくれたすべての人に感謝します。私のコンテキストで一貫して機能した唯一の解決策は、ジェネリックデリゲート(アクション)を使用することでした...アクションはmscorlibから発生するため、JITはプライベートメソッドを呼び出せるように完全に満足しているようです。独自のデリゲートを使用してみてください。JITは、ターゲットに直接CallまたはCallvirtを発行する場合とまったく同じように実行します。

動作するコードを確認することに興味がある人は誰でも、 codeplexにアクセスできます。ここに記載されている回答は、WeakDelegate機能の実装に役立ちました。

4

3 に答える 3

5

DynamicMethodILを動的アセンブリ内のメソッドまたはメソッドに挿入していますか?私が理解しているように、動的アセンブリ内から可視性チェックをスキップする方法はありませんが、を使用する場合はスキップできますDynamicMethodここを参照)。

于 2010-10-27T13:36:55.670 に答える
5

(私の特定の問題に対する)解決策は、直接メソッド呼び出しの代わりにデリゲートを使用することでした。オープンデリゲートを快適に構築してILコードに渡すことができます。その後、ILコードがデリゲートのInvokeメソッドを呼び出すと、JITはそのパターンを正当なものとして受け入れ、プライベートメソッドの呼び出しを許可します。

私が言ったように、これは解決策です(ランタイムで生成されたコードがプライベートメソッドを呼び出すことができます)が、ExpressionTreesやReflectionなどの技術がプライベートメソッドを呼び出す方法についてはまだ説明していません。

于 2010-10-28T08:37:51.243 に答える
-1

CallvirtではなくCallを使用します。

[編集:一般的な推奨事項としてではなく、特にこの問題に対処するため]

Callvirtは仮想メソッドを呼び出すためのものであり、宛先アドレスはインスタンスの正確なタイプにも依存します。弱参照を使用している場合、これは機能しません。

一方、プライベートメソッドのターゲットは、コンパイル時に決定できます。したがって、Callを使用して呼び出すのが適切です。

于 2010-10-27T00:57:30.810 に答える