c# - 组合 Lambda 表达式

标签 c# asp.net-mvc-3 expression-trees

我正在寻找一种方法来组合两个 lambda 表达式,而无需在任一表达式上使用 Expression.Invoke。我想从本质上构建一个链接两个独立表达式的新表达式。考虑以下代码:

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

假设我有两个表达式:

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

我想将它们连接在一起以在功能上获得以下表达式:

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

我能想到的唯一方法是像这样使用 ExpressionVisitor:

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

然后它像这样使用:

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

它有效,但对我来说感觉有点笨拙。我仍在尝试整理我的头部表情,并想知道是否有更惯用的表达方式,我偷偷怀疑我遗漏了一些明显的东西。


我想这样做的原因是为了将它与 ASP.net mvc 3 Html 助手一起使用。我有一些深度嵌套的 ViewModel 和一些帮助处理这些问题的 HtmlHelper 扩展,因此表达式需要只是 MemberExpressions 的集合,以便内置的 MVC 帮助程序正确处理它们并构建正确的深度嵌套名称属性值。我的第一直觉是使用 Expression.Invoke() 并调用第一个表达式并将其链接到第二个,但是 MVC 助手不太喜欢这样。它失去了层次结构上下文。

最佳答案

使用访问者将参数f的所有实例交换为m.SubModel.Foo,并创建一个带有m的新表达式作为参数:

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

关于c# - 组合 Lambda 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9132479/

相关文章:

asp.net-mvc-3 - 模型验证/ASP.NET MVC 3-条件必需属性

c# - 托管站点的 MVC 3 动态路由

c++ - 使用 C++ boost::spirit PEG 文法生成表达式

c# - 尝试按如下方式访问母版页代码中的 telerik RadEditor 控件...它给出了一些错误

c# - ASP.NET 标识使用电子邮件而不是用户名

javascript - 通过 JavaScript 刷新 MVC 3 PartialView

wpf - 使用 linq 表达式的类型安全 NotifyPropertyChanged

c# - 如何动态创建 Expression<Func<MyClass, bool>> 谓词?

c# - 抽象,还是不抽象

c# - 如何识别零宽字符?