Czym są delegaty? Krótka książkowa definicja, mówiąca czym jest delegat:

Podobne dokumenty
Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Czym są właściwości. Poprawne projektowanie klas

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

TEMAT : KLASY DZIEDZICZENIE

Klasy abstrakcyjne i interfejsy

Programowanie obiektowe

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

.NET Klasy, obiekty. ciąg dalszy

Czym jest stos i sterta?

Programowanie obiektowe

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Podstawy Programowania Obiektowego

Wykład 8: klasy cz. 4

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Programowanie obiektowe

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

PARADYGMATY PROGRAMOWANIA Wykład 4

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Rozdział 4 KLASY, OBIEKTY, METODY

Dokumentacja do API Javy.

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

Programowanie obiektowe

Deklaracja struktury w C++

Dekoratora używa się wstawiając linijkę zaczynającą się przed definicją dekorowanego obiektu (klasy czy funkcji).

Programowanie zaawansowane

Dziedziczenie. Tomasz Borzyszkowski

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Kurs WWW. Paweł Rajba.

C++ Przeładowanie operatorów i wzorce w klasach

Wykład 5: Klasy cz. 3

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Programowanie w środowiskach graficznych. Wykład 3 Język C#

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

Szablony funkcji i szablony klas

Programowanie obiektowe

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

PHP 5 język obiektowy

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

MVVM Light Toolkit. Julita Borkowska

C# 6.0 : kompletny przewodnik dla praktyków / Mark Michaelis, Eric Lippert. Gliwice, cop Spis treści

Klasy cd. Struktury Interfejsy Wyjątki

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Programowanie obiektowe - 1.

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Wykład 5 Okna MDI i SDI, dziedziczenie

Materiały do zajęć VII

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Szablony klas, zastosowanie szablonów w programach

Klasy abstrakcyjne, interfejsy i polimorfizm

Programowanie obiektowe

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Metody Metody, parametry, zwracanie wartości

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Interfejsy i klasy wewnętrzne

Szablony funkcji i klas (templates)

Programowanie strukturalne i obiektowe. Funkcje

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

Do czego służą klasy?

1 Atrybuty i metody klasowe

Technologie obiektowe

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

KOTLIN. Język programowania dla Androida

Programowanie 2. Język C++. Wykład 3.

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Wprowadzenie do szablonów szablony funkcji

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Dziedziczenie jednobazowe, poliformizm

Wprowadzenie do szablonów szablony funkcji

Wykład 4 Delegat (delegate), właściwości indeksowane, zdarzenie (event) Zofia Kruczkiewicz

Programowanie obiektowe i zdarzeniowe

Klasy i obiekty cz I Klasy, obiekty, podstawy używania obiektów

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Obiekty: co i jak. Wykonał: Piotr Pięda dla koła naukowego KNI

Podstawy programowania. Wykład PASCAL. Zmienne wskaźnikowe i dynamiczne. dr Artur Bartoszewski - Podstawy prograowania, sem.

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

Aplikacje w środowisku Java

Polimorfizm. dr Jarosław Skaruz

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Co to jest klasa? Z programistycznego punktu widzenia klasa stanowi typ danych, który odwzorowuje wspólne cechy jakiegoś obiektu.

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Transkrypt:

