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

Запрос создает объект класса, реализующего интерфейс IEnumerable<int[]>. Это означает, что запрос возвратит последовательность массивов, каждый их которых представляет собой перечень оценок текущего студента типа int[]. Мы пользуемся удобной возможностью задать тип запроса (то есть, тип переменной q) с помощью ключевого слова var. Но компилятор должен вычислить тип T параметра шаблона IEnumerable<T>. Он анализирует λ-выражение s => s.Marks и приходит к выводу, что нужен параметр типа int[].

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

int i=0;

Console.WriteLine ("Student\t\t Mark\n" + new string('\u2500', 31));

foreach (var array in q)

{

Console.Write (studs[i++].Name + ":\t ");

foreach (int m in array)

Console.Write (m + ", ");

Console.WriteLine();

}

Альтернативный вариант предыдущего примера может быть получен путем замены массива на список.

¨  При этом перечень оценок текущего студента будет иметь тип List<int>.

¨  Тип запроса будет иметь тип IEnumerable<List<int>>.

¨  Вместо int[] Marks { get; set; } подставьте List<int> Marks { get; set; }

¨  Вместо строк вида: Marks = new[] { 5, 5, 5, 5, 5 } }, подставьте Marks = new List<int>{ 5, 5, 5, 5, 5 } }.

¨  Остальные строки кода можно оставить неизменными.

Итак, метод Select в применении к последовательности объектов Stud возвращает коллекцию коллекций. Рассморим, тип возвращаемой последовательности при использовании метода SelectMany. Так как этот метод соединяет все подпоследовательности в одну, то ее тип будет — IEnumerable<int>. Для перечисления такого результата (отложенного) запроса достаточно написать лишь один цикл.

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

Console.Write ("\nAll the Marks: ");

foreach (var m in query)

Console.Write (m + ", ");

Говорят, что SelectMany — проекция, имеющая тип one-to-many. При проходе по последовательности она заменяет каждый список множеством его значений (сливает списки в одну последовательность).

Существует еще пара версий методов Select и SelectMany. Они, также, как и в случае с Where, работают с делегатным типом, требующим три параметра Func<T,int,S>. Рассмотрим пример.

static void TestNumbers()

{

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var q = numbers.Select ((n, i) => new { Value = n, InPlace = (n == i) });

Console.WriteLine("Number     In-place?\r\n" + new string('\u2500', 20));

foreach (var v in q)

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

}

Здесь метод Select возвращает не коллекцию коллекций, а последовательность анонимных объектов. В таких случаях принято говорить о проецировании множества целых чисел (массива) на множество объектов с именованными свойствами.

Каждый объект результирующего множества имеет свойства: int Value, bool InPlace. Свойство InPlace равно true для тех чисел, местоположение в массиве которых совпадает с их значением. Это справедливо для чисел: 3, 6, 7 (учтите, что число 5 занимает нулевую позицию). Напомним, что второй параметр левой части λ-выражения (n, i), как и ранее, является индексом элемента в массиве.

В данном случае найти замену описателю var при переменной q найти не удается. Отладчик сообщает, что его типом является IEnumerable<<>f__AnonymousType0<int,bool>>. Этот описатель лишь приоткрывает внутреннюю кухню LINQ, но не воспринимается компилятором C#, если вы попытаетесь заменить им var.

Последовательность анонимных объектов генерируется в отложенном процессе и имеет конкретное внешнее представление. Например, первый элемент этой последовательности отображается таким образом.

v = { Value = 5, InPlace = false }