3

リフレクション ツリーと式ツリーを組み合わせて使用​​しており、特定のプロパティ アクセサーをクラスから呼び出し元のメソッドに戻したいと考えています。私の現在のコードには、クラスをトラバースして s のリストを返すメソッドがありMemberExpressionます。次に、呼び出し元はメンバー式を反復処理し、ラムダを作成します。ラムダは、検査されたクラスのインスタンスで呼び出され、プロパティの値を返します。

メソッド呼び出しがない場合の例を次に示します (LINQPad で実行可能)。

void Main()
{
    var t = new Test { Prop = "Test" };

    var property = t.GetType().GetProperty("Prop");

    var baseType = Expression.Parameter(typeof(Test), "baseType");
    var memberAccess = Expression.MakeMemberAccess(baseType, property);
    var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
    var func = lambda.Compile();
    var result = func(t);
    result.Dump();
}

class Test {
    public string Prop { get; set; }
}

これは機能せず、次の例外がスローされます。

InvalidOperationException: タイプ 'UserQuery+Test' の変数 'baseType' がスコープ '' から参照されましたが、定義されていません

ただし、ラムダの作成を次のように変更すると:

var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, baseType);

つまり、Expression.Parameterを以前に使用した変数に置き換えると、機能します。リストと一緒に元のパラメーターを返す必要があるため、これを使用したいシナリオでは (簡単に) 可能ではありません (もちろん、タプルを返すこともできますが、必要はありません)。

なぜこのように機能するのですか?ラムダの を検査するDebugViewと、どのアプローチが使用されても、それらはまったく同じです。

.Lambda #Lambda1<System.Func`2[UserQuery+Test,System.String]>(UserQuery+Test $baseType)
{
    $baseType.S
}
4

1 に答える 1

3

はい、以前に使用した を参照する必要がありますParameterExpression。これもコンパイルされません:

private String Foo(Test myParam)
{
  return myAnotherParam.MyProperty;
}

ラムダでnewParameterExpressionを開始すると、同じことを実行します (ただし、ラムダを作成するときは、逆の順序で実行していることに注意してください。最初にメソッド本体を作成し、次にメソッド宣言を作成します)。

// return myAnotherParam.MyProperty;
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);

// private String Foo(MyClass myParam)
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
于 2012-08-20T19:02:40.190 に答える