简体   繁体   中英

Linq Expression from Lambda: specify parameter explicitly

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM