[WikiDyd] [TitleIndex] [WordIndex

AiSD - Zajęcia 7

W ramach ćwiczenia 5 do wykonania będą zadania, których celem jest zapoznanie się z biblioteką standardową typów "generycznych", opartych na szablonach. Pewien zbiór algorytmów oraz struktur danych (listy, drzewa itp) jest zaimplementowane w taki sposób, że potrafią przechować dowolne dane których użytkownik biblioteki zażąda w momencie wywołania metody lub tworzenia obiektu danego typu. Do tej pory pisaliśmy algorytmy oraz klasy które były przeznaczone do przechowywania konkretnych danych (double, Osoba itp.). W tym ćwiczeniu będziemy wykorzystywać standardowe funkcje biblioteki dla własnych typów danych. Aby biblioteka standardowa mogła wykorzystać 'nasze' typy danych muszą one implementować specjalne funkcje o określonych przez bibliotekę nazwach. Pierwszym przykładem wzorcowym jest program, który wykorzystuje standardowy algorytm sortowania dla tablicy przechowującej obiekty typu Osoba. Jedynym warunkiem koniecznym w stosunku do naszej klasy Osoba, jest to aby implementował on 'interfejs' IComparable. Klasa Osoba będzie implementowała interfejs IComparable wtedy gdy będzie miała zdefiniowane funkcje określone w tym interfejsie. W przypadku IComparable jest to jedna funkcja:  public int CompareTo(object o)  . Oczywiście niektóre interfejsy wymagają implementacji większej liczby funkcji.

Jedną z głównych cech typów generycznych jest to, że pomagają nam sprawdzać poprawność typów przekazywanych danych w momencie kompilacji kodu, a nie w momencie uruchamiania. Zapobiega to powstawaniu błędów.

Programy wzorcowe

1. Pierwszy program, demonstruje wykorzystanie standardowej funkcji sortującej w klasie Array. Proszę zwrócić uwagę, że metoda Array.Sort(..) jest metodą statyczną, ponieważ wywołujemy ją używając bezpośrednio typu klasy. Nie potrzebujemy do jej wywołania instancji obiektu.

Założeniem podstawowym tego programu, jest to że posiadamy listę osób. Osoby są reprezentowane jako klasa Osoba i posiadają jako atrybuty imię i nazwisko. Porównując dwie osoby, na początku sprawdzamy kto ma alfabetycznie pierwsze nazwisko, a następnie w przypadku gdy nazwiska są sobie alfabetycznie równe, porównujemy ich imiona.

W .NET (i w rzeczywistości w większości innych języków programowania) aby lista (lub tablica) mogła być sortowana przez funkcje standardowe, obiekty które na niej się znajdują muszą implementować wspomniany wcześniej interfejs  IComparable .

Oto cały program przykładowy, razem z programem testującym:

   1 using System;
   2 using System.Collections.Generic;
   3 using System.Text;
   4 
   5 /** Przedstawiony program pokazuje przyklad wykorzystania interfejsu IComparable.
   6  *  W przykladzie tym do sortowania tablicy wlasnych elementow (klasa Osoba) 
   7  *  wykorzystujemy standardowa funkcje sortujaca w srodowisku .NET: Array.Sort
   8  *  Funkcja ta, do poprawnego dzialania, wymaga aby obiekty tablicy, ktore
   9  *  maja ulec sortowaniu implementowaly interfejs IComparable:
  10  * 
  11  *  interface IComparable {
  12  *      public int CompareTo( object o );
  13  *  }
  14  * 
  15  *  Metoda porownujaca ma zwrocic wartosci -1, 1 lub 0 w zaleznosci od tego
  16  *  czy wartosc obiektu przekazanego jako argument do danej metody jest wieksza,
  17  *  mniejsza lub rowna od wartosci obiektu, ktorego metoda jest wywolywana.
  18  * 
  19  *  Posluzmy sie przykladem:
  20  * 
  21  *  Osoba os1 = new Osoba("Ala","Kot");
  22  *  Osoba os2 = new Osoba("Ala","Zrebiecka");
  23  *  
  24  *  Console.WriteLine("{0}", os1.CompareTo( os2 ) ); // tutaj wypiszemy -1
  25  *  Console.WriteLine("{0}", os2.CompareTo( os1 ) ); // tutaj wypiszemy 1
  26  * 
  27  *  W pierwszym przypadku obiektem przekazywanym jest os2, a obiektem, ktorego
  28  *  metoda jest wywolywana jest obiekt os1
  29  */
  30 namespace ImplComparable
  31 {
  32     // Deklaracja klasy implementujacej interfejs IComparable
  33     //
  34     // Zalozeniem podastawowym jest to iz mamy imie i nazwisko,
  35     // jezeli dwie porownywane osoby, maja takie samo nazwisko 
  36     // to powinnismy porownac ich imiona.
  37     class Osoba : IComparable
  38     {
  39         // Atrybuty prywatne...
  40         String imie;
  41         String nazwisko;
  42 
  43         // Konstruktor obiektu, inicjalizujacy atrybuty prywatne.
  44         public Osoba(string i, string n)
  45         {
  46             imie = i;
  47             nazwisko = n;
  48         }
  49         
  50         
  51         // Pierwsza, klasyczna wersja funkcji porownujacej.
  52         public int CompareTo(object o)
  53         {
  54             // dwie nastepne linijki sprawdzaja, czy obiekt przekazany jako
  55             // argument (object o) jest typu klasa Osoba.
  56             // jezeli, przekazany obiekt nie bedzie klasa Osoba, wowczas
  57             // w zmiennej osoba bedzie wartosc null.
  58             Osoba osoba = o as Osoba;
  59             if (osoba != null)
  60             {
  61                 // ponizej znajduje sie szereg metod sprawdzajacych
  62                 // na poczatku nazwisko, a  nastepnie imie...
  63                 if (this.nazwisko.CompareTo(osoba.nazwisko) > 0)
  64                     return 1;
  65                 else
  66                     if (this.nazwisko.CompareTo(osoba.nazwisko) < 0)
  67                         return -1;
  68                     else
  69                         if (this.imie.CompareTo(osoba.imie) > 0)
  70                             return 1;
  71                         else
  72                             if (this.imie.CompareTo(osoba.imie) < 0)
  73                                 return -1;
  74                             else
  75                                 return 0;
  76             }
  77             return 0;
  78         }
  79 
  80         
  81 
  82         /*
  83    
  84         // Druga, zdecydowanie krotsza wersja funkcji porownujacej.
  85         // ktora bazuje na tym, ze imie porownujemy tylko wtedy gdy
  86         // nazwiska sa takie same.
  87         // (przy odkomentowaniu ponizszej funkcji, nalezy zakomentowac 
  88         // funkcjezaimplementowane wczesniej, poniewazklasa nie moze
  89         // implementowac dwoch takich samych metod)
  90         public int CompareTo(object o)
  91         {
  92             Osoba osoba = o as Osoba;
  93             if (osoba != null)
  94             {
  95                 if (osoba.nazwisko.CompareTo(this.nazwisko) == 0)
  96                     return this.imie.CompareTo(osoba.imie);
  97                 else
  98                     return this.nazwisko.CompareTo(osoba.nazwisko);
  99             }
 100             return 0;
 101         }
 102         
 103         */
 104 
 105         // Implenetujemy ta metode, aby bylo nam latwiej wypisywac osoby
 106         // na ekranie z funkcji Main. Dzieki temu mozemy napisac:
 107         //     Console.WriteLine("{0}", osoba);
 108         // zamiast:
 109         //     Console.WriteLine("{0} {1}", osoba.imie, osoba.nazwisko);
 110         public override String ToString()
 111         {
 112             return nazwisko + " " + imie;
 113         }
 114     }
 115 
 116     // standardowy program testujacy...
 117     class Program
 118     {
 119         static void Main(string[] args)
 120         {
 121             // deklarujemy tablice osob, i rezerwujemy miejsce dla pieciu
 122             Osoba[] lista = new Osoba[5];
 123 
 124             // inicjalizujemy liste testowych osob
 125             lista[0] = new Osoba("Ala", "Kot");
 126             lista[1] = new Osoba("Ala", "Kot");
 127             lista[2] = new Osoba("Ala", "Zotowa");
 128             lista[3] = new Osoba("Zuzia", "Kot");
 129             lista[4] = new Osoba("Basia", "Kot");
 130 
 131             Console.WriteLine("Przed sortowaniem...");
 132             foreach( Osoba osoba in lista)
 133                 Console.WriteLine("{0}", osoba);
 134 
 135             // Wywolujemy statyczna metode klasy Array, zdefiniowanej w .NET,
 136             // i przekazujemy jej nasza tablice osob do posortowania
 137             Array.Sort(lista);
 138 
 139             Console.WriteLine("Po sortowaniu...");
 140             foreach (Osoba osoba in lista)
 141                 Console.WriteLine("{0}", osoba);
 142 
 143             // aby czarny ekran nie znikal... :-)
 144             Console.ReadLine();
 145         }
 146     }
 147 }

2. Drugi program przykładowy pokazuje wykorzystanie typu generycznego List, do przechowywania danych, które w sposób dynamiczny zmieniają się. Oznacza, to że często są dodawane nowe dane, wstawiane w środku oraz usuwane. W poniższym przykładzie zobrazowane jest sprawdzanie typów na etapie kompilacji a nie na etapie uruchamiania.

   1     static void Main(string[] args)
   2     {
   3         // Używamy generycznej listy do przechowywania obiektów typu Osoba
   4         List<Osoba> duzoOsob = new List<Osoba>();
   5         duzoOsob.Add(new Osoba());
   6 
   7         // Uzywamy generycznej listy do przechowywania obiektow typu Samochod
   8         List<Samochod> duzoSamochodow = new List<Samochod>();
   9         // Błąd kompilacji: Compile-time error!
  10         duzoSamochodow.Add(new Samochod());
  11 }

3. W .NET do dyspozycji mamy następujące interfejsy typów generycznych, które określają w jaki sposób powinniśmy na nich operować:

4. W .NET mamy do dyspozycji następujące kontenery generyczne:

5. Funkcje generyczne. W naszych programach możemy pisać własne funkcje generyczne, które 'mogą' potrafić (o ile są specjalnie napisane) operować na dowolnych danych określanych w momencie użycia funkcji. W poniższym przykładzie pokazana jest generyczna funkcja Swap, która zamienia wartości dwóch elementów, których typ jest określony w momencie użycia tej funkcji.

   1 using System;
   2 using System.Collections.Generic;
   3 using System.Text;
   4 
   5 namespace ConsoleApplication2
   6 {
   7     class Program
   8     {
   9         // Generyczna funkcja, ktora potrawi zamieniac wartosci dowolnego typu,
  10         // okreslonego w momencie wykorzystania funkcji
  11         static void Swap<T>(ref T a, ref T b)
  12         {
  13             Console.WriteLine("Poprosiles funkcje Swap() o zamiane danych typu {0}", typeof(T));
  14             T temp;
  15             temp = a;
  16             a = b;
  17             b = temp;
  18         }
  19 
  20         static void Main(string[] args)
  21         {
  22             int liczba1, liczba2;
  23             liczba1 = 5;
  24             liczba2 = 11;
  25 
  26             Console.WriteLine("Przed zamiana liczba1= {0}, liczba2 = {1}", liczba1, liczba2);
  27 
  28             // zamieniamy wartosci liczb
  29             Swap<int>(ref liczba1, ref liczba2);
  30             Console.WriteLine("Po zamianie liczba1= {0}, liczba2 = {1}", liczba1, liczba2);
  31 
  32             String napis1, napis2;
  33             napis1 = "Ala ma kota.";
  34             napis2 = "Norbert ma chomika.";
  35 
  36             Console.WriteLine("Przed zamiana napis1= {0}, napis2 = {1}", napis1, napis2);
  37 
  38             // W ponizszej linii specjalnie zostal zostawiony blad. 
  39             // Bardzo prosze sprobowac znalezc go samodzielnie.
  40             // poprzez analogie do wykorzystania funkcji kilka linijek wczesniej.
  41             Swap<int>(ref napis1, ref napis2);
  42             Console.WriteLine("Po zamianie napis1= {0}, napis2 = {1}", napis1, napis2);
  43 
  44             Console.ReadLine();
  45         }
  46     }
  47 }

Przykładowe zadania

1. Proszę zaimlementować program, który wykorzystując standardową funkcję sortowania tablicy danych wypisze w uporządkowany sposób tablicę danych opartych na typie wskazanym przez prowadzącego. Typy podane na zajęciach będą podobne do:

    class Osoba : IComparable
    {
        String imie;
        String nazwisko;
        String pesel;
        String dataUrodzenia;
    }

2. Proszę zaimplementować program, który wczyta dane z pliku o określonej przez prowadzącego strukturze i zbuduje na ich podstawie listę rekordów, przefiltruje ją, a następnie zapisze do drugiego pliku przefiltrowany wynik w identycznej strukturze jak plik wejściowy. Do zaimplementowania należy wykorzystać wskazany przez prowadzącego generyczny typ danych: List<T>, tablica, Queue<T>

3. Napisz generyczną funkcję, która będzie wykonywała określoną przez prowadzącego operację.

4. Napisz własną generyczną klasę na bazie List<T> tak aby potrafiła wypisać na ekranie wszystkie wartości. Nalezy założyć, że obeikty przechowywane na naszej liście implementują funkcję  public void toString()  , ta jak to zostało pokazane w przykładzie 1.


2015-09-23 06:32