Árboles de expresión (C# y Visual Basic)

Los árboles de expresión representan el código en una estructura de datos similar a un árbol, donde cada nodo es una expresión; por ejemplo, una llamada a un método o una operación binaria como x < y.

El código representado en árboles de expresión se puede compilar y ejecutar.Esto permite realizar cambios dinámicos en código ejecutable, ejecutar consultas LINQ en varias bases de datos y crear consultas dinámicas.Para obtener más información sobre los árboles de expresión en LINQ, vea Cómo: Usar árboles de expresión para crear consultas dinámicas (C# y Visual Basic) y Tutorial: Crear un proveedor LINQ IQueryable.

Los árboles de expresión también se utilizan en Dynamic Language Runtime (DLR) para proporcionar interoperabilidad entre los lenguajes dinámicos y .NET Framework y para permitir que los programadores de compiladores emitan árboles de expresión en lugar de Lenguaje Intermedio de Microsoft (MSIL).Para obtener más información acerca de DLR, vea Información general acerca de Dynamic Language Runtime.

El compilador de C# o Visual Basic puede crear árboles de expresión basándose en una expresión lambda anónima o se pueden crear árboles de expresión manualmente mediante el espacio de nombres System.Linq.Expressions.

Crear árboles de expresión a partir de expresiones lambda

Cuando una expresión lambda está asignada a una variable de tipo Expression<TDelegate>, el compilador emite un código para compilar un árbol de expresión que representa la expresión lambda.

Los compiladores de C# y Visual Basic solo pueden generar árboles de expresión a partir de lambdas de expresión (o lambdas de una sola línea).No pueden analizar lambdas de instrucción (o lambdas de varias líneas).Para obtener más información sobre las expresiones lambda en C#, vea Expresiones lambda (Guía de programación de C#); para Visual Basic, vea Lambda (expresiones) (Visual Basic).

En los ejemplos de código siguientes, se muestra cómo los compiladores de C# y Visual Basic crean un árbol de expresión que representa la expresión 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;

Crear árboles de expresión mediante la API

Para crear árboles de expresión mediante la API, utilice la clase Expression.Esta clase contiene métodos de generador estáticos que crean nodos de árbol de expresión de tipos específicos; por ejemplo, un objeto ParameterExpression, que representa una variable o un parámetro, o MethodCallExpression, que representa una llamada a un método.ParameterExpression, MethodCallExpression y los demás tipos específicos de la expresión se definen también en el espacio de nombres System.Linq.Expressions.Estos tipos se derivan del tipo abstracto Expression.

En el ejemplo de código siguiente, se muestra cómo crear mediante la API un árbol de expresión que representa la expresión 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 });

En .NET Framework 4 Beta 2, la API de los árboles de expresión también admite asignaciones y expresiones de flujo de control, como bucles, bloques condicionales y bloques try-catch.Con la API, se pueden crear árboles de expresión más complejos que los que se pueden crear a partir de expresiones lambda con los compiladores de Visual Basic y C#.En el siguiente ejemplo, se muestra cómo crear un árbol de expresión que calcula el factorial de un número.

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

Para obtener más información, vea Generating Dynamic Methods with Expression Trees in Visual Studio 2010.

Analizar árboles de expresión

En el ejemplo de código siguiente se muestra cómo el árbol de expresión que representa la expresión lambda num => num < 5 (C#) o Function(num) num < 5 (Visual Basic) se puede descomponer en partes.


        ' 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            

Inmutabilidad de los árboles de expresiones

Los árboles de expresión deben ser inmutables.Esto significa que, si desea modificar un árbol de expresión, debe construir un nuevo árbol de expresión copiando el existente y reemplazando algunos de sus nodos.Puede utilizar un visitante de árbol de expresión para que recorra el árbol de expresión existente.Para obtener más información, vea Cómo: Modificar árboles de expresión (C# y Visual Basic).

Compilar árboles de expresión

El tipo Expression<TDelegate> proporciona el método Compile, que compila el código representado por un árbol de expresión en un delegado ejecutable.

En el siguiente ejemplo de código, se muestra cómo compilar un árbol de expresión y ejecutar el código resultante.

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

Para obtener más información, vea Cómo: Ejecutar árboles de expresión (C# y Visual Basic).

Vea también

Tareas

Cómo: Ejecutar árboles de expresión (C# y Visual Basic)

Cómo: Modificar árboles de expresión (C# y Visual Basic)

Referencia

Expresiones lambda (Guía de programación de C#)

System.Linq.Expressions

Conceptos

Información general acerca de Dynamic Language Runtime

Lambda (expresiones) (Visual Basic)

Otros recursos

Expression Tree Basics

Generating Dynamic Methods with Expression Trees in Visual Studio 2010

Programar los conceptos