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

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

Можно компилировать и выполнять код, представленный деревьями выражений.Это обеспечивает возможность динамической модификации выполняемого кода, выполнения запросов LINQ в различных базах данных и создания динамических запросов.Дополнительные сведения о деревьях выражений в LINQ см. в разделах Практическое руководство. Использование деревьев выражений для построения динамических запросов (C# и Visual Basic) и Пошаговое руководство. Создание поставщика IQueryable LINQ.

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

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

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

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

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

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

Создание деревьев выражений с использованием API-интерфейса

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

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

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

В .NET Framework 4 API-интерфейс деревьев выражений также поддерживает назначения и выражения потока управления, такие как циклы, блоки условий и блоки try-catch.С помощью API-интерфейса можно создавать более сложные деревья выражений, чем те, которые создаются компиляторами C# и 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.
// 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.

Дополнительные сведения см. на странице Generating Dynamic Methods with Expression Trees in Visual Studio 2010.

Синтаксический анализ деревьев выражений

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

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

Деревья выражений должны быть неизменными.Это означает, что если требуется изменить дерево выражений, следует создать новое дерево выражений копированием существующего дерева, а затем заменить узлы в нем.Можно использовать посетителя дерева выражений для прохода по существующему дереву выражений.Дополнительные сведения см. в разделе Практическое руководство. Изменение деревьев выражений (C# и 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.
// 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.

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

См. также

Задачи

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

Практическое руководство. Изменение деревьев выражений (C# и Visual Basic)

Ссылки

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

System.Linq.Expressions

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

Общие сведения о среде DLR

Лямбда-выражения (Visual Basic)

Другие ресурсы

Expression Tree Basics

Generating Dynamic Methods with Expression Trees in Visual Studio 2010

Основные понятия программирования