web-dev-qa-db-ja.com

型 ''の変数 ''はスコープ ''から参照されていますが、定義されていません

さて、次のコードは自明です。 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
27
Hans

みんな協力してくれてありがとう。

@dasblinkenlightが指摘したように、2つの式の2つのパラメーターは同じではありません。理由?まあ、それはコンパイラのトリックです。コンパイル時に、各式のクラスを作成し、各パラメーターにxxx1、xxx2、...などの名前を付けます。元の名前とはまったく異なります。

.NET 4.0+の答え:

2つのラムダを組み合わせる方法

11
Hans

問題は、式_e1_と_e2_で変数yを表すパラメーター式オブジェクトが異なることです。 2つの変数に同じ名前が付けられ、同じ型を持っているという事実は重要ではありません。e1.Parameters.First()e2.Parameters.First()は同じオブジェクトではありません。

これにより、表示される問題が発生します:_e1_のパラメーターyのみが_Lambda<>_で使用できますが、_e2_のパラメーターyは範囲外です。

この問題を修正するには、Expression AP​​Iを使用して_e1_および_e2_を作成します。これにより、パラメータ式をそれら全体で共有できるため、スコープの問題が解消されます。

19
dasblinkenlight

他の答えに示されているように、両方に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");
13
CodeCaster