问题描述
考虑到这一点code:
公共类Foo
{
公众诠释一个{搞定;组; }
公众诠释B {搞定;组; }
}私人无效测试()
{
清单<富> FOOS =新的List<富>();
foos.Add(新的Foo());
foos.Add(新的Foo());
防爆pression<&Func键LT;富,INT>> exp0 = F => F.A * f.b;
防爆pression<&Func键LT; INT>> EXP1 =()=> FOOS [0] .A * FOOS [0] .B;
防爆pression<&Func键LT; INT>> EXP2 =()=> FOOS [1] .A * FOOS [1] .B;
}
你怎么能采取 exp0
并把它变成两位前pressions等同于 EXP1
和 EXP2
。请注意,我不希望只是评估 exp0
每个富
在 FOOS
,而是得到两个新的前pressions。
[更新]
基本上,我希望能够扩展或扁平化传递给一个的LINQ
扩展方法的前pression如总和
成一个前pression每件枚举,因为这些枚举将是静态的,因为我已经有code,上面写着前pressions不带参数(和然后把它们转化成另一种语言)。
我使用了 MetadataToken
为具有特定属性(在这种情况下属性引用 A
和 b
会有这个属性),并用它与相关的C#性能到另一种语言的变量的字典:
富富=新的Foo();
防爆pression<&Func键LT; INT>> EXP =()=> foo.a * foo.a + foo.b;
字符串RESULT1 =调用getResult(EXP); //得到v_001 * v_001 + v_002清单<富>敌人=新的List<富>();
foes.Add(新的Foo());
foes.Add(新的Foo());
防爆pression<&Func键LT; INT>> EXP2 =()=> foes.Sum(F => F.A * F.A + f.b);
字符串结果2 =调用getResult(EXP2); //应该得到(v_001 * v_001 + v_002)+(v_003 * v_003 + v_004)
我会做这种方式:
写参数替代前pression-游客的操纵原来的前pression如下:
- 摆脱了参数的不完全从拉姆达签名想要的。
- 替换参数的所有使用与所需的索引前pression。
下面是一个快速和肮脏的样品我刮起了基于我在一个不同的问题:
公共静态类ParameterReplacer
{
//产生前pression等同于'前pression
//除了与目标前pression替换成'源'参数。
公共静态防爆pression< TOutput>更换< TInput,TOutput>
(防爆pression< TInput>前pression,
ParameterEx pression源,
防爆pression目标)
{
返回新ParameterReplacerVisitor< TOutput>(源,目标)
.VisitAndConvert(如pression);
} 私有类ParameterReplacerVisitor< TOutput> :防爆pressionVisitor
{
私人ParameterEx pression _source;
私人防爆pression _target; 公共ParameterReplacerVisitor
(ParameterEx pression源,防爆pression目标)
{
_source =来源;
_target =目标;
} 内部防爆pression< TOutput> VisitAndConvert< T>(防爆pression< T>根)
{
返回(前pression< TOutput>)VisitLambda(根);
} 保护覆盖防爆pression VisitLambda< T>(防爆pression< T>节点)
{
//别碰所有的参数,除了我们要替换的。
VAR参数= node.Parameters
。凡(P =>!P = _source); 返回前pression.Lambda< TOutput>(参观(node.Body),参数);
} 保护覆盖防爆pression VisitParameter(ParameterEx pression节点)
{
//与目标替换源,访问其他PARAMS如常。
返回节点== _source? _target:base.VisitParameter(节点);
}
}
}
为您的方案使用(测试):
VAR zeroIndexIndexer =前pression.MakeIndex
(防爆pression.Constant(FOOS)
typeof运算(列表<富>)的getProperty(项目)。
新的[] {防爆pression.Constant(0)});
//的下面的ToString()如下所示:
//()= GT; (值(System.Collections.Generic.List`1 [App.Foo])。项目[0] .A
// *值(System.Collections.Generic.List`1 [App.Foo])。项目[0] .B)
VAR exp1Clone = ParameterReplacer.Replace<&Func键LT;富,INT>中Func键< INT>>
(exp0,exp0.Parameters.Single(),zeroIndexIndexer);
Considering this code:
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;
}
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.
[Update]:
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).
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)"
I would do it this way:
Write a parameter-replacer expression-visitor that manipulates the original expression as follows:
- Gets rid of the parameter you don't want entirely from the lambda signature.
- Replaces all uses of the parameter with the desired indexer expression.
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);
这篇关于在拉姆达前pression替换参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!