English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Вы уже узнали о выражениях в предыдущем разделе. Теперь давайте рассмотрим деревья выражений здесь.
Как следует из названия, деревья выражений - это выражения, организованные в виде древовидной структуры данных. Каждый узел в дереве выражений - это выражение. Например, дерево выражений может использоваться для представления математической формулы x < y, где x, < и y будут представлены как выражения и организованы в древовидной структуре.
Деревья выражений - это память представления lambda-выражений. Они сохраняют фактические элементы запроса, а не результат запроса.
Деревья выражений делают структуру lambda-выражений прозрачной и явной. Вы можете взаимодействовать с данными в деревьях выражений, как с любыми другими данными.
Например, посмотрите на выражение isTeenAgerExpr:
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.age > 12 && s.age < 20;
Компилятор преобразует выражение в следующий деревьев выражений:
Пример: древовидное выражение в C#
Expression.Lambda<Func<Student, bool>>( Expression.AndAlso( Expression.GreaterThan(Expression.Property(pe, "Age"), Expression.Constant(12, typeof(int))), Expression.LessThan(Expression.Property(pe, "Age"), Expression.Constant(20, typeof(int)))), new[] { pe });
Вы также можете вручную построить дерево выражений. Давайте посмотрим, как построить дерево выражений для следующего простого выражения lambda:
Пример: делегат Func в C#:
Func<Student, bool> isAdult = s => s.age >= 18;
Этот тип делегата Func будет рассматриваться как следующий метод:
C#:
public bool function(Student s) { return s.Age > 18; }
Чтобы создать дерево выражений, сначала создайте выражение параметра, где Student является типом параметра, а 's' является именем параметра, как показано ниже:
Шаг 1: Создание выражения параметра в C#
ParameterExpression pe = Expression.Parameter(typeof(Student), "s");
Теперь создайте выражение свойств s.Age с помощью Expression.Property(), где s является параметром, а Age является именем свойства Student.Expressionявляется абстрактным классом, который содержит статические помощники для ручного создания дерева выражений.)
Шаг 2: Создание выражения свойств в C#
MemberExpression me = Expression.Property(pe, "Age");
Теперь создайте константное выражение для 18:
Шаг 3: Создание константного выражения в C#
ConstantExpression constant = Expression.Constant(18, typeof(int));
До сих пор мы построили дерево выражений для члена выражения s.Age (выражение члена) и константы 18 (константное выражение). Теперь нам нужно проверить, больше ли член выражения, чем константное выражение. Для этого используйте метод Expression.GreaterThanOrEqual() и передайте член выражения и константное выражение в качестве параметров:
Шаг 4: Создание двоичного выражения в C#
BinaryExpression body = Expression.GreaterThanOrEqual(me, constant);
Таким образом, мы построили дерево выражений для тела выражения lambda s.Age >= 18. Теперь нам нужно соединить выражение параметра и выражение тела. Используйте Expression.Lambda
Шаг 5: Создание выражения Lambda в C#
var isAdultExprTree = Expression.Lambda<Func<Student, bool>>(body, new[] { pe });
Таким образом, вы можете создать дерево выражений для простого Func делегата с lambda-выражением.
Пример: древовидное выражение в C#
ParameterExpression pe = Expression.Parameter(typeof(Student), "s"); MemberExpression me = Expression.Property(pe, "Age"); ConstantExpression constant = Expression.Constant(18, typeof(int)); BinaryExpression body = Expression.GreaterThanOrEqual(me, constant); var ExpressionTree = Expression.Lambda<Func<Student, bool>>(body, new[] { pe }); Console.WriteLine("Выражение дерева: {0}", ExpressionTree); Console.WriteLine("Тело выражения дерева: {0}", ExpressionTree.Body); Console.WriteLine("Количество параметров в дереве выражений: {0}", ExpressionTree.Parameters.Count); Console.WriteLine("Параметры выражения дерева: {0}", ExpressionTree.Parameters[0]);
Dim pe As ParameterExpression = Expression.Parameter(GetType(Student), "s") Dim mexp As MemberExpression = Expression.Property(pe, "Age") Dim constant As ConstantExpression = Expression.Constant(18, GetType(Integer)) Dim body As BinaryExpression = Expression.GreaterThanOrEqual(mexp, constant) Dim ExpressionTree As Expression(Of Func(Of Student, Boolean)) = Expression.Lambda(Of Func(Of Student, Boolean))(body, New ParameterExpression() { pe }) Console.WriteLine("Дерево выражений: {0}", ExpressionTree) Console.WriteLine("Тело дерева выражений: {0}", ExpressionTree.Body) Console.WriteLine("Количество параметров в дереве выражений: {0}", ExpressionTree.Parameters.Count) Console.WriteLine("Параметр дерева выражений: {0}", ExpressionTree.Parameters(0))
Дерево выражений: s => (s.Age >= 18) Тело дерева выражений: (s.Age >= 18) Количество параметров в дереве выражений: 1 Параметр дерева выражений: s
Следующий рисунок поясняет整个过程 создания дерева выражений:
В предыдущем разделе мы уже видели, что назначается лямбда-выражениюFunc<T>компилируется в исполнимый код и назначается лямбда-выражениюExpression<TDelegate>типов, компилируемых в дерево выражений.
Исполняемый код выполняется в той же приложенческой домене для обработки集合 в памяти. Статический класс enumerable содержит методы для реализацииIEnumerable <T>расширяемые методы памяти для интерфейсов, таких как List <T>, Dictionary <T> и т.д. Расширяемые методы класса Enumerable принимаютFuncпараметр предиката типа делегата. Например,WhereРасширяемый метод принимаетПредикат Func <TSource, bool>. Затем его компилируют в IL (промежуточный язык) для обработки集合 в той же AppDomain.
На следующем рисунке показано, как расширяемый метод Where класса Enumerable включает делегат Func в качестве параметра:
FuncДелегат является исходным исполнимым кодом, поэтому, если отладить код, то можно обнаружить, что:FuncДелегат будет представлен в виде неопределенного кода. Вы не можете видеть его параметры, тип возвращаемого значения и тело:
FuncДелегаты используются для集合ов в памяти, так как они будут обрабатываться в одном AppDomain, но что делать с удаленными LINQ-запросами, такими как LINQ-to-SQL, EntityFramework или другими третьими сторонними продуктами, предоставляющими функции LINQ? Как они будут анализировать лямбда-выражения, скомпилированные в исходный выполняемый код, чтобы понять параметры, тип возвращаемого значения лямбда-выражения и построить runtime-запрос для дальнейшей обработки? Ответ:Деревовидное выражение.
Expression<TDelegate> компилируется в данные структуры, называемые древовидным выражением.
Если вы отладываете код, выражение будет представлено следующим образом:
Теперь вы можете видеть различие между обычными делегатами и выражениями. Деревовидное выражение прозрачно. Вы можете извлечь из выражения параметры, тип возвращаемого значения и информацию о теле выражения, как показано ниже:
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20; Console.WriteLine("Expression: {0}", isTeenAgerExpr); Console.WriteLine("Тип выражения: {0}", isTeenAgerExpr.NodeType); var parameters = isTeenAgerExpr.Parameters; foreach (var param in parameters) { Console.WriteLine("Имя параметра: {0}", param.Name); Console.WriteLine("Тип параметра: {0}", param.Type.Name); } var bodyExpr = isTeenAgerExpr.Body as BinaryExpression; Console.WriteLine("Левый край выражения: {0}", bodyExpr.Left); Console.WriteLine("Тип бинарного выражения: {0}", bodyExpr.NodeType); Console.WriteLine("Правый край выражения: {0}", bodyExpr.Right); Console.WriteLine("Возврат типа: {0}", isTeenAgerExpr.ReturnType);
Выражение: s => ((s.Age > 12) AndAlso (s.Age < 20)) Тип выражения: Lambda Имя параметра: s Тип параметра: Student Левая сторона выражения: (s.Age > 12) Тип бинарного выражения: AndAlso Правая сторона выражения: (s.Age < 20) Возврат типа: System.Boolean
Запросы LINQ к LINQ-to-SQL или Entity Framework не выполняются в одной и той же области приложения. Например, следующий запрос LINQ к Entity Framework никогда не выполняется внутри программы:
var query = from s in dbContext.Students where s.Age >= 18 select s;
Сначала преобразуйте его в команду SQL, а затем выполните на сервере базы данных.
Код, найденный в выражениях запроса, необходимо преобразовать в запрос SQL, который можно отправить в другой процесс в виде строки. Для LINQ-to-SQL или Entity Framework этот процесс именно SQL Server база данных. Преобразование структуры данных (например, дерева выражений) в SQL значительно проще, чем преобразование исходного IL или исполняемого кода в SQL, потому что, как вы видите, извлечение информации из выражений легко.
Цель создания дерева выражений - преобразовать код, такой как запрос выражения, в строку, которую можно передать другому процессу и выполнить здесь.
Классы, подлежащие запросу, включают расширяемые методы, принимающие параметры предиката типа Expression. Преобразуйте выражение предиката в дерево выражений и передайте его в качестве структуры данных в удаленный поставщик LINQ, чтобы поставщик мог построить соответствующий запрос и выполнить его.