Главная страница

программирование. Руководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт


Скачать 3.32 Mb.
НазваниеРуководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт
Анкорпрограммирование
Дата25.01.2022
Размер3.32 Mb.
Формат файлаrtf
Имя файлаc-40-polnoe-rukovodstvo-2011.rtf
ТипРуководство
#341448
страница53 из 97
1   ...   49   50   51   52   53   54   55   56   ...   97
ГЛАВА 19

LINQ

Без сомнения, LINQ относится к одним из самых интересных средств языка С#. Эти средства были внедрены в версии C# 3.0 и явились едва ли не самым главным его дополнением, которое состояло не только во внесении совершенно нового элемента в синтаксис С#, добавлении нескольких ключевых слов и предоставлении больших возможностей, но и в значительном расширении рамок данного языка программирования и круга задач, которые он позволяет решать. Проще говоря, внедрение LINQ стало поворотным моментом в истории развития С#.

Аббревиатура LINQ означает Language‑Integrated Query, т.е. язык интегрированных запросов. Это понятие охватывает ряд средств, позволяющих извлекать информацию из источника данных. Как вам должно быть известно, извлечение данных составляет важную часть многих программ. Например, программа может получать информацию из списка заказчиков, искать информацию в каталоге продукции или получать доступ к учетному документу, заведенному на работника. Как правило, такая информация хранится в базе данных, существующей отдельно от приложения. Так, каталог продукции может храниться в реляционной базе данных. В прошлом для взаимодействия с такой базой данных приходилось формировать запросы на языке структурированных запросов (SQL). А для доступа к другим источникам данных, например в формате XML, требовался отдельный подход. Следовательно, до версии 3.0 поддержка подобных запросов в C# отсутствовала. Но это положение изменилось после внедрения LINQ.
LINQ дополняет C# средствами, позволяющими формировать запросы для любого LINQ‑совместимого источника

данных. При этом синтаксис, используемый для формирования запросов, остается неизменным, независимо от типа источника данных. Это, в частности, означает, что синтаксис, требующийся для формирования запроса к реляционной базе данных, практически ничем не отличается от синтаксиса запроса данных, хранящихся в массиве. Для этой цели теперь не нужно прибегать к средствам SQL или другого внешнего по отношению к C# механизма извлечения данных из источника. Возможности формировать запросы отныне полностью интегрированы в язык С#.

Помимо SQL, LINQ можно использовать вместе с XML‑файлами и наборами данных ADO.NET Dataset. Не менее важным является применение LINQ вместе с массивами и коллекциями в C# (подробнее рассматриваемыми в главе 25). Таким образом, средства LINQ предоставляют, в целом, единообразный доступ к данным. И хотя такой принцип уже сам по себе является весьма эффективным и новаторским, преимущества LINQ этим не ограничиваются. LINQ предлагает осмыслить иначе и подойти по‑другому к решению многих видов задач программирования, помимо традиционной организации доступа к базам данных. И в конечном итоге многие решения могут быть выработаны на основе LINQ.

LINQ поддерживается целым рядом взаимосвязанных средств, включая внедренный в C# синтаксис запросов, лямбда‑выражения, анонимные типы и методы расширения. О лямбда‑выражениях речь уже шла в главе 15, а остальные средства рассматриваются в этой главе.

ПРИМЕЧАНИЕ

LINQ в C# – это, по сути, язык в языке. Поэтому предмет рассмотрения LINQ довольно обширен и включает в себя многие средства, возможности и альтернативы. Несмотря на то что в этой главе дается подробное описание средств LINQ, рассмотреть здесь все их возможности, особенности и области применения просто невозможно. Для этого потребовалась бы отдельная книга. В связи с этим в настоящей главе основное внимание уделяется главным элементам LINQ, применение которых демонстрируется на многочисленных примерах. А в долгосрочной перспективе LINQ представляет собой подсистему, которую придется изучать самостоятельно и достаточно подробно.

