Полное руководство. С# 4.0
Шрифт:
Для того чтобы стало более понятным особое назначение анонимных типов, рас смотрим переделанную версию программы из предыдущего раздела, посвященного оператору 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) }; //
Обратите особое внимание на следующий оператор 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 { //
Ниже приведен результат выполнения этой программы. К категории <Воздушный транспорт> относится: аэростат самолет биплан К категории <Морской транспорт> относится: судно К категории <Наземный транспорт> относится: велосипед автомашина поезд К категории <Речной транспорт> относится: лодка каноэ
Главной частью данной программы, безусловно, является следующий запрос. 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; }
Во внешнем цикле получается объект, содержащий наименование общей катего рии транспорта, и список видов транспорта, относящихся к этой категории. А во вну треннем цикле выводятся отдельные виды транспорта. Методы запроса
Синтаксис запроса, описанный в предыдущих разделах, применяется при форми ровании большинства запросов в С#. Он удобен, эффективен и компактен, хотя и не является единственным способом формирования запросов. Другой способ состоит в использовании методов запроса, которые могут вызываться для любого перечислимо го объекта, например массива. Основные методы запроса
Методы запроса определяются в классе System.Linq.Enumerable и реализуются в виде методов расширения функций обобщенной формы интерфейса IEnumerable. (Методы запроса определяются также в классе System.Linq.Queryable, расширяю щем функции обобщенной формы интерфейса IQueryable<T>, но этот интерфейс в настоящей главе не рассматривается.) Метод расширения дополняет функции друго го класса, но без наследования. Поддержка методов расширения была внедрена в вер сию C# 3.0 и более подробно рассматривается далее в этой главе. А до тех пор доста точно сказать, что методы запроса могут вызываться только для тех объектов, которые реализуют интерфейс IEnumerable<T>.
В классе Enumerable предоставляется немало методов запроса, но основными счи таются те методы, которые соответствуют описанным ранее операторам запроса. Эти методы перечислены ниже вместе с соответствующими операторами запроса. Следует, однако, иметь в виду, что эти методы имеют также перегружаемые формы, а здесь они представлены лишь в самой простой своей форме. Но именно эта их форма использу ется чаще всего. Оператор запроса Эквивалентный метод запроса select Select(selector) where Where(predicate) orderby OrderBy(keySelector) или OrderByDescending(keySelector) join Join(inner, outerKeySelector, innerKeySelector, resultSelector) group GroupBy(keySelector)