Выбрать главу

Ниже приведен результат выполнения этой программы. К категории <Воздушный транспорт> относится: аэростат самолет биплан К категории <Морской транспорт> относится: судно К категории <Наземный транспорт> относится: велосипед автомашина поезд К категории <Речной транспорт> относится: лодка каноэ

Главной частью данной программы, безусловно, является следующий запрос. 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)

За исключением метода Join(), остальные методы запроса принимают един ственный аргумент, который представляет собой объект некоторой разновидности обобщенного типа Func. Это тип встроенного делегата, объявляемый следующим образом: delegate TResult Func<in Т, out TResult>(T arg)

где TResult обозначает тип результата, который дает делегат, а Т — тип элемента. В методах запроса аргументы selector, predicate или keySelector определяют действие, которое предпринимает метод запроса. Например, в методе Where() аргу мент prediсаte определяет порядок отбора данных в запросе. Каждый метод запроса возвращает перечислимый объект. Поэтому результат выполнения одного метода за проса можно использовать для вызова другого, соединяя эти методы в цепочку.

Метод Join() принимает четыре аргумента. Первый аргумент (inner) представляет собой ссылку на вторую объединяемую последовательность, а первой является последо вательность, для которой вызывается метод Join(). Селектор ключа для первой последо вательности передается в качестве аргумента outerKeySelector, а селектор ключа для второй последовательности — в качестве аргумента LnnerKeySelector. Результат объе динения обозначается как аргумент resultSelector. Аргумент outerKeySelector имеет тип Func, аргумент innerKeySelector — тип Func, тогда как аргумент resultSelector — тип Func, где TOuter — тип элемента из вызывающей последовательности; Tinner — тип эле мента из передаваемой последовательности; TResult — тип элемента из объеди няемой в итоге последовательности, возвращаемой в виде перечислимого объекта.

Аргумент метода запроса представляет собой метод, совместимый с указываемой формой делегата Func, но он не обязательно должен быть явно объявляемым мето дом. На самом деле вместо него чаще всего используется лямбда-выражение. Как по яснялось в главе 15, лямбда-выражение обеспечивает более простой, но эффективный способ определения того, что, по существу, является анонимным методом, а компиля тор C# автоматически преобразует лямбда-выражение в форму, которая может быть передана в качестве параметра делегату Func. Благодаря тому что лямбда-выражения обеспечивают более простой и рациональный способ программирования, они исполь зуются во всех примерах, представленных далее в этом разделе. Формирование запросов с помощью методов запроса

Используя методы запроса одновременно с лямбда-выражениями, можно форми ровать запросы, вообще не пользуясь синтаксисом, предусмотренным в C# для запро сов. Вместо этого достаточно вызвать соответствующие методы запроса. Обратимся сначала к простому примеру. Он представляет собой вариант первого примера про граммы из этой главы, переделанный с целью продемонстрировать применение мето дов запроса Where() и Select() вместо соответствующих операторов. // Использовать методы запроса для формирования простого запроса. // Это переделанный вариант первого примера программы из настоящей главы. using System; using System.Linq; class SimpQuery { static void Main() { int[] nums = { 1, -2, 3, 0, -4, 5 }; // Использовать методы Where() и Select() для // формирования простого запроса. var posNums = nums.Where(n => n > 0).Select(r => r); Console.Write("Положительные значения из массива nums: "); // Выполнить запрос и вывести его результаты. foreachfint i in posNums) Console.Write(i + " "); Console.WriteLine(); } }

Эта версия программы дает такой же результат, как и исходная. Положительные значения из массива nums: 1 3 5

Обратите особое внимание в данной программе на следующую строку кода. var posNums = nums.Where(n => n > 0).Select(r => r);

В этой строке кода формируется запрос, сохраняемый в переменной posNums. По этому запросу, в свою очередь, формируется последовательность положительных зна чений, извлекаемых из массива nums. Для этой цели служит метод Where(), отбираю щий запрашиваемые значения, а также метод Select(), избирательно формирующий из этих значений окончательный результат. Метод Where() может быть вызван для массива nums, поскольку во всех массивах реализуется интерфейс IEnumerable, поддерживающий методы расширения запроса.

Формально метод Select() в рассматриваемом здесь примере не нужен, посколь ку это простой запрос. Ведь последовательность, возвращаемая методом Where(), уже содержит конечный результат. Но окончательный выбор можно сделать и по более сложному критерию, как это было показано ранее на примерах использования син таксиса запросов. Так, по приведенному ниже запросу из массива nums возвращаются положительные значения, увеличенные на порядок величины. var posNums = nums.Where(n => n > 0).Select(r => r * 10);

Как и следовало ожидать, в цепочку можно объединять и другие операции над дан ными, получаемыми по запросу. Например, по следующему запросу выбираются по ложительные значения, которые затем сортируются по убывающей и возвращаются в виде результирующей последовательности: var posNums = nums.Where(n => n > 0).OrderByDescending(j => j);

где выражение j => j обозначает, что упорядочение зависит от входного параметра, который является элементом данных из последовательности, получаемой из метода Where().

В приведенном ниже примере демонстрируется применение метода запроса GroupBy(). Это измененный вариант представленного ранее примера. // Продемонстрировать применение метода запроса GroupBy(). // Это переработанный вариант примера, представленного ранее // для демонстрации синтаксиса запросов. using System; using System.Linq; class GroupByDemo { static void Main() { string[] websites = { "hsNameA.com", "hsNameB.net", "hsNameC.net", "hsNameD.com", "hsNameE.org", "hsNameF.org", "hsNameG.tv", "hsNameH.net", "hsNameI.tv" }; // Использовать методы запроса для группирования // веб-сайтов по имени домена самого верхнего уровня. var webAddrs = websites.Where(w => w.LastIndexOf('.') != 1). GroupBy(x => x.Substring(x.LastIndexOf(".", x.Length))); // Выполнить запрос и вывести его результаты. foreach(var sites in webAddrs) { Console.WriteLine("Веб-сайты, сгруппированные " + "по имени домена " + sites.Key); foreach(var site in sites) Console.WriteLine(" " + site); Console.WriteLine(); } } }