从范围“”引用了类型“”的变量“”,但未定义
variable '' of type '' referenced from scope '', but it is not defined
好吧,下面的代码是不言自明的;我想使用 And
运算符将两个表达式合并为一个。最后一行导致运行时错误:
Additional information: variable 'y' of type 'System.String' referenced from scope '', but it is not defined
代码:
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
问题是表达式e1
和e2
中表示变量y
的参数表达式对象不同。这两个变量的名称相同且类型相同这一事实无关紧要:e1.Parameters.First()
和 e2.Parameters.First()
不是同一个对象。
这会导致您看到的问题:只有 e1
的参数 y
可用于 Lambda<>
,而 e2
的参数 y
超出范围。
要解决此问题,请使用 Expression
API 来创建 e1
和 e2
。这样您就可以在它们之间共享参数表达式,从而消除范围问题。
如另一个答案所示,您有两个表达式,其中都有一个名为 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# and AndAlso
from Replacing the parameter name in the Body of an Expression 中的 ExpressionRewriter
:
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");
感谢大家的配合。
正如@dasblinkenlight 所指出的,两个表达式中的两个参数并不相同。原因?好吧,这是编译器的把戏。编译时,它会为每个表达式创建一个 class,并将每个参数命名为 xxx1、xxx2、... 与原始名称完全不同。
.Net 4.0+ 的答案:
How to Combine two lambdas
好吧,下面的代码是不言自明的;我想使用 And
运算符将两个表达式合并为一个。最后一行导致运行时错误:
Additional information: variable 'y' of type 'System.String' referenced from scope '', but it is not defined
代码:
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
问题是表达式e1
和e2
中表示变量y
的参数表达式对象不同。这两个变量的名称相同且类型相同这一事实无关紧要:e1.Parameters.First()
和 e2.Parameters.First()
不是同一个对象。
这会导致您看到的问题:只有 e1
的参数 y
可用于 Lambda<>
,而 e2
的参数 y
超出范围。
要解决此问题,请使用 Expression
API 来创建 e1
和 e2
。这样您就可以在它们之间共享参数表达式,从而消除范围问题。
如另一个答案所示,您有两个表达式,其中都有一个名为 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# and AndAlso
from Replacing the parameter name in the Body of an Expression 中的 ExpressionRewriter
:
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");
感谢大家的配合。
正如@dasblinkenlight 所指出的,两个表达式中的两个参数并不相同。原因?好吧,这是编译器的把戏。编译时,它会为每个表达式创建一个 class,并将每个参数命名为 xxx1、xxx2、... 与原始名称完全不同。
.Net 4.0+ 的答案:
How to Combine two lambdas