Delegaty są ściśle związane z językiem C#. Delegaty są bardzo często porównywane do wskaźników na funkcje znanych z języka C++. Oferują bardzo podobną funkcjonalność, jednak są o wiele bezpieczniejsze i udostępniają większe możliwości. Zapewniają kontrolę typów oraz wywołania asynchroniczne metod. Czym są delegaty? Krótka książkowa definicja, mówiąca czym jest delegat: Delegaty to obiekty, które wskazują na dowolne metody znajdujące się w programie. Są definiowane za pomocą słowa kluczowego delegate. Przypominają działanie wskaźników z języka C++, jednak zapewniają kontrolę typów. Mogą wskazywać metody instancyjne oraz statyczne. Po co są potrzebne delegaty? Problem używania wskaźników na funkcje istnieje od zawsze jeszcze od czasów świetności języków C/C++. Projektując różne rozbudowane systemy istniała potrzeba, aby funkcja wywoływana mogła komunikować się z funkcją wywołującą. Wskaźniki na funkcje mogą być argumentami metod, dzięki temu do funkcji można przesłać adresy różnych funkcji zależnie od wymaganej sytuacji lub wyboru użytkownika. Taką samą elastyczność zapewniają delegaty w języku C#. Idąc dalej tym tropem dochodzimy do tematu funkcji wywołań zwrotnych (callbacks). Znane przede wszystkim z C++ callbacki są po prostu wskaźnikami na funkcję, więc ich odpowiednikiem jest delegata w C#. Czym są callbaki? Ideologia używania callbacków jest odwrotną do ideologii używania zwykłych funkcji. Posiadając dowolną metodę, programista zajmuje się jej wywołaniem względem instancji danej klasy. Dzięki użyciu callbacków sytuacja się odwraca. Programista nie wywołuje funkcji manualnie, a jedynie definiuje funkcję o odpowiedniej nazwie i parametrach, do późniejszego wywołania przez inne elementy systemu (niezależne od naszego programu). Opracowanie takiego mechanizmu było koniecznością aby zapewnić np. współpracę API Win32 z programami pisanymi pod Windowsa. Posługując się wskaźnikami na funkcje oraz callbackami, funkcje API Windowsa nie miały narzuconego wywołania konkretnej Karol Trybulec p-programowanie.pl 1

metody mogły wywoływać wskaźnik na funkcję, a funkcję dopisywał na własną rękę programista. Jest to niesamowicie elastyczne rozwiązanie. Dokładnie taki sam mechanizm zapewnia nam język C# w postaci delegat mimo, że jest to język wysokiego poziomu i komunikacja z API Win32 nie jest już potrzebna. Nie tylko API Windowsa Powyższe zastosowanie jest tylko przykładem, nie należy wiązać delegatów konkretnie z funkcjami API Windowsa. Delegat jest wskaźnikiem na dowolną funkcję lub metodę. Niejednokrotnie ich użycie może przyczynić się do znacznej optymalizacji kodu naszego programu. Wszystko zależy od sytuacji. Przejdźmy do przykładów. Budowa delegatów Delegaty są obiektami dziedziczącymi bezpośrednio niejawnie z MulticastDelegate. Ta z kolei dziedziczy z klasy Delegate oraz Object. Deklarując nowy delegat kompilator automatycznie przekształca go na klasę (widać to w języku CIL). Klasa ta jest automatycznie zapieczętowana (sealed) a więc nie można z niej dziedziczyć. Automatycznie są także generowane niektóre metody. Oto lista najważniejszych metod każdego delegata: Invoke() wywołuje metody podpięte do delegata (jedną lub listę metod). Przyjmuje takie same argumenty jak metoda związana z delegatem. Działa synchronicznie względem głównego wątku programu. BeginInvoke() wywołuje metody związane z delegatem w sposób asynchroniczny. Wywołana metoda zostaje wykonana w osobnym wątku. Jak widzisz w odróżnieniu od wskaźników funkcyjnych znanych z C++, delegaty w języku C# zapewniają bardzo wygodną możliwość asynchronicznego wywoływania metod. Jest to w wielu sytuacjach główna przyczyna używania w danym momencie delegata zamiast zwykłego wywołania funkcji. Tworzenie delegatów W bardzo prosty sposób jesteśmy w stanie stworzyć dowolny delegat wskazujący na dowolne metody. Do tego celu należy posłużyć się słowem kluczowym delegate. Aby delegat mógł wskazywać na metodę, musi posiadać taki sam typ zwracany oraz takie same Karol Trybulec p-programowanie.pl 2

