Strutture ad albero dell'espressione (C# e Visual Basic)

Gli alberi delle espressioni rappresentano codice in una struttura dei dati simile a un albero, dove ogni nodo è un'espressione, ad esempio una chiamata al metodo o un'operazione binaria come x < y.

È possibile compilare ed eseguire codice rappresentato dagli alberi delle espressioni. In questo modo è possibile modificare dinamicamente codice eseguibile, eseguire query LINQ in vari database e creare query dinamiche. Per altre informazioni sulle strutture ad albero in LINQ, vedere Procedura: utilizzare strutture ad albero dell'espressione per la compilazione di query dinamiche (C# e Visual Basic).

Le strutture ad albero sono anche usate in Dynamic Language Runtime (DLR) per fornire interoperabilità tra linguaggi dinamici e .NET Framework e per consentire ai writer dei compilatori di creare alberi delle espressioni invece di codice MSIL (Microsoft Intermediate Language). Per altre informazioni su DRL, vedere Cenni preliminari su Dynamic Language Runtime.

È possibile creare un albero delle espressioni tramite il compilatore di C# o di Visual Basic in base a un'espressione lambda anonima o creare tali alberi di espressioni manualmente tramite il nome spazio System.Linq.Expressions.

Creazione di alberi di espressioni da espressioni lambda

Quando un'espressione lambda viene assegnata a una variabile di tipo Expression, il compilatore genera codice per compilare un albero delle espressioni che rappresenta l'espressione lambda.

I compilatori di C# e Visual Basic possono generare alberi delle espressioni solo da espressioni lambda (o lambda a riga singola). Non possono analizzare espressioni lambda dell'istruzione (o le espressioni lambda a più righe). Per informazioni sulle espressioni lambda in C#, vedere Espressioni lambda (Guida per programmatori C#); per le espressioni lambda in Visual Basic, vedere Espressioni lambda (Visual Basic).

Gli esempi di codice seguenti illustrano in che modo i compilatori di C# e Visual Basic creano un albero delle espressioni che rappresenta l'espressione lambda num => num < 5 (C#) o Function(num) num < 5 (Visual Basic).

Dim lambda As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5
Expression<Func<int, bool>> lambda = num => num < 5;

Creazione di alberi delle espressioni tramite l'API

Per creare alberi delle espressioni tramite l'API, usare la classe Expression. Questa classe contiene metodi factory statici che creano nodi degli alberi delle espressioni di tipi specifici, ad esempio ParameterExpression, che rappresenta una variabile o un parametro, o MethodCallExpression, che rappresenta una chiamata al metodo. Anche ParameterExpression, MethodCallExpression e gli altri tipi specifici delle espressioni sono definiti nello spazio dei nomi System.Linq.Expressions. Questi tipi derivano dal tipo astratto Expression.

L'esempio di codice seguente dimostra come creare un albero delle espressioni che rappresenta l'espressione lambda num => num < 5 (C#) o Function(num) num < 5 (Visual Basic).

' Import the following namespace to your project: System.Linq.Expressions 

' Manually build the expression tree for the lambda expression num => num < 5. 
Dim numParam As ParameterExpression = Expression.Parameter(GetType(Integer), "num")
Dim five As ConstantExpression = Expression.Constant(5, GetType(Integer))
Dim numLessThanFive As BinaryExpression = Expression.LessThan(numParam, five)
Dim lambda1 As Expression(Of Func(Of Integer, Boolean)) =
  Expression.Lambda(Of Func(Of Integer, Boolean))(
        numLessThanFive,
        New ParameterExpression() {numParam})
            // Add the following using directive to your code file: 
            // using System.Linq.Expressions; 

            // Manually build the expression tree for  
            // the lambda expression num => num < 5.
            ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
            ConstantExpression five = Expression.Constant(5, typeof(int));
            BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
            Expression<Func<int, bool>> lambda1 =
                Expression.Lambda<Func<int, bool>>(
                    numLessThanFive,
                    new ParameterExpression[] { numParam });

In.NET Framework 4 l'API degli alberi delle espressioni supporta anche assegnazioni ed espressioni del flusso di controllo quali cicli, blocchi condizionali e blocchi try-catch. Tramite l'API è possibile creare alberi delle espressioni più complessi rispetto a quelli che è possibile creare da espressioni lambda tramite i compilatori di C# e Visual Basic. L'esempio seguente illustra come creare un albero delle espressioni che calcola il fattoriale di un numero.

' Creating a parameter expression. 
Dim value As ParameterExpression =
    Expression.Parameter(GetType(Integer), "value")

' Creating an expression to hold a local variable.  
Dim result As ParameterExpression =
    Expression.Parameter(GetType(Integer), "result")

' Creating a label to jump to from a loop. 
Dim label As LabelTarget = Expression.Label(GetType(Integer))

' Creating a method body. 
Dim block As BlockExpression = Expression.Block(
    New ParameterExpression() {result},
    Expression.Assign(result, Expression.Constant(1)),
    Expression.Loop(
        Expression.IfThenElse(
            Expression.GreaterThan(value, Expression.Constant(1)),
            Expression.MultiplyAssign(result,
                Expression.PostDecrementAssign(value)),
            Expression.Break(label, result)
        ),
        label
    )
)

' Compile an expression tree and return a delegate. 
Dim factorial As Integer =
    Expression.Lambda(Of Func(Of Integer, Integer))(block, value).Compile()(5)

Console.WriteLine(factorial)
' Prints 120.
// Creating a parameter expression.
ParameterExpression value = Expression.Parameter(typeof(int), "value");

// Creating an expression to hold a local variable. 
ParameterExpression result = Expression.Parameter(typeof(int), "result");

// Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int));

// Creating a method body.
BlockExpression block = Expression.Block(
    // Adding a local variable. 
    new[] { result },
    // Assigning a constant to a local variable: result = 1
    Expression.Assign(result, Expression.Constant(1)),
    // Adding a loop.
        Expression.Loop(
    // Adding a conditional block into the loop.
           Expression.IfThenElse(
    // Condition: value > 1
               Expression.GreaterThan(value, Expression.Constant(1)),
    // If true: result *= value --
               Expression.MultiplyAssign(result,
                   Expression.PostDecrementAssign(value)),
    // If false, exit the loop and go to the label.
               Expression.Break(label, result)
           ),
    // Label to jump to.
       label
    )
);

// Compile and execute an expression tree. 
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);

