Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Programowanie zaawansowane

Podobne prezentacje


Prezentacja na temat: "Programowanie zaawansowane"— Zapis prezentacji:

1 Programowanie zaawansowane
Delegaty

2 Plan wykładu Delegaty Zdarzenia Metody anonimowe Operator lamda

3 Delegaty

4 Spojrzenie w przeszłość
W Windows API często korzystano ze wskaźników do funkcji w celu tworzenia elementów zwanych funkcjami wywołań zwrotnych, albo po prostu wywołaniami zwrotnymi (ang. callback). Stosowanie tego mechanizmu pozwalało programistom na konfigurowanie wybranej funkcji tak, aby była ona w stanie wywoływać inne, dynamicznie przekazywane, funkcje. Przy pomocy tego podejścia radzono sobie z obsługą kliknięcia przycisku, poruszenia myszką, wyboru opcji z menu i innymi przykładami dwukierunkowej komunikacji między dwoma bytami programistycznymi.

5 Spojrzenie w przeszłość c. d.
Standardowe funkcje wywołań zwrotnych (rodem z języka C) reprezentują jedynie adres w pamięci. Wywołania zwrotne mogłyby zawierać informacje związane z bezpieczeństwem typów, takie jak ilość i typ parametrów oraz typ zwracanej wartość przez metodę, na którą wskazują. Z powodu braku tych informacji, standardowe wywołania zwrotne były często przyczyną błędów i zawieszania się aplikacji. Nie zmienia to faktu, że sam mechanizm wywołań zwrotnych jest bardzo użyteczny.

6 Powrót w przyszłość Na platformie .NET, stosowanie wywołań zwrotnych jest nadal możliwe, a ich funkcjonalność została zrealizowana w bardziej bezpieczny i obiektowo zorientowany sposób. Delegat to obiekt zapewniający bezpieczeństwo typu, wskazujący na metodę (lub zbiór metody), które mogą być wywołane w późniejszym czasie. Delegat przechowuje następujące informacje: Adres metody, którą może wywołać, Ilość i typ argumentów metody, Typ wartości (lub jej brak) zwracanej przez daną metodę.

7 Cechy delegatów W odróżnieniu od wskaźników do funkcji w C i C++, delegaty mogą wskazywać na statyczne i instancyjne metody. Po stworzeniu delegata i dostarczeniu potrzebnych informacji, może on dynamicznie wywoływać metody, przekazywane do niego w czasie wykonania programu. Każdy delegat jest automatycznie wyposażany w możliwość wywoływania metod w sposób synchroniczny i asynchroniczny. Upraszcza to w dużym stopniu wywoływanie metod w drugoplanowym wątku, bez konieczności tworzenia i utrzymywania obiektu wątku.

8 Definiowanie delegata
Delegaty tworzymy za pomocą słowa kluczowego delegate. Możemy mu nadać dowolną nazwę. Definiowany delegat musi odpowiadać sygnaturze metod, które ma przechowywać. Na przykład, chcąc stworzyć delegat o nazwie BinaryOp, który może wskazywać na dowolną metodę przyjmującą dwa parametru typu int i zwracającą typ int, zapiszemy następującą definicję:

9 Klasy związane z delegatami
Kompilator C#, przetwarzając definicję delegata, automatycznie generuje klasę dziedziczącą po klasie System.MulticastDelegate. Klasa ta, razem z bazową klasą System.Delegate, zapewnia wymaganą infrastrukturę pozwalającą delegatom na przechowywanie listy metod do późniejszego wywołania.

10 Klasy związane z delegatami c. d.
Wygenerowana przez kompilator klasa posiada trzy metody: Metoda Invoke() służy do wywoływania każdej z metod przechowywanych w delegacie w sposób synchroniczny (wywołujący musi poczekać na zakończenie się wywoływanej metody). Metoda ta jest wywoływana niejawnie. Metody BeginInvoke() i EndInvoke() powalają na wywołanie metody w sposób asynchroniczny w osobnym wątku wykonania. Najczęstszym powodem korzystania z wątku jest przerzucenie do niego wykonania funkcji zajmującej dużo czasu. Dzięki delegatom, mamy taką możliwość bez ręcznego tworzenia obiektu wątku.

