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

Параметр pred типа Func<T, int, bool> является ссылкой на делегатный метод, который на сей раз должен иметь два аргумента: первый, как и ранее, настраивает generic-делегат на пользовательский тип, второй передает целочисленный индекс текущего элемента входной последовательности. Тип возвращаемого значения определен, как bool (все предикаты должны возвращать bool). Индекс текущего элемента можно использовать для фильтрации данных. Следующий пример показывает, как это сделать.

string[] digitNames = { "zero","one","two","three","four","five","six","seven","eight","nine" };

var q = digitNames.Where((s, i) => s.Length <= i);

Console.WriteLine("Name:  Length:  Pos:\n{0}", new string('\u2500', 21));

int k = 0;

foreach (var s in q)

Console.WriteLine("{0,-8}  {1,-5}   {2}", s, s.Length, ++k);

Здесь используется вторая версия extension-метода Where. Она фильтрует последовательность символьных строк так, чтобы на выходе были только те строки, длина которых меньше или равна их порядковому номеру. Этот алгоритм задан λ-выражением (анонимным делегатом). Учтите, что второй параметр λ-выражения имеет тип int и означает индекс текущего элемента в массиве digitNames.

В обеих версиях метода Where параметр должен быть предикатом (то есть, адресом функции, которая возвращает true или false). Во втором варианте делегатный тип вычисляется на основе анализа λ-выражения:

(s, i) => s.Length <= i

¨  Левая часть (s, i) определяет количество входных параметров. Их два, поэтому компилятор выбирает версию метода Where, которая соответствует делегатному типу Func<T,int,bool>.

¨  Правая часть (s.Length <= i) должна соответствовать типу возвращаемого значения (bool).

¨  Тип параметра T в Func<T,int,bool> определяется типом элементов входной последоватетьности digitNames. Это — string.

¨  Тип второго параметра пары (s, i) определяется сигнатурой Func<T,int,bool>. Это —int.

Рассматриваемый фрагмент выводит в консольное окно такой результат.

Name:  Length:  Pos:

____________________

four      4       1

five      4       2

six       3       3

seven     5       4

eight     5       5

nine      4       6

Задание. С помощью отладчика студии определите тип переменной q. Ответ. Компилятор пользуется типом Enumerable.WhereIterator<string>, однако он отражает внутреннюю кухню. Мы не можем использовать этот тип в качестве альтернативы описателю var.

Отложенное выполнение

В двух предыдущих примерах было показано, как работает метод Where класса Enumerable. Заметим, что выполнение метода, а именно: проход по последовательности и отбор значений, удовлетворяющих критерию фильтрации, откладывается до тех пор, когда эти значения не начнут перебираться (be enumerated). В нашем случае это происходит при выполнении цикла foreach. Видимый вызов метода:

var q = digitNames.Where((s, i) => s.Length <= i);

не производит никаких вычислений. Таким же (отложенным) способом выполняется множество других методов из категории extension. Описанный способ вычислений иногда называют ленивым (lazy evaluation). Он реализуется, как вы, вероятно, догадались, с помощью итератора yieldreturn. Такой подход к реализации extension-методов повышает эффективность цепочки вычислений, за счет экономии памяти, отводимой на хранение промежуточных результатов. Рассмотрм пример, в котором используется цепочка вызовов extension-методов.

var songs = new[]

{

new { Artist="Low Rowles", Date=new DateTime(1965,3,28), Song="Cheek to cheek" },

new { Artist="Ray Charles", Date=new DateTime(1952,6,11), Song="Alexander's Ragtime Band" },

new { Artist="Frank Sinatra", Date=new DateTime(1958,6,7), Song="Emily" },

new { Artist="Frank Sinatra", Date=new DateTime(1956,3,28), Song="September In the Rain" },