【问题标题】:Create dynamic Expression lambda from two others (chaining the Expressions)从另外两个创建动态表达式 lambda(链接表达式)
【发布时间】:2011-12-13 23:29:53
【问题描述】:

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

Expression<Func<Identification, object>> fx = _ => _.Id;

还有一个将对象转换为标识实例的转换 lambda:

ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression @new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);

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

我需要结果是Expression,它应该是Expression&lt;Func&lt;object, object&gt;&gt; 类型,它应该将入站参数转换为Identification,然后获取Id 属性。

【问题讨论】:

    标签: c# dynamic lambda runtime expression


    【解决方案1】:

    首先,请注意,如果您适当地键入@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);
    

    虽然为了更简洁的表达式,你也可以使用ExpressionVisitor来完全替换内部表达式:

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

    与:

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

    这是做什么的:

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

    【讨论】:

    • 嗨,我实际上已经接近尝试了,但在 linqpad 中看起来完全错误。最终的表达式树看起来很可怕,即使简化了——但它完美地完成了工作,谢谢。如果您一次完成所有操作,编译器会生成一个更整洁的树。 (object p) => ((Identification)p).Id 你会得到 Convert(Convert(p).Id) 这真的很整洁。无论如何,谢谢你的回答。
    • @Jim - 你刷新了吗?第二个版本完全创建 o =&gt; Convert(Convert(o).Id),而不是 o =&gt; Invoke(_ =&gt; Convert(_.Id), Invoke(o =&gt; Convert(o), o))
    • 我已经使用了访问者,并生成了新的表达式。效果很好,谢谢。
    【解决方案2】:

    使用来自Marc Gravell's answer 的代码,您可以通过辅助函数很好地简化此过程:

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

    它适用于实体框架和其他需要干净的Expression 而不会尝试在其中调用Func 的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多