Запрос создает объект класса, реализующего интерфейс 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 }
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.