лекция. Зиборов. Справочник для опытных и как пособие для начинающих программистов. Компактдиск содержит исходные коды примеров из книги
Скачать 7.39 Mb.
|
ГЛАВА 11. Использование технологии LINQ. Технология LINQ (Language Integrated Query) предназначена для обработки (запросов и преобразований) практически любого типа источника данных, начиная от массивов, файлов, строк, коллекций объектов .NET Framework, баз данных SQL Server, наборов данных ADO.NET (DataSet) и XML-документов. LINQ упрощает ситуацию, предлагая единообразные стандартные шаблоны для работы с данными в различных видах источников и форматов данных. Стандартные шаблоны включают в себя основные операции запросов LINQ: фильтрация, упорядочение, группировка, соединение, выбор (проецирование), статистическая обработка. По форме синтаксис языка LINQ очень похож на язык запросов SQL. В данной главе рассмотрим типичные LINQ-запросы и преобразования к некоторым источникам данных. Пример 75. LINQ-запрос к массиву данных LINQ-запрос представляет собой выражение, извлекающее данные из источника данных. Все операции запроса LINQ состоят из трех различных действий: получение источника (в нашем случае — это присвоение начальных значений исходному массиву) данных, создание запроса (начинается с предложения from) и непосредственное выполнение запроса (обычно это цикл foreach). В данном разделе рассмотрим две задачи, первая — из массива имен извлекаем имена длиной шесть символов, записывая их или в список (коллекцию), или в новый массив. Другая задача — из массива целых чисел выбираем только те, значения которых больше четырех, также записывая результат запроса в список или массив. Итак, запустим Visual Studio 2010, закажем новый проект шаблона Windows forms Application С#. Затем из панели элементов перенесем в форму текстовое поле. Далее через щелчок правой кнопкой мыши перейдем к вкладке программного кода (листинг 11.1). Листинг 11.1. Извлечение данных из массивов // Решаем две задачи по выбору элементов из массива с помощью стандартных запросов технологии LINQ using System; using System.Linq; using System.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Linq1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Text = "Технология LINQ"; textBox1.Multiline = true; // ЗАДАЧА 1. //Из массива имен выбрать имена с количеством букв, равным шести, // вывести эти имена в текстовое поле TextBox в алфавитном порядке, // при этом все буквы перевести в верхний регистр. // Решение: string СтрокаИмен = "Витя Лариса Лариса Лена Андрей Женя " + "Александр Лариса Виктор Света Оксана Наташа"; //Из строки имен получаем массив имен, задавая в качестве // разделителя подстрок символ пробела: string[] Имена = СтрокаИмен.Split(' '); // или проще: string[] Имена = // { "Витя", "Лариса", "Лариса", "Лена", "Андрей", "Женя", // "Александр", "Лариса", "Виктор", "Света", "Оксана", "Наташа" } textBox1.Text = "ЗАДАЧА 1, В списке имен:\r\n\r\n"; foreach (string х in Имена) textBoxl.Text = textBox1.Text + x + " "; // В результате LINQ-запроса получаем список имен с количеством букв, // равным шести: var Запрос = from s in Имена where s.Length ==6 // - условие выбора orderby s // - сортировать в алфавитном порядку select s.ToUpper(); // - перевод в верхний регистр // s - переменная диапазона, схожа с переменной итерации в foreach // Удаляем элементы-дубликаты из списка имен: Запрос = Запрос.Distinct(); // Или таким образом: // Запрос = Запрос.Union(Запрос); textBox1.Text = textBox1.Text + "\r\n\r\n" + "выбираем имена с количеством букв равным шести, при " + "этом избавляемся от дублирования имен:\r\n\r\n"; foreach (string x in Запрос) // x - переменная итерации в цикле foreach textBoxl.Text = textBox1.Text + x + " "; textBoxl.Text = textBox1.Text + "\r\n\r\n"; // // ЗАДАЧА 2. Из массива целых чисел X[] требуется выбрать числа, // значения которых >= 4, и записать эти числа в список Y, // отсортировав выбранные числа по возрастанию. // Решение. // Инициализация массива целых чисел: int[] X = { -2, 5, -23, 0, 7, -10, 11, 11, 14, 3, 8, 5, -5, 27, 8 }; textBox1.Text += "ЗАДАЧА 2. Из заданного массива X:\r\n\r\n"; foreach (int х in X) textBox1.Text = textBoxl.Text + x + " "; textBox1.Text = textBox1.Text + "\г\п\г\пвыбираем числа, значения " + "которых >= 4 и записываем их в список (коллекцию) Y, " + "исключая элементы-дубликаты:\r\n\r\n"; // Y - это список, в который помещаются выбранные элйменты: var Y = from х in X where х >= 4 // - условие выбора orderby х // - сортируем по возрастанию select х; // Таким образом можно получить результат запроса в массив: // int[] Y = (from х in X // Здесь Y - это уже массив; // where х >= 4 /./ - условие выбора; // orderby х // - сортируем по возрастанию; // select х).ToArray(); // - преобразование списка в массив // Удаляем элементы-дубликаты из списка целых чисел: var Z = Y.Distinct(); // Или таким образом: // var Z = Y.Union(Y); // Вывод элементов списка Y в метку textBox1: foreach (var z in Z) textBox1.Text = textBox1.Text + z.ToString() + " "; } } } Как видно из программного кода, после присвоения массиву Имена начальных значений создаем запрос, который предусматривает выбор (select) из (from) массива Имена строк длиной (Length) ровно шесть символов (условие where). Запись выбранных имен выполняется в список Запрос с сортировкой списка в алфавитном порядке имен. Далее для удаления повторяющихся имен в списке использу, функцию Distinct. В комментарии показано, как можно для этой же цели используем функцию Union, которая, вообще говоря, находит объединение двух множеств, т. е. выбирает в двух списках одинаковые элементы с удалением повторяющихся элементов. Поскольку мы объединяем два одинаковых списка, то получаем просто удаление повторяющихся элементов. Цикл foreach выводит список Запрос в текстовое поле textBox1. Следующая задача, решенная в данном программном коде, аналогична. Задан массив целых чисел х. Из этого массива выбираем в список Y элементы массщ значения которых больше или равны четырем. В комментарии показано, как можно записать результаты LINQ-запроса в массив. Удаление повторяющихся элементов в списке выполнено также с использованием функции Distinct. На рис. 11.1 приведен фрагмент работы программы. Рис. 11.1 LINQ-запросы к массивам данных Убедиться в работоспособности программы можно, открыв решение Linq1.sln папки Linq1. Пример 76. LINQ-запрос к коллекции (списку) данных В некоторых случаях хранение данных в коллекции (скажем, в списке типаList) может оказаться более эффективным, чем в массиве. Например, если число элементов в массиве при работе изменяется часто или нельзя предсказать максимальное количество необходимых элементов, то можно получить большую производительность при использовании коллекции. Но если размер не изменяется или изменяется довольно редко, то массив, пожалуй, более эффективен. Как всегда, производительность в большей степени зависит от конкретного приложения. Как советуют в документации MSDN, часто стоит потратить время на испытание и массива и коллекции, чтобы выбрать наиболее практичный и эффективный вариант. Решим три типичных задачи, их условия сформулируем при объяснении работы программы. Запустим Visual Studio 2010, закажем новый проект шаблона Windows Forms Application C#. Затем из панели элементов перенесем в форму текстовое поле. Далее через щелчок правой кнопкой мыши перейдем к вкладке программного кода (листинг 11.2). Листинг 11.2. Извлечение данных из списков // Решаем три различных задачи по выбору элементов (объектов) из списка // с помощью стандартных запросов технологии LINQ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Linq2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Text = "Технология LINQ"; textBox1.Multiline = true; // ЗАДАЧА 1. Из списка строк выбрать нужные записи, // задав условие выбора textBox1.Text = "ЗАДАЧА 1: Из списка имен:\r\n"; // Объявление списка строк и его заполнение: var Список = new List "Лариса", "Маша", "Наташа" }; // или var Список = new List // Список.Add("Витя"); Список.Add("Света"); Список.Add("Андрей"); // Список.Add("Лариса"); Список.Add("Маша"); Список.Add("Наташа"); // Некоторые манипуляции со списком: int n = Список.Count; // количество элементов в списке // Получение из списка третьего элемента (как в массиве): string А = Список.ElementAt Boolean Ответ = Список.Remove("Лариса"); // - удаление из списка // Преобразовать список в строковый массив: string[] МассивСтрок = Список.ToArray(); foreach (var x in Список) textBox1.Text = textBoxl.Text + x.ToStringf) + " "; textBox1.Text += "\r\nвыбираем имена длиной четыре символа:\r\n"; // СписокВыбранныхИмен - это новый список, в который попадают // выбранные строки в результате LINQ-запроса: var СписокВыбранныхИмен = from Имя in Список where Имя.Length ==4 // условие выбора orderby Имя // сортировать список select Имя; // Вывод списка выбранных имен в текстовое поле textBox1: foreach (var х in СписокВыбранныхИмен) textBox1.Text = textBox1.Text + x.ToString(J + " "; //---------------------------------------------------------------------------------------------------------------------- // ЗАДАЧА 2. textBox1.Text += "\r\n\r\nЗАДАЧА 2: Из списка сотрудников предприятия " + "выбираем некурящих для повышения зарплаты:\r\n\r\n"; // Заполняем список сотрудников: List new Сотрудник {Имя="Карапузова Ирина", Возраст=27, КуритЛи=true }, new Сотрудник {Имя="Зиборов Виктор", Возраст=47, KypитЛи=false }, new Сотрудник {Имя="Ломачинская Светлана", Возраст=31, KypитЛи=false }, new Сотрудник {Имя="Стороженко Светлана", Возраст=34, KypитЛи=false }, new Сотрудник {Имя="Еременко Татьяна", Возраст=22, КуритЛи= true }, new Сотрудник {Имя="Погребицкий Олег", Возраст=42, КуритЛи= true}, }; var СписокНекурящихСотрудников = from Сотрудник in Сотрудники where Сотрудник.КуритЛи == false orderby Сотрудник.Имя select Сотрудник; // Вывод списка некурящих сотрудников в текстовое поле textBox1: foreach (var х in СписокНекурящихСотрудников) textBox1.Text = textBox1.Text + string.Format("{0} - возраст " + "- {1}\r\n", х.Имя, x.Возраст); //------------------------------------------------------------------------------------------------------------------------------- // ЗАДАЧА 3. textBox1.Text += "\г\пЗАДАЧА 3: Из списка студентов факультета " + "выбираем двоечников:\r\n\r\n"; // Каждый элемент в списке содержит фамилию студента // и полученные им текущие оценки: List<Студент> Студенты = new List<Студент> { new Студент {Фамилия="Зиборов", Оценки= new List new Студент {Фамилия="Стороженко", Оценки= new List new Студент {Фамилия="Ломачинская", Оценки= new List new Студент {Фамилия="Погребицкий", Оценки= new List new Студент {Фамилия="Левочкин", Оценки= new List }; //Для доступа к внутреннему списку оценок предложение From используем два раза: var СписокДвоечников = from Студент in Студенты from Оценка in Студент.Оценки where Оценка <= 2 orderby Студент.Фамилия select new { Студент.Фамилия, Оценка }; foreach (var Студик in СписокДвоечников) textBox1.Text += string.Format("Студент {0} " + "имеет оценку: {1}\r\n", Студик.Фамилия, Студик.Оценка); // Строка со студентом Погребицким выводится два раза, // поскольку он имеет две двойки } } // Объявляем класс, содержащий имя сотрудника, его возраст, а также информацию, курит ли он: public class Сотрудник { public string Имя { get; set; } public int Возраст { get; set; } public bool КуритЛи { get; set; } ) //Объявляем класс, содержащий фамилию студента и список полученных им оценок: Public class Студент { public string Фамилия { get; set; } public List } } Первая задача довольно простая и очень похожа на задачи, которые мы решали с помощью технологии LINQ в предыдущем примере. Здесь, в этой новой задаче вместо массива имен требуется создать список имен, а далее из этого списка вы. брать имена длиной четыре символа. Решение этой задачи, построение LINQ-запроса аналогично решению задачи из предыдущего примера, отличие состоит лишь в применении синтаксиса манипуляций со списком типа List, а не с массивом. Здесь также приведены некоторые важные техники для манипуляции списком в частности получение элемента списка по указанному индексу (аналогия с массивом), удаление элемента, преобразование списка в строковый массив. Вторая задача заключается в том, чтобы создать список сотрудников предприятия и из этого списка выбрать некоторых сотрудников по какому-либо признаку например тех, кто не курит. При создании списка объявлен класс Сотрудник, который содержит три свойства: Имя, Возраст и булеву переменную КуритЛи. В начале решения заполняем список сотрудников, а затем строим LINQ-запрос для заполнения списка некурящих сотрудников. Этот список выводим в текстовое поле textBox1, используя цикл foreach. Третья задача немного сложнее. Требуется создать список студентов факультета, содержащий фамилию студента и список полученных им текущих оценок, т. е. список оценок должен быть "вложен" в список студентов. Из списка студентов необходимо выбрать тех, кто имеет в списке своих оценок хотя бы одну двойку. Для решения этой задачи вначале объявляем новый класс Студент, который имеет в качестве свойств класса фамилию студента и список (типа List) оценок. Далее в начале решения третьей задачи мы привели синтаксис заполнения списка студентов. Затем строим LINQ-запрос, где, поскольку нам необходимо искать элемент списка внутри "вложенного" списка, мы используем предложение From два раза. Поскольку студент Погребицкий имеет две двойки в списке своих текущих оценок, он в списке двоечников фигурирует дважды (рис. 11.2). Рис. 11.2. Три LINQ-запроса к спискам данных Убедиться в работоспособности программы можно, открыв решение Linq2.sln папки Linq2. Пример 77. Группировка элементов списка с помощью LINQ-запроса В данном примере используем стандартный шаблон LINQ-запроса для группивки элементов списка. Программа формирует список продуктов питания. Следует организовать такие LINQ-запросы, которые разделят искомый список на две уппы по критерию цены (больше или меньше 90 руб. за единицу) и вычислят среднюю цену по каждой группе продуктов. Для решения этой задачи запустим Visual Studio 2010 и выберем проект шаблона Windows Forms Application, укажем имя Name — LinqЦеныНаПродукты. Далее, попав в конструктор формы, из панели элементов Toolbox перетащим текстовое поле TextBox для вывода в него групп списка. В свойствах текстового поля разрешим ввод множества строк, для этого свойство Multiline переведем в состояние true. Затем на вкладке программного кода введем текст, представленный в листинге 11.3. |