Co to są te delegaty?

Dzisiejszy wpis jest przeznaczony dla osób, które (jak ja na studiach) widząc taką podpowiedź z IntelliSense:

Łapały się za głowę / znowu czarna magia / a komu to potrzebne / przecież da się to jakoś objeść po ludzku.

Gorzej jak ktoś, z kim pracujemy, wykorzysta ten mechanizm. Wtedy lepiej wiedzieć jak działa, bo przeważnie nie ma czasu na domysły i doszkalanie w pracy. A z samą delegatą jest jak z większością innych spraw – wydają się trudne, ale po chwili czytania o nich, w głowie pojawia się zawsze „I co, to takie proste? Z daleka wydawało się nie do ogarnięcia.”

Własne delegaty

Najprościej mówiąc – delegata to wskaźnik na funkcję lub kilka funkcji, które wywołają się po kolei. Definiując ją tworzymy nowy typ zmiennej, aby jej użyć będziemy musieli zadeklarować tą zmienną. Spójrzmy najpierw na przykład, powinno być trochę łatwiej.

  1. Aby utworzyć delegatę musimy użyć słowa kluczowego delegate.
  2. Następnie typ – chcę żeby wywoływała tylko Console.WriteLine dlatego użyłem tutaj void.
  3. Parametry – ważna sprawa, delegata musi posiadać takie same parametry jak funkcje, które będzie wywoływać.
  4. Utworzyłem zmienną doCalculations typu Calculations.
  5. W konstruktorze tej klasy podpiąłem dwie metody do naszej delegaty.

Myślę że wielokrotnie widziałaś/-eś poniższe metody, dlatego nie będę ich tłumaczył:

Etap tworzenia naszego kalkulatora uważam za zamknięty. Pora na uruchomienie programu.

Aby wywołać naszą delegatę należy użyć polecenia Invoke (lub BeginInvoke, jeśli chcemy wywołać asynchronicznie).

Gotowe delegaty

W C# mamy możliwość tworzenia swoich delegat, jak i korzystania z tych wbudowanych. Są różne opinie odnośnie pisania swoich, na pewno warto wiedzieć co tam pod spodem siedzi. Cała zabawa zaczyna się jednak w tym miejscu, bo używanie gotowców jest dużo przyjemniejsze. Przyjrzymy się trzem z nich – Func, Action i Predicate.

Standardowo najpierw rzucimy okiem na kod (uwaga: krótki ale treściwy):

Action to funkcja która nic nie zwraca. Przyjmuje aż do 16 parametrów, będących typami generycznymi.

Func działa podobnie jak Action. Jedyną różnicą jest to, że ostatni parametr jest typem zwracanym.
Może przyjąć od 0 do 16 parametrów wejściowych, jednak zawsze musi coś zwrócić.

Predicate przyjmuje tylko jeden parametr i zwraca Boolean.

Do czego możemy to wykorzystać? Wróć na górę posta i spójrz na pierwszy obrazek. Niektóre funkcje z przestrzeni nazw System.Linq wymagają używania Func czy Predicate. Dzięki możliwości stosowania wyrażeń lambda tworzymy krótkie 1-linijkowe metody. Ułatwia nam także wyszukiwanie konkretnych rzeczy w naszym systemie, ale o tym może nieco więcej przy Drzewach Wyrażeń (Expression Trees).

Jeśli zrozumiałeś o co chodzi z podstawami, zachęcam do poczytania wpisów Cezarego, aby rozszerzyć i utrwalić te informacje 🙂