将两个表达式组合成一个管道

我们假设我有以下两种表达方式:

Expression<Func<T, IEnumerable>> collectionSelector; Expression<Func<IEnumerable, TNested>> elementSelector; 

有没有办法“组合”这些以形成以下:(?)

 Expression<Func> selector; 

编辑:

性能非常关键,因此如果可能的话,我会非常感谢能够以极少的开销实现最佳解决方案。

非常感谢!

 static Expression> Foo( Expression> f, Expression> g) { var x = Expression.Parameter(typeof(A)); return Expression.Lambda>( Expression.Invoke(f, Expression.Invoke(g, x)), x); } 

不幸的是,我无法访问计算机(这在性能方面是不好的解决方案)。 实际上,我认为您可以通过调用Expression来优化代码。

另一种方式似乎是(使用辅助function):

 Func Foo(Func f, Func g) { return (A x) => f(g(x)); } 

然后你应该通过调用带有Foo函数的Expression来创建管道表达式。 像那个伪代码:

  var expr1 = get expresstion var expr2 = get Expression var foo = get method info of Foo method specialize method with generic types A, B, C (via make generic method) return Expression.Call(foo, expr1, expr2); 

另一种解决方案是使用ExpressionVisitor将右表达式中的参数替换为整个左表达式,换句话说,将左侧表达式嵌入右侧表达式中。

表达式访问者将非常简单,向构造函数添加所需的数据,覆盖一个方法,这就是全部。

 internal sealed class ParameterReplaceVisitor : ExpressionVisitor { private readonly ParameterExpression _searched; private readonly Expression _replaced; public ParameterReplaceVisitor(ParameterExpression searched, Expression replaced) { if (searched == null) throw new ArgumentNullException(nameof(searched)); if (replaced == null) throw new ArgumentNullException(nameof(replaced)); _searched = searched; _replaced = replaced; } protected override Expression VisitParameter(ParameterExpression node) { if (node == _searched) return _replaced; return base.VisitParameter(node); } } 

可以很容易地扩展它来处理构造函数中的表达式集合,但我保持简短。

现在,您只需要在表达式主体上使用它并构造新的lambda。

 private static Expression> Merge(Expression> left, Expression> right) { var merged = new ParameterReplaceVisitor(right.Parameters[0], left.Body).Visit(right.Body); var lambda = Expression.Lambda>(merged, left.Parameters[0]); return lambda; } 

我在这段代码上测试了它:

 Expression> l = s => s.Length + 5; Expression> r = i => i.ToString() + " something"; var merged = Merge(l, r); var res = merged.Compile()("test"); 

结果如预期: 9 something

编辑:如果您关心性能,为什么使用表达式而不是普通的Func ? 然后你可以一个接一个地调用。 表达树后来被分析了吗?

 Expression> ChainExpressions( Expression> firstExpression, Expression> secondExpression ) { var sourceInput = Expression.Parameter(typeof(TSourceType)); var expressionForIntermediaryValue = Expression.Invoke(firstExpression, sourceInput); var expressionToGetTypedIntermediaryValue = Expression.Convert(expressionForIntermediaryValue, typeof(TIntermediaryType)); var expressionForFinalValue = Expression.Invoke(secondExpression, expressionToGetTypedIntermediaryValue); var expressionToGetTypedFinalValue = Expression.Convert(expressionForFinalValue, typeof(TFinalType)); var finalOutputExpression = Expression.Lambda(expressionToGetTypedFinalValue, sourceInput); return (Expression>)finalOutputExpression; }