argumenty. Przykładowy delegat może wyglądać następująco: 1 public delegate int Dzialanie(int x, int y); // delegat w C# 2 typedef int ( * Dzialanie ) ( int, int ); // wskaźnik na funkcje w C++ Oprócz przykładu delegata w C#, umieściłem identyczny w działaniu wskaźnik na funkcję w języku C++. Zapewne od razu widzisz różnicę w czytelności i prostocie kodu. W C# nie występują żadne gwiazdki ani podejrzany format składni. Delegat wygląda w C# tak samo, jak zwykła deklaracja metody z dodanym słowem delegate. Jak należy rozumieć utworzony przez nas delegat? Może on wskazywać na dowolną funkcję przyjmującą dwa argumenty typu int oraz zwracającą wartość typu int. Podpinanie metod do delegatów Mając zadeklarowany delegat, możemy podpiąć do niego dowolną metodę. Ważne jest to, aby zgodne były argumenty oraz typ zwracany. Co ciekawe, delegaty w C# obsługują tzw. multicasting. Dzięki temu możliwe jest podpięcie wielu metod do jednego delegata. Podpinanie metod pod delegat odbywa się za pomocą: pierwsza metoda podpinana jest poprzez konstruktor, podczas tworzenia instancji delegata każdą następną metodę możemy dodać/usunąć za pomocą operatorów += oraz -= Wywołując metody delegata możemy, ale nie musimy, użyć metody Invoke(). Spójrzmy na prosty przykład: Karol Trybulec p-programowanie.pl 3

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public delegate void Dzialanie(int x, int y); public class Matma public void Dodaj(int l1, int l2) Console.WriteLine(l1 + l2); public void Odejmij(int l1, int l2) Console.WriteLine(l1 - l2); class Program static void Main() Matma matma = new Matma(); Dzialanie dzialanie = new Dzialanie(matma.Dodaj); // wywoła matma.dodaj(5,5) dzialanie(5,5); dzialanie += matma.odejmij; // wywoła matma.dodaj(7,4) // wywoła matma.odejmij(7,4) dzialanie(7, 4); dzialanie.invoke(7,4) // to samo co wyzej Console.ReadKey(); Do delegata Dzialanie mogliśmy podpiąć dwie metody Matma.Dodaj oraz Matma.Odejmij, dlatego że mają taki sam typ zwracany i przyjmują takie same argumenty. Delegaty jako funkcje wywołań zwrotnych W rzeczywistych przypadkach, delegaty nie służą nam do robienia nikomu niepotrzebnych wskaźników na funkcje, tylko po to aby wywoływać funkcję poprzez delegat a nie jej nazwę. Takie zjawisko występuje tylko wtedy, kiedy zależy nam na szybkim wywołaniu asynchronicznym danej metody. W większości przypadków delegaty używane są do tworzenia funkcji zwrotnych tzw. callbacków. W tym celu deklarujemy delegat wewnątrz interesującej nas klasy nie ma w tym zachowaniu nic złego. Wręcz przeciwnie, deklarowanie delegatów wewnątrz innych klas jest całkiem normalne i często spotykane. Deklaracja delegata wewnątrz jakiejkolwiek klasy zostanie niejawnie rozwinięta przez kompilator do postaci klasy zagnieżdżonej. Karol Trybulec p-programowanie.pl 4

