Пример разработки приложения демонстрации работы списков и ассоциативных массивов. Классы Dictionary, List, StreamReader

Пример разработки приложения демонстрации работы списков и ассоциативных массивов. Классы Dictionary, List, StreamReader

В данной теме приведен пример разработки приложения типа Windows Forms Application, которое демонстрирует использование списков (класс List) и ассоциативных массивов (класс Dictionary).

Приложение реализует лабораторную работу одного из учебных заведений.

Используя данный пример Вы получите опыт работы с классами Dictionary<TKey,TValue> и List<T>, которые относятся к обобщенным коллекциям.

При разработке приложения реализованы следующие задачи:

  • чтение данных из файла и отображение их в элементе управления типа ListBox;
  • демонстрация использования класса StreamReader из модуля System.IO для чтения данных из файла;
  • демонстрация работы класса Dictionary<TKey, TValue> для организации коллекции. Класс сохраняет пары «ключ-значение»;
  • демонстрация работы класса List<T> для организации динамического массива или списка.

Содержание



Условие задачи

Реализовать приложение типа Windows Forms Application с использованием списков и ассоциативных массивов.

Дано два текстовых файла: «Flight.txt» и «Tickets.txt«.

Файлы содержат неограниченное количество строк. Каждая строка файла содержит данные, которые разделены символом ‘ , ‘ (запятая).

Одна строка файла «Flight.txt» имеет следующую структуру:

  • номер рейса (целое число);
  • пункт отправки (строка символов);
  • время вылета, часа (целое число);
  • общее количество мест (целое число).

Одна строка файла «Tickets.txt» имеет следующую структуру:

  • номер билета (целое число);
  • номер рейса (целое число);
  • место (целое число);
  • дата вылета (строка символов);
  • пункт назначения (строка символов);
  • дата прибытия (строка символов);
  • время прибытия (целое число);
  • цена (число с плавающей запятой);
  • время продажи билета (строка символов).

В программе нужно решить следующие задачи:

1. Прочитать данные из файла «Flight.txt» и выполнить над ними следующие операции:

  • отобразить их на Windows-форме в элементе управления типа ListBox;
  • организовать данные в коллекцию (класс Dictionary<TKey, TValue>).

2. Прочитать данные из файла «Tickets.txt» и выполнить над ними следующие операции:

  • отобразить их на Windows-форме в элементе управления типа ListBox;
  • организовать данные в динамический массив (класс List<T>).

3. Вывести на форму рейсы с максимальной продолжительностью полета. Вычисление выполнить с использованием классов Dictionary<TKey, TValue> и List<T>.

4. Вывести на форму число пассажиров, которые ждут отправления в введенный момент времени. Вычисление выполнить с использованием классов Dictionary<TKey, TValue> и List<T>.

Пример содержимого файла Flight.txt:

1,Kiev,18,120
2,Kiev ,19,100
3,Kiev,20,90
4,Kiev,22,150

Пример файла «Tickets.txt«:

1,1,1,05.10.2016,California,06.10.2016,21,500,14:10
2,1,1,05.10.2016,California,06.10.2016,21,500,14:30
3,2,1,05.10.2016,Croatia,06.10.2016,22,245,14:45
4,2,2,05.10.2016,Croatia,06.10.2016,22,245,14:50
5,2,3,05.10.2016,Croatia,06.10.2016,22,245,14:51
6,3,1,05.10.2016,Amsterdam,06.10.2016,23,400,14:55
7,3,3,05.10.2016,Amsterdam,06.10.2016,23,400,15:10
8,3,7,05.10.2016,Amsterdam,06.10.2016,23,400,15:25
9,4,1,05.10.2016,Moscow,06.10.2016,24,350,15:27
10,4,10,05.10.2016,Moscow,06.10.2016,24,350,16:08

 

Выполнение

1. Запустить MS Visual Studio.

Создать проект по шаблону C# как Windows Forms Application. Пример создания такого проекта подробно описывается здесь.

 

2. Создание формы приложения.

