1

I want to embed an expression tree such as

Expression<Func<MyObject, double>> expr = (o) => o.Value;

into a larger expression tree generated by a parser. However, the parameter o is already defined inside the outer expression tree. In principle I would have to search the body of expr and replace all occurences of the parameter by the instance from the parsed expression tree.

Is there a built in way to do this? Or is there even a way to directly generate the lambda expression while specifying the instance of the parameter in advance?

svick
  • 236,525
  • 50
  • 385
  • 514
LionAM
  • 1,271
  • 10
  • 28

1 Answers1

2

You can't directly instruct the compiler to reuse your existing ParameterExpression instances, but you can definitely replace them (in effect creating new expression trees) afterwards.

The built-in ExpressionVisitor helps a lot with the heavy lifting; it's a no-op visitor that you derive from to add the required functionality. In this case you need to instruct it to replace ParameterExpression instances, so you could have this:

// Sorry for the atrocious formatting, wanted to keep it scrollbar-free
class ParameterReplacementVisitor : ExpressionVisitor
{
    private readonly
    IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>>
    replacementMap;

    public ParameterReplacementVisitor(
        IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>> map)
    {
        this.replacementMap = map;
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        return Expression.Lambda<T>(
            Visit(node.Body),
            node.Parameters.Select(Visit).Cast<ParameterExpression>());
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        var replacement = this.replacementMap
                              .Where(p => p.Key == node)
                              .DefaultIfEmpty()
                              .First().Value;
        return base.VisitParameter(replacement ?? node);
    }
}

which you can use like this:

Expression<Func<int, bool>> e1 = i => true;
Expression<Func<int, bool>> e2 = j => false;

Console.WriteLine(e1.Parameters[0] == e2.Parameters[0]); // false

var replacements = new Dictionary<ParameterExpression, ParameterExpression>
{
    { e1.Parameters[0], e2.Parameters[0] }
};

var replacer = new ParameterReplacementVisitor(replacements);
var e3 = replacer.VisitAndConvert(e1, "replacing parameters");

Console.WriteLine(e3.Parameters[0] == e2.Parameters[0]); // true
Jon
  • 428,835
  • 81
  • 738
  • 806