Jeszcze raz, bardzo krótko czym jest callback? Jest to funkcja zwrotna, której idea używania jest przeciwna do standardowego używania funkcji. Zamiast wywoływać interesujące nas funkcje, nasz obiekt wywołuje delegat. To na co będzie wskazywał delegat w przyszłości, zależy już od innych obiektów i programistów pracujących na naszej klasie. Umieszczając delegat wewnątrz jakiejś klasy trzymajmy się zasad: definicja delegata musi być publiczna zostanie niejawnie rozwinięta do definicji zagnieżdżonej klasy zgodnie z zasadą hermetyzacji, zmienna delegata musi być prywatna. Obudowujemy ją setterem w celu zapewnienia dostępu z zewnątrz (kiedyś tę rolę przejmą za Ciebie eventy) Dla przykładu, zaprojektujmy klasę udającą element jakiegoś systemu. Klasa będzie miała podstawowe funkcjonalności takie jak autoryzacja czy logowanie. Dzięki użyciu delegatów będziemy mogli zaimplementować w naszej klasie system logowania operacji w postaci callbacka. Karol Trybulec p-programowanie.pl 5

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class System //publiczna definicja delegaty (będzie klasą zagnieżdżoną) public delegate void Logi(string wiadomosc); //prywatna zmienna delegata private Logi _wyslijlogi; //setter dla zmeinnej delegata public void DodajCallback(Logi funkcja) _wyslijlogi += funkcja; public void UsunCallback(Logi funkcja) _wyslijlogi += funkcja; //przykladowa funkcjonalność naszej klasy public bool Logowanie(string user, string password) /* jakeiś operacje odpowiedzialne za Autoryzację */ _wyslijlogi("nastąpiła próba logowania użytkownika: " + user); return true; class Program static void Main() //tworzymy instancję klasy i podpinamy funckję zwrotną System system = new System(); system.dodajcallback(callbacklogi); system.logowanie("user", "pass"); Console.ReadKey(); static void CallbackLogi(string wiadomosc) Console.WriteLine(wiadomosc); Console.WriteLine("Została wywołana funkcja zwrotna"); Karol Trybulec p-programowanie.pl 6

Powyższy przykład ukazuje ideologię używania delegatów jako funkcji zwrotnych. Programista otrzymujący gotową klasę, może zaimplementować funkcję wywołań zwrotnych i podpiąć ją w odpowiedni sposób. Sytuacja wyglądała by jeszcze lepiej, gdybyśmy podpinali funkcję wywołań zwrotnych w konstruktorze klasy podczas tworzenia jej instancji. W ten sposób działa wiele funkcji z API Win32. Co ciekawe, dzięki obsłudze multicastingu, w powyższym przykładzie możemy podpiąć dowolną ilość funkcji wywołań zwrotnych. Zostaną one wywołane wszystkie podczas wywołania metody System.Logowanie(). Delegaty jako parametry funkcji Nic nie stoi na przeszkodzie aby użyć delegata jako parametr funkcji. Takie działanie nadal jest nazywane mianem funkcji zwrotnej. Spójrz na przykład: Karol Trybulec p-programowanie.pl 7

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Liczby // deklaracja publicznej delegaty public delegate void DelOperacja(ref ArrayList list); //prywatna kolekcja ArrayList _listaliczb = new ArrayList(); //konstruktor public Liczby(params int[] liczby) _listaliczb.addrange(liczby); //callback wywołujący delegatę. Co wywoła? Nie wiadomo! //zalezy to od kogoś, kto będzie pracował na naszej klasie public void Operacja(DelOperacja operacja) operacja(ref _listaliczb); //statyczna klasa z metodami, które pasują do callbacka public static class Operacje public static void Drukuj(ref ArrayList lista) foreach (var i in lista) Console.Write(i); Console.WriteLine(); public static void Odwroc(ref ArrayList lista) lista.reverse(); class Program static void Main() Liczby l = new Liczby(1, 2, 3, 4, 5, 6); //wypisze liczby l.operacja(operacje.drukuj); //wypisze liczby w odwrotnej kolejności l.operacja(operacje.odwroc); l.operacja(operacje.drukuj); Console.ReadKey(); Zwróć uwagę na dziwny przypadek ukazany w przykładzie. Mimo, że klasa Liczby pozornie nie udostępnia żadnej funkcjonalności wygląda bardziej jak niestandardowa kolekcja obiektów to dzięki funkcjom wywołań zwrotnych i delegatom możemy bardzo rozszerzać jej funkcjonalność. Karol Trybulec p-programowanie.pl 8

