Создание группового объединения
Как пояснялось ранее, оператор into можно использовать вместе с оператором join для создания группового объединения, образующего последовательность, в которой каждый результат состоит из элементов данных из первой последовательности и группы всех совпадающих элементов из второй последовательности. Примеры группового объединения не приводились выше потому, что в этом объединении нередко применяется анонимный тип. Но теперь, когда представлены анонимные типы, можно обратиться к простому примеру группового объединения.
В приведенном ниже примере программы групповое объединение используется для составления списка, в котором различные транспортные средства (автомашины, суда и самолеты) организованы по общим для них категориям транспорта: наземного, морского, воздушного и речного. В этой программе сначала создается класс Transport, связывающий вид транспорта с его классификацией. Затем в методе Main() формируются две входные последовательности. Первая из них представляет собой массив символьных строк, содержащих названия общих категорий транспорта: наземного, морского, воздушного и речного, а вторая — массив объектов типа Transport, инкапсулирующих различные транспортные средства. Полученное в итоге групповое объединение используется для составления списка транспортных средств, организованных по соответствующим категориям.
// Продемонстрировать применение простого группового объединения.
using System;
using System.Linq;
// Этот класс связывает наименование вида транспорта,
// например поезда, с общей классификацией транспорта:
// наземного, морского, воздушного или речного,
class Transport {
public string Name { get; set; }
public string How { get; set; }
public Transport(string n, string h) {
Name = n;
How = h;
}
}
class GroupJoinDemo {
static void Main() {
// Массив классификации видов транспорта,
string[] travelTypes = {
"Воздушный",
"Морской",
"Наземный",
"Речной",
};
// Массив видов транспорта.
Transport[] transports = {
new Transport("велосипед", "Наземный"),
new Transport ("аэростат", "Воздушный"),
new Transport("лодка", "Речной"),
new Transport("самолет", "Воздушный"),
new Transport("каноэ", "Речной"),
new Transport("биплан", "Воздушный"),
new Transport("автомашина", "Наземный"),
new Transport("судно", "Морской"),
new Transport("поезд", "Наземный")
};
// Сформировать запрос, в котором групповое
// объединение используется для составления списка
// видов транспорта по соответствующим категориям,
var byHow = from how in travelTypes
join trans in transports
on how equals trans.How
into lst
select new { How = how, Tlist = lst };
// Выполнить запрос и вывести его результаты,
foreach(var t in byHow) {
Console.WriteLine("К категории <{0} транспорт> относится:", t.How);
foreach(var m in t.Tlist)
Console.WriteLine(" " + m.Name);
Console.WriteLine();
}
}
}
Ниже приведен результат выполнения этой программы.
К категории <Воздушный транспорт> относится:
аэростат
самолет
биплан
К категории <Морской транспорт> относится:
судно
К категории <Наземный транспорт> относится:
велосипед
автомашина
поезд
К категории <Речной транспорт> относится:
лодка
каноэ
Главной частью данной программы, безусловно, является следующий запрос.
var byHow = from how in travelTypes
join trans in transports
on how equals trans.How
into 1st
select new { How = how, Tlist = 1st };
Этот запрос формируется следующим образом. В операторе from используется переменная диапазона how для охвата всего массива travelTypes. Напомним, что массив travelTypes содержит названия общих категорий транспорта: воздушного, наземного, морского и речного. Каждый вид транспорта объединяется в операторе join со своей категорией. Например, велосипед, автомашина и поезд объединяются с наземным транспортом. Но благодаря оператору into для каждой категории транспорта в операторе join составляется список видов транспорта, относящихся к данной категории. Этот список сохраняется в переменной lst. И наконец, оператор select возвращает объект анонимного типа, инкапсулирующий каждое значение переменной how (категории транспорта) вместе со списком видов транспорта. Именно поэтому для вывода результатов запроса требуются два цикла foreach.
foreach(var t in byHow) {
Console.WriteLine("К категории <{0} транспорт> относится:", t.How);
foreach(var m in t.Tlist)
Console.WriteLine(" " + m.Name);
Console.WriteLine();
}
Во внешнем цикле получается объект, содержащий наименование общей категории транспорта, и список видов транспорта, относящихся к этой категории. А во внутреннем цикле выводятся отдельные виды транспорта.