Приклад розробки додатку демонстрації роботи списків та асоціативних масивів. Класи 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(), який вводить текстовий рядок і повертає його у вигляді об’єкту типу string. Цей метод буде використаний при читанні файлу в даній задачі.

 

7. Програмування методів читання даних з файлів “Flight.txt” та “Tickets.txt“.

Прочитані з файлів “Flight.txt” та “Ticket.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. Результат роботи програми