Dzieje się tak dlatego, że programista tworzący klasę Liczby pomyślał, że warto będzie zaimplementować w niej callback o nazwie Operacja. Wywołuje ona funkcję zwrotną z referencją do prywatnej kolekcji, do której nie można byłoby dostać się winny sposób. Delegaty generyczne Z poprzednich artykułów od razu rzuca się w oczy, jak nieelastyczne są delegaty. Zapewniają one bardzo ścisłą kontrolę typów, dlatego nie reagują dobrze na jakiekolwiek zmiany w kodzie. Rozwiązaniem tego problemu są delegaty generyczne. Zanim typy generyczne został wprowadzone do języka C#, można było uzyskać podobny efekt korzystając z mechanizmu pakowania (boxing). Polegało to po prostu na utworzeniu delegaty, której argumentem był object. Takie rozwiązanie przynosiło kilka wad: wszystkie argumenty musiały podlegać pakowaniu i rozpakowywaniu, przez co spada wydajność nie istniała zupełnie żadna kontrola typów, ponieważ wszystko da się rzutować na object Gotowe delegaty Func i Action W którejś wersji frameworka.net pojawił się zestaw kilku gotowych delegatów, z czego najbardziej interesującymi są Func oraz Action. Dzięki nim proces tworzenia delegatów został skrócony do absolutnego minimum. Nie trzeba tworzyć własnych deklaracji, wystarczy użyć gotowego rozwiązania. Delegat Func Delegat Func jest delegatem generycznym. Podczas deklaracji trzeba określić typ zwracany oraz typ parametrów. Konstruktor został przeciążony aż 17 razy, dlatego możemy być pewni, że dopasujemy go do każdej naszej funkcji. Należy pamiętać, że w kwadratowych dzióbkach końcowy parametr jest zawsze tym zwracanym. Wszystkie jakie dopiszemy wcześniej będą wejściowymi: Func<out> Func<in, out> Func<in, in, out> Karol Trybulec p-programowanie.pl 9

itd.. Formalna definicja delegata Func wygląda następująco: 1 public delegate TResult Func<in T, out TResult>(T arg); Oprócz tego został on oczywiście 17 razy przeciążony. Oto Przykład użycia delegaty Func: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Matematyka public int Dodawanie(int x, int y) return x + y; class Program static void Main() Matematyka m = new Matematyka(); //podpięcie delegata Func do metody klasy Matematyka Func<int,int,int> dodawanie = m.dodawanie; Console.WriteLine(dodawanie(1, 2)); Console.ReadKey(); Delegat Action Działanie delegata Action jest identyczne, jednak używamy go w przypadku procedur. Definicja formalna może wyglądać następująco: 1 public delegate void Action<in T>(T arg); Jedyna różnica występuje podczas określania parametru generycznego. Wszystkie typy wymienione w kwadratowych dzióbkach będą parametrami. Spójrzmy na przykład: Karol Trybulec p-programowanie.pl 10

1 2 3 4 5 6 7 8 9 10 11 static void Main() Matematyka m = new Matematyka(); //podpięcie delegata Action do Console.WriteLine Action<string> wypisz = System.Console.WriteLine; wypisz("jakis tekst"); Console.ReadKey(); W przykładzie utworzyliśmy delegat o nazwie wypisz, który wskazuje na funkcję System.Console.WriteLine. Określiliśmy mu jeden argument typu string. Podsumowanie Delegaty są bardzo prostym i lekkim działem. Ich zrozumienie jest bardzo ważne, ponieważ delegaty są fundamentem do dobrego zrozumienia wątków oraz zdarzeń. O zdarzeniach, metodach anonimowych i wyrażeniach lambda napiszę w innym wpisie. Karol Trybulec p-programowanie.pl 11