结合LAMBDA防爆pressions [英] Combine Lambda Expressions

查看:129
本文介绍了结合LAMBDA防爆pressions的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要寻找一种方式来两个拉姆达前pressions结合起来,没有在任的前pression使用防爆pression.Invoke 。我想基本上是建立一个新的前pression的链两个独立的。请看下面的code:

I am looking for a way to combine two lambda expressions, without using an Expression.Invoke on either expression. I want to essentially build a new expression that chains two separate ones. Consider the following code:

class Model {
    public SubModel SubModel { get; set;}
}

class SubModel {
    public Foo Foo { get; set; }
}

class Foo {
    public Bar Bar { get; set; }
}

class Bar {
    public string Value { get; set; }
}

和让说,我有两个前pressions:

And lets say I had two expressions:

Expression<Func<Model, Foo>> expression1 = m => m.SubModel.Foo;
Expression<Func<Foo, string>> expression2 = f => f.Bar.Value;

和我想加入他们共同功能得到下面前pression:

And I want to join them together to functionally get the following expression:

Expression<Func<Model, string>> joinedExpression = m => m.SubModel.Foo.Bar.Value;

我能想到做到这一点的唯一方法是使用防爆pressionVisitor是这样的:

The only way I could think to do this is to use a ExpressionVisitor like this:

public class ExpressionExtender<TModel, TIntermediate> : ExpressionVisitor
{
    private readonly Expression<Func<TModel, TIntermediate>> _baseExpression;

    public ExpressionExtender(Expression<Func<TModel, TIntermediate>> baseExpression)
    {
        _baseExpression = baseExpression;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        _memberNodes.Push(node.Member.Name);
        return base.VisitMember(node);
    }

    private Stack<string> _memberNodes;

    public Expression<Func<TModel, T>> Extend<T>(Expression<Func<TIntermediate, T>>  extend)
    {
        _memberNodes = new Stack<string>();
        base.Visit(extend);
        var propertyExpression  = _memberNodes.Aggregate(_baseExpression.Body, Expression.Property);
        return Expression.Lambda<Func<TModel, T>>(propertyExpression, _baseExpression.Parameters);
    }
}

然后将其用是这样的:

And then its used like this:

var expExt = new ExpressionExtender<Model, Foo>(expression1);
var joinedExpression = expExt.Extend(expression2);

它的工作原理,但感觉有点笨重给我。我仍然在试图总结我的头前pressions,不知道是否有一个更惯用的方式来恩preSS这一点,我有鬼鬼祟祟的怀疑,我失去了一些东西明显。

It works, but it feels a bit clunky to me. I'm still trying to wrap my head expressions and wondering if there is a more idiomatic way to express this, and I have the sneaky suspicion that I missing something obvious.

的原因的我想这样做是为了与ASP.net MVC 3 HTML辅助使用。我有一些嵌套很深的ViewModels和部分的HtmlHelper扩展,帮助处理这些,所以前pression需要只是一个为 MemberEx pressions 的集合内置的MVC佣工正确处理它们并建立正确的深层嵌套的name属性值。我的第一直觉是使用防爆pression.Invoke()并调用第一八佰伴pression和IT连锁到第二,但MVC佣工没' t等非常乐意。它失去了其层次背景。

The reason I want to do this is to use it with the ASP.net mvc 3 Html helpers. I have some deeply nested ViewModels and some HtmlHelper extensions that help deal with those, so the expression needs to be just a collection of MemberExpressions for the built in MVC helpers to process them correctly and build the correctly deeply nested name attribute values. My first instinct was to use Expression.Invoke() and invoke the first expression and chain it to the second, but the MVC helpers didn't like that very much. It lost its hierarchical context.

推荐答案

使用访客交换参数的所有实例˚F米SubModel.Foo ,并创建 M 新前pression作为参数:

Use a visitor to swap all instances of the parameter f to m.SubModel.Foo, and create a new expression with m as the parameter:

internal static class Program
{
    static void Main()
    {

        Expression<Func<Model, Foo>> expression1 = m => m.SubModel.Foo;
        Expression<Func<Foo, string>> expression2 = f => f.Bar.Value;

        var swap = new SwapVisitor(expression2.Parameters[0], expression1.Body);
        var lambda = Expression.Lambda<Func<Model, string>>(
               swap.Visit(expression2.Body), expression1.Parameters);

        // test it worked
        var func = lambda.Compile();
        Model test = new Model {SubModel = new SubModel {Foo = new Foo {
             Bar = new Bar { Value = "abc"}}}};
        Console.WriteLine(func(test)); // "abc"
    }
}
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);
    }
}

这篇关于结合LAMBDA防爆pressions的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