Como faço para compilar uma árvore de expressão em um método que pode ser chamado, C #?

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

  •  19-08-2019
  •  | 
  •  

Pergunta

Eu tenho uma árvore de expressão que eu criei ao analisar um XML usando a classe expressão em C #. Veja esta questão .

Eu só adicionar, subtrair, dividir, multiplicar, parâmetros e e ou no meu Expression Tree. Existe uma maneira de converter este ExpressionTree em um método que pode ser chamado? ... ou eu tenho para emitir o IL manualmente?

Atenciosamente,

Foi útil?

Solução

Aqui está um exemplo de ambas as abordagens. Se eu perdi alguma coisa, ou você quiser mais informações, é só me avisar.

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

Pessoalmente, gostaria de apontar para o primeiro método; que ele mais simples e mais eficiente. Isso pode envolver passando o parâmetro originais ao longo de uma pilha de código aninhada, mas que assim seja. Eu tenho alguns em algum lugar código que leva a abordagem "Chamar" (composto), e re-escreve a árvore como a primeira abordagem (single) - mas é bastante complexo e longo. Mas muito útil para Entity Framework (que não suporta Expression.Invoke).

Outras dicas

Você precisa criar um lambda -. I

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

onde "corpo" é a sua árvore de expressão (tendo uma bóia, retornando um int) envolvendo o param ParameterExpression.

Você também pode encontrar este e este útil.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top