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


Деревья выражений (Visual Basic)

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

Вы можете скомпилировать и запустить код, представленный деревьями выражений. Это позволяет динамически изменять исполняемый код, выполнять запросы LINQ в различных базах данных и создавать динамические запросы. Дополнительные сведения о деревах выражений в LINQ см. в статье "Практическое руководство. Использование деревьев выражений для создания динамических запросов (Visual Basic)".

Деревья выражений также используются в динамической среде выполнения языка (DLR) для обеспечения взаимодействия между динамическими языками и платформой .NET Framework, а также для того, чтобы компиляторы могли генерировать деревья выражений вместо общего промежуточного языка (CIL). Дополнительные сведения о DLR см. в разделе "Обзор динамической языковой среды выполнения".

Вы можете поручить компилятору C# или Visual Basic создать для вас дерево выражений на основе анонимного лямбда-выражения или создать деревья выражений вручную с помощью пространства имен System.Linq.Expressions.

Создание деревьев выражений из лямбда-выражений

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

Компилятор Visual Basic может создавать деревья выражений только из лямбда-выражений (или однострочных лямбда-кодов). Не удается синтаксически проанализировать лямбда-выражения (или многострочные лямбда-выражения). Дополнительные сведения о лямбда-выражениях в Visual Basic см. в лямбда-выражениях.

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

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

Создание деревьев выражений с помощью API

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

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

' 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})

В .NET Framework 4 или более поздних версиях API деревьев выражений также поддерживает назначения и операторы управления потоком, такие как циклы, условные блоки и try-catch блоки. С помощью API можно создавать деревья выражений, которые являются более сложными, чем те, которые можно создать из лямбда-выражений компилятором Visual Basic. В следующем примере показано, как создать дерево выражений, вычисляющее факториал числа.

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

Дополнительные сведения см. в статье "Создание динамических методов с помощью деревьев выражений" в Visual Studio 2010, которое также относится к более поздним версиям Visual Studio.

Анализ деревьев выражений

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

' 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

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

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

Компиляция деревьев выражений

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

В следующем примере кода показано, как скомпилировать дерево выражений и запустить полученный код.

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

Дополнительные сведения см. в статье "Практическое руководство. Выполнение деревьев выражений (Visual Basic)".

См. также