Книга: C# 4.0: полное руководство

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

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

Еще одним средством, связанным с LINQ, является дерево выражений, которое представляет лямбда-выражение в виде данных. Это означает, что само лямбда-выражение нельзя выполнить, но можно преобразовать в исполняемую форму. Деревья выражений инкапсулируются в классе System.Linq.Expressions.Expression<TDelegate>. Они оказываются пригодными в тех случаях, когда запрос выполняется вне программы, например средствами SQL в базе данных. Если запрос представлен в виде данных, то его можно преобразовать в формат, понятный для базы данных. Этот процесс выполняется, например, средствами LINQ to SQL в интегрированной среде разработки Visual Studio. Таким образом, деревья выражений способствуют поддержке в C# различных баз данных.

Для получения исполняемой формы дерева выражений достаточно вызвать метод Compile(), определенный в классе Expression. Этот метод возвращает ссылку, которая может быть присвоена делегату для последующего выполнения. А тип делегата может быть объявлен собственным или же одним из предопределенных типов делегата Func в пространстве имен System. Две формы делегата Func уже упоминались ранее при рассмотрении методов запроса, но существует и другие его формы.

Деревьям выражений присуще следующее существенное ограничение: они могут представлять только одиночные лямбда-выражения. С их помощью нельзя представить блочные лямбда-выражения.

Ниже приведен пример программы, демонстрирующий конкретное применение дерева выражений. В этой программе сначала создается дерево выражений, данные которого представляют метод, определяющий, является ли одно целое число множителем другого. Затем это дерево выражений компилируется в исполняемый код. И наконец, в этой программе демонстрируется выполнение скомпилированного кода.

// Пример простого дерева выражений.
using System;
using System.Linq;
using System.Linq.Expressions;
class SimpleExpTree {
  static void Main() {
    // Представить лямбда-выражение в виде данных.
    Expression<Func<int, int, bool>>
      IsFactorExp = (n, d) => (d != 0) ? (n % d) == 0 : false;
    // Скомпилировать данные выражения в исполняемый код.
    Func<int, int, bool>
      IsFactor = IsFactorExp.Compile();
    // Выполнить выражение,
    if(IsFactor(10, 5))
      Console.WriteLine("Число 5 является множителем 10.");
    if(!IsFactor(10, 7))
      Console.WriteLine("Число 7 не является множителем 10.");
    Console.WriteLine();
  }
}

Вот к какому результату приводит выполнение этой программы.

Число 5 является множителем 10.
Число 7 не является множителем 10.

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

Expression<Func<int, int, bool»
IsFactorExp = (n, d) => (d != 0) ? (n % d) == 0 : false;

В этом операторе конструируется представление лямбда-выражения в оперативной памяти. Как пояснялось выше, это представление доступно по ссылке, присваиваемой делегату IsFactorExp. А в следующем операторе данные выражения преобразуются в исполняемый код.

Func<int, int, bool>
  IsFactor = IsFactorExp.Compile();

После выполнения этого оператора делегат IsFactorExp может быть вызван, чтобы определить, является ли одно целое число множителем другого.

Обратите также внимание на то, что <Func<int, int, bool> обозначает тип делегата. В этой форме делегата Funс указываются два параметра типа int и возвращаемый тип bool. В рассматриваемой здесь программе использована именно эта форма делегата Funс, совместимая с лямбда-выражениями, поскольку для выражения требуются два параметра. Для других лямбда-выражений могут подойти иные формы делегата Funс в зависимости от количества требуемых параметров. Вообще говоря, конкретная форма делегата Funс должна удовлетворять требованиям лямбда-выражения.

Оглавление книги


Генерация: 1.418. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз