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

new { Artist="Frank Sinatra", Date=new DateTime(1957,4,12), Song="It started all over again" },

new { Artist="Nat King Cole", Date=new DateTime(1956,11,29), Song="Impossible" },

new { Artist="Dinah Washington", Date=new DateTime(1975,9,21), Song="Call Me Irresponsible" },

new { Artist="Peggy Lee", Date=new DateTime(1978,5,9), Song="As Time Goes By" }

};

var q = songs

.Select(s => new { len = s.Song.Length, s.Song, s.Artist, s.Date })

.Where(s => s.Date > DateTime.Now.AddYears(-60) )

.OrderBy(s => s.len)

.Select(s => new { s.Song, Date = s.Date, s.len });

int k = 0;

foreach (var s in q)

Console.WriteLine("{0}. {1}, {2,-2}, {3}", ++k, s.Date.ToShortDateString(), s.len, s.Song);

Отметим, что компилятор распознает контекстное ключевое слово yield только если после него следует оператор return или break. Конструкция yield return, помещенная в итерационный блок, заставляет перечислимый класс работать в рамках следующего в цепочке перечислимого класса. Распросраняясь по цепочке, это правило осуществляет задержку выполнения всей последовательности вызовов расширяющих методов.

Немедленное выполнение (Eager Evaluation)

Любопытно, что если изменить первый из двух запросов, рассмотренных ранее (в разделе "Фильтрация данных"), и добавить в него вызов метода ToArray()

string[] oo = words.Where(c => c.Contains("oo")).ToArray();

то метод Where выполнится сразу, без задержки. То же самое произойдет с любым другим отложенным запросом. Методы ToArray() и ToArray<T>() также являются расширениями IEnumerable<T>.  Они преобразуют ленивую последовательность в массив. Отметьте, что следующий код эквивалентен предыдущему.

string[] oo = words.Where(c => c.Contains("oo")).ToArray<string>();

Вместо массива вы можете преобразовать выходную последовательность в generic-список. Для этого надо воспользоваться extension-методом ToList() или ToList<string>().

List<string> ooList = words.Where(c => c.Contains("oo")).ToList();

Методы Select и SelectMany

Рассмотрим, как работают методы Select и SelectMany, которые позволяют проецировать данные на удобные нам структуры. Тип элементов выходной последовательности может отличаться от типа данных входной. Методы Select и SelectMany требуют в качестве параметра задать адрес трансформирующей функции. Она называется селектором (selector) и должна создавать одно значение для каждого значения перебираемой последовательности.

Запомните. Метод Where требует задать предикат, а методы Select и SelectMany требуют задать селектор.

Если селектор возвращает значение, которое, в свою очередь, является последовательностью, то нам необходимо написать код прохода по ней. В случае использования SelectMany этого делать не надо, так как сам метод SelectMany обрабатывает последовательность возвращаемых последовательностей. Он сливает их в одну общую последовательность. Рассмотрим пример, который иллюстрирует использование обоих методов для вывода результатов экзаменов для массива студентов. Пусть каждый студент задан объектом класса Stud с таким набором автогенерируемых свойств.

class Stud

{

public string Name { get; set; }

public int[] Marks { get; set; }

}

Массив студентов можно задать таким образом.

Stud[] studs =

{

new Stud { Name="John Best",  Marks = new[]{ 5, 5, 5, 5, 5 } },

new Stud { Name="Joy Amore",  Marks = new[]{ 4, 4, 3, 4 } },

new Stud { Name="Jim Lowson", Marks = new[]{ 3, 3, 2 } },

};

Далее мы создаем отложенный запрос с помощью метода Select.

var q = studs.Select (s => s.Marks); // Тип переменной q - IEnumerable<int[]>