变量& # 39;& # 39;类型为''从作用域''引用,但没有定义

本文关键字:定义 作用域 类型 变量 引用 | 更新日期: 2023-09-27 17:51:12

下面的代码是不言自明的;我想用And算子把两个表达式组合成一个。最后一行导致运行时错误:

附加信息:类型为System的变量'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

变量& # 39;& # 39;类型为''从作用域''引用,但没有定义

如另一个答案所示,您有两个表达式,它们都有一个名为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");

至于你的评论,你不想重建表达式,但做一个现有的表达式的主体和参数:这工作使用ExpressionRewriter结合两个lambda表达式在c#和AndAlso替换表达式主体中的参数名称:

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

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

这会导致您看到的问题:只有e1的参数yLambda<>可用,而e2的参数y不在范围内。

要解决这个问题,使用Expression api创建e1e2。这样您就可以在它们之间共享参数表达式,从而消除了作用域的问题。

感谢大家的合作

正如@dasblinkenlight所指出的,两个表达式中的两个参数是不相同的。原因吗?这是编译器的技巧。在编译时,它为每个表达式创建一个类,并将每个参数命名为xxx1, xxx2,…与原来的名字完全不同。

对于。net 4.0+的答案是:

如何组合两个lambdas