Arborescences d'expression (C# et Visual Basic)

Les arborescences d'expression représentent du code dans une structure de données arborescente, où chaque nœud correspond à une expression, par exemple, un appel de méthode ou une opération binaire telle que x < y.

Vous pouvez compiler et exécuter le code représenté par les arborescences d'expression. Cela permet de modifier dynamiquement du code exécutable, d'exécuter des requêtes LINQ dans différentes bases de données et de créer des requêtes dynamiques. Pour plus d'informations sur les arborescences d'expression dans LINQ, consultez Comment : utiliser des arborescences d'expression pour générer des requêtes dynamiques (C# et Visual Basic) et Procédure pas à pas : création d'un fournisseur LINQ IQueryable.

Les arborescences d'expression sont également utilisées dans le Dynamic Language Runtime (DLR) pour permettre l'interopérabilité entre les langages dynamiques et .NET Framework, et permettre aux writers de compilateur d'émettre des arborescences d'expression à la place de Microsoft intermediate language (MSIL). Pour plus d'informations sur le DLR, consultez Vue d'ensemble du Dynamic Language Runtime.

Vous pouvez faire en sorte que le compilateur C# ou Visual Basic crée une arborescence d'expression basée sur une expression lambda anonyme, ou vous pouvez créer manuellement des arborescences d'expression à l'aide de l'espace de noms System.Linq.Expressions.

Création d'arborescences d'expression à partir d'expressions lambda

Lorsqu'une expression lambda est assignée à une variable de type Expression<TDelegate>, le compilateur émet du code pour générer une arborescence d'expression qui représente l'expression lambda.

Les compilateurs C# et Visual Basic peuvent générer des arborescences d'expression uniquement à partir de lambda-expressions (ou de lambdas sur une seule ligne). Ils ne peuvent pas analyser de lambda-instructions (ou de lambdas multilignes). Pour plus d'informations sur les expressions lambda en C#, consultez Expressions lambda (Guide de programmation C#) ; Pour Visual Basic, consultez Expressions lambda (Visual Basic).

Les exemples de code suivants montrent comment procéder pour que les compilateurs C# et Visual Basic créent une arborescence d'expression qui représente l'expression lambda num => num < 5 (C#) ou 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;

Création d'arborescences d'expression à l'aide de l'API

Pour créer des arborescences d'expression à l'aide de l'API, utilisez la classe Expression. Cette classe contient des méthodes de fabrique statiques qui créent des nœuds d'arborescence d'expression de types spécifiques, par exemple ParameterExpression, qui représente une variable ou un paramètre, ou MethodCallExpression qui représente un appel de méthode. ParameterExpression, MethodCallExpression et les autres types spécifiques aux expressions sont également définis dans l'espace de noms System.Linq.Expressions. Ces types dérivent du type abstrait Expression.

L'exemple de code suivant montre comment créer une arborescence d'expression qui représente l'expression lambda num => num < 5 (C#) ou 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 });

Dans .NET Framework 4, l'API d'arborescences d'expression prend également en charge les assignations et les expressions de flux de contrôle telles que les boucles, les blocs conditionnels et les blocs try-catch. L'API vous permet de créer des arborescences d'expression plus complexes que celles qui peuvent être créées à partir d'expressions lambda par les compilateurs C# et Visual Basic. L'exemple suivant montre comment créer une arborescence d'expression qui calcule la factorielle d'un nombre.

' 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.

Pour plus d'informations, consultez Génération de méthodes dynamiques avec les arborescences d'expression dans Visual Studio 2010 (page éventuellement en anglais).

Analyse des arborescences d'expression

L'exemple de code suivant montre comment l'arborescence d'expression qui représente l'expression lambda num => num < 5 (C#) ou Function(num) num < 5 (Visual Basic) peut être décomposée en parties.


        ' 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            

Immuabilité des arborescences d'expression

Les arborescences d'expression doivent être immuables. Ainsi, si vous souhaitez modifier une arborescence d'expression, vous devez construire une nouvelle arborescence d'expression en copiant l'arborescence existante, puis en remplaçant les nœuds qui s'y trouvent. Vous pouvez utiliser un visiteur de l'arborescence d'expression pour parcourir l'arborescence d'expression existante. Pour plus d'informations, consultez Comment : modifier des arborescences d'expression (C# et Visual Basic).

Compilation des arborescences d'expression

Le type Expression<TDelegate> fournit la méthode Compile qui compile le code représenté par une arborescence d'expression en un délégué exécutable.

L'exemple de code suivant montre comment compiler une arborescence d'expression et exécuter le code résultant.

' 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.

Pour plus d'informations, consultez Comment : exécuter des arborescences d'expression (C# et Visual Basic).

Voir aussi

Tâches

Comment : exécuter des arborescences d'expression (C# et Visual Basic)

Comment : modifier des arborescences d'expression (C# et Visual Basic)

Référence

Expressions lambda (Guide de programmation C#)

System.Linq.Expressions

Concepts

Vue d'ensemble du Dynamic Language Runtime

Expressions lambda (Visual Basic)

Autres ressources

Concepts de base des arborescences de l'expression (page éventuellement en anglais)

Génération de méthodes dynamiques avec les arborescences d'expression dans Visual Studio 2010 (page éventuellement en anglais)

Concepts de programmation