さて、次のコードは自明です。 And
演算子を使用して2つの式を1つに結合したい。最後の行は、ルーンタイムエラーの原因です。
追加情報:型 'System.String'の変数 'y'はスコープ ''から参照されていますが、定義されていません
コード:
Expression<Func<string, bool>> e1 = y => y.Length < 100;
Expression<Func<string, bool>> e2 = y => y.Length < 200;
var e3 = Expression.And(e1.Body, e2.Body);
var e4 = Expression.Lambda<Func<string, bool>>(e3, e1.Parameters.ToArray());
e4.Compile(); // <--- causes run-time error
みんな協力してくれてありがとう。
@dasblinkenlightが指摘したように、2つの式の2つのパラメーターは同じではありません。理由?まあ、それはコンパイラのトリックです。コンパイル時に、各式のクラスを作成し、各パラメーターにxxx1、xxx2、...などの名前を付けます。元の名前とはまったく異なります。
.NET 4.0+の答え:
問題は、式_e1
_と_e2
_で変数y
を表すパラメーター式オブジェクトが異なることです。 2つの変数に同じ名前が付けられ、同じ型を持っているという事実は重要ではありません。e1.Parameters.First()
とe2.Parameters.First()
は同じオブジェクトではありません。
これにより、表示される問題が発生します:_e1
_のパラメーターy
のみが_Lambda<>
_で使用できますが、_e2
_のパラメーターy
は範囲外です。
この問題を修正するには、Expression
APIを使用して_e1
_および_e2
_を作成します。これにより、パラメータ式をそれら全体で共有できるため、スコープの問題が解消されます。
他の答えに示されているように、両方にy
という名前のパラメーターがある2つの式があります。それらは自動的に相互に関係しません。
式を適切にコンパイルするには、両方のソース式のパラメーターを指定する必要があります。
Expression<Func<string, bool>> e1 = (y => y.Length > 0);
Expression<Func<string, bool>> e2 = (y => y.Length < 5);
var e3 = Expression.And(e1.Body, e2.Body);
// (string, string) by adding both expressions' parameters.
var e4 = Expression.Lambda<Func<string, string, bool>>(e3, new[]
{
e1.Parameters[0],
e2.Parameters[0]
});
Func<string, string, bool> compiledExpression = e4.Compile();
bool result = compiledExpression("Foo", "Foo");
もちろん、両方の式を1つのパラメーターのみと組み合わせた式が必要です。次のような式を再構築できます。
ParameterExpression param = Expression.Parameter(typeof(string), "y");
var lengthPropertyExpression = Expression.Property(param, "Length");
var e1 = Expression.GreaterThan(lengthPropertyExpression, Expression.Constant(0));
var e2 = Expression.LessThan(lengthPropertyExpression, Expression.Constant(5));
var e3 = Expression.AndAlso(e1, e2);
var e4 = Expression.Lambda<Func<string, bool>>(e3, new[] { param });
Func<string, bool> compiledExpression = e4.Compile();
bool result = compiledExpression("Foo");
式を再構築したくないが、既存の式の本体とパラメータでそれを行うというコメントについては、これはExpressionRewriter
from c#の2つのラムダ式を組み合わせて を使用して機能しますAndAlso
from 式の本体のパラメーター名を置き換える :
Expression<Func<string, bool>> e1 = (y => y.Length > 0);
Expression<Func<string, bool>> e2 = (z => z.Length < 10);
var e3 = ParameterReplacer.AndAlso<string>(e1, e2);
Func<string, bool> compiledExpression = e3.Compile();
bool result = compiledExpression("Foo");