Основы LINQ

В основу LINQ положено понятие запроса, в котором определяется информация, получаемая из источника данных. Например, запрос списка рассылки почтовых сообщений заказчикам может потребовать предоставления адресов всех заказчиков, проживающих в конкретном городе; запрос базы данных товарных запасов – список товаров, запасы которых исчерпались на складе; а запрос журнала, регистрирующего интенсивность использования Интерента, – список наиболее часто посещаемых вебсайтов. И хотя все эти запросы отличаются в деталях, их можно выразить, используя одни и те же синтаксические элементы LINQ.

Как только запрос будет сформирован, его можно выполнить. Это делается, в частности, в цикле foreach. В результате выполнения запроса выводятся его результаты. Поэтому использование запроса может быть разделено на две главные стадии. На первой стадии запрос формируется, а на второй – выполняется. Таким образом, при формировании запроса определяется, что именно следует извлечь из источника данных. А при выполнении запроса выводятся конкретные результаты.

Для обращения к источнику данных по запросу, сформированному средствами LINQ, в этом источнике должен быть реализован интерфейс I Enumerable. Он имеет две формы: обобщенную и необобщенную. Как правило, работать с источником данных легче, если в нем реализуется обобщенная форма IEnumerable, где Т обозначает обобщенный тип перечисляемых данных. Здесь и далее предполагается, что в источнике данных реализуется форма интерфейса IEnumerable. Этот интерфейс объявляется в пространстве имен System. Collections. Generic. Класс, в котором реализуется форма интерфейса IEnumerable, поддерживает перечисление, а это означает, что его содержимое может быть получено по очереди или в определенном порядке. Форма интерфейса IEnumerable поддерживается всеми массивами в С#. Поэтому на примере массивов можно наглядно продемонстрировать основные принципы работы LINQ. Следует, однако, иметь в виду, что применение LINQ не ограничивается одними массивами.

Простой запрос

А теперь самое время обратиться к простому примеру использования LINQ. В приведенной ниже программе используется запрос для получения положительных значений, содержащихся в массиве целых значений.

// Сформировать простой запрос LINQ.

using System; using System.Linq;

class SimpQuery {

static void Main() {

int[] nums = { .1, ‑2, 3, 0, ‑4, 5 };

// Сформировать простой запрос на получение только положительных значений, var posNums = from n in nums where n > 0 select n;

Console.Write("Положительные значения из массива nums: ");

// Выполнить запрос и отобразить его результаты, foreach(int i in posNums) Console.Write(i + " ");

Console.WriteLine();

}

}

Эта программа дает следующий результат.

Положительные значения из массива nums: 1 3 5

Как видите, в конечном итоге отображаются только положительные значения, хранящиеся в массиве nums. Несмотря на всю свою простоту, этот пример наглядно демонстрирует основные возможности LINQ. Поэтому рассмотрим его более подробно.

Прежде всего обратите внимание на применение в данном примере программы следующего оператора.

Для применения средств LINQ в исходный текст программы следует включить пространство имен System.Linq.

Затем в программе объявляется массив nums типа int. Все массивы в C# неявным образом преобразуются в форму интерфейса IEnumerable. Благодаря этому любой массив в C# может служить в качестве источника данных, извлекаемых по запросу LINQ.

Далее объявляется запрос, по которому из массива nums извлекаются элементы только с положительными значениями.

var posNums = from n in nums where n > 0 select по переменная posNums называется переменной запроса. В ней хранится ссылка на ряд правил, определяемых в запросе. Обратите внимание на применение ключевого слова var для объявления переменной posNums неявным образом. Как вам должно быть уже известно, благодаря этому переменная posNums становится неявно типизированной. Такими переменными удобно пользоваться в запросах, хотя их тип можно объявить и явным образом (это должна быть одна из форм интерфейса IEnumerable). Объявляемой переменной posNums в итоге присваивается выражение запроса.

