简体   繁体   English

如何在二进制表达式中将参数表达式更改为常数表达式

[英]How to change Parameters Expression to Constant Expression in Binary Expression

I have my custom Visitor which looks to right and left and changes parameters to constant. 我有自定义的Visitor,它左右左右移动并将参数更改为常量。
I know that just change node is not possible. 我知道仅更改节点是不可能的。
I should return new lambda expression which contains constants instead parameters. 我应该返回新的lambda表达式,其中包含常量而不是参数。 But I can not create an expression myself :( 但是我自己不能创建一个表达式:(
I have this code: 我有以下代码:

    public class ParametersTransformToConstantVisitor : ExpressionVisitor
    {

    private Dictionary<string, ConstantExpression> parameters = new Dictionary<string, ConstantExpression>();

    public ParametersTransformToConstantVisitor(Dictionary<string, ConstantExpression> parameters)
    {
        this.parameters = parameters;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        var constExprLeftName = new Lazy<string>(() => ((ParameterExpression) node.Left)?.Name);
        var constExprRightName = new Lazy<string>(() => ((ParameterExpression) node.Right)?.Name);
        var constExprName = new Lazy<string>(() => ((ParameterExpression) node.Reduce())?.Name);

        ParameterExpression leftParam = null;
        ParameterExpression rightParam = null;

        if (node.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprName.Value))
        {
            return parameters[constExprName.Value];
        }

        if (node.Left.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
        {
            leftParam = (ParameterExpression) node.Left;
        }       

        if (node.Right.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
        {
            rightParam = (ParameterExpression) node.Right;
        }

        if (leftParam != null || rightParam != null)
        {
            //return Expression.Lambda();
        }       

        return base.VisitBinary(node);
    }
}

Help me to build lambda expression, please 请帮我建立lambda表达式

It feels like all you actually need here is: 感觉您实际上真正需要的是:

protected override Expression VisitParameter(ParameterExpression node)
    => parameters.TryGetValue(node.Name, out var ce) ? (Expression)ce : node;

protected override Expression VisitLambda<T>(Expression<T> node)
    => Expression.Lambda(Visit(node.Body), node.Parameters); // don't visit the parameters

ie whenever the visitor sees a ParameterExpression , if there is a corresponding item in the parameters map, use that value. 即,每当访客看到ParameterExpression如果 parameters映射中有对应的项目,则使用该值。

The override on VisitLambda is because VisitLambda still needs to return a lambda of the same shape , and the default implementation would also visit (and thus swap) out the parameters from the declaration . VisitLambda上的覆盖是因为VisitLambda仍需要返回相同形状的lambda,并且默认实现也将访问(并交换) 声明中的参数。

It is the visitor's job to worry about reassembling the tree around your changes. 访客的工作是担心围绕您的更改重新组装树。

Note, however, that if you are trying to create a parameterless lambda , you might also need to rewrite the root. 但是请注意,如果您尝试创建无参数的lambda ,则可能还需要重写根目录。 Or you could just use the .Body and forget about the parameters. 或者,您可以只使用.Body.Body参数。

Example: 例:

Expression<Func<int, int, string>> add = (x, y) => ((2 * x) + y).ToString();
Console.WriteLine(add);

var args = new Dictionary<string, ConstantExpression>
{
    ["x"] = Expression.Constant(4),
    ["y"] = Expression.Constant(1),
};

var visitor = new ParametersTransformToConstantVisitor(args);
var result = (LambdaExpression)visitor.Visit(add);
Console.WriteLine(result);

which gives: 这使:

(x, y) => ((2 * x) + y).ToString()
(x, y) => ((2 * 4) + 1).ToString()

You can make this into a parameterless lambda via: 您可以通过以下方式将其设置为无参数lambda:

var withoutArgs = Expression.Lambda<Func<string>>(result.Body);
Console.WriteLine(withoutArgs);

which gives: 这使:

() => ((2 * 4) + 1).ToString()

minor addition: you might also want to simplify in the visitor: 次要补充:您可能还想简化访问者:

protected override Expression VisitBinary(BinaryExpression node)
{
    var visited = base.VisitBinary(node);

    if(visited is BinaryExpression be
        && be.Method == null && be.Conversion == null
        && !be.IsLifted
        && be.Left is ConstantExpression left
        && be.Right is ConstantExpression right)
    {

        object val;
        switch(be.NodeType)
        {
            case ExpressionType.Add:
                val = (dynamic)left.Value + (dynamic)right.Value;
                break;
            case ExpressionType.Multiply:
                val = (dynamic)left.Value * (dynamic)right.Value;
                break;
            case ExpressionType.Subtract:
                val = (dynamic)left.Value - (dynamic)right.Value;
                break;
            case ExpressionType.Divide:
                val = (dynamic)left.Value / (dynamic)right.Value;
                break;
            default:
                return visited; // unknown
        }

        return Expression.Constant(
            Convert.ChangeType(val, visited.Type), visited.Type);
    }
    return visited;
}

This changes the outputs to: 这会将输出更改为:

(x, y) => ((2 * x) + y).ToString()
(x, y) => 9.ToString()
() => 9.ToString()

and we could possibly also even hoist the ToString() ! 我们甚至还可以提升ToString()

protected override Expression VisitMethodCall(MethodCallExpression node)
{
    var visited = base.VisitMethodCall(node);
    if (visited is MethodCallExpression mce)
    {
        if ((mce.Object == null || mce.Object is ConstantExpression)
            && mce.Arguments.All(x => x is ConstantExpression))
        {
            var obj = (mce.Object as ConstantExpression)?.Value;
            var args = mce.Arguments.Select(x => ((ConstantExpression)x).Value).ToArray();
            var result = mce.Method.Invoke(obj, args);
            return Expression.Constant(result, mce.Type);
        }
    }
    return visited;
}

which now gives us: 现在可以给我们:

(x, y) => ((2 * x) + y).ToString()
(x, y) => "9"
() => "9"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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