繁体   English   中英

用常量替换表达式中的参数

[英]Replace a parameter in an expression with a constant

我有一个Expression<Func<TElement, TElement, bool>>类型的Expression<Func<TElement, TElement, bool>>和一个TElement类型的常量。 我需要一个Expression<Func<TElement, bool>>类型的Expression<Func<TElement, bool>> ,其中一个参数被常量替换。 换句话说,我需要身体以下方法:

public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>
(
    Expression<Func<TElement, TElement, bool>> inputExpression,
    TElement element
)
{
    ...
}

如果我调用ReplaceParameter((i1, i2) => i1 > i2, 5) ,我希望结果为i => i > 5

我在想,它可能能够递归地解构,然后重构输入表达式,并用常量表达式替换所有出现的第二个参数。 由于有很多不同类型的表达,我不确定如何做到这一点。

ExpressionVisitor是你的朋友:

static void Main()
{
    Expression<Func<int, int, bool>> before = (x, y) => x * 2 == y + 1;
    var after = ReplaceParameter(before, 3);
    Console.WriteLine(after);
}
public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>
(
    Expression<Func<TElement, TElement, bool>> inputExpression,
    TElement element
)
{
    var replacer = new Replacer(inputExpression.Parameters[0],
        Expression.Constant(element, typeof(TElement)));
    var body = replacer.Visit(inputExpression.Body);
    return Expression.Lambda<Func<TElement, bool>>(body,
        inputExpression.Parameters[1]);
}
class Replacer : ExpressionVisitor
{
    private readonly Expression _from, _to;
    public Replacer(Expression from, Expression to)
    {
        _from = from;
        _to = to;
    }
    public override Expression Visit(Expression node)
        => node == _from ? _to : base.Visit(node);
}

请注意,这不会自动折叠纯常量表达式,即显示的代码导致:

y => ((3 * 2) == (y + 1))

可以但是,如果你想,尝试寻找BinaryExpression仅具有ConstantExpression内作为输入,并直接评价节点,再次Replacer

您应该使用ExpressionVisitor来帮助您访问表达式并替换表达式的一部分。

public class ReplaceParameterVisitor : ExpressionVisitor
{
    public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>(
        Expression<Func<TElement, TElement, bool>> inputExpression, 
        TElement element)
    {

        Expression body = inputExpression.Body;
        ReplaceParameterVisitor visitor = 
            new ReplaceParameterVisitor(inputExpression.Parameters[1], 
                                        Expression.Constant(element, typeof(TElement)));
        Expression newBody = visitor.Visit(body);

        Expression<Func<TElement, bool>> newExpression = 
            Expression.Lambda<Func<TElement, Boolean>>(
                newBody, 
                new ParameterExpression[] { inputExpression.Parameters[0] });
        return newExpression;
    }

    private ReplaceParameterVisitor(
        ParameterExpression param, 
        ConstantExpression constant)
    {
        this._param = param;
        this._constant = constant;
    }

    private readonly ParameterExpression _param;
    private readonly ConstantExpression _constant;


    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == this._param)
        {
            return this._constant;
        }
        else
        {
            return base.VisitParameter(node);
        }
    }
}

然后

Expression<Func<Int32, Int32, Boolean>> f = (i1, i2) => i1 > i2;
Expression<Func<Int32, Boolean>> f2 = ReplaceParameterVisitor.ReplaceParameter(f, 5);
Boolean b = f2.Compile()(4);

您可以像这样创建工厂Func而不是使用ExpressionVisitor

public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>
(
    Expression<Func<TElement, TElement, bool>> inputExpression,
    TElement element
)
{
    var inner = Expression.Lambda<Func<TElement, bool>>
    (
        inputExpression.Body,
        inputExpression.Parameters[1]
    );
    var outer = Expression.Lambda<Func<TElement, Expression<Func<TElement, bool>>>>
    (
        inner,
        inputExpression.Parameters[0]
    );
    var factory = outer.Compile();
    return factory(element);
}

为了使它更有用,您可以保存factory并在每次要替换参数时调用它:

public static Func<TElement, Expression<Func<TElement, bool>>> CreateFactory<TElement>
(
    Expression<Func<TElement, TElement, bool>> inputExpression
)
{
    var inner = Expression.Lambda<Func<TElement, bool>>
    (
        inputExpression.Body,
        inputExpression.Parameters[1]
    );
    var outer = Expression.Lambda<Func<TElement, Expression<Func<TElement, bool>>>>
    (
        inner,
        inputExpression.Parameters[0]
    );
    return outer.Compile();
}

public static void Test()
{
    var factory = CreateFactory<int>((i1, i2) => i1 > i2);
    var greater5 = factory(5);
    var greater2 = factory(2);
}

这里到底发生了什么?
inputExpression(i1, i2) => i1 > i2 inner将是i1 => i1 > i2outer / factory将是i2 => i1 => i1 > i2

暂无
暂无

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

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