变量''类型''从范围''引用,但未定义

时间:2015-05-31 11:34:31

标签: c# expression-trees

嗯,以下代码是自我解释的;我想使用And运算符将两个表达式合并为一个。最后一行导致符文时间错误:

  

其他信息:变量' y'类型' System.String'引用范围'',但未定义

代码:

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

3 个答案:

答案 0 :(得分:16)

问题是表达式e1e2中表示变量e1.Parameters.First()的参数表达式对象是不同的。这两个变量的名称相同且类型相同这一事实并不重要:e2.Parameters.First()e1不是同一个对象。

这会导致您看到的问题:只有y的参数Lambda<>可用于e2,而y的参数{{} {1}}超出范围。

要解决此问题,请使用Expression API创建e1e2。这样您就可以在它们之间共享参数表达式,从而消除了范围问题。

答案 1 :(得分:11)

如另一个答案中所示,您有两个表达式,其中两个都有一个名为y的参数。那些不会自动相互关联。

要正确编译表达式,您需要指定源表达式的两个参数:

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");

当然,您需要一个只用一个参数组合两个表达式的表达式。您可以像这样重建表达式:

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");

至于您的评论,您不想重建表达式,而是对现有表达式的正文和参数执行此操作:这可以使用Combining two lambda expressions in c#中的ExpressionRewriter和来自Replacing the parameter name in the Body of an ExpressionAndAlso

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");

答案 2 :(得分:8)

谢谢大家的合作。

由于@dasblinkenlight指出两个表达式中的两个参数不一样。原因?好吧,这是编译技巧。编译时,它为每个表达式创建一个类,并将每个参数命名为xxx1,xxx2,......与原始名称完全不同。

.Net 4.0 +的答案:

How to Combine two lambdas