Разместить на форме следующие элементы управления (рисунок 1):

  • элемент управления типа ListBox для отображения данных из файла «Flights.txt«. Автоматически создается объект с именем listBox1;
  • элемент управления типа ListBox для отображения данных из файла «Tickets.txt«. Автоматически создается объект с именем listBox2;
  • элементы управления типа Label для отображения информационных сообщений. Создаются объекты с именами label1, label2, label3, label4, label5;
  • элемент управления типа TextBox который представляет строку ввода данных (время отправления). Создается объект с именем textBox1;
  • элемент управления типа Button (кнопка «Вычислить«). Создается объект с именем button1;
  • элемент управления типа ListBox (список рейсов с максимальной продолжительностью полетов). Создается объект с именем listBox3.

Скорректировать размеры и позиции элементов управления так как показано на рисунке 1.

C#. Шаблон Windows Forms. Форма приложения Рис. 1. Форма приложения после размещения элементов управления

 

3. Настройка свойств элементов управления.

Настройка свойств элементов управления осуществляется с помощью окна «Properties» системы Microsoft Visual Studio 2010.

Настроить следующие свойства элементов управления:

  • в элементе управления label1 свойство Text = «Файл Flight.txt»;
  • в элементе управления label2 свойство Text = «Файл Tickets.txt»;
  • в элементе управления label3 свойство Text = «Время отправления»;
  • в элементе управления textBox1 свойство Text = «»;
  • в элементе управления label4 свойство Text = «Число пассажиров, которые ждут отправления: «;
  • в элементе управления button1 свойство Text = «Вычислить»;
  • в элементе управления label5 свойство Text = «Рейсы максимальной продолжительностью полета:».

После настройки свойств и корректирования позиций и размеров элементов управления форма приложения будет иметь вид как показано на рисунке 2.

C#. Шаблон Windows Forms. Форма приложения Рис. 2. Форма приложения после настройки элементов управления

 

4. Организация данных в виде структур.

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

Представим данные одной строки файла «Flight.txt» в виде структуры Flight:

// Структура "Авиарейсы"
public struct Flight
{
    public int num_r; // номер рейса
    public string punkt_vd; // пункт отправления
    public int time_v; // время вылета
    public int n_places; // общее количество мест
}

По тому же принципу представляются данные одной строки файла «Tickets.txt» в структуре Tickets:

// Структура "Билеты"
public struct Tickets
{
    public int num_t; // номер билета
    public int num_r; // номер рейса
    public int place; // место
    public string date_v; // дата вылета
    public string punkt_pr; //пункт назначения
    public string data_pr; // дата прибытия
    public int time_pr; // время прибытия
    public double price; // цена
    public string time_prod; // время продажи билета
}

Фрагмент листинга файла «Form1.cs» после ввода структур имеет следующий вид:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Kontrolna_UZHNU_1
{
    public partial class Form1 : Form
    {
        // Структура "Авиарейсы"
        public struct Flight
        {
            public int num_r; // номер рейса
            public string punkt_vd; // пункт отправления
            public int time_v; // время вылета
            public int n_places; // общее количество мест
        }

        public struct Tickets
        {
            public int num_t; // номер билета
            public int num_r; // номер рейса
            public int place; // место
            public string date_v; // дата вылета
            public string punkt_pr; //пункт назначения
            public string data_pr; // дата прибытия
            public int time_pr; // время прибытия
            public double price; // цена
            public string time_prod; // время продажи билета
        }

        public Form1()
        {
            InitializeComponent();
        }
    }
} 

 

5. Классы Dictionary<Key,Value> и List<T>. Описание основных структур данных.

В соответствии с условием задачи, данные, которые размещены в файлах «Flight.txt» и «Tickets.txt«, формируются в коллекции.

Коллекция типа «словарь» (Dictionary) представляется в виде: «ключ-значение». Причем значение ключа есть уникальным (не может повторяться).

Для структуры Flight целесообразно сформировать данные в виде словаря или ассоциированного массива. Это связано с тем, что номер рейса в списке рейсов есть уникальным (не может повторяться).

В C# для представления коллекции типа «словарь» (ассоциированный массив) реализовано два базовых класса:

  • класс HashTable – принадлежит к необобщенным коллекциям;
  • класс Dictionary – принадлежит к обобщенным коллекциям.

В соответствии с условием задачи, нужно использовать класс Dictionary.

