English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Hai già imparato l'espressione nel capitolo precedente. Ora, diamo un'occhiata all'albero di espressioni qui.
Come suggerisce il nome, l'albero di espressioni non è altro che un'espressione organizzata in una struttura ad albero. Ogni nodo dell'albero di espressioni è un'espressione. Ad esempio, un albero di espressioni può essere utilizzato per rappresentare un'equazione matematica x < y, dove x, < e y saranno rappresentati come espressioni e organizzati in una struttura ad albero.
L'albero di espressioni è la rappresentazione in memoria dell'espressione lambda. Salva gli elementi effettivi della query, non il risultato della query.
L'albero di espressioni rende la struttura del lambda espressione trasparente ed esplicita. Puoi interagire con i dati dell'albero di espressioni come con qualsiasi altra struttura dati.
Ad esempio, guardiamo l'espressione isTeenAgerExpr di seguito:
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.age > 12 && s.age < 20;
Il compilatore convertirà l'espressione sopra in un albero di espressioni seguente:
Esempio: albero di espressioni in 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 });
Puoi anche costruire manualmente un albero di espressioni. Vediamo come costruire un albero di espressioni per la seguente espressione lambda semplice:
Esempio: Delegato Func in C#:
Func<Student, bool> isAdult = s => s.age >= 18;
Questo tipo di delega Func sarà considerato come il seguente metodo:
C#:
public bool function(Student s) { return s.Age > 18; }
Per creare un albero di espressioni, prima di tutto, crea un'espressione parametrica, dove Student è il tipo del parametro, 's' è il nome del parametro, come segue:
Passo 1: Creare un'espressione parametrica in C#
ParameterExpression pe = Expression.Parameter(typeof(Student), "s");
Ora, crea l'espressione s.Age utilizzando Expression.Property(), dove s è il parametro e Age è il nome dell'attributo Student.ExpressionÈ una classe astratta che contiene metodi statici di aiuto per creare manualmente un albero di espressioni).
Passo 2: Creare un'espressione proprietà in C#
MemberExpression me = Expression.Property(pe, "Age");
Ora, crea un'espressione costante per 18:
Passo 3: Creare un'espressione costante in C#
ConstantExpression constant = Expression.Constant(18, typeof(int));
Finora, abbiamo costruito un albero di espressioni per l'espressione membro s.Age (espressione membro) e il costante 18 (espressione costante). Ora, dobbiamo verificare se l'espressione membro è maggiore dell'espressione costante. Per fare questo, usa il metodo Expression.GreaterThanOrEqual() e passa l'espressione membro e l'espressione costante come parametri: :
Passo 4: Creare un'espressione binaria in C#
BinaryExpression body = Expression.GreaterThanOrEqual(me, constant);
Quindi, abbiamo costruito un albero di espressioni per il corpo dell'espressione lambda s.Age >= 18. Ora dobbiamo connettere l'espressione parametrica e l'espressione del corpo. Usa Expression.Lambda
Passo 5: Creare un'espressione Lambda in C#
var isAdultExprTree = Expression.Lambda<Func<Student, bool>>(body, new[] { pe });
In questo modo, puoi costruire un albero di espressioni per un semplice delegato Func con espressioni lambda.
Esempio: albero di espressioni in 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("Albero di espressioni: {0}", ExpressionTree); Console.WriteLine("Corpo dell'albero di espressioni: {0}", ExpressionTree.Body); Console.WriteLine("Numero di parametri dell'albero di espressioni: {0}", ExpressionTree.Parameters.Count; Console.WriteLine("Parametro dell'albero di espressioni: {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)) =}} Expression.Lambda(Of Func(Of Student, Boolean))(body, New ParameterExpression() {pe}) Console.WriteLine("Albero di espressioni: {0}", ExpressionTree) Console.WriteLine("Corpo dell'albero di espressioni: {0}", ExpressionTree.Body) Console.WriteLine("Numero di parametri dell'albero di espressioni: {0}", ExpressionTree.Parameters.Count) Console.WriteLine("Parametro dell'albero di espressioni: {0}", ExpressionTree.Parameters(0))
Albero di espressioni: s => (s.Age >= 18) Corpo dell'albero di espressioni: (s.Age >= 18) Numero di parametri dell'albero di espressioni: 1 Parametro dell'albero di espressioni: s
La figura seguente illustra l'intero processo di creazione dell'albero di espressioni:
Nella sezione precedente, abbiamo visto che viene assegnato all'espressione lambdaFunc<T>compilato in codice eseguibile e assegnato all'espressione lambdaExpression<TDelegate>il tipo viene compilato in un albero di espressioni.
Il codice eseguibile viene eseguito nello stesso dominio dell'applicazione per gestire le collezioni in memoria. La classe statica enumerabile contiene strumenti per implementareIEnumerable <T>metodi estesi delle collezioni in memoria dell'interfaccia, ad esempio List <T>, Dictionary <T> ecc. I metodi estesi dell'oggetto Enumerable accettanoFuncparametro predicato del tipo delegato. Ad esempio:WhereIl metodo esteso accettaPredicato Func <TSource, bool>. Poi, lo compila in IL (linguaggio intermedio) per gestire le collezioni in memoria dello stesso AppDomain.
La figura seguente mostra il metodo esteso di where dell'oggetto Enumerable che include il delegato Func come parametro:
FuncIl delegato è un codice eseguibile originale, quindi, se si debugga il codice, si scoprirà:FuncIl delegato viene rappresentato come codice opaco. Non è possibile vedere i suoi parametri, il tipo di ritorno e il corpo:
FuncI delegati vengono utilizzati per collectioni in memoria perché verranno elaborati nello stesso AppDomain, ma cosa succede per i fornitori di query LINQ remoto come LINQ-to-SQL, EntityFramework o altri prodotti di terze parti che offrono funzionalità LINQ? Come possono解析已编译为原始可执行代码的lambda表达式,以了解参数,lambda表达式的返回类型以及构建运行时查询以进一步处理?答案是Albero di espressioni.
Expression<TDelegate> viene compilato in una struttura di dati chiamata albero di espressioni.
Se il codice è in modalità di debug, l'espressione rappresenta il seguente:
Ora puoi vedere la differenza tra delegati normali e espressioni. Gli alberi di espressioni sono trasparenti. Puoi recuperare informazioni sui parametri, il tipo di ritorno e l'espressione principale dell'espressione, come segue:
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20; Console.WriteLine("Espressione: {0}", isTeenAgerExpr); Console.WriteLine("Tipo di espressione: {0}", isTeenAgerExpr.NodeType); var parameters = isTeenAgerExpr.Parameters; foreach (var param in parameters) { Console.WriteLine("Nome del parametro: {0}", param.Name); Console.WriteLine("Tipo di parametro: {0}", param.Type.Name); } var bodyExpr = isTeenAgerExpr.Body as BinaryExpression; Console.WriteLine("Lato sinistro dell'espressione: {0}", bodyExpr.Left); Console.WriteLine("Tipo di espressione binaria: {0}", bodyExpr.NodeType); Console.WriteLine("Lato destro dell'espressione: {0}", bodyExpr.Right); Console.WriteLine("Tipo di ritorno: {0}", isTeenAgerExpr.ReturnType);
Expression: s => ((s.Age > 12) AndAlso (s.Age < 20)) 表达式类型: Lambda 参数名称: s 参数类型: Student 表达式主体左侧: (s.Age > 12) 二进制表达式类型: AndAlso 表达式主体右侧: (s.Age < 20) 返回类型: System.Boolean
不在同一应用程序域中执行针对LINQ-to-SQL或Entity Framework的LINQ查询。例如,对于Entity Framework的以下LINQ查询永远不会在程序内部实际执行:
var query = from s in dbContext.Students where s.Age >= 18 select s;
首先将其转换为SQL语句,然后在数据库服务器上执行。
在查询表达式中找到的代码必须转换为SQL查询,该查询可以作为字符串发送到另一个进程。对于LINQ-to-SQL或Entity Framework,该过程恰好是SQL Server数据库。将数据结构(如表达式树)转换为SQL显然比将原始IL或可执行代码转换为SQL容易得多,因为正如您所看到的,从表达式中检索信息很容易。
创建表达式树的目的在于将诸如查询表达式之类的代码转换为可以传递给其他进程并在该处执行的字符串。
可查询的静态类包括接受Expression类型的谓词参数的扩展方法。将谓词表达式转换为表达式树,然后将其作为数据结构传递到远程LINQ提供程序,以便提供程序可以从表达式树构建适当的查询并执行查询。