C# 从其他两个表达式创建动态表达式lambda(链接表达式)

C# 从其他两个表达式创建动态表达式lambda(链接表达式),c#,dynamic,lambda,runtime,expression,C#,Dynamic,Lambda,Runtime,Expression,给定接受标识对象并返回属性的lambda: Expression<Func<Identification, object>> fx = _ => _.Id; 如何构建一个新的lambda来执行@new(获取标识实例)并将结果传递到fx。我需要@new的结果以某种方式绑定到fx的第一个参数,但我找不到示例 我需要结果是一个表达式,它的类型应该是表达式,它应该将入站参数转换为标识,然后获取Id属性。首先,请注意,如果您适当地键入@new,这会更容易,即: Lambda

给定接受标识对象并返回属性的lambda:

Expression<Func<Identification, object>> fx = _ => _.Id;
如何构建一个新的lambda来执行
@new
(获取标识实例)并将结果传递到
fx
。我需要
@new
的结果以某种方式绑定到
fx
的第一个参数,但我找不到示例


我需要结果是一个
表达式
,它的类型应该是
表达式
,它应该将入站参数转换为
标识
,然后获取
Id
属性。

首先,请注意,如果您适当地键入
@new
,这会更容易,即:

LambdaExpression @new = ...
因为这样可以方便地访问
@new.Body
@new.Parameters
;就这样,,
Expression.Invoke在这里可能很有用:

var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
    Expression.Invoke(fx,
        Expression.Invoke(@new, combinedParam)), combinedParam);
与:

它的作用是:

  • 检查
    fx.Body
    树,用
    @new.Body
    替换
    的所有实例(请注意,这将包含对
    o
    参数的引用(也称
    p
  • 然后,我们从替换的表达式构建一个新的lambda,重新使用
    @new
    中的相同参数,这确保了我们注入的值将被正确绑定
使用中的代码,您可以通过一个助手函数很好地简化此过程:

public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}
公共静态类ExpressionHelper{
公共静态表达式链(
这句话首先,,
表达秒
) {
返回表达式.Lambda(
新建SwapVisitor(第二个参数[0],第一个主体)。访问(第二个主体),
第一,参数
);
}
私有类交换访问者:ExpressionVisitor{
私有只读表达式_from;
私有只读表达式_to;
公共SwapVisitor(表达式from,表达式to){
_from=from;
_to=to;
}
公共重写表达式访问(表达式节点){
return node==\u from?\u to:base.Visit(节点);
}
}
}
现在看那有多干净

var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);
var-valueSelector=新表达式(o=>o.Value);
var intSelector=新表达式(x=>x>5);
var选择器=valueSelector.Chain(intSelector);

它与实体框架和其他需要干净的
表达式的东西一起工作,而这些表达式不试图在其中调用
Func

嗨,我实际上差点就这样做了,但在linqpad中它看起来完全错了。最终的表达式树看起来很可怕,即使经过简化-但它做得很好,谢谢你一次完成这一切,编译器生成一个更整洁的树。(对象p)=>((标识)p).Id你得到的Convert(Convert(p).Id)非常整洁。无论如何,谢谢你的回答。@Jim-你刷新了吗?第二个版本创建的正是
o=>Convert(Convert(o).Id)
,而不是
o=>Invoke(\u=>Convert(\uid),Invoke(o=>Convert(o,o))
我已经使用了访问者,并生成了新的表达式。非常感谢。
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);
    }
}
public static class ExpressionHelper {
    public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
        this Expression<Func<TFrom, TMiddle>> first,
        Expression<Func<TMiddle, TTo>> second
    ) {
        return Expression.Lambda<Func<TFrom, TTo>>(
           new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
           first.Parameters
        );
    }

    private class SwapVisitor : ExpressionVisitor {
        private readonly Expression _from;
        private readonly Expression _to;

        public SwapVisitor(Expression from, Expression to) {
            _from = from;
            _to = to;
        }

        public override Expression Visit(Expression node) {
            return node == _from ? _to : base.Visit(node);
        }
    }
}
var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);