Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 22

¨  В первом варианте примера мы намеренно не пользуемся λ-выражением с целью показать, как в действительности работает внутренняя кухня LINQ.

¨  Во втором варианте, наоборот, включаются все средства минимизации выражений, присущие LINQ.

Как было сказано ранее, компилятор заменяет λ-выражение кодом, который вызывает функцию. Эта функция соответствует делегатному типу Func<>. Код первого варианта выполняет это действие явным образом. Итак, для реализации первого варианта надо:

¨  создать метод, соответствующий делегатному типу Func<T, TResult>,

¨  создать последовательность объектов типа T,

¨  создать LINQ-запрос, вызвав, например, extension-метод Where (Func<T, TResult>),

¨  создать перечислитель IEnumerable<T>, настроенный на выбранный ранее тип,

¨  перечислить выходную последовательность, генерируемую при выполнении отложенного запроса.

Следующий кодовый фрагмент реализует намеченный план. В качестве предиката, который следует задать при вызове метода Where, мы используем метод IsLeapYear, проверяющий целое число на соответствие високосным годам.

static bool IsLeapYear(int y)

{

bool res = y % 4 == 0 && y % 100 != 0 || y % 1000 == 0;

return res;

}

static void TestLeapYear()

{

Console.WriteLine("\nTest Verbose version of IsLeapYear Query");

var years = new int[] { 2000, 2001, 2002, 2003, 2004, 2005 };

Func<int, bool> tester = IsLeapYear;

IEnumerable<int> result = years.Where<int>(tester);

IEnumerator<int> en = result.GetEnumerator();

while (en.MoveNext())

Console.WriteLine(en.Current);

}

Теперь рассмотрим код второго варианта этого же запроса. Он использует операции from, where и select, встроенные в язык C# 3.5. С их помощью реализуются интегрированные запросы.

Console.WriteLine("\nConcise Version of IsLeapYear Query");

var q = from y in Enumerable.Range(2000, 6)

where (y % 4 == 0 && y % 100 != 0 || y % 1000 == 0)

select y;

foreach (var v in q)

Console.WriteLine(v);

Этот запрос реализует тот же самый алгоритм — отбирает из массива целых чисел только те, которые соответствуют високосным годам. Согласитесь, что второй вариант намного проще создать, прочесть и при желании модифицировать. Интегрированные запросы дружелюбны, но компилятор превращает их в вызовы extension-методов. C некоторыми допущениями можно сказать, что компилятр языка C# превращает второй вариант запроса в первый.

Синтаксис оператора from x in коллекция выстроен так, чтобы задать переменную x для прохода вдоль всей последовательности (или коллекции). Эту переменную называют псевдонимом (alias). Заметим, что оператор from не имеет соответствующего extension-метода.

Вы заметили, что, в отличие от обычных SQL-запросов, операторы select и from стоят не на своих местах, но благодаря такому решению, появляется возможность включить механизм Intellisense. Как только стала известна коллекция (после предложения from x in коллекция), начинают работать метаданные, и, включаются подсказки. Такой подход значительно упрощает и ускоряет процесс разработки приложения.

Рассмотрим еще два варианта выполнения одного и того же запроса. Первый — использует extension-метод Select, а второй — встроенную операцию select. На сей раз запрос проецирует диапазон целых чисел на множество анонимных объектов. Вот код первого варианта:

var q = Enumerable.Range(20,10).Select(n => new { Value = n, Mult3 = n % 3 == 0 });

Заметьте, что здесь необходимо использовать λ-операцию =>. Теперь рассмотрим второй вариант запроса, который обходится без λ-операции, но требует дополнить select двумя другими операциями (from и in).

q = from n in Enumerable.Range(20, 10)

select new { Value = n, Mult3 = n % 3 == 0 };

Для вывода результатов запросов воспользуемся уже опробованным приемом.

Console.WriteLine("\nNumber   Mult3?\r\n" + new string('\u2500', 21));

foreach (var v in q)

Console.WriteLine("{0,-10} {1}", v.Value, v.Mult3);

Сортировка