🔥 Darmowa Roadmapa .NET — kompletny plan nauki do pierwszej pracy. Pobierz i dołącz do Listy VIP →

Delegat jako parametr w C# – pułapka bez komunikatu

Delegat jako parametr metody w C# — nazwa metody wchodzi jako argument do Orchestra.Perform
public void Perform(SongDelegate song)
{
    Console.WriteLine("Orkiestra gotowa, zaczynamy...");
    song();
    Console.WriteLine("Koniec utworu. Brawo!\n");
}

public void PerformSilently(SongDelegate song)
{
    Console.WriteLine("Orkiestra gotowa, zaczynamy...");
    Console.WriteLine("Koniec utworu. Brawo!\n");
}

Dwie metody. Identyczna sygnatura – (SongDelegate) → void. Obie kompilują się bez ostrzeżenia. Wywołaj pierwszą, a usłyszysz utwór. Wywołaj drugą, a usłyszysz ciszę między dwoma tymi samymi napisami na konsoli.

Kompilator nie zgłosi ani jednego błędu w żadnym z tych dwóch przypadków. Sprawdza wyłącznie to, czy parametr song ma poprawny typ, nigdy to, czy ciało metody faktycznie go wywołuje.

To jest sedno delegata jako parametru metody: mechanika przekazania jest identyczna jak przy int czy string, ale przyjęcie delegata w liście parametrów niczego nie gwarantuje.

Delegat, który podróżuje

Do tej pory w tej serii delegat mieszkał w jednej metodzie, definiowałeś go, przypisywałeś i wywoływałeś, wszystko w Main(). Tym razem delegat pierwszy raz podróżuje jako argument do innej metody.

using System;

namespace BeginnerDelegateParameterExample;

// 1. Definicja delegata, reprezentuje utwór bez parametrów i bez wyniku
public delegate void SongDelegate();

// 2. Orkiestra, jej metoda PRZYJMUJE delegat jako zwykły parametr
public class Orchestra
{
    public void Perform(SongDelegate song)
    {
        Console.WriteLine("Orkiestra gotowa, zaczynamy...");
        song();
        Console.WriteLine("Koniec utworu. Brawo!\n");
    }
}

public class Program
{
    public static void Main()
    {
        var orchestra = new Orchestra();

        // KROK 1: Przekazujemy metodę PlayJazz jako argument
        orchestra.Perform(PlayJazz);

        // KROK 2: Ta sama metoda Perform, INNY utwór jako argument
        orchestra.Perform(PlayRock);

        // BONUS: Przekazujemy lambdę “w locie”, bez osobnej metody
        orchestra.Perform(() => Console.WriteLine("...gra Twoją własną melodię!"));
    }

    private static void PlayJazz()
    {
        Console.WriteLine("Gramy improwizowanego jazza...");
    }

    private static void PlayRock()
    {
        Console.WriteLine("Gramy energetycznego rocka...");
    }
}

Popatrz na listę parametrów Perform(SongDelegate song) – typ parametru to nazwa delegata, dokładnie tak samo, jak gdyby to był int volume albo string titlePerform nie wie, CZYM jest song. Wie tylko, że dostanie coś, co da się wywołać bez parametrów i bez wyniku i że ma to jawnie odpalić w linijce song().

Zasada: 
parametr metody może mieć typ delegata. Przekazujesz nazwę metody bez nawiasów, dokładnie jak przy przypisaniu do zmiennej, różni się tylko miejsce składniowe: argument wywołania, nie prawa strona przypisania.

Wypróbuj

Ten sam parametr, dwa różne utwory jako argument

public delegate void SongDelegate();public void Perform(SongDelegate song) => song();orchestra.Perform(PlayJazz);orchestra.Perform(PlayRock);
Krok 1 · Definiujesz

Powstaje typ. Kontraktem jest () → void — nic nie wchodzi, nic nie wraca.

Krok 2 · Metoda przyjmuje jako parametr

