10

式ツリーを使用してデリゲートを作成する関数があります。この式の中で、関数に渡された複数のパラメーターからキャプチャされた変数を使用します。例として、実際の式ツリーはかなり大きいので、次のようになります。

Delegate GenerateFunction<T>(T current, IList<T> parents) {
    var currentExpr = Expression.Parameter(typeof(T), "current");
    var parentsExpr = Expression.Parameter(parents.getType(), "parents");
    var parameters = new List<ParameterExpression>();

    ....

    return Expression.Lambda(Expression.Block(new List<ParameterExpression> { parentsExpr, currentExpr }, ....), parameters.ToArray()).Compile();
}

次に、このメソッドを別のメソッドから呼び出してから、その関数を別の関数に渡して使用します。それがすべて終わったら、式ツリー内で更新される親のコンテンツにアクセスしたいと思います。

すべてがコンパイルされているようで、式は問題ないように見えますが、実行すると、(式/クロージャー内の)親変数にアクセスするときにnull参照例外が発生しているように見えます(実際にはわかりませんが)。

何か間違ったことをしているのか、それが可能かどうか、そして何が起こっているのかを理解するためのヒントを知りたいと思います。メソッド内でホイストされた(?)ローカル変数を見つけることができないようですので、それらがキャプチャされているかどうか疑問に思っていますか?

ありがとう、マーク

4

2 に答える 2

19

メソッド内でホイストされたローカル変数を見つけることができないようですので、それらがキャプチャされているかどうか疑問に思っていますか?

ファクトリメソッドを「手動で」呼び出すことにより、式ツリーラムダを自分で構築しているように見えます。コンパイラは、それがあなたがしていることだとは知りません。メソッド呼び出しを見るだけです。ローカルを持ち上げたい場合は、(1)コンパイラにラムダを書き換えさせることでそれを実行させる、(2)自分で持ち上げる必要があります。

あれは:

int x = 123;
Expression<Func<int>> ex = ()=>x; 

コンパイラはラムダを書き直して、あなたが言ったかのようにそれを持ち上げます。

Closure c = new Closure();
c.x = 123;
Expression<Func<int>> ex = ()=>c.x; 

通常c、定数式になります。

しかし、あなたが言うなら

Expression<Func<int>> ex = Expression.Lambda( ...something that uses x ... );

コンパイラは、xを持ち上げる必要がある場所で何かをしていることを認識していません。xはラムダ式の中にありません。ファクトリを使用している場合、コンパイラは、あなたが何をしているかを知っていると想定し、それを書き直すことをいじりません。自分で持ち上げる必要があります。

于 2013-01-29T15:27:28.073 に答える
0

Expression.QuoteLambda式の変数キャプチャをサポートするを探していると思います。基本的に、内部LambdaExpression(キャプチャされた変数を参照する)は呼び出しでラップする必要がありExpression.Quote(...)ます。

ここでの例と説明:Expression.Quote()は、Expression.Constant()がまだ実行できないことを実行しますか?

于 2014-02-01T10:11:14.937 に答える