Compartilhar via


Árvores de expressão

As árvores de expressão representam código em uma estrutura de dados semelhante a uma árvore, em que cada nó é uma expressão, por exemplo, uma chamada de método ou uma operação binária, como x < y.

Se você usou LINQ, terá experiência com uma biblioteca avançada em que os Func tipos fazem parte do conjunto de API. (Se você não estiver familiarizado com LINQ, provavelmente deseja ler o tutorial linq e o artigo sobre expressões lambda antes desta.) As Árvores de Expressão fornecem uma interação mais avançada com os argumentos que são funções.

Você escreve argumentos de função, normalmente usando expressões Lambda, ao criar consultas LINQ. Em uma consulta LINQ típica, esses argumentos de função são transformados em um delegado que o compilador cria.

Você já escreve código que usa árvores de expressão. As APIs LINQ do Entity Framework aceitam árvores de expressão como argumentos para o padrão de expressão de consulta LINQ. Isso permite que o Entity Framework traduza a consulta que você escreveu em C# para o SQL que é executado no mecanismo de banco de dados. Outro exemplo é Moq, que é uma estrutura de simulação popular para .NET.

Quando você deseja ter uma interação mais avançada, precisa usar árvores de expressão. As Árvores de Expressão representam o código como uma estrutura que você examina, modifica ou executa. Essas ferramentas fornecem o poder de manipular o código durante o tempo de execução. Você escreve um código que examina algoritmos em execução ou injeta novos recursos. Em cenários mais avançados, você modifica algoritmos em execução e até converte expressões C# em outro formulário para execução em outro ambiente.

Você compila e executa o código representado por árvores de expressão. A criação e a execução de árvores de expressão permitem a modificação dinâmica do código executável, a execução de consultas LINQ em vários bancos de dados e a criação de consultas dinâmicas. Para obter mais informações sobre árvores de expressão no LINQ, consulte Como usar árvores de expressão para criar consultas dinâmicas.

As árvores de expressão também são usadas no DLR (Dynamic Language Runtime) para fornecer interoperabilidade entre linguagens dinâmicas e .NET e para permitir que os gravadores de compilador emitam árvores de expressão em vez de CIL (linguagem intermediária da Microsoft). Para obter mais informações sobre a DLR, consulte Visão geral do Dynamic Language Runtime.

Você pode fazer com que o compilador C# ou Visual Basic crie uma árvore de expressão para você com base em uma expressão lambda anônima ou crie árvores de expressão manualmente usando o System.Linq.Expressions namespace.

Quando uma expressão lambda é atribuída a uma variável de tipo Expression<TDelegate>, o compilador emite o código para criar uma árvore de expressão que representa a expressão lambda.

Os exemplos de código a seguir demonstram como fazer com que o compilador C# crie uma árvore de expressão que represente a expressão num => num < 5lambda.

Expression<Func<int, bool>> lambda = num => num < 5;

Você cria árvores de expressão em seu código. Você cria a árvore criando cada nó e anexando os nós a uma estrutura de árvore. Você aprenderá a criar expressões no artigo sobre como criar árvores de expressão.

Árvores de expressão são imutáveis. Se você quiser modificar uma árvore de expressão, será necessário construir uma nova árvore de expressão copiando a existente e substituindo nós nela. Você usa um visitante de árvore expressão para percorrer a árvore de expressão existente. Para obter mais informações, consulte o artigo sobre como traduzir árvores de expressão.

Depois de criar uma árvore de expressão, execute o código representado pela árvore de expressão.

Limitações

O compilador C# gera árvores de expressão somente a partir de lambdas de expressão (ou lambdas de linha única). Ele não consegue analisar instruções lambdas (ou lambdas de várias linhas). Para obter mais informações sobre expressões lambda em C#, consulte Expressões Lambda.

Há alguns elementos de linguagem C# mais recentes que não se traduzem bem em árvores de expressão. Árvores de expressão não podem conter await expressões ou async expressões lambda. Muitos dos recursos adicionados em C# 6 e posterior não aparecem exatamente como escritos em árvores de expressão. Em vez disso, os recursos mais recentes são expostos em árvores de expressão na sintaxe equivalente e anterior, sempre que possível. Outros constructos não estão disponíveis. Isso significa que o código que interpreta árvores de expressão funciona da mesma forma quando novos recursos de linguagem são introduzidos. No entanto, mesmo com essas limitações, as árvores de expressão permitem que você crie algoritmos dinâmicos que dependem da interpretação e modificação de código que é representado como uma estrutura de dados. Ele permite que bibliotecas avançadas, como o Entity Framework, realizem o que fazem.

As árvores de expressão não dão suporte a novos tipos de nó de expressão. Seria uma mudança significativa para todas as bibliotecas que interpretam árvores de expressão a introdução de novos tipos de nó. A lista a seguir inclui a maioria dos elementos de linguagem C# que não podem ser usados: