Поделиться через


Деревья выражений

Обновлен: Ноябрь 2007

Деревья выражений представляют языковый код в виде данных. Данные хранятся в древовидной структуре. Каждый узел в дереве выражений представляет выражение, например вызов метода или двоичную операцию, такую как x < y.

На следующем рисунке показан пример выражения и его представление в виде дерева выражений. Разные части выражения кодируются цветом для согласования с соответствующим узлом дерева выражений. Также показаны разные типы узлов дерева выражений.

Схема дерева выражения

В следующем примере кода показано, как дерево выражений, представляющее лямбда-выражение num => num < 5 (C#) или Function(num) num < 5 (Visual Basic), может быть разложено на части.

' 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
*/

Построение деревьев выражений

Пространство имен System.Linq.Expressions предоставляет функции API (программного интерфейса приложений) для создания деревьев выражений вручную. Класс Expression содержит статические заводские методы, которые создают узлы дерева выражений особых типов, например выражение ParameterExpression, представляющее именованное параметрическое выражение или выражение MethodCallExpression, представляющее вызов метода. ParameterExpression, MethodCallExpression и другие типы дерева выражений, зависящие от выражения, также определяются в пространстве имен System.Linq.Expressions. Эти типы являются производными от абстрактного типа Expression.

Компилятор может также создать дерево выражений вместо пользователя. Начало создаваемого компилятором дерева выражений всегда находится в узле с типомExpression<TDelegate>; то есть корневой узел дерева представляет лямбда-выражение.

В следующем примере кода демонстрируются два способа создания дерева выражений, которые представляют лямбда-выражение num => num < 5 (C#) или 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;

Неизменность деревьев выражений

Деревья выражений являются неизменными. Это означает, что если требуется изменить дерево выражений, следует создать новое дерево выражений путем копирования существующего дерева, а затем изменить его. Можно использовать посетителя дерева выражений для прохода по существующему дереву выражений. Дополнительные сведения см. в разделах Практическое руководство. Реализация обхода дерева выражения и Практическое руководство. Изменение деревьев выражений.

Лямбда-выражения

Когда лямбда-выражение назначается переменной с типом Expression<TDelegate>, компилятор отражает дерево выражений, представляющее лямбда-выражение. Например, некоторые стандартные методы операторов запроса, которые определены в классе Queryable, имеют параметры с типом Expression<TDelegate>. При вызове этих методов можно передать лямбда-выражение и компилятор сгенерирует дерево выражений.

Тип Expression<TDelegate> предоставляет метод Compile, который компилирует код, представляемый деревом выражений, в исполняемый делегат. Этот исполняемый код эквивалентен исполняемому коду, который бы генерировался, если бы лямбда-выражение было назначено типу делегата первоначально.

Bb397951.alert_note(ru-ru,VS.90).gifПримечание.

Только те деревья выражений, которые представляют функции, а именно Expression<TDelegate> и его родительский тип LambdaExpression, могут быть скомпилированы в исполняемый код. Для выполнения других типов деревьев выражений необходимо сначала заключить их в оболочку узла LambdaExpression. Такое выражение LambdaExpression можно получить, вызвав метод Lambda и передав ему дерево выражений в качестве аргумента.

См. также

Задачи

Практическое руководство. Выполнение деревьев выражений

Практическое руководство. Изменение деревьев выражений

Практическое руководство. Реализация обхода дерева выражения

Основные понятия

Деревья выражений в LINQ

Лямбда-выражения

Ссылки

Лямбда-выражения (Руководство по программированию в C#)

System.Linq.Expressions