在编译器生成的表达式树中使用Expression

我知道我可以用以下方法创建表达式树:

  1. 工厂方法。

  2. 编译器将lambda表达式转换为Expression

对于复杂的表达式树,我更喜欢2,因为它更简洁。

是否可以使用这种方式引用已构建的Expressions

 using System; using System.Linq.Expressions; public class Test { public static Expression<Func> Add(Expression expr) { #if false // works ParameterExpression i = Expression.Parameter(typeof(int)); return Expression.Lambda<Func>(Expression.Add(i, expr), i); #else // compiler error, can I pass expr here somehow? return i => i + expr; #endif } public static void Main() { Func f = Add(Expression.Constant(42)).Compile(); Console.WriteLine(f(1)); } } 

您不能将任意Expression实例与编译时表达式树混合使用。 您可以做的是构造一个替换了特定节点的新表达式树,因此您可以使用i => i + marker ,然后构造一个新的树,并将marker节点替换为您的运行时表达式。 这需要编写适当的ExpressionVisitor

 public static class ExpressionExtensions { public static T AsPlaceholder(this Expression expression) { throw new InvalidOperationException( "Expression contains placeholders." ); } public static Expression FillPlaceholders(this Expression expression) { return new PlaceholderExpressionVisitor().Visit(expression); } } class PlaceholderExpressionVisitor : ExpressionVisitor { protected override Expression VisitMethodCall(MethodCallExpression node) { if ( node.Method.DeclaringType == typeof(ExpressionExtensions) && node.Method.Name == "AsPlaceholder" // in C# 6, we would use nameof() ) { return Expression.Lambda>(node.Arguments[0]).Compile()(); } else { return base.VisitMethodCall(node); } } } 

现在Add成为:

 public static Expression> Add(Expression expr) { Expression> add = i => i + expr.AsPlaceholder(); return (Expression>) add.FillPlaceholders(); } 

略带神秘的表情

 Expression.Lambda>(node.Arguments[0]).Compile()() 

可以通过观察编译器将捕获我们在闭包中插入的表达式来解释,无论它来自何处,因此方法调用的参数始终是对此闭包的引用,我们需要对此闭包进行评估以获取实际表达。

这可以扩展到任意数量的替换表达式,并且显式键入最少。

没有任何开箱即用的东西,但您可以自己构建一个工具来提供此function。

您可以编写一个接受具有两个参数的表达式的方法,一个“实际”参数和一个要用另一个表达式的值替换的某个值的参数。 然后,您可以使用一个解析为该值的表达式,并将该参数的所有实例替换为第二个表达式:

 public static Expression> BuildExpression ( Expression> function, Expression> innerExpression) { var body = function.Body.Replace(function.Parameters[1], innerExpression.Body); return Expression.Lambda>(body, function.Parameters[0]); } 

您可以使用以下方法将一个表达式的所有实例替换为另一个:

 public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx) { return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); } internal class ReplaceVisitor : ExpressionVisitor { private readonly Expression from, to; public ReplaceVisitor(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 Expression> Add(Expression> expr) { return BuildExpression((int i, int n) => i + n, expr); }