C# LanguageAlberi di espressione

introduzione

Gli alberi di espressione sono espressioni disposte in una struttura di dati simile a un albero. Ogni nodo nell'albero è una rappresentazione di un'espressione, un'espressione essendo un codice. Una rappresentazione in memoria di un'espressione Lambda sarebbe un albero di espressione, che contiene gli elementi effettivi (cioè il codice) della query, ma non il suo risultato. Gli alberi di espressione rendono la struttura di un'espressione lambda trasparente ed esplicita.

Sintassi

  • Espressione <TDelegate> name = lambdaExpression;

Parametri

Parametro Dettagli
TDelegate Il tipo di delegato da utilizzare per l'espressione
lambdaExpression L'espressione lambda (es. num => num < 5 )

Osservazioni

Introduzione agli alberi delle espressioni

Da dove veniamo

Gli alberi delle espressioni si occupano solo del consumo di "codice sorgente" in fase di esecuzione. Considerare un metodo che calcola l'imposta sulle vendite dovuta su un decimal CalculateTotalTaxDue(SalesOrder order) dell'ordine di vendita decimal CalculateTotalTaxDue(SalesOrder order) . L'utilizzo di tale metodo in un programma .NET è semplice: basta chiamarlo decimal taxDue = CalculateTotalTaxDue(order); . Cosa succede se si desidera applicarlo a tutti i risultati di una query remota (SQL, XML, un server remoto, ecc.)? Quelle fonti di query remote non possono chiamare il metodo! Tradizionalmente, dovresti invertire il flusso in tutti questi casi. Crea l'intera query, memorizzala in memoria, quindi visualizza i risultati e calcola le imposte per ogni risultato.

Come evitare i problemi di memoria e latenza di inversione del flusso

Gli alberi di espressione sono strutture di dati in un formato di un albero, in cui ogni nodo contiene un'espressione. Sono utilizzati per tradurre le istruzioni compilate (come i metodi utilizzati per filtrare i dati) in espressioni che potrebbero essere utilizzate al di fuori dell'ambiente del programma, come all'interno di una query del database.

Il problema qui è che una query remota non può accedere al nostro metodo . Potremmo evitare questo problema se, invece, abbiamo inviato le istruzioni per il metodo alla query remota. Nel nostro esempio CalculateTotalTaxDue , ciò significa che inviamo queste informazioni:

  1. Creare una variabile per memorizzare l'imposta totale
  2. Passa attraverso tutte le linee sull'ordine
  3. Per ogni riga, controlla se il prodotto è tassabile
  4. Se lo è, moltiplica la linea totale per l'aliquota fiscale applicabile e aggiungi tale importo al totale
  5. Altrimenti non fare nulla

Con queste istruzioni, la query remota può eseguire il lavoro mentre crea i dati.

Ci sono due sfide per l'implementazione di questo. Come si trasforma un metodo .NET compilato in un elenco di istruzioni e come si formattano le istruzioni in modo che possano essere utilizzate dal sistema remoto?

Senza alberi di espressione, è possibile risolvere solo il primo problema con MSIL. (MSIL è il codice simile all'assembler creato dal compilatore .NET). L'analisi di MSIL è possibile , ma non è facile. Anche quando lo si analizza correttamente, può essere difficile determinare quale fosse l'intento del programmatore originale con una particolare routine.

Gli alberi delle espressioni salvano il giorno

Gli alberi delle espressioni affrontano questi problemi esatti. Rappresentano le istruzioni del programma una struttura di dati dell'albero in cui ogni nodo rappresenta un'istruzione e contiene riferimenti a tutte le informazioni necessarie per eseguire tale istruzione. Ad esempio, un MethodCallExpression ha riferimento a 1) il MethodInfo che chiamerà, 2) un elenco di Expression che passerà a quel metodo, 3) per i metodi di istanza, l' Expression cui chiamerai il metodo. Puoi "camminare sull'albero" e applicare le istruzioni sulla tua query remota.

Creazione di alberi di espressione

Il modo più semplice per creare un albero di espressioni è con un'espressione lambda. Queste espressioni sembrano quasi le stesse dei normali metodi C #. È importante rendersi conto che questo è magia del compilatore . Quando crei per la prima volta un'espressione lambda, il compilatore verifica a cosa lo assegni. Se si tratta di un tipo di Delegate (incluso Action o Func ), il compilatore converte l'espressione lambda in un delegato. Se è una LambdaExpression (o Expression<Action<T>> o Expression<Func<T>> che sono fortemente digitate LambdaExpression ), il compilatore la trasforma in LambdaExpression . Qui è dove entra la magia. Dietro le quinte, il compilatore usa l'albero delle espressioni API per trasformare la tua espressione lambda in una LambdaExpression .

Le espressioni Lambda non possono creare ogni tipo di albero delle espressioni. In questi casi, è possibile utilizzare manualmente l'API di espressioni per creare l'albero necessario. Nell'esempio Comprendere le espressioni API , creiamo l'espressione CalculateTotalSalesTax utilizzando l'API.

NOTA: i nomi diventano un po 'confusi qui. Un'espressione lambda (due parole, in minuscolo) si riferisce al blocco di codice con un indicatore => . Rappresenta un metodo anonimo in C # e viene convertito in un Delegate o in Expression . A LambdaExpression (una parola, PascalCase) fa riferimento al tipo di nodo all'interno dell'API di espressione che rappresenta un metodo che è possibile eseguire.

Alberi di espressione e LINQ

Uno degli usi più comuni degli alberi di espressione è con LINQ e query di database. LINQ accoppia un albero di espressioni con un provider di query per applicare le istruzioni alla query remota di destinazione. Ad esempio, il provider di query LINQ to Entity Framework trasforma un albero di espressioni in SQL che viene eseguito direttamente sul database.

Mettendo insieme tutti i pezzi, puoi vedere il vero potere dietro LINQ.

  1. Scrivi una query usando un'espressione lambda: products.Where(x => x.Cost > 5)
  2. Il compilatore trasforma quell'espressione in un albero di espressioni con le istruzioni "controlla se la proprietà Cost del parametro è maggiore di cinque".
  3. Il provider di query analizza la struttura dell'espressione e produce una query SQL valida SELECT * FROM products WHERE Cost > 5
  4. L'ORM proietta tutti i risultati in POCO e ottieni un elenco di oggetti

Gli appunti

  • Gli alberi delle espressioni sono immutabili. Se si desidera modificare un albero di espressioni è necessario crearne uno nuovo, copiare quello esistente in quello nuovo (per attraversare un albero di espressioni è possibile utilizzare ExpressionVisitor ) e apportare le modifiche desiderate.

Alberi di espressione Esempi correlati