表达式树 - 替换表达式

4
考虑两棵表达式树:
Expression<Func<float, float, float>> f1 = (x, y) => x + y;
Expression<Func<float, float>> f2 = x => x * x;

我想将表达式f2作为f1的第二个参数替换,并得到以下表达式:

Expression<Func<float, float, float>> f3 = (x, y) => x + y * y;

最简单的方法是使用 Expression.LambdaExpression.Invoke,但结果看起来像这样
(x, y) => f1(x, f2(y))

但是对于我来说,这是不可接受的,因为ORM限制无法正确处理调用/lambda。

有没有可能构造表达式而无需完全遍历表达式树?可以在这里找到满足我的需求的工作示例,但我想要更简单的解决方案。


没有完全遍历表达式树 - 不行。基本上你需要参数替换器,这是用 ExpressionVisitor 实现的。 - Ivan Stoev
1个回答

4

没有完全遍历两个表达式,你就无法完成它。幸运的是,ExpressionVisitor 使得完全遍历变得非常容易:

class ReplaceParameter : ExpressionVisitor {
    private readonly Expression replacement;
    private readonly ParameterExpression parameter;
    public ReplaceParameter(
        ParameterExpression parameter
    ,   Expression replacement
    ) {
        this.replacement = replacement;
        this.parameter = parameter;
    }
    protected override Expression VisitParameter(ParameterExpression node) {
        return node == parameter ? replacement : node;
    }
}

使用这个访客两次来完成替换:
Expression<Func<float,float,float>> f1 = (x, y) => x + y;
Expression<Func<float,float>> f2 = x => x * x;
var pX = f2.Parameters[0];
var pY = f1.Parameters[1];
var replacerF2 = new ReplaceParameter(pX, pY);
var replacerF1 = new ReplaceParameter(pY, replacerF2.Visit(f2.Body));
var modifiedF1 = Expression.Lambda(
    replacerF1.Visit(f1.Body)
,   f1.Parameters
);
Console.WriteLine(modifiedF1);

上述内容打印如下:
(x, y) => (x + (y * y))

Demo.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接