Árboles de expresiones

Actualización: noviembre 2007

Los árboles de expresiones representan el código de nivel del lenguaje en forma de datos. Los datos se almacenan en una estructura con forma de árbol. Cada nodo del árbol de expresión representa una expresión, por ejemplo, una llamada al método o una operación binaria, como x < y.

En la ilustración siguiente se muestra un ejemplo de una expresión y su representación en forma de un árbol de expresión. Las diferentes partes de la expresión tienen un color distinto para hacerlas coincidir con el nodo correspondiente del árbol de expresión. También se muestran los diferentes tipos de los nodos del árbol de expresión.

Diagrama de árbol de expresiones

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(ByVal 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

MsgBox(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
*/

Generar árboles de expresiones

El espacio de nombres System.Linq.Expressions proporciona una API para la compilación manual de árboles de expresiones. La clase Expression contiene métodos de generador estáticos que crean nodos del árbol de expresión de tipos específicos, por ejemplo, un objeto ParameterExpression, que representa una expresión de parámetro con nombre, o un objeto, MethodCallExpression, que representa una llamada a un método. ParameterExpression, MethodCallExpression y los demás tipos de árboles de expresiones 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.

El compilador también puede generar un árbol de expresión. Un árbol de expresión generado por el compilador siempre tiene como raíz un nodo de tipo Expression<TDelegate>; es decir, su nodo raíz representa una expresión lambda.

En el ejemplo de código siguiente se muestran dos mecanismos para crear 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})

' Let the compiler generate the expression tree for
' the lambda expression num => num < 5.
Dim lambda2 As Expression(Of Func(Of Integer, Boolean)) = Function(ByVal num) num < 5

// 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 });

// Let the compiler generate the expression tree for
// the lambda expression num => num < 5.
Expression<Func<int, bool>> lambda2 = num => num < 5;

Inmutabilidad de los árboles de expresiones

Los árboles de expresiones son inmutables. Esto significa que si desea modificar un árbol de expresión, debe construir un nuevo árbol de expresión; para ello, deberá copiar el árbol existente y modificarlo. 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: Implementar un visitante de árbol de expresión y Cómo: Modificar árboles de expresiones.

Expresiones lambda

Cuando una expresión lambda está asignada a una variable de tipo Expression<TDelegate>, el compilador emite un árbol de expresión que representa la expresión lambda. Por ejemplo, algunos métodos de operador de consulta estándar que se definen en la clase Queryable tienen parámetros de tipo Expression<TDelegate>. Al llamar a estos métodos, puede pasar una expresión lambda y el compilador generará un árbol de expresión.

El tipo Expression<TDelegate> proporciona el método Compile, que compila el código representado por el árbol de expresión en un delegado ejecutable. Este código ejecutable es equivalente al código ejecutable que se habría generado si originalmente la expresión lambda hubiera estado asignada a un tipo de delegado.

Nota:

Sólo los árboles de expresiones que representan funciones, concretamente Expression<TDelegate> y su tipo primario LambdaExpression, se pueden compilar en código ejecutable. Para ejecutar otros tipos de árboles de expresiones, debe incluirlos primero en un nodo LambdaExpression. Puede obtener un objeto de tipo LambdaExpression llamando al método Lambda y pasando el árbol de expresión como argumento.

Vea también

Tareas

Cómo: Ejecutar árboles de expresiones

Cómo: Modificar árboles de expresiones

Cómo: Implementar un visitante de árbol de expresión

Conceptos

Árboles de expresión en LINQ

Expresiones lambda

Referencia

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

System.Linq.Expressions