[英]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 > i2
, outer
/ factory
將是i2 => i1 => i1 > i2
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.