Perform(SongDelegate song) — typ parametru to nazwa delegata, jak int czy string.

Krok 3 · Przekazujesz jako argument

Bez nawiasów, jak zawsze — ale tym razem to argument wywołania, nie przypisanie.

Konsola — naciśnij „Następny krok”…

Zwróć uwagę: linijka 2 (definicja Perform) nie zmienia się między krokami 3 i 4 — zmienia się tylko to, KTÓRA metoda wpływa jako argument. Dokładnie ta sama Złota Zasada co przy zwykłym przypisaniu, tylko w nowym miejscu składniowym.

Krok 2 i 3 — parametr i argument

poprzednich wpisach tej serii delegat zawsze lądował w zmiennej lokalnej. Tutaj ląduje w parametrze metody:

// KROK 2 — metoda PRZYJMUJE delegat
public void Perform(SongDelegate song) 
{
    // wywołanie w środku ciała metody
    song();                             
}

// KROK 3 — PRZEKAZUJESZ jako argument
orchestra.Perform(PlayJazz);

Nazwa metody bez nawiasów wpływa jako argument, ląduje w parametrze song - a dopiero WEWNĄTRZ ciała Perform, w linijce song(), faktycznie się wykonuje. To rozdzielenie „przekazania” od „wywołania” jest źródłem obu pułapek poniżej.

Pułapka #1: delegat-parametr, którego nikt nie wywołuje

public void PerformSilently(SongDelegate song)
{
    Console.WriteLine("Orkiestra gotowa, zaczynamy...");
    // song(); ← ta linijka zniknęła
    Console.WriteLine("Koniec utworu. Brawo!\n");
}

PerformSilently ma dokładnie taką samą sygnaturę jak Perform. Parametr song jest poprawnym SongDelegate - kompilator jest w pełni usatysfakcjonowany. A mimo to na konsoli zobaczysz „gotowa, zaczynamy”, potem ciszę, potem „koniec utworu”. Utwór nigdy nie zagrał, bo nikt w środku metody nie napisał song().

To jest lustrzane odbicie pułapki, którą znasz z delegatu zwracającego wartość: tam wynik ginął, bo nikt go nie ODBIERAŁ z delegata. Tutaj dźwięk ginie, bo nikt nie ODPALA delegata, który już czeka w środku metody, gotowy do użycia.

Zasada: 
sam fakt, że metoda PRZYJMUJE delegat jako parametr, niczego nie gwarantuje. Musisz go jawnie wywołać w ciele metody, tak jak każdą inną zmienną typu delegata.

Obie metody kompilują się

Gdzie ginie song()?

