Looking for c# Answers? Try Ask4KnowledgeBase
Looking for c# Keywords? Try Ask4Keywords

C# LanguageArbres d'expression


Introduction

Les arbres d'expression sont des expressions organisées dans une structure de données arborescente. Chaque nœud de l'arborescence est une représentation d'une expression, une expression étant du code. Une représentation en mémoire d'une expression Lambda serait un arbre d'expression qui contiendrait les éléments réels (c'est-à-dire le code) de la requête, mais pas son résultat. Les arbres d'expression rendent la structure d'une expression lambda transparente et explicite.

Syntaxe

  • Expression <TDelegate> name = lambdaExpression;

Paramètres

Paramètre Détails
Délégué TD Le type de délégué à utiliser pour l'expression
lambdaExpression L'expression lambda (ex. num => num < 5 )

Remarques

Introduction aux arbres d'expression

D'où nous venons

Les arborescences d'expression sont toutes axées sur la consommation de "code source" au moment de l'exécution. Envisagez une méthode qui calcule la taxe de vente due sur une decimal CalculateTotalTaxDue(SalesOrder order) . Utiliser cette méthode dans un programme .NET est facile - vous appelez simplement decimal taxDue = CalculateTotalTaxDue(order); . Que faire si vous souhaitez l'appliquer à tous les résultats d'une requête distante (SQL, XML, un serveur distant, etc.)? Ces sources de requêtes distantes ne peuvent pas appeler la méthode! Traditionnellement, vous devez inverser le flux dans tous ces cas. Effectuez l'intégralité de la requête, stockez-la en mémoire, puis parcourez les résultats et calculez la taxe pour chaque résultat.

Comment éviter les problèmes de mémoire et de latence de l'inversion de flux

Les arbres d'expression sont des structures de données dans un format d'arbre, où chaque noeud contient une expression. Ils sont utilisés pour traduire les instructions compilées (comme les méthodes utilisées pour filtrer les données) dans des expressions qui pourraient être utilisées en dehors de l'environnement du programme, par exemple dans une requête de base de données.

Le problème ici est qu'une requête distante ne peut pas accéder à notre méthode . Nous pourrions éviter ce problème si, à la place, nous avons envoyé les instructions pour la méthode à la requête distante. Dans notre exemple CalculateTotalTaxDue , cela signifie que nous envoyons ces informations:

  1. Créer une variable pour stocker la taxe totale
  2. Traverser toutes les lignes de la commande
  3. Pour chaque ligne, vérifiez si le produit est taxable
  4. Si c'est le cas, multipliez le total de la ligne par le taux de taxe applicable et ajoutez ce montant au total.
  5. Sinon ne rien faire

Avec ces instructions, la requête distante peut exécuter le travail lors de la création des données.

Il y a deux défis à relever pour la mettre en œuvre. Comment transformer une méthode .NET compilée en une liste d'instructions et comment formater les instructions de manière à ce qu'elles puissent être utilisées par le système distant?

Sans arbres d'expression, vous ne pouvez résoudre le premier problème avec MSIL. (MSIL est le code de type assembleur créé par le compilateur .NET.) L'analyse MSIL est possible , mais ce n'est pas facile. Même si vous analysez correctement le contenu, il peut être difficile de déterminer quelle était l'intention du programmeur d'origine avec une routine particulière.

Les arbres d'expression sauvent la journée

Les arbres d'expression traitent ces problèmes exacts. Ils représentent des instructions de programme dans une structure de données arborescente où chaque nœud représente une instruction et contient des références à toutes les informations dont vous avez besoin pour exécuter cette instruction. Par exemple, un objet MethodCallExpression fait référence à 1) le MethodInfo qu'il va appeler, 2) une liste d' Expression qu'il passera à cette méthode, 3) pour les méthodes d'instance, l' Expression que vous appellerez la méthode. Vous pouvez "parcourir l'arborescence" et appliquer les instructions sur votre requête distante.

Créer des arbres d'expression

Le moyen le plus simple de créer un arbre d'expression est d'utiliser une expression lambda. Ces expressions sont presque identiques aux méthodes C # normales. Il est important de réaliser que c'est la magie du compilateur . Lorsque vous créez une expression lambda pour la première fois, le compilateur vérifie ce que vous lui attribuez. S'il s'agit d'un type Delegate (y compris Action ou Func ), le compilateur convertit l'expression lambda en un délégué. Si c'est un LambdaExpression (ou une Expression<Action<T>> ou Expression<Func<T>> qui est fortement typé LambdaExpression ), le compilateur le transforme en LambdaExpression . C'est là LambdaExpression la magie. En arrière-plan, le compilateur utilise l'API API pour transformer votre expression lambda en une expression LambdaExpression .

Les expressions lambda ne peuvent pas créer chaque type d'arbre d'expression. Dans ces cas, vous pouvez utiliser l'API Expressions manuellement pour créer l'arborescence requise. Dans l'exemple de l' API Comprendre les expressions , nous créons l'expression CalculateTotalSalesTax à l'aide de l'API.

NOTE: Les noms sont un peu confus ici. Une expression lambda (deux mots, minuscule) fait référence au bloc de code avec un indicateur => . Il représente une méthode anonyme en C # et est converti en Delegate ou Expression . Un LambdaExpression (un mot, PascalCase) fait référence au type de noeud dans l'API Expression qui représente une méthode que vous pouvez exécuter.

Arbres d'expression et LINQ

L'une des utilisations les plus courantes des arborescences d'expression concerne les requêtes LINQ et de base de données. LINQ associe une arborescence d'expression à un fournisseur de requêtes pour appliquer vos instructions à la requête distante cible. Par exemple, le fournisseur de requêtes LINQ to Entity Framework transforme un arbre d'expression en SQL qui est exécuté directement sur la base de données.

En rassemblant toutes les pièces, vous pouvez voir le véritable pouvoir derrière LINQ.

  1. Ecrivez une requête en utilisant une expression lambda: products.Where(x => x.Cost > 5)
  2. Le compilateur transforme cette expression en une arborescence d'expression avec les instructions "vérifier si la propriété Cost du paramètre est supérieure à cinq".
  3. Le fournisseur de requêtes analyse l'arborescence des expressions et génère une requête SQL valide SELECT * FROM products WHERE Cost > 5
  4. L'ORM projette tous les résultats dans des POCO et vous obtenez une liste d'objets

Remarques

  • Les arbres d'expression sont immuables. Si vous souhaitez modifier une arborescence d'expression pour en créer une nouvelle, copiez celle existante dans la nouvelle (pour parcourir une arborescence d'expression que vous pouvez utiliser avec ExpressionVisitor ) et apportez les modifications souhaitées.

Arbres d'expression Exemples Liés