11 Jak kompilator generuje klasę delegata?
Parametry i wartość zwracana przez metodę Invoke() dokładnie odpowiadają definicji delegata. Parametry metody BeginInvoko() odpowiadają parametrom delegata, Oprócz nich, metoda ta zawsze przyjmuje dwa dodatkowe parametry (AsyncCallback i object) w związku z asynchronicznym wywołaniem. Wartość zwracana przez metodę EndInvoke() odpowiada tej zdefiniowanej w delegacie. Dodatkowo przyjmuje pojedynczy parametr – obiekt klasy implementującej interfejs IAsyncResult.

12 Jak kompilator generuje klasę delegata? c. d.
W kolejnym przykładzie, załóżmy, że chcemy zdefiniować delegat, który może przechowywać i wywoływać metody zwracające obiekt klasy System.String i przyjmujące trzy argumenty typu System.Boolean.

13 Jak kompilator generuje klasę delegata? c. d. 2
Delegaty mogą również wskazywać na metody przyjmujące dowolną liczbę parametrów z atrybutami ref i out. Dwie pierwsze metody zostały wygenerowane zgonie z poprzednimi przykładami. Różnica polega na tym, że metoda EndInvoke() została rozszerzona o parametry z atrybutami ref i out.

14 Ogólna zasada generowania klasy delegata
Zasadę tą obrazuje następujący pseudo kod:

15 Klasa System.Delegate Klasa bazowa System.Delegate posiada, między innymi, następujące składowe.

16 Klasa System.MulticastDelegate
Każda klasa wygenerowana przez kompilator dziedziczy po klasie System.MulticastDelegate.

17 Wspólne składowe delegatów
W swoim kodzie, nie możemy dziedziczyć po tych dwóch klasach. Powoduje to błąd kompilacji. Jednakże, używając słowa kluczowego delegate, pośrednio tworzymy klasę dziedziczącą po System.MulticastDelegate. Kilka składowych wspólnych dla wszystkich delegatów to: Method – Właściwość ta zwraca obiekt klasy System.Reflection.MethodInfo, która reprezentuje szczegółowe informacje o metodzie przechowywanej przez delegata. Target – Właściwość ta zwraca informację o typie, w którym została zdefiniowana przechowywana metoda, jeżeli jest ona zdefiniowana na poziomie obiektu. W przeciwnym razie zwraca null, co oznacza, że jest to metoda statyczna.

18 Wspólne składowe delegatów c. d.
Combine() – Ta statyczna metoda dodaje wskazaną metodę do listy przechowywanej przez delegata. W C# metoda ta jest wyzwalana przez użycie przeładowanego operatora +=. GetInvocationList() – Metoda ta zwraca tablicę obiektów typu System.Delegate, reprezentujących poszczególne metody do wywołania. Remove() , RemoveAll() – Te statyczne metody usuwają wskazaną metodę (lub wszystkie) z listy wywołań delegata.

19 Najprostszy przykład użycia delegata

20 Najprostszy przykład użycia delegata c. d.
W przykładzie zdefiniowaliśmy delegata, który może wskazywać na metody przyjmujące dwa parametry liczbowe i zwracające liczbę. Nazwy metod są nieistotne. Jeżeli chcemy umieścić docelową metodę w delegacie, po prostu przekazujemy ją poprzez konstruktor. Metodę, na którą wskazuje nasz delegat wywołujemy w sposób przypominający bezpośrednie wywołanie funkcji. W rzeczywistości, wywoływana jest niejawnie metoda Invoke(). Możemy równie dobrze wywołać ją jawnie.

21 Najprostszy przykład użycia delegata c. d. 2
Delegaty na platformie .NET zapewniają bezpieczeństwo typów. Dlatego też próba przekazania do delegata metody nie odpowiadającej zadanemu wzorcowi (sygnaturze metody) spowoduje wyświetlenie błędu podczas kompilacji. Załóżmy, że klasa SimpleMath posiada jeszcze jedną metodę. Próba przekazania jej do delegata spowoduje błąd w czasie kompilacji (metoda nie odpowiada sygnaturze delegata).

22 Pozyskiwanie informacji o metodach
Metoda wyświetlająca informacje o metodach przechowywanych w podanym delegacie. Po odpowiedniej zmianie metody main() w naszym przykładzie, otrzymamy następujący rezultat. Dlaczego?

23 Pozyskiwanie informacji o metodach c. d.
Po zmianie metod ze statycznych na wywoływane z poziomu instancji, nadal możemy przekazywać je do delegata. Informacje o metodzie instancyjnej zawierają informacje o typie, w którym zostały zdefiniowane (właściwość Target).