Для представления ассоциированного массива в текст класса формы нужно ввести объект с именем avia, что есть типа Dictionary<int, Flight>:

// Ассоциированный массив структур "Авиарейсы"
Dictionary<int, Flight> avia = new Dictionary<int, Flight>();

В этом описании тип int есть номером рейса, а тип Flight есть структурой, которая соответствует этому рейсу.

Для разных рейсов номер билета может повторяться. Поэтому, удобно реализовать билеты в виде обобщенного динамического массива типа List<T>. Вместо типа <T> подставляется структура Tickets – в итоге получается List<Tickets>.

Ввести объект с именем tickets:

// Динамический массив (список) структур "Билеты"
List<Tickets> tickets = new List<Tickets>();

 

6. Подключение пространства имен System.IO. Класс StreamReader.

В соответствии с условием задачи, в программе осуществляется чтение из файла. Библиотека классов .NET Framework представляет множество средств для организации взаимодействия программы с файлами (открытие файла, чтение файла и т.д.).

В данной работе использованы возможности класса StreamReader. Этот класс описывается в просторные имен System.IO. Итак, в начале файла «Form1.cs» нужно ввести строку:

using System.IO;

Класс StreamReader предназначен для ввода символов из байтового потока. Для вывода символов в байтовый поток используется класс StreamWriter.

В классе StreamReader есть метод ReadLine(), читающий текстовую строку из файла. Метод ReadLine() возвращает строку типа string. Этот метод будет использован при чтении файла в данной задаче.

 

7. Программирование методов чтения данных из файлов «Flight.txt» и «Tickets.txt«.

Прочитанные из файлов «Flight.txt» и «Tickets.txt» данные отображаются в элементах управления listBox1 и listBox2. Для отображения данных нужно создать два метода.

Первый метод Read_Avia() выполняет следующие операции:

  • читает список авиарейсов из файла «Flight.txt«;
  • формирует ассоциированный массив avia типа Dictionary<int, Flight>;
  • формирует список авиарейсов в listBox1 для его отображения на форме.

Второй метод Read_Tickets() выполняет следующие операции:

  • читает перечень купленных билетов из файла «Tickets.txt«;
  • формирует динамический массив tickets типа List<Tickets>;
  • выводит содержимое файла «Tickets.txt» в listBox2.

Листинг методов Read_Avia() и Read_Tickets() следующий:

public void Read_Avia()
{
  StreamReader sr = new StreamReader("Flight.txt", Encoding.Default);
  string s = "";
  string[] fields;
  Flight sa; // дополнительная переменная - структура типа Flight

  // Основной цикл.
  // В цикле:
  // 1. Читаются данные из файла "Flight.txt"
  // 2. Формируется словарь avia типа Dictionary<int, Flight>

  while (s != null)
  {
    s = sr.ReadLine(); // прочитать строку из файла
    if (s != null)
    {
      fields = s.Split(',');
      // формирование структуры sa типа struct Flight
      sa.num_r = Convert.ToInt32(fields[0]);
      sa.punkt_vd = fields[1];
      sa.time_v = Convert.ToInt32(fields[2]);
      sa.n_places = Convert.ToInt32(fields[3]);

      // добавляем пару <num_r, sa> в словарь avia
      // ключом к структуре есть номер рейса num_r
      avia.Add(sa.num_r, sa);

      // добавить строку s в listBox1
      listBox1.Items.Add(s);
    }
  }
}

public void Read_Tickets()
{
  StreamReader sr = new StreamReader("Tickets.txt", Encoding.Default);
  string s;
  string[] fields; // массив строк-полей структуры Tickets
  Tickets tk; // вспомогательная переменная-структура

  s = sr.ReadLine();

  // Основной цикл.
  // В цикле:
  // 1. Читаются данные из файла "Tickets.txt"
  // 2. Формируется список tickets типа List<Tickets>

  // используем цикл do...while()
  do
  {
    // разбить строку s на части по признаку символа ','
    fields = s.Split(',');

    // заполнить структуру tk
    tk.num_t = Convert.ToInt32(fields[0]);
    tk.num_r = Convert.ToInt32(fields[1]);
    tk.place = Convert.ToInt32(fields[2]);
    tk.date_v = fields[3];
    tk.punkt_pr = fields[4];
    tk.data_pr = fields[5];
    tk.time_pr = Convert.ToInt32(fields[6]);
    tk.price = Convert.ToDouble(fields[7]);
    tk.time_prod = fields[8];

    // добавить заполненную структуру в список tickets
    tickets.Add(tk);

    listBox2.Items.Add(s);
    s = sr.ReadLine();
  }
  while (s != null);
}

