Шрифт:
Эта версия программы дает такой же результат, как и предыдущая. Единственное отличие между ними заключается в том, как формируется запрос. В данной версии для этой цели используются методы запроса.
Рассмотрим другой пример. Но сначала приведем еще раз запрос из представлен ного ранее примера применения оператора join. var inStockList = from item in items join entry in statusList on item.ItemNumber equals entry.ItemNumber select new Temp(item.Name, entry.InStock);
По этому запросу формируется последовательность, состоящая из объектов, инкап сулирующих наименование товара и состояние его запасов на складе. Вся эта инфор мация получается путем объединения двух источников данных: items и statusList. Ниже приведен переделанный вариант данного запроса, в котором вместо синтаксиса, предусмотренного в C# для запросов, используется метод запроса Join. // Использовать метод запроса Join для составления списка // наименований товаров и состояния их запасов на складе. var inStockList = items.Join(statusList, k1 => k1.ItemNumber, k2 => k2.ItemNumber, (k1, k2) => new Temp(k1.Name, k2.InStock) );
В данном варианте именованный класс Temp используется для хранения результи рующего объекта, но вместо него можно воспользоваться анонимным типом. Такой вариант запроса приведен ниже. var inStockList = items.Join(statusList, k1 => k1.ItemNumber, k2 => k2.ItemNumber, (k1, k2) => new { k1.Name, k2.InStock} ); Синтаксис запросов и методы запроса
Как пояснялось в предыдущем разделе, запросы в C# можно формировать двумя способами, используя синтаксис запросов или методы запроса. Любопытно, что оба способа связаны друг с другом более тесно, чем кажется, глядя на исходный код про граммы. Дело в том, что синтаксис запросов компилируется в вызовы методов запроса. Поэтому код where х < 10
будет преобразован компилятором в следующий вызов. Where(х => х < 10)
Таким образом, оба способа формирования запросов в конечном итоге сходятся на одном и том же.
Но если оба способа оказываются в конечном счете равноценными, то какой из них лучше для программирования на С#? В целом, рекомендуется чаще пользоваться син таксисом запросов, поскольку он полностью интегрирован в язык С#, поддерживается соответствующими ключевыми словами и синтаксическим конструкциями. Дополнительные методы расширения, связанные с запросами
Помимо методов, соответствующих операторам запроса, поддерживаемым в С#, име ется ряд других методов расширения, связанных с запросами и зачастую оказывающих помощь в формировании запросов. Эти методы предоставляются в среде .NET Framework и определены для интерфейса IEnumerable в классе Enumerable. Ниже приведе ны наиболее часто используемые методы расширения, связанные с запросами. Многие из них могут перегружаться, поэтому они представлены лишь в самой общей форме. Метод Описание All(predicate) Возвращает логическое значение true, если все элементы в последовательности удовлетворяют условию, задаваемому параметром predicate Any(predicate) Возвращает логическое значение true, если любой элемент в последовательности удовлетворяет условию, задаваемому параметром predicate Average Возвращает среднее всех значений в числовой последовательности Contains(value) Возвращает логическое значение true, если в последовательности содержится указанный объект Count Возвращает длину последовательности, т.е. количество составляющих ее элементов First Возвращает первый элемент в последовательности Last Возвращает последний элемент в последовательности Max Возвращает максимальное значение в последовательности Min Возвращает минимальное значение в последовательности Sum Возвращает сумму значений в числовой последовательности
Метод Count уже демонстрировался ранее в этой главе. А в следующей програм ме демонстрируются остальные методы расширения, связанные с запросами. // Использовать ряд методов расширения, определенных в классе Enumerable. using System; using System.Linq; class ExtMethods { static void Main { int[] nums ={ 3, 1, 2, 5, 4 }; Console.WriteLine("Минимальное значение равно " + nums.Min); Console.WriteLine("Максимальное значение равно " + nums.Max); Console.WriteLine("Первое значение равно " + nums.First); Console.WriteLine("Последнее значение равно " + nums.Last); Console.WriteLine("Суммарное значение равно " + nums.Sum); Console.WriteLine("Среднее значение равно " + nums.Average); if(nums.All(n => n > 0)) Console.WriteLine("Все значения больше нуля."); if(nums.Any(n => (n % 2) == 0)) Console.WriteLine("По крайней мере одно значение является четным."); if(nums.Contains(3)) Console.WriteLine("Массив содержит значение 3."); } }
Вот к какому результату приводит выполнение этой программы. Минимальное значение равно 1 Максимальное значение равно 5 Первое значение равно 3 Последнее значение равно 4 Суммарное значение равно 15 Среднее значение равно 3 Все значения больше нуля. По крайней мере одно значение является четным Массив содержит значение 3.
Методы расширения, связанные с запросами, можно также использовать в самом запросе, основываясь на синтаксисе запросов, предусмотренном в С#. И в действитель ности это делается очень часто. Например, метод Average используется в приве денной ниже программе для получения последовательности, состоящей только из тех значений, которые оказываются меньше среднего всех значений в массиве. // Использовать метод Average вместе с синтаксисом запросов. using System; using System.Linq; class ExtMethods2 { static void Main { int[] nums = { 1, 2, 4, 8, 6, 9, 10, 3, 6, 7 }; var ltAvg = from n in nums let x = nums.Average where n < x select n; Console.WriteLine("Среднее значение равно " + nums.Average); Console.Write("Значения меньше среднего: "); // Выполнить запрос и вывести его результаты. foreach(int i in ltAvg) Console.Write(i + " "); Console.WriteLine; } }
При выполнении этой программы получается следующий результат. Среднее значение равно 5.6 Значения меньше среднего: 1 2 4 3
Обратите особое внимание в этой программе на следующий код запроса. var ltAvg = from n in nums let x = nums.Average where n < x select n;
Как видите, переменной x в операторе let присваивается среднее всех значений в массиве nums. Это значение получается в результате вызова метода Average для массива nums. Режимы выполнения запросов: отложенный и немедленный
В LINQ запросы выполняются в двух разных режимах: немедленном и отложенном. Как пояснялось ранее в этой главе, при формировании запроса определяется ряд пра вил, которые не выполняются вплоть до оператора цикла foreach. Это так называе мое отложенное выполнение.
Но если используются методы расширения, дающие результат, отличающийся от последовательности, то запрос должен быть выполнен для получения этого результа та. Рассмотрим, например, метод расширения Count. Для того чтобы этот метод возвратил количество элементов в последовательности, необходимо выполнить за прос, и это делается автоматически при вызове метода Count. В этом случае имеет место немедленное выполнение, когда запрос выполняется автоматически для получения требуемого результата. Таким образом, запрос все равно выполняется, даже если он не используется явно в цикле foreach.