Delegat z parametrem w C# – typ jest kontraktem, nazwa nie

private static void ShowHello(string name)
{
Console.WriteLine($"Cześć, {name}!");
}
private static void ShowGoodbye(string name)
{
Console.WriteLine($"Do widzenia, {name}!");
}Dwie metody o identycznym kształcie: przyjmują string, nic nie zwracają. W poprzednim wpisie o delegatach delegat był rozkazem bez treści – „skocz”, „strzelaj”. Nie dało się mu nic przekazać.
Teraz dokładamy parametr. I przy okazji odpowiemy na pytanie, które przy pierwszym kontakcie z delegatami zadaje sobie każdy: co właściwie musi się zgadzać, żeby metoda „pasowała” do delegata?
Złota Zasada 3 Kroków
Praca z delegatem wygląda tak samo w konsolowej zabawce i w silniku reguł biznesowych. Zawsze trzy kroki, zawsze w tej kolejności.
Złota Zasada 3 Kroków — krok po kroku
Powstaje typ. Kontraktem jest string, nie nazwa text.
Bez nawiasów. Kompilator sprawdza tu całą sygnaturę.
Z nawiasami i z argumentem. Dopiero teraz coś się dzieje.
Zwróć uwagę: linijka myDelegate("Jan"); w krokach 3 i 5 jest
identyczna — ten sam argument, ten sam zapis. Zmieniło się wyłącznie to, co siedzi
w zmiennej.
using System;
// KROK 1: DEFINIUJESZ — szablon metody: przyjmuje string, zwraca void.
public delegate void DisplayMessage(string text);
public class Program
{
public static void Main()
{
// KROK 2: PRZYPISUJESZ — bez nawiasów.
DisplayMessage myDelegate = ShowHello;
// KROK 3: WYWOŁUJESZ — z nawiasami i z argumentem.
myDelegate("Jan");
// Ta sama zmienna, inna metoda, niezmieniona linijka wywołania.
myDelegate = ShowGoodbye;
myDelegate("Jan");
}
private static void ShowHello(string name)
{
Console.WriteLine($"Cześć, {name}!");
}
private static void ShowGoodbye(string name)
{
Console.WriteLine($"Do widzenia, {name}!");
}
}Definicję czytamy od prawej do lewej: (string text) - przyjmuje jeden tekst; void - nic nie zwraca; DisplayMessage - tak nazywa się nowy typ; delegate - a całość to definicja typu, nie metody.
Argument "Jan" wchodzi do zmiennej myDelegate, ta przekazuje go do metody, na którą aktualnie wskazuje, i tam ląduje w parametrze name. Zmienna delegata jest przewodem - nie interesuje jej, co metoda zrobi z argumentem.
Zasada:
gubisz się przy jakimkolwiek delegacie?
Zapytaj: gdzie jest definicja, gdzie przypisanie, gdzie wywołanie.
W dziewięciu przypadkach na dziesięć któryś z kroków jest po prostu w innym pliku.
Kontraktem jest typ, nie nazwa parametru
Tu jest sedno tego wpisu i punkt, którego większość kursów nie tłumaczy.
W definicji delegata parametr nazywa się text. W metodzie ShowHello nazywa się name. Zupełnie inne słowo. I to jest w pełni poprawne.
public delegate void DisplayMessage(string text);
private static void ShowHello(string name) { } // ✅ pasuje
private static void ShowHello2(string wiadomosc) { } // ✅ też pasuje
private static void ShowHello3(string xyz) { } // ✅ i to pasuje
Kompilator porównuje wyłącznie typy. Nazwa parametru w definicji delegata to dokumentacja dla człowieka, nic więcej.
Dlatego na code review zobaczysz delegaty, w których nazwy parametrów nie mają nic wspólnego z podpiętymi metodami. To nie jest błąd ani niechlujstwo.
Zasada:
nazwa metody nie liczy się wcale,
nazwa parametru liczy się tylko w jednym miejscu, patrz następna sekcja.
Pułapka #1: argument nazwany bierze nazwę z delegata
Jedyne miejsce, w którym nazwa parametru z definicji delegata realnie coś zmienia:
myDelegate(text: "Jan"); // ✅ 'text' — z definicji delegata
myDelegate(name: "Jan"); // ❌ CS1739
// The best overload does not have a parameter named 'name'Wygląda to jak niekonsekwencja, ale jest logiczne:
w momencie kompilacji nikt nie wie, która metoda wyląduje w zmiennej w czasie działania programu. Kompilator ma do dyspozycji wyłącznie deklarację delegata, więc tylko stamtąd może wziąć nazwę.
Praktyczny wniosek:
nazywaj parametry delegatów sensownie. Trafiają do IntelliSense każdego, kto ten delegat wywoła, nawet jeśli podpięta metoda nazywa je zupełnie inaczej.
Zasada:
textwdelegate void DisplayMessage(string text)to nie ozdobnik, to publiczne API Twojego delegata.
Pułapka #2: delegat jest tak samo rygorystyczny jak metoda
myDelegate(); // ❌ CS7036 — brak wymaganego argumentu
myDelegate("Jan", "Kowalski"); // ❌ CS1593 — delegat nie przyjmuje 2 argumentów
myDelegate(42); // ❌ CS1503 — cannot convert from 'int' to 'string'
Żadnej niespodzianki i właśnie dlatego warto to zobaczyć. Delegat nie jest luźniejszy od zwykłego wywołania metody. To normalne wywołanie, tylko adres metody siedzi w zmiennej. Jeśli traktowałeś dotąd delegaty jako coś magicznego, ta sekcja powinna to odczarować.
Pułapka #3: sygnatura to komplet
Dopasowanie działa po całej sygnaturze: liczbie parametrów, ich typach i typie zwracanym. Zgodność jednego elementu nie wystarcza.
private static void ShowAge(int age) { } // zły typ parametru
private static void Jump() { } // brak parametru
private static string AskName() { return "Jan"; } // zły typ zwracanyDisplayMessage a = ShowAge;
// ❌ CS0123: No overload for 'ShowAge' matches delegate 'DisplayMessage'
DisplayMessage b = Jump;
// ❌ CS0123 — zero parametrów, a delegat wymaga jednego
DisplayMessage c = AskName;
// ❌ CS0407: 'string AskName()' has the wrong return typeZwróć uwagę na Jump, metodę z poprzedniego wpisu. Pasowała do ButtonAction, ale do DisplayMessage już nie, bo liczba parametrów też jest częścią kontraktu.
To realna przewaga delegatów nad refleksją czy dynamic: niedopasowanie wychodzi w edytorze, nie u użytkownika.
Zasada:
czytaj numer błędu.
CS0123 to niezgodne parametry,
CS0407 to niezgodny typ zwracany,
CS1503 to zły typ argumentu przy wywołaniu.
Ściągawka na code review
| Zapis | Wynik | Dlaczego |
|---|---|---|
DisplayMessage d = ShowHello; | ✅ | void (string) — kształt się zgadza |
d("Jan"); | ✅ | dokładnie jeden string |
d(); | ❌ CS7036 | brak wymaganego argumentu text |
d("Jan", "Kowalski"); | ❌ CS1593 | delegat nie przyjmuje dwóch argumentów |
d(42); | ❌ CS1503 | int to nie string |
d(name: "Jan"); | ❌ CS1739 | nazwa pochodzi z delegata, nie z metody |
DisplayMessage d = ShowAge; | ❌ CS0123 | int zamiast string |
DisplayMessage d = AskName; | ❌ CS0407 | zwraca string zamiast void |
Czy ta metoda pasuje do delegata?
Sześć kandydatur, jedna zagadka na trzy sekundy. Kliknij — jeśli nie pasuje, zobaczysz dokładnie ten komunikat, który pokaże Ci kompilator.
Po co to, skoro mogę wywołać metodę wprost?
Uczciwe pytanie.
W powyższym przykładzie faktycznie nic nie zyskujesz, wszystko dzieje się w jednej metodzie Main.
Wartość pojawia się wtedy, gdy kod decydujący i kod wykonujący przestają siedzieć w tym samym miejscu. Kiedy definicja jest w jednym pliku, przypisanie w drugim, a wywołanie w trzecim, delegat staje się jedynym sensownym sposobem, żeby te trzy pliki nie musiały o sobie wiedzieć.
Na tej samej zasadzie działa każda lambda w LINQ i każdy handler zdarzenia - tam też ktoś inny decyduje, co ma się wykonać, a ktoś inny to wykonuje. W nowoczesnym C# rzadko piszesz własny delegate, bo wystarczają gotowe Func i Action, ale mechanizm pod spodem jest dokładnie ten sam.
Podsumowanie
- Złota Zasada 3 Kroków:
definiujesz → przypisujesz (bez nawiasów) → wywołujesz (z nawiasami). - Kontraktem jest typ parametru, nie jego nazwa.
textinamepasują do siebie bez problemu. - Wyjątek:
argument nazwany bierze nazwę z definicji delegata, bo kompilator nie wie, która metoda tam wyląduje. - Sygnatura to komplet:
liczba parametrów, ich typy i typ zwracany. Nazwa metody nie liczy się wcale. - Delegat jest tak samo rygorystyczny jak zwykłe wywołanie metody:
CS7036, CS1593 i CS1503 to te same błędy, które dostałbyś bez delegata.
📘 Darmowa roadmapa Junior .NET Developer: dev-hobby.pl/lista-vip
Zadanie na 5 minut:
zdefiniuj delegate void ShowPrice(decimal amount);, napisz ShowNetPrice i ShowGrossPrice (+23% VAT),
podepnij obie pod tę samą zmienną i wywołaj dla 100m.
Potem spróbuj podpiąć ShowPriceWithCurrency(decimal amount, string currency)
— który numer błędu dostaniesz i która część sygnatury się nie zgadza?
Napisz w komentarzu.
Zobacz
- Delegaty w C# — dlaczego przypisujesz bez nawiasów - poprzedni wpis, delegat bez parametru
- Func i Action w C# - gotowe delegaty zamiast własnych
- Zdarzenia (Events) w C# - delegat ze słowem kluczowym
event - LINQ w C# — kompletny przewodnik - każda lambda to delegat
📦 Kod źródłowy tego artykułu: zobacz na GitHub
🚀 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.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
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ę →