簡體   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