Search code examples
c#expression-trees

Expression trees - substitute expression


Consider two expression trees:

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

I want to substitute the expression f2 as a second parameter of f1 and obtain the following expression:

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

The simplest way is to use Expression.Lambda and Expression.Invoke, but the result will look like

(x, y) => f1(x, f2(y))

But this is unacceptable for me due to ORM limitations that cannot handle invoke/lambda properly.

Is it possible to construct the expression without full traversal of expression trees? A working example that fulfill my needs can be found here but I want simpler solution.


Solution

  • You cannot do it without full traversal of both expressions. Fortunately, ExpressionVisitor makes full traversal really easy:

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

    Use this visitor twice to complete the replacement:

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

    The above prints

    (x, y) => (x + (y * y))
    

    Demo.