嵌套两个lambda样式的函数表达式
本文关键字:样式 函数 表达式 lambda 两个 嵌套 | 更新日期: 2023-09-27 17:58:51
有没有一种简单的方法可以像下面这样组合两个lambda样式的表达式(我知道可以手动将示例表达式组合为一个,但如果innerExpression是由某个函数返回的,并且事先不知道该怎么办)。它们具有相同的输入参数类型,因此理论上ParameterExpression可以用于这两种类型。
Expression<Func<Source, Subtype>> innerExpression = x => new Subtype {
Subfield1 = x.SomeField;
Subfield2 = x.SomeOtherField;
}
Expression<Func<Source, Target>> finalExpression = x => new Target {
Field1 = x.Other1,
Field2 = x.Other2,
Field3 = x.Items.Where(y => y.Field == true).SingleOrDefault(),
Field4 = innerExpression(x) // <= Does not work that way
}
因此,我们在这里要做的是创建一个方法,该方法接受一个表达式,该表达式接受一个参数并计算另一个参数,然后另一个表达式接受与第一个参数相同的参数,即第一个函数的输出类型,然后计算一个全新的值。
这里的想法是,这个表达式将表示第一个函数的调用,然后第二个函数获取相同的输入值及其输出,并计算一个新值。然而,实际上,它不是在内部调用表达式,而是在使用表示其输出的参数的任何地方内联表达式。
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这取决于以下用另一个表达式替换一个表达式的所有实例的方法:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
这个想法真的很简单。只需将表示输出的参数的所有实例替换为另一个方法的主体,同时确保两者之间的参数表达式一致。
现在我们可以这样写:
Expression<Func<Source, Subtype>> innerExpression = x => new Subtype
{
Subfield1 = x.SomeField,
Subfield2 = x.SomeOtherField,
};
Expression<Func<Source, Target>> finalExpression = innerExpression.Combine(
(x, sub) => new Target
{
Field1 = x.Other1,
Field2 = x.Other2,
Field3 = x.Items.Where(y => y.Field == true).SingleOrDefault(),
Field4 = sub
});
@svick在评论中指出,正确的方法是使用LINQKit,这是一个支持构建表达式树的常见操作的库。
在你的情况下,你会有以下:
Expression<Func<Source, Subtype>> innerExpression = x => new Subtype {
Subfield1 = x.SomeField;
Subfield2 = x.SomeOtherField;
}
Expression<Func<Source, Target>> secondExpression = x => new Target {
Field1 = x.Other1,
Field2 = x.Other2,
Field3 = x.Items.Where(y => y.Field == true).SingleOrDefault(),
Field4 = innerExpression.Invoke(x)
}
Expression<Func<Source, Target>> finalExpression = secondExpression.Expand();