매개 변수를 람다 식으로 바꾸기

c# expression-trees lambda partial-application

문제

다음 코드를 고려하십시오.

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 을 가져 exp0 exp1exp2 와 동일한 두 표현식으로 변환 할 수 exp0 ? exp0 에있는 각 Foo 에 대해 exp0 를 평가하기보다는 두 개의 새로운 표현식을 얻고 foos .

[업데이트] :

기본적으로, Sum 와 같은 Linq 확장 메서드에 전달 된 식을 열거 형의 항목 당 하나의 식으로 확장하거나 "평평하게"할 수 있기를 원합니다.이 열거 형은 정적 일 것이므로 이미 열거 된 식을 읽는 코드가 있기 때문입니다. 매개 변수를 가져 와서 다른 언어로 변환합니다.

MetadataToken 을 특정 속성 (이 경우 ab 는이 속성을 가짐)이있는 속성에 대한 참조로 사용하고 있으며 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. 람다 서명으로부터 완전히 원하지 않는 매개 변수를 제거합니다.
  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);


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.