来自Lambda的Linq表达式:显式指定参数

本文关键字:参数 Lambda Linq 表达式 来自 | 更新日期: 2023-09-27 18:05:17

我想嵌入一个表达式树,如

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

转换为解析器生成的更大的表达式树。但是,参数0已经在外部表达式树中定义了。原则上,我必须搜索expr的主体,并用解析表达式树中的实例替换参数的所有出现。

是否有一个内置的方法来做到这一点?或者是否有一种方法可以直接生成lambda表达式,同时提前指定参数的实例?

来自Lambda的Linq表达式:显式指定参数

您不能直接指示编译器重用现有的ParameterExpression实例,但您可以在之后替换它们(实际上创建新的表达式树)。

内置的ExpressionVisitor帮助了很多繁重的工作;它是一个无操作访问器,您可以从中派生它来添加所需的功能。在本例中,您需要指示它替换ParameterExpression实例,因此可以这样:

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

可以这样使用:

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