【问题标题】:Find and remove parameter declaration inside Expression.Block在 Expression.Block 中查找和删除参数声明
【发布时间】:2017-03-29 09:51:20
【问题描述】:

我知道如何用 ExpressionVisitor 替换参数,但我想知道是否有办法从 Expression.Block 中删除参数。

理想情况下,我应该爬取整个表达式树,并在每次在块中声明参数时删除它。

知道如何使用 ExpressionVisitor 做到这一点吗?

【问题讨论】:

  • 你能举个例子说明你想得到什么吗?如果你有(x, y) => x + y + 1,去掉y是什么意思?你想要x => x +1吗?但是如果你有(x, y) => Math.Max(x, y),会发生什么?或者更复杂的表达式......你不能删除一个参数并留下一个洞
  • 这是场景。我的表达式生成器声明了一个列表。这个表达式构建器被递归使用,因此声明了许多列表(这样各种项目被插入到它们的“本地”列表中)。通过删除列表的内部声明并用 ExpressionVisitor 替换参数,我可以将所有插入重定向到唯一列表(第一次调用我的表达式生成器声明的列表)。
  • 所以将new List<> (an ExpressionType.New) 替换为代表“主”列表的参数)。所以你会有类似的东西:var localList = masterList; localList.Add(something) 这个问题太具体了,如果没有具体的例子就无法解决。
  • 除此之外,从我的具体情况来看。问题是如何删除而不是替换参数。这个问题在这方面并不具体。
  • 你所说的参数是指块的局部变量吗? (所以Expression.Block(variable1, variable2, expression1, expression2) 你想删除variable1(和/或variable2)?

标签: c# expression expressionvisitor


【解决方案1】:

一个简单的类,用于从 BlockExpression 中删除局部变量并用您想要的任何内容替换它们。

public class BlockVariableRemover : ExpressionVisitor
{
    private readonly Dictionary<Expression, Expression> replaces = new Dictionary<Expression, Expression>();

    public readonly Func<ParameterExpression, int, Expression> Replacer;

    public BlockVariableRemover(Func<ParameterExpression, int, Expression> replacer)
    {
        Replacer = replacer;
    }

    protected override Expression VisitBlock(BlockExpression node)
    {
        var removed = new List<Expression>();

        var variables = node.Variables.ToList();

        for (int i = 0; i < variables.Count; i++)
        {
            var variable = variables[i];
            var to = Replacer(variable, i);

            if (to != variable)
            {
                removed.Add(variable);
                replaces.Add(variable, to);
                variables.RemoveAt(i);
                i--;
            }
        }

        if (removed.Count == 0)
        {
            return base.VisitBlock(node);
        }

        var expressions = node.Expressions.ToArray();

        for (int i = 0; i < expressions.Length; i++)
        {
            expressions[i] = Visit(expressions[i]);
        }

        foreach (var rem in removed)
        {
            replaces.Remove(rem);
        }

        return Expression.Block(variables, expressions);
    }

    public override Expression Visit(Expression node)
    {
        Expression to;

        if (node != null && replaces.TryGetValue(node, out to))
        {
            return base.Visit(to);
        }

        return base.Visit(node);
    }
}

像这样使用它:

Expression<Func<int, int>> exp;

{
    var var1 = Expression.Variable(typeof(int), "var1");
    var var2 = Expression.Variable(typeof(long), "var2");

    var par1 = Expression.Parameter(typeof(int), "par1");

    var block = Expression.Block(new[] { var1, var2 }, Expression.Increment(var1));

    exp = Expression.Lambda<Func<int, int>>(block, par1);

    // Test
    var compiled = exp.Compile();
    Console.WriteLine(compiled(10));
}

// Begin replace
{
    var par1 = exp.Parameters[0];

    var block2 = new BlockVariableRemover(
        // ix is the index of the variable, 
        // return x if you don't want to modify,
        // return whatever you want (even Expression.Empty()) to do 
        // a replace
        (x, ix) => ix == 0 && x.Type == typeof(int) ? par1 : x)
        .Visit(exp.Body);

    // Final result
    var exp2 = Expression.Lambda<Func<int, int>>(block2, par1);

    // Test
    var compiled = exp2.Compile();
    Console.WriteLine(compiled(10));
}

【讨论】:

  • 我会试试这个,过几天再告诉你。谢谢。
猜你喜欢
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多