[英]Replace a parameter in an expression with a constant
I have an expression of type Expression<Func<TElement, TElement, bool>>
and a constant of type TElement
. 我有一个
Expression<Func<TElement, TElement, bool>>
类型的Expression<Func<TElement, TElement, bool>>
和一个TElement
类型的常量。 I need an expression of type Expression<Func<TElement, bool>>
with one of the parameters replaced by the constant. 我需要一个
Expression<Func<TElement, bool>>
类型的Expression<Func<TElement, bool>>
,其中一个参数被常量替换。 In other words, I need the body to the following method: 换句话说,我需要身体以下方法:
public static Expression<Func<TElement, bool>> ReplaceParameter<TElement>
(
Expression<Func<TElement, TElement, bool>> inputExpression,
TElement element
)
{
...
}
If I call ReplaceParameter((i1, i2) => i1 > i2, 5)
, I expect the result to be i => i > 5
. 如果我调用
ReplaceParameter((i1, i2) => i1 > i2, 5)
,我希望结果为i => i > 5
。
I was thinking, it might be able to recursively deconstruct and then reconstruct the input expression and replace all occurrences of the second parameter with a constant expression. 我在想,它可能能够递归地解构,然后重构输入表达式,并用常量表达式替换所有出现的第二个参数。 Since there are so many different kind of expressions, I'm unsure on how to do that, though.
由于有很多不同类型的表达,我不确定如何做到这一点。
ExpressionVisitor
is your friend: 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);
}
Note that this does not automatically collapse pure constant expressions, ie the code shown results in: 请注意,这不会自动折叠纯常量表达式,即显示的代码导致:
y => ((3 * 2) == (y + 1))
You could however, if you wanted, try looking for BinaryExpression
that only has ConstantExpression
as inputs, and evaluate the node directly, again inside Replacer
. 您可以但是,如果你想,尝试寻找
BinaryExpression
仅具有ConstantExpression
内作为输入,并直接评价节点,再次Replacer
。
You should use an ExpressionVisitor
which will helps you visit the expression and replace part of the expression. 您应该使用
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);
}
}
}
Then 然后
Expression<Func<Int32, Int32, Boolean>> f = (i1, i2) => i1 > i2;
Expression<Func<Int32, Boolean>> f2 = ReplaceParameterVisitor.ReplaceParameter(f, 5);
Boolean b = f2.Compile()(4);
Instead of using the ExpressionVisitor
you could create a factory Func
like this: 您可以像这样创建工厂
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);
}
To make it even more useful, you could save factory
and call it every time you want to replace a parameter: 为了使它更有用,您可以保存
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);
}
What is actually happening here? 这里到底发生了什么?
When inputExpression
is (i1, i2) => i1 > i2
then inner
would be i1 => i1 > i2
and outer
/ factory
would be i2 => i1 => i1 > i2
. 当
inputExpression
是(i1, i2) => i1 > i2
inner
将是i1 => i1 > i2
, outer
/ factory
将是i2 => i1 => i1 > i2
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.