2011-04-22 11 views
17

私はユーザーが任意の式を入力できるようにするためのライブラリに取り組んでいます。私のライブラリは、より大きな表現の一部としてそれらの表現をデリゲートにコンパイルします。さて、まだ知られていない理由のために、Compileで式をコンパイルすることは、コンパイルされた式ではない場合よりもはるかに低速なコードになります。 I asked a question about this以前の回避策は、CompileではなくCompileToMethodを使用し、新しいダイナミックアセンブリの新しいタイプに対してstaticメソッドを作成することでした。それは動作し、コードは高速です。.NET:動的アセンブリから非公開のメンバーにアクセスする

ユーザーは任意の式を入力できます。ユーザーが非公開関数を呼び出したり、式内の非公開フィールドにアクセスした場合は、System.MethodAccessException(非公開メソッドの場合)がスローされます。デリゲートが呼び出されたとき。

私はおそらくここに何ができることは表現が何も非パブリックにアクセスし、それらの例にCompile遅くを使用するかどうかを確認すること、新規ExpressionVisitorを作成することですが、私はむしろ、動的アセンブリが何らかの形でアクセス権を取得していることがあるだろう非公開のメンバー。または、私ができることがあれば、Compileが遅い(時には)ことがわかります。

この問題を再現するために、完全なコード:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Linq.Expressions; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace DynamicAssembly 
{ 
    public class Program 
    { 
    private static int GetValue() 
    { 
     return 1; 
    } 

    public static int GetValuePublic() 
    { 
     return 1; 
    } 

    public static int Foo; 

    static void Main(string[] args) 
    { 
     Expression<Func<int>> expression =() => 10 + GetValue(); 

     Foo = expression.Compile()(); 

     Console.WriteLine("This works, value: " + Foo); 

     Expression<Func<int>> expressionPublic =() => 10 + GetValuePublic(); 

     var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic); 

     Foo = compiledDynamicAssemblyPublic(); 

     Console.WriteLine("This works too, value: " + Foo); 

     var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression); 

     Console.WriteLine("This crashes"); 

     Foo = compiledDynamicAssemblyNonPublic(); 
    } 

    static Delegate CompileExpression(LambdaExpression expression) 
    { 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
     new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")), 
     AssemblyBuilderAccess.Run); 

     var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module"); 

     var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public); 

     var methodBuilder = typeBuilder.DefineMethod("MyMethod", 
     MethodAttributes.Public | MethodAttributes.Static); 

     expression.CompileToMethod(methodBuilder); 

     var resultingType = typeBuilder.CreateType(); 

     var function = Delegate.CreateDelegate(expression.Type, 
     resultingType.GetMethod("MyMethod")); 

     return function; 
    } 
    } 
} 
+0

私はあなたに答えがありませんが、プライベートメソッドの呼び出しをサポートする必要があるのはなぜですか? – jlew

+0

ユーザは可能であると予想しているため。 '()=> CallPrivateMethod()'のように、式を作成するときに*アクセス可能ですが、実行時には失敗します。彼がそれを実行し、それがクラッシュして燃えるまでは動作しないことを示すものは何もありません。それは本当に悪く、 "最小の驚き"のルールに違反しているので、私はそれを正当化することはできませんし、遅いコードを解決しなければなりません。 – JulianR

+0

ユーザーがC#プログラマーである場合(たとえば、フォームに式を入力するのではなく)、意味があります。コンパイルされたデリゲートのリリース対デバッグモードをベンチマークしましたか?彼らはどのようにお互いを比較しているのですか? – jlew

答えて

5

この問題は、非公開のフィールドや別のクラスのメンバに反映させることを許可するアクセス許可がないため、アクセス許可ではありません。これは、2つの非ダイナミックアセンブリをコンパイルし、1つのアセンブリが2番目のアセンブリでパブリックメソッドを呼び出す場合に似ています。最初のアセンブリを再コンパイルせずにメソッドをprivateに変更した場合、実行時に最初のアセンブリ呼び出しはに失敗します。言い換えれば、ダイナミックアセンブリ内の式は、同じアセンブリ内であっても、別のクラスから呼び出すことができない通常のメソッド呼び出しにコンパイルされています。

あなたの問題を解決する権限がないので、非公開フィールドとメソッド参照をリフレクションを使用する部分式に変換することができます。

テストケースの例を示します。これは失敗します。

Expression<Func<int>> expression =() => 10 + GetValue(); 

が、これは成功します:

Expression<Func<int>> expression =() => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); 

を、これは例外でクラッシュしないので、あなたはあなたの動的アセンブリは、反射権限を持っていることがわかります、それはプライベートメソッドにアクセスすることができ、 CompileToMethodという結果が得られる通常のメソッド呼び出しを使用することはできません。

1

に私は一度DynamicMethodを使用して生成されたILコードから、クラスのプライベートな要素にアクセスする問題がありました。

http://msdn.microsoft.com/en-us/library/exczf7b9.aspx

このリンクにアクセスする方法のサンプルが含まれています

それはウィッヒプライベートアクセスにクラスの型を受け取るDynamicMethodが許されるクラスのコンストラクタのオーバーロードがあったことが判明しましたプライベートデータ...これは表現木とは関係ないことがわかっていますが、それはあなたにそれを行う方法の手がかりを与えるかもしれません。

表現木をコンパイルするときに似たようなことがあるかもしれません。その式ツリーをDynamicMethodとして作成することもできます。

+0

しかし、 'CompileToMethod'には呼び出す方法が1つしかありません:' MethodBuilder'のインスタンスを渡す必要があります。そして、あなたは 'TypeBuilder'から' MethodBuilder'を手に入れることができ、そのためには 'ModuleBuilder'が必要です。* AssemblyBuilderが必要です。 – JulianR

1

ダイナミックアセンブリの場合は、実際にはInternalsVisibleToを使用することができます(厳密な名前)。それはあなたのケースで十分かもしれない内部のメンバーを使用することができますか?アイデアを得るために

は、ここで別のアセンブリから内部のものを使用する部品番号の動的アセンブリを有効にするには、ホット示した例です: http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/

このアプローチが十分でない場合、私は組み合わせでいいよRickとMiguelの提案:非公開メンバーへの呼び出しごとに「プロキシ」DynamicMethodを作成し、元の呼び出しの代わりに使用するように式ツリーを変更します。

関連する問題