从另外两个创建动态表达式lambda(链接表达式)

Jim*_*Jim 8 c# lambda expression runtime dynamic

给定一个带有Identification对象的lambda,并返回一个属性:

Expression<Func<Identification, object>> fx = _ => _.Id;
Run Code Online (Sandbox Code Playgroud)

以及将对象转换为标识实例的转换lambda:

ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);
Run Code Online (Sandbox Code Playgroud)

如何构建一个执行@new(获取标识实例)并将结果传递给的新lambda fx.我需要@new的结果以fx某种方式绑定到第一个参数,我找不到一个例子.

我需要结果是一个Expression,它应该是类型Expression<Func<object, object>>,它应该将入站参数转换为a Identification然后获取Id属性.

Mar*_*ell 9

首先,请注意,如果您@new正确输入,这会更容易,即:

LambdaExpression @new = ...
Run Code Online (Sandbox Code Playgroud)

因为这样可以轻松访问@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);
Run Code Online (Sandbox Code Playgroud)

虽然对于更清晰的表达式,您也可以使用ExpressionVisitor完全替换内部表达式:

var injected = new SwapVisitor(fx.Parameters[0], @new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,@new.Parameters);
Run Code Online (Sandbox Code Playgroud)

有:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

它的作用是:

  • 检查fx.Body树,用_(替换)参数的所有实例@new.Body(注意这将包含对o参数的引用(又名p)
  • 然后我们从被替换的表达式构建一个新的lambda,重新使用相同的参数@new,这可以确保我们注入的值将被正确绑定


Eri*_*ikE 5

使用Marc Gravell 的回答中的代码,您可以通过辅助函数很好地简化此操作:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在看看那多干净!

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);
Run Code Online (Sandbox Code Playgroud)

它可以与实体框架和其他需要清理的东西一起使用Expression,而不会尝试在Func其中调用。