표현식 트리를 호출 가능한 방법으로 컴파일하려면 어떻게하나요?

StackOverflow https://stackoverflow.com/questions/346523

  •  19-08-2019
  •  | 
  •  

문제

C#의 표현식 클래스를 사용하여 XML을 구문 분석하여 생성 한 표현식 트리가 있습니다. 이 질문을 참조하십시오.

추가, 빼기, 분열, 곱하기, 매개 변수 및 내 표현 트리에만 추가됩니다. 이 expressionTree를 호출 가능한 방법으로 변환하는 방법이 있습니까? ... 아니면 IL을 수동으로 방출해야합니까?

친절한 안부,

도움이 되었습니까?

해결책

다음은 두 가지 접근 방식의 예입니다. 내가 뭔가를 놓쳤거나 더 많은 정보를 원한다면 알려주세요.

static void Main()
{
    // try to do "x + (3 * x)"

    var single = BuildSingle<decimal>();
    var composite = BuildComposite<decimal>();

    Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M));
}
// utility method to get the 3 as the correct type, since there is not always a "int x T"
static Expression ConvertConstant<TSource, TDestination>(TSource value)
{
    return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination));
}
// option 1: a single expression tree; this is the most efficient
static Func<T,T> BuildSingle<T>()
{        
    var param = Expression.Parameter(typeof(T), "x");
    Expression body = Expression.Add(param, Expression.Multiply(
        ConvertConstant<int, T>(3), param));
    var lambda = Expression.Lambda<Func<T, T>>(body, param);
    return lambda.Compile();
}
// option 2: nested expression trees:
static Func<T, T> BuildComposite<T>()
{

    // step 1: do the multiply:
    var paramInner = Expression.Parameter(typeof(T), "inner");
    Expression bodyInner = Expression.Multiply(
        ConvertConstant<int, T>(3), paramInner);
    var lambdaInner = Expression.Lambda(bodyInner, paramInner);

    // step 2: do the add, invoking the existing tree
    var paramOuter = Expression.Parameter(typeof(T), "outer");
    Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter));
    var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter);

    return lambdaOuter.Compile();
}

개인적으로, 나는 첫 번째 방법을 목표로 할 것이다. 그것은 더 단순하고 효율적입니다. 여기에는 중첩 된 코드 스택 전체에 원래 매개 변수를 전달하는 것이 포함될 수 있지만 그렇습니다. 나는 "호출"접근법 (composite)을 취하고 트리를 첫 번째 접근법 (단일)으로 다시 쓰는 코드를 가지고 있지만 매우 복잡하고 길다. 그러나 엔티티 프레임 워크 (expression.invoke를 지원하지 않음)에 매우 유용합니다.

다른 팁

람다를 만들어야합니다 - 즉

var lambda = Expression.Lambda<Func<float,int>>(body, param);
Func<float,int> method = lambda.Compile();
int v = method(1.0); // test

여기서 "Body"는 당신의 표현 트리 (부유물을 가져 가서 int를 반환)입니다. ParameterExpression 파라.

당신은 또한 찾을 수 있습니다 이것 그리고 이것 도움이 되는.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top