19

ユーザーが任意の式を入力できるようにするライブラリに取り組んでいます。私のライブラリは、これらの式をより大きな式の一部としてデリゲートにコンパイルします。現在、理由はまだ不明ですが、Compile時々/頻繁に式をコンパイルすると、コンパイルされた式ではない場合よりもはるかに遅いコードになります。以前にこれについて質問しましたが、回避策の 1 つは を使用せず、新しいCompile動的アセンブリの新しい型でメソッドCompileToMethodを作成することでした。staticそれは機能し、コードは高速です。

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

ここでおそらくできることはExpressionVisitor、式が非パブリックにアクセスするかどうかをチェックする新しいものを作成し、そのような場合は低速を使用するCompileことですが、動的アセンブリが非パブリックメンバーにアクセスする権利を何らかの方法で取得することをお勧めします。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;
    }
  }
}
4

3 に答える 3

6

リフレクションなしで非公開フィールドまたは別のクラスのメンバーにアクセスできる権限がないため、問題は権限ではありません。これは、2 つの非動的アセンブリをコンパイルし、1 つのアセンブリが 2 番目のアセンブリのパブリック メソッドを呼び出す状況に似ています。次に、最初のアセンブリを再コンパイルせずにメソッドをプライベートに変更すると、最初のアセンブリ呼び出しが実行時に失敗するようになりました。つまり、動的アセンブリの式は通常のメソッド呼び出しにコンパイルされており、同じアセンブリ内であっても別のクラスから呼び出す権限がありません。

パーミッションで問題を解決することはできないため、パブリックでないフィールドとメソッドの参照を、リフレクションを使用する部分式に変換できる場合があります。

テストケースから取った例を次に示します。これは失敗します:

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

しかし、これは成功します:

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

これは例外でクラッシュしないため、動的アセンブリにはリフレクションのアクセス許可があり、プライベート メソッドにアクセスできることがわかります。通常のメソッド呼び出しを使用してアクセスすることはできませんCompileToMethod

于 2011-04-27T19:40:14.020 に答える
1

非動的アセンブリが作成された場合、実際には動的アセンブリに を含めることができますInternalsVisibleTo(厳密な名前でも機能します)。これにより、内部メンバーを使用できるようになりますが、あなたの場合はこれで十分でしょうか?

アイデアを得るために、Moq の動的アセンブリが別のアセンブリから内部のものを使用できるようにするホットな例を次に示します 。 moq/

このアプローチが不十分な場合は、Rick と Miguel の提案を組み合わせて使用​​します。非パブリック メンバーへの呼び出しごとに「プロキシ」DynamicMethods を作成し、元の呼び出しの代わりに使用されるように式ツリーを変更します。

于 2011-05-01T16:16:40.753 に答える
1

以前、DynamicMethod を使用して生成された IL コードから、クラスのプライベート要素にアクセスする際に問題が発生しました。

DynamicMethodプライベート アクセスが許可されるクラスの型を受け取るクラスのコンストラクターのオーバーロードがあることが判明しました。

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

このリンクには、プライベート データにアクセスする方法のサンプルが含まれています...これは式ツリーとは関係がないことはわかっていますが、その方法についての手がかりが得られるかもしれません。

式ツリーをコンパイルするときに、ある種の同様のことがあるかもしれません...または、その式ツリーをDynamicMethodとして作成できます。

于 2011-04-22T19:45:26.673 に答える