Объясним некоторые фрагменты кода в методах Read_Avia() и Read_Tickets().

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

StreamReader sr = new StreamReader("Flight.txt", Encoding.Default);
StreamReader sr = new StreamReader("Tickets.txt", Encoding.Default);

Чтение строки из файла осуществляется методом ReadLine() класса StreamReader. Если достигнут конец файла, то метод возвращает null.

С помощью строки

fields = s.Split(',');

происходит разделение строки на составные части. Разделитель между составными частями – это символ ‘ , ‘.

Строка

avia.Add(sa.num_r, sa);

добавляет в коллекцию avia типа Dictionary<int, Flight> номер рейса и заполненную структуру sa.

Строка

tickets.Add(tk);

добавляет в коллекцию tk типа List<Tickets> структуру tk.

 

8. Изменение кода конструктора формы Form1().

Методы Read_Avia() и Read_Tickets() размещаются в конструкторе формы Form1().

Листинг конструктора формы Form1() следующий:

public Form1()
{
    InitializeComponent();
    Read_Avia();
    Read_Tickets();
}

 

9. Программирование события клика на кнопке «Вычислить«.

Листинг обработчика события клика на кнопке «Вычислить» имеет вид:

private void button1_Click(object sender, EventArgs e)
{
  // Расчет
  // 1. Рейсы с максимальной продолжительностью полета
  int i, j;
  int max, tmp;
  bool f_first = true;

  max = 0;

  foreach (var a in avia)
  {
    foreach (var t in tickets)
      if (a.Value.num_r == t.num_r)
      {
        if (f_first)
        {
          max = t.time_pr - a.Value.time_v;
          f_first = false;
        }
        else
        {
          tmp = t.time_pr - a.Value.time_v;
          if (max < tmp) max = tmp;
        }
      }
  } // на выходе получаем максимальную длительность полета в переменной max

  // формирование списка рейсов с максимальной продолжительностью полета
  listBox3.Items.Clear();

  foreach (var a in avia)
  {
    foreach (var t in tickets)
      if (a.Value.num_r == t.num_r)
      {
        tmp = t.time_pr - a.Value.time_v;
        if (tmp == max)
        {
          string s;
          s = a.Value.num_r.ToString() + ", " +
              a.Value.punkt_vd + ", " +
              a.Value.time_v.ToString() + ", " +
              a.Value.n_places.ToString() + " - " +
              t.num_t.ToString() + ", " +
              t.punkt_pr + ", " +
              t.time_pr.ToString() + ", " +
              t.place.ToString();

              listBox3.Items.Add(s);
        }
      }
  }

  label4.Text = "Рейсы с максимальной продолжительностью полета: " + max.ToString();

  // 2. Число пассажиров, которые ждут отправления в введенный момент времени
  int tm, k;

  tm = Int32.Parse(textBox1.Text); // получить время
  k = 0; // число пассажиров
  foreach (var a in avia)
  {
    foreach (var t in tickets)
      if ((a.Value.num_r == t.num_r)&&(tm==a.Value.time_v))
        k++;
  }

  label5.Text = "Число пассажиров, которые ждут отправления: " + k.ToString();
}

В вышеприведенном листинге следует отметить. При просмотре коллекций используется оператор foreach (и только foreach). Оператор foreach служит для циклического обращения к элементам коллекции, которая представляет собой группу объектов. Использование других операторов цикла приводит к ошибке.

Список рейсов с максимальной продолжительностью полета выводится в элемент управления listBox3.

 

10. Запуск приложения на выполнение.

Результат работы приложения изображен на рисунке 3.

C#. Шаблон Windows Forms. Выполнение приложения работы со списками Рис. 3. Результат работы приложения