我如何合并两个lambda表达式,而不使用Invoke方法? [英] How can I combine two lambda expressions without using Invoke method?
问题描述
我有两个lambda表达式:
表达式来; Func键< myEntity所,布尔>> E1 = I => i.FName.Contain(约翰);
和
表达式来; Func键< myEntity所,布尔>> E2 = I => i.LName.Contain(史密斯);
i型,来自于我的POCO实体,不能与调用使用。我想这些在运行时将
我想这些表达式在运行时以类似的方式结合起来:
表达式来; Func键< myEntity所,布尔>> E3 =联合(E1,E2);
的问题是,你不能只是和/或他们,因为你需要重新编写内部改变参数;如果您使用。体
从 E1
,而参数的从 E2
,它不会工作 - 因为。体
E1 引用>未定义一个完全无关的参数实例。这是更加的明显的如果你使用:
表达式来; Func键< myEntity所,布尔>> E1 = I => i.FName.Contains(约翰);
表达式来; Func键< myEntity所,布尔>> E2 = J => j.LName.Contains(史密斯);
(注意 E1
之间的区别使用 I
和 E2
使用Ĵ
)
如果我们不重写参数将它们结合起来,我们会得到无意义的:
表达式来; FUNC< myEntity所,布尔>>合并=
I => i.FName.Contains(约翰)及和放大器; j.LName.Contains(史密斯);
(哇....放哪儿Ĵ
来自)
但?;不管的名称的参数的问题是相同的:它仍然是一个不同的参数
和因为表达式是不可变的,你可以'第•只要将它来代替
诀窍是使用一个访客重写节点,就像这样:
使用系统;使用System.Linq.Expressions
;
类SwapVisitor:ExpressionVisitor
{
私人只读从表达到;
公共SwapVisitor(从表达,表达于)
{
this.from =距离;
this.to =来;
}
公众覆盖式访问(Expression节点)
{
返回节点==而来?于:base.Visit(节点);
}
}
静态类节目
{
静态无效的主要()
{
表达式来; Func键< myEntity所,布尔>> E1 = I => i.FName.Contains(约翰);
表达式来; Func键< myEntity所,布尔>> E2 = I => i.LName.Contains(史密斯);
//重写E1,使用从E2的参数;与&&放大器;
VAR lambda1 = Expression.Lambda<&Func键LT; myEntity所,布尔>>(Expression.AndAlso(
新SwapVisitor(e1.Parameters [0],e2.Parameters [0])访问(E1。机身),
e2.Body),e2.Parameters);
//重写E1,使用从E2的参数; ||
VAR lambda2 = Expression.Lambda<&Func键LT; myEntity所,布尔>>(Expression.OrElse(
新SwapVisitor(e1.Parameters [0],e2.Parameters [0])访问(E1。机身),
e2.Body),e2.Parameters);
}
}
I have two lambda expressions:
Expression<Func<MyEntity, bool>> e1 = i=>i.FName.Contain("john");
and
Expression<Func<MyEntity, bool>> e2=i=>i.LName.Contain("smith");
the i type, comes from my poco entities, that can't used with invoke. I want to combine these in runtime.
I want to combine these expressions in runtime in a similar way as:
Expression<Func<MyEntity, bool>> e3 = Combine(e1,e2);
The problem is that you can't just "and"/"or" them, because you need to re-write the internals to change the parameters; if you use the .Body
from e1
, but the parameter from e2
, it won't work - because the .Body
of e1
references a completely unrelated parameter instance that isn't defined. This is more obvious if you use:
Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john");
Expression<Func<MyEntity, bool>> e2 = j => j.LName.Contains("smith");
(note the difference between e1
using i
and e2
using j
)
If we combine them without rewriting the parameter, we would get the nonsensical:
Expression<Func<MyEntity, bool>> combined =
i => i.FName.Contains("john") && j.LName.Contains("smith");
(woah.... where did j
come from?)
HOWEVER; the problem is identical regardless of the name of the parameter: it is still a different parameter.
And since the expression is immutable you can't just swap it "in place".
The trick is to use a "visitor" to rewrite the nodes, like so:
using System;
using System.Linq.Expressions;
class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
static class Program
{
static void Main()
{
Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john");
Expression<Func<MyEntity, bool>> e2 = i => i.LName.Contains("smith");
// rewrite e1, using the parameter from e2; "&&"
var lambda1 = Expression.Lambda<Func<MyEntity, bool>>(Expression.AndAlso(
new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body),
e2.Body), e2.Parameters);
// rewrite e1, using the parameter from e2; "||"
var lambda2 = Expression.Lambda<Func<MyEntity, bool>>(Expression.OrElse(
new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body),
e2.Body), e2.Parameters);
}
}
这篇关于我如何合并两个lambda表达式,而不使用Invoke方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!