C# 将两个表达式合并到管道中

C# 将两个表达式合并到管道中,c#,expression,combinators,C#,Expression,Combinators,假设我有以下两个表达: Expression<Func<T, IEnumerable<TNested>>> collectionSelector; Expression<Func<IEnumerable<TNested>, TNested>> elementSelector; 表达式集合选择器; 表达式元素选择器; 是否有一种方法可以“组合”这些元素以形成以下内容:(?) 表达式选择器; 编辑: 性能是非常关键的,所以

假设我有以下两个表达:

Expression<Func<T, IEnumerable<TNested>>> collectionSelector;
Expression<Func<IEnumerable<TNested>, TNested>> elementSelector;
表达式集合选择器;
表达式元素选择器;
是否有一种方法可以“组合”这些元素以形成以下内容:(?)

表达式选择器;
编辑:

性能是非常关键的,所以如果可能的话,我希望能找到一个开销很小的最佳解决方案

非常感谢

静态表达式Foo(
static Expression<Func<A, C>> Foo<A, B, C>(
Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
    var x = Expression.Parameter(typeof(A));
    return Expression.Lambda<Func<A, C>>(
        Expression.Invoke(f, Expression.Invoke(g, x)), x);
}
表达式f, 表达式g) { var x=表达式参数(typeof(A)); 返回表达式.Lambda( Expression.Invoke(f,Expression.Invoke(g,x)),x); }
不幸的是,我无法访问计算机(这是一个性能较差的解决方案)。实际上,我认为可以通过调用表达式优化代码

另一种方式是(使用辅助功能):

Func-Foo(Func-f,Func-g)
{ 
返回值(ax)=>f(g(x));
}
然后,您应该使用Foo函数通过调用表达式创建管道表达式。类似于伪代码:

   var expr1 = get expresstion<B,C> 
   var expr2 = get Expression<A, B>
   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);
var expr1=获取表达式
var expr2=get表达式
var foo=获取foo方法的方法信息
使用泛型类型A、B、C专门化方法(通过生成泛型方法)
返回表达式.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<Func<TIn, TOut>> Merge<TIn, TInter, TOut>(Expression<Func<TIn, TInter>> left, Expression<Func<TInter, TOut>> right)
{
    var merged = new ParameterReplaceVisitor(right.Parameters[0], left.Body).Visit(right.Body);

    var lambda = Expression.Lambda<Func<TIn, TOut>>(merged, left.Parameters[0]);

    return lambda;
}
私有静态表达式合并(表达式左、表达式右)
{
var merged=新参数replaceVisitor(right.Parameters[0],left.Body).Visit(right.Body);
var lambda=Expression.lambda(合并,左.参数[0]);
返回lambda;
}
我在这段代码上测试了它:

Expression<Func<string, int>> l = s => s.Length + 5;
Expression<Func<int, string>> r = i => i.ToString() + " something";

var merged = Merge(l, r);

var res = merged.Compile()("test");
表达式l=s=>s.长度+5;
表达式r=i=>i.ToString()+“某物”;
var合并=合并(l,r);
var res=merged.Compile()(“测试”);
结果正如预期的那样:
9something

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

表达式链表达式(
Expression<Func<TSourceType, TFinalType>> ChainExpressions<TSourceType, TIntermediaryType, TFinalType>(
        Expression<Func<TSourceType, TIntermediaryType>> firstExpression,
        Expression<Func<TIntermediaryType, TFinalType>> 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<Func<TSourceType, TFinalType>>)finalOutputExpression;
    }
表情第一表情, 表达式第二个表达式 ) { var sourceInput=Expression.Parameter(typeof(TSourceType)); var expressionForIntermediaryValue=Expression.Invoke(firstExpression,sourceInput); var expressiontogettypedinterminaliaryvalue=Expression.Convert(expressionForIntermediaryValue,typeof(TIntermediaryType)); var expressionForFinalValue=Expression.Invoke(secondExpression,expressiontogettypedinterminaliaryvalue); var expressionToGetTypedFinalValue=Expression.Convert(expressionForFinalValue,typeof(TFinalType)); var finaloutput Expression=Expression.Lambda(expressionToGetTypedFinalValue,sourceInput); 返回(表达式)finalOutExpression; }
I modified answer第二个建议的解决方案创建委托和匿名对象,我认为这在性能方面不是很好。。或者我遗漏了什么?它将生成带有所需参数的调用函数,而不是使用动态调用。动态调用太慢,请参见。非常感谢,但是我正在寻找表达式级别上不会产生匿名对象实例化的东西。反正我给了你一票,因为这是一个解决办法。只是不是我想要的。我不知道为什么这个解决方案没有成功。就是这样!这太完美了!谢谢。通常最好解释一个解决方案,而不是仅仅发布几行匿名代码。你可以阅读,也可以阅读
Expression<Func<string, int>> l = s => s.Length + 5;
Expression<Func<int, string>> r = i => i.ToString() + " something";

var merged = Merge(l, r);

var res = merged.Compile()("test");
Expression<Func<TSourceType, TFinalType>> ChainExpressions<TSourceType, TIntermediaryType, TFinalType>(
        Expression<Func<TSourceType, TIntermediaryType>> firstExpression,
        Expression<Func<TIntermediaryType, TFinalType>> 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<Func<TSourceType, TFinalType>>)finalOutputExpression;
    }