English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Vous avez déjà découvert l'expression dans la section précédente. Maintenant, comprenons l'arbre d'expression ici.
Comme son nom l'indique, un arbre d'expression n'est rien d'autre qu'une expression disposée selon une structure de données en arbre. Chaque nœud d'un arbre d'expression est une expression. Par exemple, un arbre d'expression peut être utilisé pour représenter une formule mathématique x < y, où x, < et y seront représentés comme des expressions et disposés en structure en arbre.
L'arbre d'expression est une représentation en mémoire des expressions lambda. Il conserve les éléments réels de la requête,而非查询的结果。
L'arbre d'expression rend la structure et l'expression des expressions lambda transparentes et explicites. Vous pouvez interagir avec les données de l'arbre d'expression de la même manière que avec toute autre structure de données.
Par exemple, regardez l'expression isTeenAgerExpr suivante :
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.age > 12 && s.age < 20;
Le compilateur convertit l'expression ci-dessus en l'arbre d'expression suivant :
Exemple : l'arbre d'expressions en C#
Expression.Lambda<Func<Student, bool>>( Expression.AndAlso( Expression.GreaterThan(Expression.Property(pe, "Age"), Expression.Constant(12, typeof(int))), Expression.LessThan(Expression.Property(pe, "Age"), Expression.Constant(20, typeof(int)))), new[] { pe });
Vous pouvez également construire manuellement un arbre d'expression. Voyons comment construire un arbre d'expression pour l'expression Lambda simple suivante :
Exemple : Delegate Func en C# :
Func<Student, bool> isAdult = s => s.age >= 18;
Le delegate de type Func sera considéré comme la méthode suivante :
C# :
public bool function(Student s) { return s.Age > 18; }
Pour créer un arbre d'expression, d'abord, créez une expression de paramètre où Student est le type du paramètre, 's' est le nom du paramètre, comme suit :
Étapes1Créer une expression de paramètre en C#
ParameterExpression pe = Expression.Parameter(typeof(Student), "s");
Maintenant, utilisez Expression.Property() pour créer l'expression s.Age, où s est le paramètre et Age est le nom de l'attribut Student. (Expressionest une classe abstraite, qui contient des méthodes d'aide statiques pour créer manuellement un arbre d'expression).
Étapes2Créer une expression de propriété en C#
MemberExpression me = Expression.Property(pe, "Age");
Maintenant, pour18Créer une expression constante :
Étapes3Créer une expression constante en C#
ConstantExpression constant = Expression.Constant(18, typeof(int));
Jusqu'à présent, nous avons pour s.Age (expression de membre) et18(expression constante) Nous avons construit un arbre d'expression. Maintenant, nous devons vérifier si l'expression de membre est supérieure à l'expression constante. Pour cela, utilisez la méthode Expression.GreaterThanOrEqual() et transmettez l'expression de membre et l'expression constante en tant que paramètres :
Étapes4Créer une expression binaire en C#
BinaryExpression body = Expression.GreaterThanOrEqual(me, constant);
Par conséquent, nous avons pour l'expression de corps de l'expression Lambda s.Age> = 18 Nous avons construit un arbre d'expression. Nous devons maintenant connecter l'expression de paramètre et l'expression de corps. Utiliser Expression.Lambda
Étapes5Créer une expression Lambda en C#
var isAdultExprTree = Expression.Lambda<Func<Student, bool>>(body, new[] { pe });
De cette manière, vous pouvez construire un arbre d'expression pour une délégation Func simple avec une expression lambda.
Exemple : l'arbre d'expressions en C#
ParameterExpression pe = Expression.Parameter(typeof(Student), "s"); MemberExpression me = Expression.Property(pe, "Age"); ConstantExpression constant = Expression.Constant(18, typeof(int)); BinaryExpression body = Expression.GreaterThanOrEqual(me, constant); var ExpressionTree = Expression.Lambda<Func<Student, bool>>(body, new[] { pe }); Console.WriteLine("Arbre d'expression: {0}", ExpressionTree); Console.WriteLine("Corps de l'arbre d'expression: {0}", ExpressionTree.Body); Console.WriteLine("Le corps de l'arbre d'expression : {0}", ExpressionTree.Body) ExpressionTree.Parameters.Count); Console.WriteLine("Paramètres de l'arbre d'expression: {0}", ExpressionTree.Parameters[0]);
Dim pe As ParameterExpression = Expression.Parameter(GetType(Student), "s") Dim mexp As MemberExpression = Expression.Property(pe, "Age") Dim constant As ConstantExpression = Expression.Constant(18, GetType(Integer)) Dim body As BinaryExpression = Expression.GreaterThanOrEqual(mexp, constant) Dim ExpressionTree As Expression(Of Func(Of Student, Boolean)) = Dim ExpressionTree As Expression(Of Func(Of Student, Boolean)) = Expression.Lambda(Of Func(Of Student, Boolean))(body, New ParameterExpression() {pe}) Console.WriteLine("L'arbre d'expression : {0}", ExpressionTree) Console.WriteLine("Le corps de l'arbre d'expression : {0}", ExpressionTree.Body) Console.WriteLine("Le nombre de paramètres de l'arbre d'expression : {0}", Console.WriteLine("Les paramètres de l'arbre d'expression : {0}", ExpressionTree.Parameters(0))
L'arbre d'expression : s => (s.Age >= 18) Le corps de l'arbre d'expression : (s.Age >= 18) Le nombre de paramètres de l'arbre d'expression : 1 Les paramètres de l'arbre d'expression : s
La figure suivante explique tout le processus de création de l'arbre d'expression :
Dans la section précédente, nous avons vu que l'expression lambda est assignéeFunc<T>Il est compilé en code exécutable et assigné à l'expression lambdaExpression<TDelegate>Le type est compilé en arbre d'expression.
Le code exécutable s'exécute dans le même domaine d'application pour traiter les collections en mémoire. La classe statique enumerable contient des fonctions pour réaliserIEnumerable <T>Les méthodes d'extension des collections en mémoire de l'interface, par exemple List <T>, Dictionary <T> et autres. Les méthodes d'extension de la classe Enumerable acceptentFuncLe paramètre prédicat de type délégation. Par exemple,WhereL'extension méthode acceptePrédicat Func <TSource, bool>Puis, il est compilé en IL (langage intermédiaire) pour traiter les collections en mémoire du même AppDomain.
La figure suivante montre le cas où l'extension méthode Where de la classe Enumerable inclut la délégation Func en tant que paramètre :
FuncLa délégation est un code exécutable original, donc, si vous déboguez le code, vous trouverezFuncLa délégation est représentée par un code opaque. Vous ne pouvez pas voir ses paramètres, le type de retour et le sujet :
FuncLes délégués sont utilisés pour les collections en mémoire, car ils seront traités dans le même AppDomain, mais des outils comme LINQ-à-Que faire des fournisseurs de requêtes LINQ distantes pour SQL, EntityFramework ou d'autres produits tiers fournissant LINQ ? Comment解析ent-ils les expressions lambda compilées en code exécutable original pour comprendre les paramètres, le type de retour de la lambda expression et construire une requête de runtime pour le traitement supplémentaire ? La réponse estArbre d'expression.
Expression <TDelegate> est compilé en une structure de données appelée arbre d'expression.
Si vous déboguez le code, l'expression représente ce qui suit :
Maintenant, vous pouvez voir la différence entre un délégué standard et une expression. L'arbre d'expression est transparent. Vous pouvez extraire les informations sur les paramètres, le type de retour et l'expression principale de l'expression, comme suit :
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20; Console.WriteLine("Expression: {0}", isTeenAgerExpr); Console.WriteLine("Type d'expression: {0}", isTeenAgerExpr.NodeType); var parameters = isTeenAgerExpr.Parameters; foreach (var param in parameters) { Console.WriteLine("Nom du paramètre: {0}", param.Name); Console.WriteLine("Type de paramètre: {0}", param.Type.Name); } var bodyExpr = isTeenAgerExpr.Body as BinaryExpression; Console.WriteLine("Côté gauche de l'expression: {0}", bodyExpr.Left); Console.WriteLine("Type d'expression binaire: {0}", bodyExpr.NodeType); Console.WriteLine("Côté droit de l'expression: {0}", bodyExpr.Right); Console.WriteLine("Retour type: {0}", isTeenAgerExpr.ReturnType);
Expression : s => ((s.Age > 12) AndAlso (s.Age < 20)) Type d'expression : Lambda Nom du paramètre : s Type de paramètre : Student Côté gauche de l'expression : (s.Age > 12) Type d'expression binaire : AndAlso Côté droit de l'expression : (s.Age < 20) Type de retour : System.Boolean
Ne pas exécuter contre LINQ dans le même domaine d'application-à-Requête LINQ en SQL ou Entity Framework. Par exemple, la requête LINQ suivante pour Entity Framework ne sera jamais réellement exécutée à l'intérieur du programme :
var query = from s in dbContext.Students where s.Age >= 18 select s;
Convertissez d'abord en instruction SQL, puis exécutez-la sur le serveur de base de données.
Le code trouvé dans l'expression de requête doit être converti en requête SQL, qui peut être envoyée en tant que chaîne de caractères à un autre processus. Pour LINQ-à-SQL ou Entity Framework, le processus est exactement celui du base de données SQL Server. Il est beaucoup plus facile de convertir une structure de données (comme un arbre d'expression) en SQL que de convertir le code IL ou le code exécutable original en SQL, car, comme vous le voyez, il est facile de récupérer des informations à partir des expressions.
L'objectif de la création d'un arbre d'expression est de convertir le code tel que les expressions de requête en une chaîne de caractères qui peut être transmise à un autre processus et exécutée ici.
Les classes statiques consultables incluent des méthodes d'extension qui acceptent un paramètre prédicat de type Expression. Convertissez l'expression prédicat en arbre d'expression, puis transmettez-le en tant que structure de données à un fournisseur LINQ distant afin que le fournisseur puisse construire la requête appropriée à partir de l'arbre d'expression et exécuter la requête.