我知道我可以使用:
创建表达式树工厂方法。
编译器将lambda表达式转换为Expression
。
对于复杂的表达式树,我更喜欢2,因为它更简洁。
是否可以使用这种方式引用已构建的Expressions
?
using System;
using System.Linq.Expressions;
public class Test
{
public static Expression<Func<int, int>> Add(Expression expr)
{
#if false
// works
ParameterExpression i = Expression.Parameter(typeof(int));
return Expression.Lambda<Func<int, int>>(Expression.Add(i, expr), i);
#else
// compiler error, can I pass expr here somehow?
return i => i + expr;
#endif
}
public static void Main()
{
Func<int, int> f = Add(Expression.Constant(42)).Compile();
Console.WriteLine(f(1));
}
}
答案 0 :(得分:7)
没有任何开箱即用的东西,但您可以自己构建一个工具来提供此功能。
您可以编写一个接受具有两个参数的表达式的方法,一个“实际”参数和一个您希望用另一个表达式的值替换的值的一个参数。然后,您可以使用一个解析为该值的表达式,并将该参数的所有实例替换为第二个表达式:
public static Expression<Func<TSource, TResult>> BuildExpression
<TSource, TOther, TResult>(
Expression<Func<TSource, TOther, TResult>> function,
Expression<Func<TOther>> innerExpression)
{
var body = function.Body.Replace(function.Parameters[1], innerExpression.Body);
return Expression.Lambda<Func<TSource, TResult>>(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<Func<int, int>> Add(Expression<Func<int>> expr)
{
return BuildExpression((int i, int n) => i + n, expr);
}
答案 1 :(得分:2)
您不能将任意Expression
个实例与编译时表达式树混合使用。您可以做的是构造一个新的表达式树,其中替换了特定的节点,因此您可以拥有i => i + marker
然后构造一个新的树,其中marker
节点被您的运行时表达式替换。这需要编写适当的ExpressionVisitor
:
public static class ExpressionExtensions {
public static T AsPlaceholder<T>(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<Func<Expression>>(node.Arguments[0]).Compile()();
} else {
return base.VisitMethodCall(node);
}
}
}
Add
现在变为:
public static Expression<Func<int, int>> Add(Expression expr) {
Expression<Func<int, int>> add = i => i + expr.AsPlaceholder<int>();
return (Expression<Func<int, int>>) add.FillPlaceholders();
}
略带神秘的表达
Expression.Lambda<Func<Expression>>(node.Arguments[0]).Compile()()
可以通过观察编译器将捕获我们在闭包中插入的表达式来解释,无论它来自何处,因此方法调用的参数始终是对此闭包的引用,我们需要评估以获得实际表达。
这可以扩展到任意数量的替换表达式,只需最少的显式输入。