Partilhar via


Suporte ao .NET Runtime para árvores de expressão

Há uma grande lista de classes no tempo de execução do .NET que funcionam com árvores de expressão. Você pode ver a lista completa em System.Linq.Expressions. Em vez de enumerar a lista completa, vamos entender como as classes de tempo de execução foram projetadas.

No design de linguagem, uma expressão é um corpo de código que avalia e retorna um valor. As expressões podem ser simples: a expressão 1 constante retorna o valor constante de 1. Eles podem ser mais complicados: A expressão (-B + Math.Sqrt(B*B - 4 * A * C)) / (2 * A) retorna uma raiz para uma equação quadrática (no caso em que a equação tem uma solução).

System.Linq.Expression e tipos derivados

Uma das complexidades de trabalhar com árvores de expressão é que muitos tipos diferentes de expressões são válidos em muitos lugares nos programas. Considere uma expressão de atribuição. O lado direito de uma atribuição pode ser um valor constante, uma variável, uma invocação de método, ou outras opções. Essa flexibilidade linguística significa que você pode encontrar muitos tipos de expressão diferentes em qualquer parte dos nós de uma árvore ao percorrer uma árvore de expressão. Portanto, quando você trabalha com o tipo de expressão base, essa é a maneira mais simples de trabalhar. No entanto, às vezes você precisa saber mais. A classe base Expression contém uma NodeType propriedade para essa finalidade. Ele retorna um ExpressionType, que é uma enumeração de tipos de expressão possíveis. Depois de identificar o tipo do nó, converte-o para esse tipo e executa ações específicas com base no tipo do nó de expressão. Você pode pesquisar determinados tipos de nó e, em seguida, trabalhar com as propriedades específicas desse tipo de expressão.

Por exemplo, esse código imprime o nome de uma variável para uma expressão de acesso variável. O código a seguir mostra a prática de verificar o tipo de nó, converter numa expressão de acesso a variável e verificar as propriedades do tipo específico da expressão:

Expression<Func<int, int>> addFive = (num) => num + 5;

if (addFive is LambdaExpression lambdaExp)
{
    var parameter = lambdaExp.Parameters[0];  -- first

    Console.WriteLine(parameter.Name);
    Console.WriteLine(parameter.Type);
}

Criar árvores de expressão

A System.Linq.Expression classe também contém muitos métodos estáticos para criar expressões. Esses métodos criam um nó de expressão usando os argumentos fornecidos para seus filhos. Desta forma, você constrói uma expressão a partir de seus nós de folha. Por exemplo, este código cria uma expressão Add:

// Addition is an add expression for "1 + 2"
var one = Expression.Constant(1, typeof(int));
var two = Expression.Constant(2, typeof(int));
var addition = Expression.Add(one, two);

Você pode ver neste exemplo simples que muitos tipos estão envolvidos na criação e no trabalho com árvores de expressão. Essa complexidade é necessária para fornecer as capacidades do rico vocabulário fornecido pela linguagem C#.

Há tipos de nós de expressão que correspondem a quase todos os elementos de sintaxe da linguagem de programação C#. Cada tipo tem métodos específicos para esse tipo de elemento de linguagem. É muito para guardar na sua cabeça ao mesmo tempo. Em vez de tentar memorizar tudo, aqui estão as técnicas que você usa para trabalhar com árvores de expressão:

  1. Observe os ExpressionType membros do enum para determinar possíveis nós que você deve examinar. Esta lista ajuda quando se deseja percorrer e compreender uma árvore de expressão.
  2. Observe os membros estáticos da Expression classe para criar uma expressão. Esses métodos podem criar qualquer tipo de expressão a partir de um conjunto dos seus nós filhos.
  3. Observe a ExpressionVisitor classe para criar uma árvore de expressão modificada.

Você encontra mais quando olha para cada uma dessas três áreas. Invariavelmente, você encontra o que precisa quando começa com um desses três passos.