组合 Lambda 表达式时出错:“'Foo' 类型的变量 'foo' 从范围 '' 引用,但未定义
Error when combining Lambda expressions: "variable 'foo' of type 'Foo' referenced from the scope '', but it is not defined
我正在尝试 combine two lambda expressions 构建带有 OR 子句的东西,但失败并显示以下异常消息:
variable 'foo' of type 'Foo' referenced from scope '', but it is not defined.
为什么,我该如何解决?
这是一个失败的代码示例,基于 Marc Gravell's answer 上面链接的问题:
static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
=> Expression.Lambda<Func<T, bool>>(Expression.OrElse(a.Body, b.Body), b.Parameters);
static Expression<Func<Foo, bool>> BarMatches(Bar bar) => foo => foo.Bar.Value == bar.Value;
static Expression<Func<Foo, bool>> BazMatches(Baz baz) => foo => foo.Baz.Value == baz.Value;
// sample usage (see below): foos.Where(Or(MatchesBar(bar), MatchesBaz(baz)))
void Main()
{
var foos = new[]
{
new Foo
{
Bar = new Bar
{
Value = "bar"
},
Baz = new Baz
{
Value = "baz"
}
},
new Foo
{
Bar = new Bar
{
Value = "not matching"
},
Baz = new Baz
{
Value = "baz"
}
}
}.AsQueryable();
var bar = new Bar { Value = "bar" };
var baz = new Baz { Value = "baz" };
Console.WriteLine(foos.Where(Or(BarMatches(bar), BazMatches(baz))).Count());
}
// Define other methods and classes here
class Foo
{
public Bar Bar { get; set; }
public Baz Baz { get; set; }
}
class Bar
{
public string Value { get; set; }
}
class Baz
{
public string Value { get; set; }
}
问题是 BarMatches
和 BazMatches
的参数 (foo
) 不同。因此,您需要统一参数,以便 Or-ed 表达式使用相同的参数。这可以通过表达式替换器(从 this answer 中窃取)来完成:
static TExpr ReplaceExpressions<TExpr>(TExpr expression,
Expression orig,
Expression replacement)
where TExpr : Expression
{
var replacer = new ExpressionReplacer(orig, replacement);
return replacer.VisitAndConvert(expression, "ReplaceExpressions");
}
private class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
public ExpressionReplacer(Expression from, Expression to) {
From = from;
To = to;
}
public override Expression Visit(Expression node) {
if (node == From) {
return To;
}
return base.Visit(node);
}
}
此 class 会将一个表达式的所有实例替换为另一个。我们现在可以使用它来创建一个统一的表达式:
static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {
// We know Parameters is exactly of length 1.
// If you had multiple parameters you would need to invoke this for each parameter
var replaced = ReplaceExpressions(a.Body, a.Parameters[0], b.Parameters[0]);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(replaced, b.Body), b.Parameters);
}
虽然此代码可以满足您的需求,但我建议使用一个库来执行此操作以及更多操作:LinqKIT
我正在尝试 combine two lambda expressions 构建带有 OR 子句的东西,但失败并显示以下异常消息:
variable 'foo' of type 'Foo' referenced from scope '', but it is not defined.
为什么,我该如何解决?
这是一个失败的代码示例,基于 Marc Gravell's answer 上面链接的问题:
static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
=> Expression.Lambda<Func<T, bool>>(Expression.OrElse(a.Body, b.Body), b.Parameters);
static Expression<Func<Foo, bool>> BarMatches(Bar bar) => foo => foo.Bar.Value == bar.Value;
static Expression<Func<Foo, bool>> BazMatches(Baz baz) => foo => foo.Baz.Value == baz.Value;
// sample usage (see below): foos.Where(Or(MatchesBar(bar), MatchesBaz(baz)))
void Main()
{
var foos = new[]
{
new Foo
{
Bar = new Bar
{
Value = "bar"
},
Baz = new Baz
{
Value = "baz"
}
},
new Foo
{
Bar = new Bar
{
Value = "not matching"
},
Baz = new Baz
{
Value = "baz"
}
}
}.AsQueryable();
var bar = new Bar { Value = "bar" };
var baz = new Baz { Value = "baz" };
Console.WriteLine(foos.Where(Or(BarMatches(bar), BazMatches(baz))).Count());
}
// Define other methods and classes here
class Foo
{
public Bar Bar { get; set; }
public Baz Baz { get; set; }
}
class Bar
{
public string Value { get; set; }
}
class Baz
{
public string Value { get; set; }
}
问题是 BarMatches
和 BazMatches
的参数 (foo
) 不同。因此,您需要统一参数,以便 Or-ed 表达式使用相同的参数。这可以通过表达式替换器(从 this answer 中窃取)来完成:
static TExpr ReplaceExpressions<TExpr>(TExpr expression,
Expression orig,
Expression replacement)
where TExpr : Expression
{
var replacer = new ExpressionReplacer(orig, replacement);
return replacer.VisitAndConvert(expression, "ReplaceExpressions");
}
private class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
public ExpressionReplacer(Expression from, Expression to) {
From = from;
To = to;
}
public override Expression Visit(Expression node) {
if (node == From) {
return To;
}
return base.Visit(node);
}
}
此 class 会将一个表达式的所有实例替换为另一个。我们现在可以使用它来创建一个统一的表达式:
static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {
// We know Parameters is exactly of length 1.
// If you had multiple parameters you would need to invoke this for each parameter
var replaced = ReplaceExpressions(a.Body, a.Parameters[0], b.Parameters[0]);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(replaced, b.Body), b.Parameters);
}
虽然此代码可以满足您的需求,但我建议使用一个库来执行此操作以及更多操作:LinqKIT