2011-08-28 16 views
14

私はC#で式ツリーを学習しています。ローカル変数と式ツリー

私はしばらくの間、今で立ち往生しています:

string filterString = "ruby"; 
Expression<Func<string, bool>> expression = x => x == filterString; 

どのように私はコードでこの表現を構築することができますか?ローカル変数をキャプチャする方法のサンプルはありません。これは簡単である:

Expression<Func<string, bool>> expression = x => x == "ruby"; 

これは次のようになります

ParameterExpression stringParam = Expression.Parameter(typeof(string), "x"); 
Expression constant = Expression.Constant("ruby"); 
BinaryExpression equals = Expression.Equal(stringParam, constant); 
Expression<Func<string, bool>> lambda1 = 
    Expression.Lambda<Func<string, bool>>(
     equals, 
     new ParameterExpression[] { stringParam }); 

デバッガプリントは(X => X == filterString)については、以下:

{X =>( X == 値(Predicate.Program + <> c__DisplayClass3).filterString)}

このトピックについていくつかの話をしてくれてありがとう。

答えて

24

ローカル変数のキャプチャは、コンパイラ生成クラスのインスタンス変数にローカル変数を「持ち上げる」ことによって実際に実行されます。 C#コンパイラは、適切な時間に余分なクラスの新しいインスタンスを作成し、ローカル変数へのアクセスを関連インスタンスのインスタンス変数へのアクセスに変更します。

したがって、式ツリーはインスタンス内のフィールドアクセスである必要があります。インスタンス自体はConstantExpressionで提供されます。

式ツリーの作成方法を操作する最も簡単な方法は、通常、ラムダ式で似たようなものを作成し、生成されたコードをReflectorで見て、Reflectorがラムダ表現。

+2

ありがとうございます。生成されたMSILコードを見るヒントは非常に役に立ちます。 – yonexbat

+3

何か '' Expression.Constant(Expression.Constant)、 "Value"); 'それを行う必要があります – Appetere

+1

@Appetere' Expression.Constant(filterString) 'はどうでしょうか?確かに、変数への変更は反映されませんが、どちらもあなたの提案ではありません。 –

5

このコードは、ローカル変数を定数として扱うクロージャブロックに式をラップします。

string filterString = "ruby"; 

var filterStringParam = Expression.Parameter(typeof(string), "filterString"); 
var stringParam = Expression.Parameter(typeof(string), "x"); 

var block = Expression.Block(
// Add a local variable. 
new[] { filterStringParam }, 
// Assign a constant to the local variable: filterStringParam = filterString 
Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))), 
// Compare the parameter to the local variable 
Expression.Equal(stringParam, filterStringParam)); 

var x = Expression.Lambda<Func<string, bool>>(block, stringParam).Compile(); 
0

古い質問が、それはSQLまで解析できないようごExpression.Blockを使用することはできませんその場合にはLINQのツーエンティティ(L2E)のために何か似た建物の表現をしようとするとき、私はそれに来ました。

ここでは、L2Eで動作するJonの回答に続く明示的な例を示します。木のよう

class ExpressionScopedVariables 
{ 
    public String Value; 
} 

ビルド:

var scope = new ExpressionScopedVariables { Value = filterString}; 
var filterStringExp = Expression.Constant(scope); 
var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0]; 
var access = Expression.MakeMemberAccess(filterStringExp, getVariable); 

そしてメンバーアクセス式と元のコードで定数を置き換えます

BinaryExpression equals = Expression.Equal(stringParam, access); 
Expression<Func<string, bool>> lambda1 = 
    Expression.Lambda<Func<string, bool>>(
     equals, 
     new ParameterExpression[] { stringParam }); 
フィルタの値を格納するヘルパークラスを作成します。
関連する問題