Looking for c# Answers? Try Ask4KnowledgeBase
Looking for c# Keywords? Try Ask4Keywords

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


Вступление

Деревья выражений - это выражения, расположенные в древовидной структуре данных. Каждый узел в дереве является представлением выражения, причем выражение является кодом. Представление Lambda с внутренней памятью было бы деревом выражений, которое содержит фактические элементы (то есть код) запроса, но не его результат. Деревья выражений делают структуру лямбда-выражения прозрачной и явной.

Синтаксис

  • Выражение <TDelegate> name = lambdaExpression;

параметры

параметр подробности
TDelegate Тип делегата, который будет использоваться для выражения
lambdaExpression Выражение лямбда (например, num => num < 5 )

замечания

Введение в деревья выражений

Откуда мы пришли

Деревья выражений - все о потреблении «исходного кода» во время выполнения. Рассмотрим метод, который рассчитывает налог с продаж, причитающийся по заказу клиента decimal CalculateTotalTaxDue(SalesOrder order) . Использование этого метода в .NET-программе легко - просто назовите его decimal taxDue = CalculateTotalTaxDue(order); , Что делать, если вы хотите применить его ко всем результатам удаленного запроса (SQL, XML, удаленный сервер и т. Д.)? Эти источники удаленного запроса не могут вызвать метод! Традиционно вам придется инвертировать поток во всех этих случаях. Сделайте весь запрос, сохраните его в памяти, затем просмотрите результаты и рассчитайте налог за каждый результат.

Как избежать ошибок инверсии потока и проблем с задержкой

Деревья выражений - это структуры данных в формате дерева, где каждый узел содержит выражение. Они используются для перевода скомпилированных инструкций (например, методов, используемых для фильтрации данных) в выражениях, которые могут использоваться вне программной среды, например внутри запроса к базе данных.

Проблема здесь в том, что удаленный запрос не может получить доступ к нашему методу . Мы могли бы избежать этой проблемы, если вместо этого мы отправили инструкции для метода в удаленный запрос. В нашем примере CalculateTotalTaxDue это означает, что мы отправляем эту информацию:

  1. Создать переменную для хранения общего налога
  2. Прокрутите все строки по порядку
  3. Для каждой строки проверьте, облагается ли продукт
  4. Если это так, умножьте общую сумму по применимой ставке налога и добавьте эту сумму к общей сумме
  5. В противном случае ничего не делать

С помощью этих инструкций удаленный запрос может выполнять работу по мере создания данных.

Для этого есть две проблемы. Как преобразовать скомпилированный метод .NET в список инструкций и как вы отформатируете инструкции таким образом, чтобы их можно было использовать удаленной системой?

Без деревьев выражений вы могли бы решить первую проблему с MSIL. (MSIL - это ассемблерный код, созданный компилятором .NET.) Разбор MSIL возможен , но это непросто. Даже если вы правильно разобрали его, может быть трудно определить, какова была цель оригинального программиста с конкретной процедурой.

Деревья выражений сохраняют день

Деревья выражений адресуют эти точные проблемы. Они представляют собой программные инструкции для структуры данных дерева, где каждый узел представляет одну инструкцию и имеет ссылки на всю информацию, необходимую для выполнения этой инструкции. Например, MethodCallExpression имеет отношение к 1) MethodInfo он собирается позвонить, 2) список Expression S будет переходить к этому методу, 3) для методов экземпляра, то Expression вы будете вызывать метод. Вы можете «ходить по дереву» и применять инструкции к вашему удаленному запросу.

Создание деревьев выражений

Самый простой способ создать дерево выражений - с помощью выражения лямбда. Эти выражения выглядят почти так же, как обычные методы C #. Важно понимать, что это магия компилятора . Когда вы сначала создаете лямбда-выражение, компилятор проверяет, к чему вы его назначили. Если это тип Delegate (включая Action или Func ), компилятор преобразует лямбда-выражение в делегат. Если это LambdaExpression (или Expression<Action<T>> или Expression<Func<T>> которое строго типизировано LambdaExpression ), компилятор преобразует его в LambdaExpression . Это - то, где волшебство начинает. За кулисами, компилятор использует API дерева выражений, чтобы преобразовать Ваше lambda выражение в LambdaExpression .

Лямбда-выражения не могут создавать каждый тип дерева выражений. В этих случаях вы можете использовать API выражений вручную, чтобы создать дерево, в котором вы нуждаетесь. В примере « Понимание примеров выражений» мы создаем выражение CalculateTotalSalesTax с использованием API.

ПРИМЕЧАНИЕ. Названия здесь немного запутывают. Лямбда-выражение (два слова, нижний регистр) относится к блоку кода с индикатором a => . Он представляет анонимный метод в C # и преобразуется в Delegate или Expression . LambdaExpression (одно слово, PascalCase) относится к типу узла в Expression API, который представляет собой метод, который вы можете выполнить.

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

Одним из наиболее распространенных применений деревьев выражений является запрос LINQ и базы данных. LINQ создает дерево выражений с поставщиком запросов для применения ваших инструкций к целевому удаленному запросу. Например, поставщик запросов LINQ to Entity Framework преобразует дерево выражений в SQL, который выполняется непосредственно из базы данных.

Объединяя все части, вы можете увидеть реальную силу LINQ.

  1. Напишите запрос, используя выражение лямбда: products.Where(x => x.Cost > 5)
  2. Компилятор преобразует это выражение в дерево выражений с инструкциями «проверьте, является ли свойство Cost параметра более пяти».
  3. Поставщик запроса анализирует дерево выражений и создает корректный SQL-запрос SELECT * FROM products WHERE Cost > 5
  4. ORM реализует все результаты в POCOs, и вы получаете список объектов назад

Заметки

  • Деревья выражений неизменяемы. Если вы хотите изменить дерево выражений, вам нужно создать новый, скопировать существующий в новый (чтобы пересечь дерево выражений, вы можете использовать ExpressionVisitor ) и внести нужные изменения.

Деревья выражений Связанные примеры