Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Un albero delle espressioni è una struttura di dati che definisce il codice. Gli alberi delle espressioni si basano sulle stesse strutture usate da un compilatore per analizzare il codice e generare l'output compilato. Mentre leggi questo articolo, noti parecchie somiglianze tra gli alberi delle espressioni e i tipi usati nelle API di Roslyn per costruire analizzatori e CodeFixes. Gli analizzatori e i codefix sono pacchetti NuGet che eseguono l'analisi statica del codice e suggeriscono potenziali correzioni per uno sviluppatore. I concetti sono simili e il risultato finale è una struttura di dati che consente l'esame del codice sorgente in modo significativo. Tuttavia, gli alberi delle espressioni si basano su un set diverso di classi e API rispetto alle API Roslyn. Ecco una riga di codice:
var sum = 1 + 2;
Se si analizza il codice precedente come albero delle espressioni, l'albero contiene diversi nodi. Il nodo più esterno è un'istruzione di dichiarazione di variabile con assegnazione (var sum = 1 + 2;
) Il nodo più esterno contiene diversi nodi figlio: una dichiarazione di variabile, un operatore di assegnazione e un'espressione che rappresenta il lato destro del segno di uguale. Tale espressione è ulteriormente suddivisa in espressioni che rappresentano l'operazione di addizione e operandi sinistro e destro dell'addizione.
Approfondiamo un po' di più le espressioni che costituiscono il lato destro del segno di uguale. L'espressione è 1 + 2
, un'espressione binaria. In particolare, si tratta di un'espressione di addizione binaria. Un'espressione di addizione binaria ha due elementi figlio, che rappresentano i nodi sinistro e destro dell'espressione di addizione. In questo caso, entrambi i nodi sono espressioni costanti: l'operando sinistro è il valore 1
e l'operando destro è il valore 2
.
Visivamente, l'intera istruzione è un albero: è possibile iniziare dal nodo radice e spostarsi in ogni nodo dell'albero per visualizzare il codice che costituisce l'istruzione:
- Istruzione di dichiarazione di variabile con assegnazione (
var sum = 1 + 2;
)- Dichiarazione implicita del tipo di variabile (
var sum
)- Parola chiave var implicita (
var
) - Dichiarazione del nome della variabile (
sum
)
- Parola chiave var implicita (
- Operatore di assegnazione (
=
) - Espressione di addizione binaria (
1 + 2
)- Operando sinistro (
1
) - Operatore di addizione (
+
) - Operando destro (
2
)
- Operando sinistro (
- Dichiarazione implicita del tipo di variabile (
L'albero precedente può sembrare complicato, ma è molto potente. Seguendo lo stesso processo, è possibile scomporre espressioni molto più complesse. Si consideri questa espressione:
var finalAnswer = this.SecretSauceFunction(
currentState.createInterimResult(), currentState.createSecondValue(1, 2),
decisionServer.considerFinalOptions("hello")) +
MoreSecretSauce('A', DateTime.Now, true);
L'espressione precedente è anche una dichiarazione di variabile con un'assegnazione. In questo caso, il lato destro dell'assegnazione è un albero molto più complicato. Non decomporrai questa espressione, ma considera quali potrebbero essere i diversi nodi. Esistono chiamate di metodo che usano l'oggetto corrente come ricevitore, uno con un ricevitore esplicito this
, uno che non lo è. Esistono chiamate di metodo che usano altri oggetti ricevitore, esistono argomenti costanti di tipi diversi. Infine, c'è un operatore di addizione binaria. A seconda del tipo di ritorno di SecretSauceFunction()
o MoreSecretSauce()
, l'operatore di addizione binaria può essere una chiamata di metodo a un operatore di addizione sovrascritto, risolvendo in una chiamata a un metodo statico dell'operatore di addizione binaria definito per una classe.
Nonostante questa complessità percepita, l'espressione precedente crea una struttura ad albero che può essere navigata facilmente come il primo esempio. Continua ad attraversare i nodi figlio per trovare i nodi fogliari nell'espressione. I nodi padre hanno riferimenti ai relativi nodi figlio e ogni nodo ha una proprietà che descrive il tipo di nodo.
La struttura di un albero delle espressioni è molto coerente. Dopo aver appreso le nozioni di base, si comprende anche il codice più complesso quando viene rappresentato come albero delle espressioni. L'eleganza nella struttura dei dati spiega in che modo il compilatore C# analizza i programmi C# più complessi e crea un output appropriato da quel codice sorgente complicato.
Una volta acquisita familiarità con la struttura degli alberi delle espressioni, si scopre che le conoscenze acquisite rapidamente consentono di lavorare con molti scenari più avanzati. C'è un'incredibile potenza negli alberi delle espressioni.
Oltre a tradurre gli algoritmi da eseguire in altri ambienti, gli alberi delle espressioni semplificano la scrittura di algoritmi che controllano il codice prima di eseguirlo. Si scrive un metodo i cui argomenti sono espressioni e quindi si esaminano tali espressioni prima di eseguire il codice. L'albero delle espressioni è una rappresentazione completa del codice: vengono visualizzati i valori di qualsiasi sottoespressione. I nomi dei metodi e delle proprietà sono visualizzati. Viene visualizzato il valore di qualsiasi espressione costante. Convertire un albero delle espressioni in un delegato eseguibile ed eseguire il codice.
Le API per alberi delle espressioni consentono di creare alberi che rappresentano quasi tutti i costrutti di codice validi. Tuttavia, per mantenere le cose il più semplici possibile, alcuni idiomi C# non possono essere creati in un albero delle espressioni. Un esempio è costituito dalle espressioni asincrone (usando le async
parole chiave e await
). Se sono necessari algoritmi asincroni, è necessario modificare direttamente gli Task
oggetti anziché basarsi sul supporto del compilatore. Un altro consiste nella creazione di cicli. In genere, questi cicli vengono creati usando for
, foreach
, while
o do
. Come illustrato più avanti in questa serie, le API per gli alberi delle espressioni supportano un'espressione a ciclo singolo, con break
espressioni e continue
che controllano la ripetizione del ciclo.
L'unica cosa che non è possibile fare è modificare un albero delle espressioni. Gli alberi delle espressioni sono strutture di dati non modificabili. Se si desidera modificare (modificare) un albero delle espressioni, è necessario creare un nuovo albero che sia una copia dell'originale, ma con le modifiche desiderate.