Perform — wywołuje song()
public void Perform(SongDelegate song){ song();}
konsola…
PerformSilently — nie wywołuje song()
public void PerformSilently(SongDelegate song){ // song();}
konsola…

Zwróć uwagę: obie karty pokażą zielony badge „kompiluje się”. Kompilator sprawdza wyłącznie typ parametru song — a jednak tylko jedna metoda faktycznie odpala przekazany delegat. Ta pułapka nie zostawia po sobie żadnego śladu w edytorze ani żadnego komunikatu w oknie Błędy.

Pułapka #2: liczba parametrów to wciąż część sygnatury

private static void PlaySongLoudly(int volume) => Console.WriteLine($"Gramy jazza na {volume}%...");

orchestra.Perform(PlaySongLoudly);
// CS1503: Argument 1: cannot convert from 'method group' to 'SongDelegate'

Jeden parametr zamiast zera. Ta sama zasada, którą znasz z wcześniejszych wpisów tej serii - różnica w tym, że konwersja metoda-grupa-na-delegat dzieje się tym razem w miejscu ARGUMENTU wywołania, nie przy przypisaniu do zmiennej. Kod błędu i komunikat są identyczne - to ten sam mechanizm sprawdzania sygnatury, uruchomiony w nowym miejscu składniowym.

Pułapka #3: typ zwracany nadal się liczy

private static bool TryPlayJazz()
{
    Console.WriteLine("Gramy improwizowanego jazza...");
    return true;
}

orchestra.Perform(TryPlayJazz);
// CS0407: 'bool Pulapki.TryPlayJazz()' has the wrong return type

TryPlayJazz zwraca boolSongDelegate deklaruje void. Dokładnie ta sama różnica, którą widziałeś przy PrintGreeting i PrintSum we wcześniejszych wpisach — tylko odwrócona: tam delegat OCZEKIWAŁ wartości, a metoda jej nie dawała. Tutaj delegat NIE oczekuje niczego, a metoda i tak coś zwraca. Kompilator jest równie surowy w obie strony.

Ściągawka na code review

ZapisWynikDlaczego
orchestra.Perform(PlayJazz);✅ gra() → void — kształt się zgadza, song() jest wywołane
orchestra.PerformSilently(PlayJazz);⚠️ ✅ kompiluje sięsygnatura pasuje — ale song() nigdy nie pada w ciele metody
orchestra.Perform(PlaySongLoudly);❌ CS1503jeden parametr zamiast zera
orchestra.Perform(TryPlayJazz);❌ CS0407zwraca bool zamiast void

Fundament pod Callback, Strategy i LINQ

Ten jeden mechanizm - delegat jako zwykły parametr metody - to fundament pod większość wzorców, które poznasz w dalszej części tej serii. Callback raportujący postęp, wzorzec Strategy podmieniający algorytm w locie, nawet to, jak pod maską działa .Where() z LINQ - wszystkie opierają się na dokładnie tej samej idei: metoda przyjmuje gotowe zachowanie jako argument, zamiast znać je na sztywno w swoim ciele.

Zero dodatkowej złożoności w samym dzisiejszym przykładzie - żadnych dodatkowych parametrów, żadnej wartości zwracanej, żadnej logiki biznesowej. Cała uwaga poszła na jeden fakt: delegat może być parametrem.

Podsumowanie

  • Parametr metody może mieć typ delegata - dokładnie tak jak int czy string. Przekazujesz nazwę metody bez nawiasów, tak jak przy przypisaniu.
  • To metoda odbierająca decyduje, KIEDY wywołać delegat - i musi to zrobić jawnie. Przyjęcie delegata jako parametru niczego nie gwarantuje.
  • Pułapka bez komunikatu: metoda o identycznej sygnaturze, która nie wywołuje przyjętego delegata, kompiluje się bez ostrzeżenia i po prostu nic nie robi.
  • Sygnatura obowiązuje także w miejscu argumentu - tylko numer błędu jest inny niż przy przypisaniu: zła liczba parametrów to CS1503 (CS0123 zobaczysz, gdy przypisujesz metodę do zmiennej delegata), a zły typ zwracany to niezmiennie CS0407.
  • To fundament pod Callback, Strategy i .Where() z LINQ - wszystkie opierają się na delegacie przekazanym jako parametr.

🎥 Wersja wideo: ten sam przykład krok po kroku — Delegaty w C# #6 — Delegat jako parametr metody
📂 Kod źródłowy: github.com/dev-hobby/CSharpDelegates — projekt DevHobby.L06-OrchestraConductor
📘 Darmowa roadmapa Junior .NET Developer: dev-hobby.pl/lista-vip

Zadanie na 5 minut: 
dopisz do Orchestra 
metodę PerformWithSupport(SongDelegate support, SongDelegate mainAct), która najpierw wywołuje support, potem mainAct.
Wywołaj ją dla co najmniej dwóch różnych par, w tym jednej z lambdą jako support. Napisz w komentarzu, co decyduje o kolejności grania - kolejność deklaracji metod w Main() czy kolejność wywołań w ciele PerformWithSupport.

Zobacz

📦 Kod źródłowy tego artykułu: zobacz na GitHub
Mariusz Jurczenko
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →