Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Напомним, что при создании объекта анонимного типа указываемые идентификаторы становятся свойствами, открытыми только для чтения. Поэтому их можно использовать в других частях кода.
Термин анонимный тип не совсем оправдывает свое название. Ведь тип оказываетсяанонимным только для программирующего, но не для компилятора, который присваивает ему внутреннее имя. Следовательно, анонимные типы не нарушают принятые вC# правила строгого контроля типов.
Для того чтобы стало более понятным особое назначение анонимных типов, рассмотрим переделанную версию программы из предыдущего раздела, посвященногооператору join. Напомним, что в этой программе класс Temp требовался для инкапсуляции результата, возвращаемого оператором join. Благодаря применениюанонимного типа необходимость в этом классе-заполнителе отпадает, а исходный кодпрограммы становится менее громоздким. Результат выполнения программы приэтом не меняется.// Использовать анонимный тип для усовершенствования// программы, демонстрирующей применение оператора join.using System;using System.Linq;// Класс, связывающий наименование товара с его порядковым номером.class Item { public string Name { get; set; } public int ItemNumber { get; set; } public Item(string n, int inum) { Name = n; ItemNumber = inum; }}// Класс, связывающий наименование товара с состоянием его запасов на складе.class InStockStatus { public int ItemNumber { get; set; } public bool InStock { get; set; } public InStockStatus(int n, bool b) { ItemNumber = n; InStock = b; }}class AnonTypeDemo { static void Main() { Item[] items = { new Item("Кусачки", 1424), new Item("Тиски", 7892), new Item("Молоток", 8534), new Item("Пила", 6411) }; InStockStatus[] statusList = { new InStockStatus(1424, true), new InStockStatus(7892, false), new InStockStatus(8534, true), new InStockStatus(6411, true) }; // Сформировать запрос, объединяющий объекты классов Item и // InStockStatus для составления списка наименований товаров и их // наличия на складе. Теперь для этой цели используется анонимный тип. var inStockList = from item in items join entry in statusList on item.ItemNumber equals entry.ItemNumber select new { Name = item.Name, InStock = entry.InStock }; Console.WriteLine("ТоварtНаличиеn"); // Выполнить запрос и вывести его результаты. foreach(var t in inStockList) Console.WriteLine("{0}t{1}", t.Name, t.InStock); }}
Обратите особое внимание на следующий оператор select.select new { Name = item.Name, InStock = entry.InStock };
Он возвращает объект анонимного типа с двумя доступными только для чтениясвойствами: Name и InStock. Этим свойствам присваиваются наименование товараи состояние его наличия на складе. Благодаря применению анонимного типа необходимость в упоминавшемся выше классе Temp отпадает.
Обратите также внимание на цикл foreach, в котором выполняется запрос. Теперьпеременная шага этого цикла объявляется с помощью ключевого слова var. Это необходимо потому, что у типа объекта, хранящегося в переменной inStockList, нетимени. Данная ситуация послужила одной из причин, по которым в C# были внедрены неявно типизированные переменные, поскольку они нужны для поддержки анонимных типов.
Прежде чем продолжить изложение, следует отметить еще один заслуживающийвнимания аспект анонимных типов. В некоторых случаях, включая и рассмотренныйвыше, синтаксис анонимного типа упрощается благодаря применению инициализатора проекции. В данном случае просто указывается имя самого инициализатора. Это имяавтоматически становится именем свойства. В качестве примера ниже приведен другой вариант оператора select из предыдущей программы.select new { item.Name, entry.InStock };
В данном примере имена свойств остаются такими же, как и прежде, а компиляторавтоматически "проецирует" идентификаторы Name и InStock, превращая их в свойства анонимного типа. Этим свойствам присваиваются прежние значения, обозначаемые item.Name и entry.InStock соответственно.Создание группового объединения
Как пояснялось ранее, оператор 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 lst select new { How = how, Tlist = lst };
Этот запрос формируется следующим образом. В операторе from используетсяпеременная диапазона how для охвата всего массива travelTypes. Напомним, чтомассив travelTypes содержит названия общих категорий транспорта: воздушного,наземного, морского и речного. Каждый вид транспорта объединяется в оператореjoin со своей категорией. Например, велосипед, автомашина и поезд объединяютсяс наземным транспортом. Но благодаря оператору into для каждой категории транспорта в операторе join составляется список видов транспорта, относящихся к даннойкатегории. Этот список сохраняется в переменной lst. И наконец, оператор selectвозвращает объект анонимного, типа, инкапсулирующий каждое значение переменной how (категории транспорта) вместе со списком видов транспорта. Именно поэтомудля вывода результатов запроса требуются два цикла foreach.foreach(var t in byHow) { Console.WrifeLine("К категории <{0} транспорт> относится:", t.How); foreach(var m in t.Tlist) Console.WriteLine(" " + m.Name); Console.WriteLine();}