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;
}