替换 lambda 表达式中的参数 [英] Replace parameter in lambda expression
问题描述
考虑这个代码:
public class Foo
{
public int a { get; set; }
public int b { get; set; }
}
private void Test()
{
List<Foo> foos = new List<Foo>();
foos.Add(new Foo());
foos.Add(new Foo());
Expression<Func<Foo, int>> exp0 = f => f.a * f.b;
Expression<Func<int>> exp1 = () => foos[0].a * foos[0].b;
Expression<Func<int>> exp2 = () => foos[1].a * foos[1].b;
}
你怎样才能把exp0
变成两个与exp1
和exp2
相同的表达式.请注意,我不想只为 foos
中的每个 Foo
计算 exp0
,而是获得两个新表达式.
How can you take exp0
and turn it into two expressions identical to exp1
and exp2
. Note that I don't want to just evaluate exp0
for each Foo
in foos
, but instead get two new expressions.
[更新]:
基本上,我希望能够将传递给 Linq
扩展方法(例如 Sum
)的表达式扩展或扁平化"为枚举中每个项目的一个表达式,因为这些枚举将是静态的,因为我已经有了读取不带参数的表达式的代码(然后将它们转换为另一种语言).
Basically, I want to be able to expand or "flatten" an expression passed to a Linq
extension method such as Sum
into one expression per item in the enumeration since these enumerations will be static, and because I already have code that reads expressions that don't take parameters (and then turns them into another language).
我使用 MetadataToken
作为对具有特定属性的属性的引用(在这种情况下 a
和 b
将具有这个属性)并将其与将 C# 属性与另一种语言的变量相关联的字典一起使用:
I'm using the MetadataToken
as a references to properties that have a certain attribute (in this case a
and b
would have this attribute) and using it with a dictionary that correlates C# properties to another language's variables:
Foo foo = new Foo();
Expression<Func<int>> exp = () => foo.a * foo.a + foo.b;
string result1 = GetResult(exp); // gets "v_001 * v_001 + v_002"
List<Foo> foes = new List<Foo>();
foes.Add(new Foo());
foes.Add(new Foo());
Expression<Func<int>> exp2 = () => foes.Sum(f => f.a * f.a + f.b);
string result2 = GetResult(exp2); // should get "(v_001 * v_001 + v_002) + (v_003 * v_003 + v_004)"
推荐答案
我会这样做:
编写一个参数替换表达式访问者,操作原始表达式如下:
Write a parameter-replacer expression-visitor that manipulates the original expression as follows:
- 从 lambda 签名中完全删除您不想要的参数.
- 将参数的所有用法替换为所需的索引器表达式.
这是我根据我在另一个问题上的早先的回答制作的一个快速而肮脏的示例:
Here's a quick and dirty sample I whipped up based on my earlier answer on a different question:
public static class ParameterReplacer
{
// Produces an expression identical to 'expression'
// except with 'source' parameter replaced with 'target' expression.
public static Expression<TOutput> Replace<TInput, TOutput>
(Expression<TInput> expression,
ParameterExpression source,
Expression target)
{
return new ParameterReplacerVisitor<TOutput>(source, target)
.VisitAndConvert(expression);
}
private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
{
private ParameterExpression _source;
private Expression _target;
public ParameterReplacerVisitor
(ParameterExpression source, Expression target)
{
_source = source;
_target = target;
}
internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
{
return (Expression<TOutput>)VisitLambda(root);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
// Leave all parameters alone except the one we want to replace.
var parameters = node.Parameters
.Where(p => p != _source);
return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
}
protected override Expression VisitParameter(ParameterExpression node)
{
// Replace the source with the target, visit other params as usual.
return node == _source ? _target : base.VisitParameter(node);
}
}
}
用于您的场景(已测试):
Usage for your scenario (tested):
var zeroIndexIndexer = Expression.MakeIndex
(Expression.Constant(foos),
typeof(List<Foo>).GetProperty("Item"),
new[] { Expression.Constant(0) });
// .ToString() of the below looks like the following:
// () => (value(System.Collections.Generic.List`1[App.Foo]).Item[0].a
// * value(System.Collections.Generic.List`1[App.Foo]).Item[0].b)
var exp1Clone = ParameterReplacer.Replace<Func<Foo, int>, Func<int>>
(exp0, exp0.Parameters.Single(), zeroIndexIndexer);
这篇关于替换 lambda 表达式中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!