在拉姆达前pression替换参数 [英] Replace parameter in lambda expression

查看:158
本文介绍了在拉姆达前pression替换参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑到这一点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如下:


  1. 摆脱了参数的不完全从拉姆达签名想要的。

  2. 替换参数的所有使用与所需的索引前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:

  1. Gets rid of the parameter you don't want entirely from the lambda signature.
  2. 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替换参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