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?
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