Console.WriteLine(factorial);
// Prints 120.

Per altre informazioni, vedere l'articolo relativo alla generazione di metodi dinamici con alberi delle espressioni in Visual Studio 2010.

Analisi degli alberi delle espressioni

L'esempio di codice seguente illustra come scomporre l'albero delle espressioni che rappresenta l'espressione lambda num => num < 5 (C#) o Function(num) num < 5 (Visual Basic) nei suoi vari componenti.

        ' Import the following namespace to your project: System.Linq.Expressions 

        ' Create an expression tree. 
        Dim exprTree As Expression(Of Func(Of Integer, Boolean)) = Function(num) num < 5

        ' Decompose the expression tree. 
        Dim param As ParameterExpression = exprTree.Parameters(0)
        Dim operation As BinaryExpression = exprTree.Body
        Dim left As ParameterExpression = operation.Left
        Dim right As ConstantExpression = operation.Right

        Console.WriteLine(String.Format("Decomposed expression: {0} => {1} {2} {3}",
                          param.Name, left.Name, operation.NodeType, right.Value))

        ' This code produces the following output: 
        ' 
        ' Decomposed expression: num => num LessThan 5
// Add the following using directive to your code file: 
// using System.Linq.Expressions; 

// Create an expression tree.
Expression<Func<int, bool>> exprTree = num => num < 5;

// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;

Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}",
                  param.Name, left.Name, operation.NodeType, right.Value);

// This code produces the following output: 

// Decomposed expression: num => num LessThan 5            

Non modificabilità degli alberi delle espressioni

Gli alberi delle espressioni devono essere non modificabili. Ciò significa che per modificare un albero delle espressioni è necessario costruirne uno nuovo copiando quello esistente e sostituendone i nodi. È possibile usare un visitatore dell'albero delle espressioni per attraversare l'albero delle espressioni esistente. Per altre informazioni, vedere Procedura: modificare strutture ad albero dell'espressione (C# e Visual Basic).

Compilazione degli alberi delle espressioni

Il tipo Expression fornisce il metodo Compile che compila il codice rappresentato da un albero delle espressioni in un delegato eseguibile.

L'esempio di codice seguente illustra come compilare un albero delle espressioni ed eseguire il codice risultante.

' Creating an expression tree. 
Dim expr As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5

' Compiling the expression tree into a delegate. 
Dim result As Func(Of Integer, Boolean) = expr.Compile()

' Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4))

' Prints True. 

' You can also use simplified syntax 
' to compile and run an expression tree. 
' The following line can replace two previous statements.
Console.WriteLine(expr.Compile()(4))

' Also prints True.
// Creating an expression tree.
Expression<Func<int, bool>> expr = num => num < 5;

// Compiling the expression tree into a delegate.
Func<int, bool> result = expr.Compile();

// Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4));

// Prints True. 

// You can also use simplified syntax 
// to compile and run an expression tree. 
// The following line can replace two previous statements.
Console.WriteLine(expr.Compile()(4));

// Also prints True.

Per altre informazioni, vedere Procedura: eseguire alberi delle espressioni (C# e Visual Basic).

Vedere anche

Attività

Procedura: eseguire alberi delle espressioni (C# e Visual Basic)

Procedura: modificare strutture ad albero dell'espressione (C# e Visual Basic)

Riferimenti

Espressioni lambda (Guida per programmatori C#)

System.Linq.Expressions

Concetti

Cenni preliminari su Dynamic Language Runtime

Espressioni lambda (Visual Basic)

Altre risorse

Nozioni fondamentali dell'albero delle espressioni

Generazione di metodi dinamici con alberi delle espressioni in Visual Studio 2010

Nozioni di base sulla programmazione