Разработка программы демонстрации использования запросов на языке LINQ
В данной теме рассматривается пошаговый процесс решения задачи, в которой используются запросы на языке LINQ. Используя данный пример, можно научиться решать подобные задачи.
В теме также показано:
- чтение данных из файла с использованием класса StreamReader;
- представление данных в виде обобщенного динамического массива List<T>;
- примеры некоторых запросов на языке LINQ, решающих данную задачу.
Данная тема отображает пошаговый процесс выполнения лабораторной работы в одном из учебных заведений нашей планеты.
Содержание
- Условие задачи
- Дополнительные соображения
- Выполнение
- 1. Создание проекта типа Windows Forms Application
- 2. Создание главной формы программы
- 3. Настройка элементов управления формы
- 4. Подключение пространства имен System.Linq
- 5. Разработка внутренних структур данных, которые соответствуют файлам «Workers.txt» и «Salary.txt»
- 6. Подключение пространства имен System.IO
- 7. Создание методов Read_Workers() и Read_Salary() для чтения данных из файлов «Workers.txt» и «Salary.txt»
- 8. Программирование конструктора Form1() главной формы программы. Чтение данных из файлов
- 9. Программирование события клика на кнопке «Задание 1»
- 10. Программирование события клика на кнопке «Задание 2»
- 11. Программирование события клика на кнопке «Задание 3»
- 12. Запуск программы на выполнение
Поиск на других ресурсах:
Условие задачи
Файл «Workers.txt» содержит следующую информацию о сотрудниках:
- идентификационный код;
- фамилия и инициалы;
- вид образования;
- специальность;
- год рождения.
Файл «Salary.txt» содержит:
- идентификационный код;
- зарплату за первое полугодие;
- зарплату за второе полугодие.
Загрузить файл «Workers.txt» можно здесь. Загрузить файл «Salary.txt» можно здесь.
Решить следующие задачи:
- Вывести фамилии и инициалы сотрудников выше 35 лет.
- Вывести идентификационный код сотрудника с наибольшей зарплатой за второе полугодие.
- Вывести фамилии, инициалы и вид образования тех сотрудников, зарплата которых за год ниже средней за год.
Задачи должны быть выполнены с использованием запросов LINQ.
⇑
Дополнительные соображения
Как видно из структур файлов «Workers.txt» и «Salary.txt«, эти файлы имеют одинаковое поле «идентификационный код«. Это означает, что при вводе данных в файлы нужно быть внимательным. Идентификационные коды должны быть уникальными в одном файле и обязательно повторяться в разных файлах.
Содержимое файла «Workers.txt«:
1; Ivanov I.I.; Bachelor; Programmer Engineer; 1900 2; Petrov P.P.; Master; Team Lead; 1950 3; Sidorov S.S.; Super Master; Software Architect; 1990 4; Johnson J.J.; Super Bachelor; HTML-coder; 1997 5; Nicolson J.J.; Bachelor; DevOps engineer; 1992
Содержимое файла «Salary.txt«:
1; 23550; 26580 2; 26800; 28390 3; 24660; 27777 4; 35880; 44444 5; 55555; 39938
⇑
Выполнение
1. Создание проекта типа Windows Forms Application
Создать проект как Windows Forms Application.
Пример создания проекта типа Windows Forms Application приведен здесь.
Сохранить файлы проекта в некоторой папке. Скопировать файлы Workers.txt и Salary.txt в один и тот же каталог, где может быть исполняемый (.exe) файл программы.
⇑
2. Создание главной формы программы
Создать форму как показано на рисунке 1. На форме размещаются следующие элементы управления:
- три элемента управления типа Label. Автоматически создается три объекта (экземпляры классов) с именами label1, label2, label3;
- три элемента управления типа Button. Автоматически создается три объекта с именами button1, button2, button3;
- три элемента управления типа ListBox. Автоматически создается три объекта с именами listBox1, listBox2, listBox3.
Рис. 1. Основная форма программы
⇑
3. Настройка элементов управления формы
Настроить следующие элементы управления на форме:
- выделить элемент управления label1 (с помощью мышки). В элементе управления label1 свойство Text = «Файл Workers.txt«;
- в элементе управления label2 свойство Text = «Файл Salary.txt«;
- в элементе управления label3 свойство Text = «Результат«;
- в элементе управления button1 свойство Text = «Задание 1«;
- в элементе управления button2 свойство Text = «Задание 2«;
- в элементе управления button3 свойство Text = «Задание 3«.
После настройки форма будет иметь вид, как показано на рисунке 2.
Рис. 2. Форма после настройки элементов управления
⇑
4. Подключение пространства имен System.Linq.
Чтобы использовать запросы на языке LINQ нужно подключить пространство имен System.Linq. Как правило, пространство имен System.Linq подключается автоматически при создании приложения типа Windows Forms Application.
В файле Form1.cs строка подключения имеет вид:
using System.Linq;
⇑
5. Разработка внутренних структур данных, которые соответствуют файлам «Workers.txt» и «Salary.txt«.
Данные, соответствующие одной строке файла «Workers.txt» целесообразно представить в виде структуры Workers:
struct Workers { public string code; // идентификационный код public string name; // фамилия и инициалы public string education; // вид образования public string profession; // специальность public int year; // год рождения }
Так же данные, соответствующие одной строке файла Salary.txt целесообразно представить в структуре Salary:
struct Salary { public string code; // идентификационный код public float salary1; // зарплата за 1 полугодие public float salary2; // зарплата за 2 полугодие }
Поскольку, строк в файлах может быть много, то все данные можно разместить в виде обобщенных динамических массивов типа List<T>.
Для структур Workers и Salary динамические массивы имеют следующее описание:
List<Workers> lw = null; // список структур типа Workers List<Salary> ls = null; // список структур типа Salary
После ввода структур, класс Form1 имеет вид:
... public partial class Form1 : Form { struct Workers { public string code; // идентификационный код public string name; // фамилия и инициалы public string education; // вид образования public string profession; // специальность public int year; // год рождения } struct Salary { public string code; // идентификационный код public float salary1; // зарплата за 1 полугодие public float salary2; // зарплата за 2 полугодие } List<Workers> lw = null; // список структур типа Workers List<Salary> ls = null; // список структур типа Salary public Form1() { InitializeComponent(); } } ...
⇑
6. Подключение пространства имен System.IO.
Для чтения данных из файлов, в программе будут использованы возможности класса StreamReader из библиотеки классов .NET Framework. Поэтому, для использования методов этих классов, в начале файла «Form1.cs» нужно добавить строку:
using System.IO;
⇑
7. Создание методов Read_Workers() и Read_Salary() для чтения данных из файлов «Workers.txt» и «Salary.txt«.
Для чтения данных из файлов «Workers.txt» и «Salary.txt» в класс Form1 нужно ввести два метода:
- Read_Workers();
- Read_Salary().
Листинг метода Read_Workers() следующий:
// чтение данных из файла "Workers.txt" public void Read_Workers() { // создать объект класса StreamReader, соответствующий файлу "Workers.txt" StreamReader sr = File.OpenText("Workers.txt"); string[] fields; // переменная, отвечающая полям структуры Workers string line = null; Workers w; // прочитать строку line = sr.ReadLine(); while (line != null) { // разбить строку на подстроки - разделителем есть символ ';' fields = line.Split(';'); // создание структуры типа Workers w.code = fields[0]; w.name = fields[1]; w.education = fields[2]; w.profession = fields[3]; w.year = Int32.Parse(fields[4]); // добавление структуры типа Workers в список List<Workers> lw.Add(w); // добавление строки в список listBox1 listBox1.Items.Add(line); // прочитать следующую строку line = sr.ReadLine(); } }
Метод Read_Workers() читает данные из файла «Workers.txt» и записывает их в:
- динамический массив lw типа List<Workers>;
- элемент управления listBox1 для отображения на форме.
Листинг метода Read_Salary() следующий:
// чтение данных из файла "Salary.txt" public void Read_Salary() { // создать объект класса StreamReader, соответствующий файлу "Salary.txt" StreamReader sr = File.OpenText("Salary.txt"); string[] fields; // переменная, соответствующая полям структуры Workers string line = null; Salary s; // прочитать строку line = sr.ReadLine(); while (line != null) { // разбить строку на подстроки - разделителем есть символ ';' fields = line.Split(';'); // создание структуры типа Salary s.code = fields[0]; s.salary1 = (float)Convert.ToDouble(fields[1]); s.salary2 = (float)Double.Parse(fields[2]); // добавление структуры типа Salary в список List<Salary> ls.Add(s); // добавление строки в список listBox2 listBox2.Items.Add(line); // прочитать следующую строку line = sr.ReadLine(); } }
Метод Read_Salary() читает данные из файла «Salary.txt» и записывает их в:
- динамический массив ls типа List<Salary>;
- элемент управления listBox2 для отображения на форме.
⇑
8. Программирование конструктора Form1() главной формы программы. Чтение данных из файлов.
После запуска программы на выполнение данные из файлов должны быть автоматически загружены в элементы управления listBox1 и listBox2.
Поэтому, в конструктор класса Form1() нужно добавить методы Read_Workers() и Read_Salary().
Также, в конструктор класса Form1() добавляется выделение памяти для динамических массивов lw и ls.
Общий листинг конструктора главной формы имеет вид:
public Form1() { InitializeComponent(); // выделение памяти для списков lw = new List<Workers>(); ls = new List<Salary>(); // очистить компоненты типа ListBox listBox1.Items.Clear(); listBox2.Items.Clear(); listBox3.Items.Clear(); // прочитать данные из файла "Workers.txt" Read_Workers(); Read_Salary(); }
⇑
9. Программирование события клика на кнопке «Задание 1«.
Выполнение пункта 1 задачи реализуется с помощью клика на кнопке «Задание 1″. В результате будет вызван соответствующий обработчик события.
Пример программирования события клика на кнопке в приложении типа Windows Forms Application приведен здесь.
Листинг обработчика события клика на кнопке «Задание 1«.
// Фамилии и инициалы сотрудников старше 35 лет private void button1_Click(object sender, EventArgs e) { // запрос с именем names var names = from nm in lw where nm.year < (2016-35) select nm.name; listBox3.Items.Clear(); // очистить список // добавить в список listBox3 результат запроса names foreach (string s in names) listBox3.Items.Add(s); }
В приведенном листинге формируется запрос на языке LINQ. Этот запрос носит имя names:
var names = from nm in lw where nm.year < (2016-35) select nm.name;
Запрос начинается с оператора from. В операторе from задается переменная диапазона, которая носит имя nm. Источником данных в операторе from есть динамический массив lw типа Workers.
Следующим идет оператор where, означающий условие, которому должен удовлетворять элемент в источнике данных, чтобы его можно было бы получить по запросу.
Запрос завершается оператором select. В операторе select задается, что именно должно выводиться по запросу. В данном примере выбирается поле name структуры Workers (фамилии и инициалы сотрудников старше 35 лет).
Для выполнения запроса в программе используется цикл foreach.
foreach (string s in names) listBox3.Items.Add(s);
Поскольку, результатом LINQ запроса есть тип string, то в цикле описывается переменная s типа string.
⇑
10. Программирование события клика на кнопке «Задание 2«.
Обработчик события клика на кнопке «Задание 2» имеет вид:
// идентификационный код сотрудника с наибольшей зарплатой // за II полугодие private void button2_Click(object sender, EventArgs e) { // запрос max_salary var max_salary = (from ms in ls select ms.salary2).Max(); // метод Max() возвращает максимальное значение listBox3.Items.Clear(); listBox3.Items.Add(max_salary); }
В этом LINQ-запросе используется метод Max(), который находит максимальное значение в перечне (списке). Этот перечень формируется в запросе с именем max_salary.
⇑
11. Программирование события клика на кнопке «Задание 3«.
Листинг обработчика события клика на кнопке «Задание 3» следующий:
// Фамилии, инициалы и вид образования тех сотрудников, // зарплата которых за год ниже средней за год private void button3_Click(object sender, EventArgs e) { var result = from w in lw from sl in ls let avg = (from s in ls select (s.salary1 + s.salary2)).Average() where ((sl.salary1 + sl.salary2) < avg) && (w.code == sl.code) select w.name +" - " + w.profession; listBox3.Items.Clear(); foreach (string s in result) listBox3.Items.Add(s); }
В этом обработчике формируется запрос с именем result.
В запросе нужно осуществить выборку с двух источников данных. В нашем случае это следующие источники:
- динамический массив lw типа List<Workers>. Из этого массива выбираются фамилия сотрудника и его вид образования;
- динамический массив ls типа List<Salary>. Из этого массива выбираются зарплаты за первое и второе полугодия.
Для того, чтобы осуществить выборку из двух источников данных, запрос содержит два вложенных оператора from. Для того, чтобы выбирать данные, которые отвечают одному идентификационному коду, в запросе (в операторе where) используется строка сравнения (w.code==sl.code):
where (...) && (w.code == sl.code)
Чтобы посчитать среднее арифметическое используется метод Average() среды .NET Framework. Само среднее арифметическое сохраняется в переменной avg, которая введена в запрос с помощью оператора let:
... let avg = (from s in ls select (s.salary1 + s.salary2)).Average() ...
⇑
12. Запуск программы на выполнение.
После программирования всех обработчиков событий можно запускать программу на выполнение.
⇑