Programowanie zaawansowane Ćwiczenie 6 Komunikacja silnie typowana I. Utwórz aplikację okienkową realizującą proste obliczenia arytmetyczne. Obsługa zdarzeń w aplikacji typu Windows Form Application odbywa się za pośrednictwem delegatów. Najważniejszym z nich jest standardowy delegat EventHandler. Utwórz interfejs graficzny podobny do przedstawionego poniżej (najłatwiej jest ustawić kontrolki dla pierwszego działania, a następnie je skopiować i podmienić znak działania). Obsługę zdarzeń kliknięcia każdego z przycisków zaimplementuj i podepnij na wymienione poniżej sposoby. W punkcie pierwszym, środowisko automatycznie podepnie wygenerowaną metodę do zdarzenia w pliku Form1.Designer.cs w tym pliku z klasą częściową znajdują się definicje kontrolek, które umieszczamy na naszym formularzu. W punkcie 2), 3), 4) i 5) metody obsługujące zdarzenie Click podpinamy ręcznie w konstruktorze formularza. Obsługa zdarzeń będzie oczywiście polegała na pobraniu wartości z odpowiednich pól tekstowych i wyświetleniu wyniku wybranego działania na odpowiedniej etykiecie. 1. Dla dodawania: obsługę kliknięcia zrealizuj poprzez środowisko programistyczne. Dwukrotne kliknięcie na przycisku powoduje utworzenie szkieletu metody przeznaczonej do obsługi zdarzenia i jej automatyczne zarejestrowanie w odpowiednim zdarzeniu. Wygenerowana metoda obsługująca zdarzenie podpowie nam również, jak mają wyglądać sygnatury metod zgodne z delegatem EventHandler (zwracają void, a parametrami są obiekty klas Object i EventArgs). 2. Dla odejmowania: najpierw utwórz metodę do obsługi zdarzenia, a następnie ręcznie podepnij ją do zdarzenia Click odpowiedniego przycisku (Wskazówka 1.a), 3. Dla mnożenia: tak samo, jak dla odejmowania,
4. Dla dzielenia: podepnij obsługę zdarzenia Click poprzez metodę anonimową (Wskazówka 1.b), 5. Dla dzielenia modulo: podepnij obsługę zdarzenia Click poprzez wyrażenie lambda(wskazówka 1.c). Zadanie na wyobraźnię: obsłuż 3 dowolnie wybranych zdarzeń dla dowolnych kontrolek. Po wybraniu kontroli, jej właściwości i zdarzenia można edytować w oknie Properties (standardowo umieszczonym w prawym dolnym rogu). Jeżeli nie jest widoczne, wystarczy nacisnąć przycisk F4. Lista zdarzeń znajduje się w pod-zakładce oznaczonej błyskawicą. Aby automatycznie wygenerować szkielet metody do obsługi danego zdarzenie, wystarczy dwa razy kliknąć na polu obok jego nazwy. Obsługę zdarzeń możemy też zrealizować ręcznie w konstruktorze formularza. Nazwy zdarzeń na liście składowych obiektu są również oznaczone symbolem błyskawicy. II. Utwórz bibliotekę zawierającą klasy i mechanizmy wspomagające zarządzaniem kadrami. 1. Zdefiniuj klasę reprezentującą pracownika firmy (imię, nazwisko, staż, liczbą sprzedaży, pensja). 2. Zdefiniuj delegat pozwalający na przechowywanie i wywoływanie metod przyjmujących jako parametr pracownika i nie zwracających żadnych wartości. 3. Zdefiniuj klasę reprezentującą firmę. Klasa ta ma odpowiadać za przechowywanie listy pracowników. Dodaj do klasy metodę przyjmującą jako parametr obiekt wcześniej zdefiniowanego delegata. Ma ona wywoływać przekazaną metodę dla każdego z pracowników. 4. Zdefiniuj statyczną klasę reprezentującą dział płac. W klasie tej zaimplementuj następujące statyczne metody (zgodne ze wcześniej zdefiniowanym delegatem): 1) metodę zwiększającą pensję o 200, jeżeli pracownik ma ponad 10 dokonanych sprzedaży, 2) metodę zmniejszającą pensję, o 10%, jeżeli pracownik pracuje mniej niż 2 lata, 3) metodę zerującą ilość dokonanych sprzedaży. Każda z metod powinna również wypisywać odpowiedni komunikat zawierający imię i nazwisko pracownika. 5. Metody Find() i FindAll() zdefiniowane w klasie generycznej Lis<T> przyjmują jako parametry obiekty typu Predicate<T>. Metody zgodne z tym delegatem zwracają wartość logiczną, a jako parametr przyjmują obiekty typu wskazanego poprzez parametr generyczny. Do klasy reprezentującej firmę dopisz metody, które za pośrednictwem metod Find() lub FindAll() będą realizowały następujące wyszukiwania: 1) wyszukanie pracownika o nazwisku Kowalski,
2) wyszukanie pracownika o wskazanym imieniu i nazwisku, 3) wyszukanie pracowników o ilości dokonanych sprzedaży większych od 10, 4) wyszukanie pracowników o stażu wyższym od podanego. Wyszukiwanie w punkcie 1) zrealizuj na trzy sposoby (czyli metoda ta powinna być napisana w trzech wersjach). Będą się one różnić sposobem przekazania argumentu dla metody Find(. Przekaż go za pomocą: 1) nazwy metody, 2) metody anonimowej, 3) operatora lambda. Wyszukiwanie w punktach 2), 3) i 4) należy zrealizować za pomocą operatora lambda. 6. Definiowanie i korzystanie ze zdarzeń (słowo kluczowe event) Dodaj do firmy trzy wersje metody do usuwania pracownika spod podanego indeksu. Dodaj do nich wywołanie odpowiednich zdarzeń w momencie usunięcia pracownika. 1) Pierwsza wersja: zdefiniuj prywatny obiekt naszego delegata (dla metod przyjmujących pracownika jak parametr i nic nie zwracających), oraz publiczne metody do rejestrowania i wyrejestrowywania metod z nim zgodnych. Podczas usunięcia wywołaj metody przechowywane o zdefiniowanym obiekcie delegata. 2) Druga wersja: zdefiniuj publiczne zdarzenie (event) dla naszego delegata i wywołaj je podczas usuwania pracownika. 3) Trzecia wersja: utwórz publiczne zdarzenie wykorzysujące standardowy delegat EventHandler orz klasę dziedziczącą po EventArgs pozwalającą na przekazanie informacji o usuwanym pracowniku. Wywołaj to zdarzenie przed usunięciem pracownika. 7. Zdefiniuj metody przyjmujące jako parametry obiekty odpowiednich delegatów i pozwalające na (Wskazówka 2): 1) wyświetlenie wybranej informacji o każdym z pracowników, 2) wyszukanie pracowników względem zadanego kryterium, 3) posortowanie pracowników względem zadanego kryterium. III. Utwórz aplikację konsolową testującą przygotowaną bibliotekę klas. 1. Zdefiniuj firmę z kilkoma pracownikami. 2. Wywołaj metodę z delegatem dla każdej z metod zdefiniowanych w dziale płac.
3. Przetestuj metody do wyszukiwania pracowników. 4. Napisz metody do obsługi zdarzenia usunięcia pracownika, podepnij je pod odpowiednie zdarzenia i przetestuj ich działanie wywołuj odpowiednie wersje metod do usunięcia. 5. Przetestuj działanie metod do wyświetlania, wyszukiwania i sortowania dla różnych kryteriów. Wskazówka 1. Definiowanie delegata, definiowanie zdarzenia i podpinanie metod. W poniższym przykładzie chcemy, aby programista korzystający z naszej klasy do logowania (zapisywania w logu), mógł przetworzyć logowaną wiadomość na dowolny, wskazany przez siebie sposób. Dla uproszczenia, logowaną wiadomość podajemy jako parametr metody Log, w rzeczywistości metoda ta byłaby automatycznie wywoływana, np., w przypadku przechwycenia wyjątków. Idealnym rozwiązaniem są oczywiście delegaty. Dzięki nim, programista będzie w stanie wskazać nam metodę (zbiór instrukcji), którą mamy wywołać dla logowanej wiadomości tuż przed jej zapisaniem do pliku. Aby to obsłużyć wystarczy: 1) zdefiniować typ delegata określający sygnaturę metod, które będziemy mogli przechowywać w obiektach tego delegata. W naszym przypadku będą one przyjmować obiekt klasy String jako parametr i nic nie zwracać (linia 10); 2) zdefiniować publiczne zdarzenie typu naszego delegata (linia 14), czyli konkretną zmienną, która będzie mogła przechowywać metody zgodne ze wskazanym schematem. Dzięki wykorzystaniu sława kluczowego event, środowisko wygeneruje prywatny obiekt delegata i metody służące do podpinania i odpinania metod (operator += i -= ). Nazwy zdarzeń rozpoczynamy oczywiście wielką literą (są publiczne). Konwencją jest również rozpoczynanie ich słowem On (przy, podczas). Jeżeli chcemy zaznaczyć, że zdarzenie będzie wywoływane przed daną aktywnością, używamy końcówki ing (np. OnLogging), a gdy po aktywności, końcówki ed (np. OnLogged). 3) wywołać zdarzenie w odpowiednim miejscu (linie 18-20). W ten sposób zostaną wywołane wszystkie metody podpięte do naszego zdarzenia. Ich parametrem będzie logowana wiadomości. Należy pamiętać, o sprawdzeniu, czy do naszego zdarzenia podpięto metody (czyli czy jest różne od null).
Programista korzystający z naszej klasy do logowania może podpiąć metody obsługujące nasze zdarzenie (czyli mające zostać wywołane w odpowiednim momencie) na kilka sposobów. Oczywiście najpierw musi utworzyć obiekt klasy Logger. Podpięcie metod do zdarzania powinno być zrealizowane przed miejscem jego wystąpienia, czyli przed wywołaniem metody Log(). a) Pierwszym sposobem na podpięcie metody jest zdefiniowanie standardowej metody zgodnej ze schematem delegata i wskazanie jej nazwy po operatorze +=. Możemy tu skorzystać z pomocy środowiska, które po wpisanie operatora += po nazwie zdarzenia zaproponuje nam nazwę metody (nazwa obiektu + znak _ + nazwa zdarzenia). Po naciśnięciu naszego ulubionego klawisza TAB szkielet metody zostanie automatycznie wygenerowany. Wystarczy wypełnić go instrukcjami wedle naszej potrzeby. Na liście podpowiedzi składowych obiektu zdarzenia oznaczone są symbolem błyskawicy. Widząc ten symbol wiemy, że możemy użyć właśnie operatora +=. b) Drugim sposobem jest zdefiniowanie metody anonimowej. Zamiast definiować standardową metodę i przekazywać jej nazwę, definiujemy metodę od razu w miejscu podpięcia do zdarzenia (w takim przypadku nazwa metody nie jest potrzebna, stąd też jej anonimowość). Rozwiązanie to stosujemy w przypadku, gdy metoda ta jest wykorzystywana tylko do obsługi danego zdarzenia i nie będzie wywoływana w innych miejscach programu. Definiowanie metody rozpoczynamy od słówka delegate, następnie podajemy parametry metody (opcjonalnie tzn. nie musimy ich
podawać, jeżeli nie będziemy z nich korzystać w ciele metody) i ciało metody. Instrukcję zakańczamy średnikiem. c) Trzecim sposobem jest wykorzystanie wyrażenia lambda skróconego zapisu metody anonimowej. W zapisie tym podajemy tylko nazwy parametrów (typy zostaną wywnioskowane automatycznie), operator lambda => i ciało metody. W przypadku pojedynczej instrukcji możemy pominąć klamerki i słowo kluczowe return (w przypadku metod zwracających wartości).
Wskazówka 2. Definiowanie metod z delegatami jako parametrami. Wywołanie