24 Dodanie delegatów do klasy Car
Aby zastąpić komunikację z klasą Car za pomocą interfejsów wywołań zwrotnych na delegaty należy: Zdefiniować nowy typ delegata, który wyśle powiadomienie do wywołującego, Zadeklarować w klasie Car zmienne odpowiednich typów delegatów, Stworzyć funkcje pomocnicze, które pozwolą wywołującemu na przekazanie metod do zadeklarowanych zmiennych (delegatów), Zaktualizować metodę Accelerate(), tak aby wywoływała metody na liście delegata w odpowiedniej sytuacji.

25 Dodanie delegatów do klasy Car c. d.

26 Dodanie delegatów do klasy Car c. d. 2

27 Dodanie delegatów do klasy Car c. d. 3

28 Multicasting Aby zapewnić możliwość rejestrowania wielu metod w jednej zmiennej delegata (i tym samym możliwość wywoływania wielu metod jednocześnie – ang. multicasting), należy odpowiednio zmienić implementację metod pomocniczych. Poprzez użycie przeładowanego operatora +=, wywołują one metodę Combine() dołączając wskazaną metodę do listy wywołań delegata.

29 Multicasting c. d.

30 Usunięcie metody z listy wywołań
Aby wywołujący mógł usunąć metodę z listy wywołań, należy dostarczyć odpowiednie metody pomocnicze. Korzystają one z przeładowanego operatora -=, który powoduje niejawne wywołanie metody Remove().

31 Usunięcie metody z listy wywołań c. d.
W poniższym przykładzie usunięcia metody, pamiętamy zmienną delegata, którą później przekazujemy do usunięcia. Możliwe jest również ponowne zdefiniowania delegata na bazie metody, którą chcemy usunąć z listy. Tak więc przetrzymywanie zmiennej z takim delegatem nie jest konieczne.

32 Bardziej złożony przykład
Tradycyjnie zaczynamy od odpowiedniej modyfikacji klasy Car.

33 Bardziej złożony przykład c. d.
Do klasy Car dodajemy również definicję kolejnego typu delegata. CarMaintenanceDelegate może przechowywać i wywoływać dowolną metodę przyjmującą jako parametr obiekt klasy Car i nie zwracającą żadnej wartości.

34 Bardziej złożony przykład c. d. 2
Dodajemy definicję klasy, przechowującej listę samochodów za pomocą kolekcji generycznej. Na kolejnym slajdzie znajduje się definicja metody tej klasy, która jako argument przyjmuje wskazany typ delegata.

35 Bardziej złożony przykład c. d. 3

36 Bardziej złożony przykład c. d. 4
Dodajemy klasę definiującą metody, które będą przechowywane i wywoływane przez delegata klasy Car.

37 Bardziej złożony przykład c. d. 5
Garaż deleguje całą pracę do działu obsługi

38 Bardziej złożony przykład c. d. 6

39 Kowariancja delegatów
Do tej pory, przykładowe delegaty zwracały proste wartości liczbowe. Mogą one również zwracać złożone klasy należące do różnych hierarchii dziedziczenia. Załóżmy, że mamy klasę Car i dziedziczącą po niej klasę SportsCar. Utwórzmy delegat, który wywołuje utworzenie obiektu.

40 Kowariancja delegatów c. d.

41 Kowariancja delegatów c. d. 2

42 Kowariancja delegatów c. d. 3
Kowariancja pozwala na przekazywanie do delegata metod, których typ zwracanej wartości nie jest identyczny z sygnaturą delegata, ale jest potomnym w stosunku do zwracanego typu zdefiniowanego w tym delegacie. Na podobnej zasadzie działa kontrkowariancja, z tym, że dotyczy ona argumentów metod przekazywanych do delegata.

43 Generyczne delegaty

44 Generyczne delegaty c. d.

45 Zdarzenia

46 Niedogodności związane z delegatami
Delegaty są konstrukcjami umożliwiającymi dwukierunkową komunikację między obiektami w pamięci. Jednak wykorzystywanie ich w surowej postaci pociąga za sobą kilka niedogodności. Aby posłużyć się delegatem musimy: Zdefiniować typ delegata, Zadeklarować potrzebne zmienne tego typu, Stworzyć własne metody pomocnicze pozwalające na rejestrowanie i wyrejestrowywanie metod z delegatów w celu zachowania hermetyzacji. W przypadku zadeklarowania zmiennej delegata jako publicznej, ryzykujemy możliwości manipulacji na liście wywołań przez potencjalnie niebezpieczny kod w obiektach korzystających z naszego delegata.