Все запросы начинаются с оператора from, определяющего два элемента. Первым из них является переменная диапазона , принимающая элементы из источника данных. В рассматриваемом здесь примере эту роль выполняет переменная п. Вторым элементом является источник данных (в данном случае – массив nums). Тип переменной диапазона выводится из источника данных. Поэтому переменная п относится к типу int. Ниже приведена общая форма оператора from.

from переменная_диапазона in источник_данных

Далее следует оператор where, обозначающий условие, которому должен удовлетворять элемент в источнике данных, чтобы его можно было получить по запросу. Ниже приведена общая форма синтаксиса оператора where.

where булево_выражение

В этой форме булево_выражение должно давать результат типа bool. Такое выражение иначе называется предикатом. В запросе можно указывать несколько операторов where. В данном примере программы используется следующий оператор where.

where n > О

Этот оператор будет давать истинный результат только для тех элементов массива, значения которых оказываются больше нуля. Выражение п > 0 будет вычисляться для каждого из п элементов массива п при выполнении запроса. В итоге будут получены только те значения, которые удовлетворяют этому условию. Иными словами, оператор where выполняет роль своеобразного фильтра, отбирая лишь определенные элементы.

Все запросы оканчиваются оператором select или group. В данном примере используется оператор select, точно определяющий, что именно должно быть получено по запросу. В таких простых примерах запросов, как рассматриваемый здесь, выбирается конкретное значение диапазона. Поэтому по данному запросу возвращаются только те целые значения, которые удовлетворяют условию, указанному в операторе where. В более сложных запросах можно дополнительно уточнять, что именно

следует выбирать. Например, по запросу списка рассылки может быть получена лишь фамилия адресата вместо его полного адреса. Обратите внимание на то, что оператор select завершается точкой с запятой, поскольку это последний оператор в запросе. А другие его операторы не оканчиваются точкой с запятой.

Итак, переменная запроса posNums создана, но результаты запроса пока еще не получены. Дело в том, что сам запрос определяет лишь ряд конкретных правил, а результаты будут только после выполнения запроса. Кроме того, один и тот же запрос может быть выполнен два раза или больше, причем с разными результатами, если в промежутке между последовательно производимыми попытками выполнить один и тот же запрос изменяется базовый источник данных. Поэтому одного лишь объявления переменной запроса posNums совершенно недостаточно для того, чтобы она содержала результаты запроса.

Для выполнения запроса в данном примере программы организуется следующий цикл.

foreach(int i in posNums) Console.WriteLine(i + " ");

В этом цикле переменная posNums указывается в качестве коллекции, к которой происходит обращение на каждом шаге цикла. В цикле foreach соблюдаются правила, определенные в запросе и доступные по ссылке из переменной posNums. На каждом шаге цикла возвращается очередной элемент, полученный из массива. Этот процесс завершается, когда запрашиваемых элементов в массиве больше не обнаружено. В данном примере тип int переменной шага цикла i указывается явно, поскольку по запросу извлекаются элементы именно этого типа. Явное указание типа переменной шага цикла вполне допустимо в тех случаях, когда заранее известен тип значения, выбираемого по запросу. Но в более сложных случаях оказывается проще, а иногда даже нужно, указывать тип переменной шага цикла неявным образом с помощью ключевого слова var.

Неоднократное выполнение запросов

Итак, в запросе определяются правила, по которым извлекаются данные, но этого явно недостаточно для получения результатов, поскольку запрос должен быть выполнен, причем это может быть сделано несколько раз. Если же в промежутке между последовательно производимыми попытками выполнить один и тот же запрос источник данных изменяется, то получаемые результаты могут отличаться. Поэтому как только запрос определен, его выполнение будет всегда давать только самые последние результаты. Обратимся к конкретному примеру. Ниже приведен другой вариант рассматриваемой здесь программы, где содержимое массива nums изменяется в промежутке между двумя последовательно производимыми попытками выполнить один и тот же запрос, хранящийся в переменной posNums.

