Деревья выражений (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#)
Основные понятия
Лямбда-выражения (Visual Basic)
Другие ресурсы
Generating Dynamic Methods with Expression Trees in Visual Studio 2010