替换lambda表达式中的参数

考虑以下代码:

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。请注意,我不希望只是评估exp0每个Foofoos,而是得到两个新的表达。

基本上,我希望能够扩展或“展平”传递给Linq扩展方法的表达式,例如Sum枚举中每个项目的一个表达式,因为这些枚举将是静态的,并且因为我已经具有读取不包含表达式的代码获取参数(然后将其转换为另一种语言)。

我将MetadataToken用作具有特定属性(在这种情况下,a并且b将具有此属性)的属性的引用,并将其与将C#属性与另一种语言的变量相关联的字典使用:

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)"

回答:

我会这样:

编写一个参数替换表达式访问者,该访问者可以如下操作原始表达式:

  1. 完全从lambda签名中删除不需要的参数。
  2. 将参数的所有使用替换为所需的索引器表达式。

这是我根据先前对另一个问题的回答得出的快速而肮脏的示例:

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);

}

}

}

您的方案的使用情况(已测试):

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表达式中的参数 的全部内容, 来源链接: utcz.com/qa/416454.html

回到顶部