// Сформировать простой запрос.

using System;

using System.Linq;

using System.Collections.Generic;

class SimpQuery {

static void Main() {

int[] nums = { 1, ‑2, 3, 0, ‑4, 5 };

// Сформировать простой запрос на получение только положительных значений, var posNums = from n in nums where n > 0 select n;

Console.Write("Положительные значения из массива nums: ");

// Выполнить запрос и отобразить его результаты, foreach(int i in posNums) Console.Write(i + " ");

Console.WriteLine ();

// Внести изменения в массив nums.

Console.WriteLine("ХпЗадать значение 99 для элемента массива nums[l]."); nums[l] = 99;

Console.Write("Положительные значения из массива nums\n" +

"после изменений в нем: ");

// Выполнить запрос второй раз.

foreach(int i in posNums) Console.Write(i + " ");

Console.WriteLine();

}

}

Вот к какому результату приводит выполнение этой программы.

Положительные значения из массива nums: 1 3 5

Задать значение 99 для элемента массива nums[l].

Положительные значения из массива nums после изменений в нем: 1 99 3 5

Как следует из результата выполнения приведенной выше программы, значение элемента массива nums [ 1 ] изменилось с ‑2 на 99, что и отражают результаты повторного выполнения запроса. Этот важный момент следует подчеркнуть особо. Каждая попытка выполнить запрос приносит свои результаты, получаемые при перечислении текущего содержимого источника данных. Поэтому если источник данных претерпевает изменения, то могут измениться и результаты выполнения запроса. Преимущества такого подхода к обработке'запросов весьма значительны. Так, если по запросу получается список необработанных заказов в Интернет‑магазине, то при каждой попытке выполнить запрос желательно получить сведения обо всех заказах, включая и только что введенные.

Связь между типами данных в запросе

Как показывает предыдущий пример, запрос включает в себя переменные, типы которых связаны друг с другом. К их числу относятся переменная запроса, переменная диапазона и источник данных. Соблюсти соответствие этих типов данных очень важно, но в то же время нелегко – по крайней мере, так кажется на первый взгляд, поэтому данный вопрос заслуживает более пристального внимания.

Тип переменной диапазона должен соответствовать типу элементов, хранящихся в источнике данных. Следовательно, тип переменной диапазона зависит от типа источника данных. Как правило, тип переменной диапазона может быть выведен средствами С#. Но выводимость типов может быть осуществлена при условии, что в источнике данных реализована форма интерфейса IEnumerable, где Т обозначает тип элементов в источнике данных. (Как упоминалось выше, форма интерфейса IEnumerable реализуется во всех массивах, как, впрочем, и во многих других источниках данных.) Но если в источнике данных реализован необобщенный вариант интерфейса I Enumerable, то тип переменной диапазона придется указывать явно. И это делается в операторе from. Ниже приведен пример явного объявления типа int переменной диапазона п.

var posNums = from int n in nums // ...

Очевидно, что явное указание типа здесь не требуется, поскольку все массивы неявно преобразуются в форму интерфейса IEnumerable, которая позволяет вывести тип переменной диапазона.

Тип объекта, возвращаемого по запросу, представляет собой экземпляр интерфейса IEnumerable, где Т – тип получаемых элементов. Следовательно, тип переменной запроса должен быть экземпляром интерфейса IEnumerable, а значение Т должно определяться типом значения, указываемым в операторе select. В предыдущих примерах значению Т соответствовал тип int, поскольку переменная п имела тип int. (Как пояснялось выше, переменная п относится к типу int, потому что элементы именно этого типа хранятся в массиве nums.) С учетом явного указания типа IEnumerable упомянутый выше запрос можно было бы составить следующим образом.
1   ...   49   50   51   52   53   54   55   56   ...   97


написать администратору сайта