Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04 Cel zajęć. Celem zajęć jest zapoznanie się ze sposobem działania popularnych kolekcji. Wprowadzenie teoretyczne. Rozważana w ramach niniejszych zajęć tematyka jest ważna, gdyż kolekcje są powszechnie używane do przechowywania danych i efektywnego zarządzania danymi. Aby ze zrozumieniem zrealizować zadania przewidziane do wykonania w ramach zajęć laboratoryjnych, należy znać znaczenie pojęcia kolekcji oraz cechy następujących kolekcji: listy jednokierunkowej, listy dwukierunkowej, kolejki, stosu. 1. Kolekcja Jest to pojęcie stosowane w odniesieniu do klas, które umożliwiają przechowywanie oraz efektywne przetwarzanie obiektów danego typu. Jedną z największych zalet kolekcji jest możliwość dynamicznej zmiany ich rozmiaru liczba przechowywanych przez kolekcje elementów nie jest ograniczona w momencie utworzenia danej kolekcji, tak jak ma to miejsce w przypadku tablicy. 2. Lista Jest kolekcją składającą się z uporządkowanych liniowo elementów. Lista umożliwia wstawianie elementów w miejsce o dowolnym indeksie. Ta sama zasada dotyczy pobierania elementu z listy. Do prawidłowego manipulowania listą wymagane jest przechowywanie przez listę adresu jej pierwszego elementu. Lista jednokierunkowa to lista, w której każdy element wskazuje na element następny. Rys. 1 Lista jednokierunkowa Lista dwukierunkowa to lista, w której każdy element wskazuje na element następny i na element poprzedni. Listy dwukierunkowe umożliwiają łatwiejszy dostęp do danych, niż listy jednokierunkowe. 1
Rys. 2 Lista dwukierunkowa 3. Kolejka Jest kolekcją składającą się z liniowo uporządkowanych elementów. Charakterystyczną cechą kolejki jest to, iż nowe elementy dodawane do kolejki są dodawane na jej końcu, natomiast elementy, które są pobierane z kolejki, są pobierane z jej początku. Oznacza to, że element, który pierwszy został umieszczony w kolejce, pierwszy ją opuści. Kolejki zwane są także kolekcjami FIFO (ang. First In First Out). Rys. 3 - Kolejka 4. Stos Jest kolekcją składającą się z liniowo uporządkowanych elementów. Charakterystyczną cechą stosu jest to, iż użytkownik posiada dostęp jedynie do elementu znajdującego się na wierzchołku stosu, a nowy element stosu można dodawać jedynie na jego wierzchołek. Oznacza to, że element, który został umieszczony na stosie jako ostatni, pierwszy opuści stos. Stosy zwane są również kolekcjami LIFO (ang. Last In First Out). 2
Rys. 4 - Stos Zadanie 1. Proszę zrealizować aplikację obiektową zgodnie z poniższymi założeniami: 1. Należy zaimplementować własną kolekcję będącą generyczną listą. 2. Lista ma posiadać strukturę odpowiadającą diagramowi klas pod zadaniem, podpowiedź znajdziecie Państwo na końcu instrukcji. 3. Utworzyć kod testujący kolejki przechowującej obiekty typu int, który między innymi wypisze listę liczb parzystych. 3
Zadanie 2. Proszę zrealizować aplikację obiektową zgodnie z poniższymi założeniami: 1. Należy zaimplementować własną kolekcję będącą generyczną listą dwukierunkową. 2. Lista ma posiadać strukturę odpowiadającą diagramowi klas pod zadaniem. 3. Zaimplementować w klasie listy interfejs IEnumerable (pomoc na końcu instrukcji). 4. Utworzyć kod dla listy obiektów Person. Wypisać osoby pełnoletnie. 4
Zadanie do domu. 1. Należy zaimplementować własną generyczną kolekcję drzewa. 2. Węzeł drzewa posiada wskaźniki na element pierwszego dziecka, element kolejnego brata, element rodzica. 3. Metoda lub klasa realizująca iterację (IEnumerator) drzewa metodą Pre-order (NLR). 4. Metoda iterująca ścieżkę od węzła do korzenia (osobna podklasa IEnumerator). 5. Na bazie kolekcji drzewa zrealizować hierarchię pracowników firmy (kierownik podwładni). Pozwalającą wypisać wszystkich podwładnych danego kierownika, również nie bezpośrednio mu podlegających. 5
Pomoc do zadania 1. Przykładowy kod realizujący strukturę listy przedstawiono na poniższym listingu: MyList<T> jest klasą generyczną tzn. Obiekt naszej listy może przechowywać dowolny typ obiektu zdefiniowany (nazwa powiązanej klasy pod literką T) w trakcie tworzenia instancji naszej klasy. Nasza klasa posiada wewnętrzną prywatną klasę Node (ukrytą, niedostępną poza klasą), służącą jako kontener dla przechowywanych obiektów typu T. T this [int i] to przeciążenie operatora tablicowego, przez którego możliwe jest odwołanie do listy poprzez indeks np. lista[1] Modyfikator partial class skutkuje, iż możliwe jest zdefiniowanie kolejnych klas o tej samej nazwie, które ostatecznie tworzą jedną połączoną klasę. W tej formie klasa oferuje nam jedynie możliwość dodawania i odwoływania się do elementu po indeksie. 6
Pomoc do zadania 2. Implementując w naszej klasie interfejs IEnumerable nasza klasa zyskuje całkiem nową funkcjonalność. Musimy w tym celu utworzyć kilka dodatkowych metod oraz kolejną podklasę, tak jak na poniższym listingu. 7
Enumerator jest klasą, która pozwala sekwencyjnie przechodzić po węzłach naszej struktury. Implementacja wymieniowych interfejsów pozwala naszą strukturę zintegrować z wieloma natywnymi bibliotekami C# takimi jak m.in. pętla foreach. 8
Od tego momentu naszą listę możemy wykorzystać jak na poniższym przykłądzie. list.where są specyficznymi metodami, które zwracają podlistę elementów. Jako parametr przyjmują wyrażenie lambda określające warunek jaki mają spełniać elementy w podliście. Wyrażenie to wywoływane jest na każdym iterowanym elemencie listy i zwraca wartość bool. list.foreach podobnie do Where, z tą różnicą, iż lambda nie musi nic zwracać i służy wyłącznie do wykonania operacji na każdym iterowanym elemencie listy. Wyrażenie Lambda składa się z: (definicja parametrów funkcji) => {definicja ciała funkcji} *nawiasy są opcjonalne 9