47 Słowo kluczowe event Rozwiązaniem tych niedogodności jest słowo kluczowe event. Gdy kompilator napotka na to słowo, automatycznie generuje metody do rejestracji i wyrejestrowywania metod oraz zmienną do przechowywania listy tych metod. Zmienne te są zawsze deklarowane jako prywatne. W sumie, słowo kluczowe event (zdarzenie) jest po prostu syntaktycznym skrótem, który oszczędza czas programiście (podobnie jak w przypadku właściwości automatycznych).

48 Definiowanie zdarzenia
Definiowanie zdarzenia to dwukrokowy proces: Najpierw definiujemy odpowiedni typ delegata, Następnie definiujemy zdarzenie skojarzone z tym delegatem.

49 „Odpalanie” zdarzeń

50 Rejestracja metod obsługujących zdarzenie

51 Korzystajmy z pomocy środowiska
Przy dodawaniu metod do zdarzeń możemy skorzystać z pomocy środowiska. Po wpisaniu operatora +=, system pomocy podpowiada nam, jakiego rodzaju delegat jest obsługiwany przez dane zdarzenie. Po kliknięciu tabulatora, system pomocy doda odpowiedni konstruktor i teraz z kolei proponuje nam nazwę metody obsługującej dane zdarzenie. Jeżeli ponownie wciśniemy tabulator, zostanie wygenerowany szkielet metody o proponowanej nazwie.

52 Wzorzec zdarzenia Zdarzenia w bibliotece klas bazowych .NET definiowane są zgodnie ze wspólnym standardem. Delegaty przez nie obsługiwane przyjmują zawsze dwa parametry: Pierwszym jest System.Object reprezentujący referencje do obiektu, który wywołał dane zdarzenie. Drugi dziedziczy po typie System.EventArgs i reprezentuje informacje o aktualnym zdarzeniu.

53 Wzorzec zdarzenia c. d. W przypadku prostych zdarzeń można bezpośrednio przekazać obiekt klasy EventArgs. W innych przypadkach należy stworzyć własną klasę dziedziczącą po klasie EventArgs.

54 Wzorzec zdarzenia c. d. 2 Obecnie, definicja naszego delegata będzie wyglądać następująco: Odpalenie zdarzenia związanego z powyższym delegatem

55 Strona wywołującego I odpowiednio dopasowujemy metody obsługujące zdarzenia po stronie wywołującego.

56 Generyczny delegat EventHandler<T>
Skoro tyle delegatów przyjmuje jako parametr obiekt klasy System.Object oraz potomka klasy EventArgs, możemy jeszcze bardziej uprościć nasz kod poprzez wykorzystanie generycznego delegata EventHandler<T>, gdzie T jest niestandardową klasą dziedziczącą po EventArgs.

57 Generyczny delegat EventHandler<T> c. d.

58 Metody anonimowe i operator lambda

59 Problem Metody służące do obsługi zdarzeń nie są przeważnie wykorzystywane w innym miejscu aplikacji.

60 Rozwiązanie

61 Metody anonimowe Możliwe jest powiązanie delegata bezpośrednio z blokiem kodu w momencie rejestracji obsługi zdarzenia. Taki blok kodu jest nazywany metodą anonimową. Składnia metody anonimowej wygląda następująco: Z metody anonimowej mamy dostęp do zewnętrznych zmiennych (oprócz zmiennych oznaczonych atrybutem ref i out.

62 Konwersja grup metod Kompilator obsługuje również konwersję grup metod. Dzięki niej możemy do zdarzenia dodać od razu nazwę metody obsługującej dane zdarzenie (bez koniczności tworzenia delegata). W razie konieczności, samą nazwę metody można rzutować na odpowiedni typ delegata.

63 Operator lambda Operator lamba (=>) pozwala jeszcze bardziej skrócić zapis metod anonimowych.

64 Operator lambda c. d. Przed samym operatorem podajemy listę argumentów (typy są nieobowiązkowe) w nawiasach. W przypadku bezargumentowej metody wstawiamy przed operatorem same nawiasy: (() => … ). Po operatorze, umieszczamy blok instrukcji do wykonania, np.:

65 Dziękuję za uwagę


Pobierz ppt "Programowanie zaawansowane"

Podobne prezentacje


Reklamy Google