09
Delegowanie zadań: delegaty i metody anonimowe
Cel zadania

Nauka wykorzystywania delegatów jako bezpiecznych typowo wskaźników na metody oraz zrozumienie idei przekazywania zachowania jako parametru (callback). Student pozna wbudowane typy delegatów (Func, Action), metody anonimowe oraz wyrażenia lambda, które znacząco skracają i unowocześniają kod C#.

Scenariusz problemowy

Pracownicy Twojego komisu "Auto-Premium" codziennie mierzą się z dziesiątkami specyficznych zapytań od potencjalnych kupców. Jeden szuka wyłącznie tanich aut do 15 tys. zł, inny interesuje się tylko markami luksusowymi, a jeszcze inny chce zobaczyć wszystkie pojazdy wyprodukowane w ciągu ostatnich trzech lat. Pisanie oddzielnych metod dla każdego z tych warunków sprawiłoby, że Twój system stałby się niezarządzalną, powtarzalną plątaniną kodu.

Rozwiązaniem są delegaty – fundament programowania funkcyjnego w C#. Pozwalają one na traktowanie metod jak zwykłych zmiennych. Dzięki temu możesz stworzyć jedną, uniwersalną "wyszukiwarkę", która jako parametr przyjmuje nie wartość, a "logikę wyboru" (warunek filtrowania). W połączeniu z wyrażeniami lambda (skróconym zapisem metod), Twoje zapytania stają się tak zwięzłe, że filtrujesz plac komisu jedną czytelną linijką kodu. To technika, która otwiera drzwi do zaawansowanych bibliotek takich jak LINQ i sprawia, że Twój system jest gotowy na najbardziej wymyślne prośby klientów.

Opis wykonania
  • Zdefiniuj własny delegat WarunekFiltrowania(Pojazd p) zwracający bool.
  • Stwórz metodę FiltrujPojazdy, która przyjmuje listę pojazdów oraz delegat jako filtr.
  • Użyj wbudowanego delegata Func<Pojazd, bool> jako nowocześniejszą alternatywę dla własnego delegata.
  • Zastosuj delegat Action<Pojazd> do zdefiniowania sposobu wyświetlania wyników.
  • Wywołaj filtrowanie przekazując jako parametr metodę nazwaną (np. JestTani).
  • Wywołaj filtrowanie używając wyrażenia lambda (np. p => p.Rok > 2020).
  • Zademonstruj delegat wielopunktowy (multicast) używając operatora += do łączenia kilku akcji w jedną.
  • Pokaż mechanizm domknięcia (closure): użyj zmiennej lokalnej z Main wewnątrz lambdy przekazywanej do filtra.
  • Wypisz wyniki filtrowania dla różnych scenariuszy biznesowych (tanie auta, auta konkretnej marki, nowe auta).
Kod źródłowy
Program.cs
using System;
using System.Collections.Generic;

namespace LaboratoriumOOP
{
    public class Pojazd
    {
        public string Marka { get; set; }
        public double Cena { get; set; }
        public int Rok { get; set; }
    }

    class Program
    {
        // DELEGAT: Typ określający "sygnaturę" metody, którą możemy tu podstawić.
        // Mówi: "przyjmę każdą metodę, która bierze Pojazd i zwraca bool".
        public delegate bool WarunekPojazdu(Pojazd p);

        static void Main(string[] args)
        {
            Console.WriteLine("--- ZADANIE 09: Delegaty i lambdy ---\n");

            List<Pojazd> komis = new List<Pojazd>
            {
                new Pojazd { Marka = "Skoda", Cena = 12000, Rok = 2012 },
                new Pojazd { Marka = "BMW", Cena = 150000, Rok = 2022 },
                new Pojazd { Marka = "Fiat", Cena = 8000, Rok = 2005 },
                new Pojazd { Marka = "Audi", Cena = 45000, Rok = 2016 }
            };

            // 1. Użycie delegata z METODĄ NAZWANĄ
            Console.WriteLine("Pojazdy tanie (filtr metodą nazwaną):");
            WyswietlFiltrowane(komis, CzyTani);

            // 2. Użycie delegata z WYRAŻENIEM LAMBDA
            // Lambda to skrócona, anonimowa metoda: (parametry) => wyrażenie
            Console.WriteLine("\nPojazdy po 2015 roku (filtr lambdą):");
            WyswietlFiltrowane(komis, p => p.Rok > 2015);

            // 3. Użycie wbudowanego delegata Func
            double limitBudzetu = 50000;
            Console.WriteLine($"\nPojazdy w budżecie {limitBudzetu} PLN (Func + closure):");
            // Lambda "zamyka" (przechwytuje) zmienną limitBudzetu z otoczenia
            List<Pojazd> wyniki = FiltrujZFunc(komis, p => p.Cena <= limitBudzetu);
            wyniki.ForEach(p => Console.WriteLine($"- {p.Marka}"));

            // 4. Delegat wielopunktowy (Multicast)
            Console.WriteLine("\nWykonanie serii akcji na obiekcie (Action multicast):");
            Action<Pojazd> operacjeLoggera = p => Console.Write($"[LOG] Analiza: {p.Marka}... ");
            operacjeLoggera += p => Console.WriteLine("Status: OK");
            
            operacjeLoggera(komis[0]);

            Console.ReadKey();
        }

        // Metoda przyjmująca delegat jako parametr (Callback)
        static void WyswietlFiltrowane(List<Pojazd> lista, WarunekPojazdu filtr)
        {
            foreach (var p in lista)
            {
                if (filtr(p)) Console.WriteLine($"- {p.Marka} ({p.Rok})");
            }
        }

        // Nowocześniejsza wersja z Func
        static List<Pojazd> FiltrujZFunc(List<Pojazd> lista, Func<Pojazd, bool> filtr)
        {
            List<Pojazd> wynik = new List<Pojazd>();
            foreach (var p in lista)
            {
                if (filtr(p)) wynik.Add(p);
            }
            return wynik;
        }

        static bool CzyTani(Pojazd p) => p.Cena < 15000;
    }
}
                    
Wynik w konsoli
--- ZADANIE 09: Delegaty i lambdy --- Pojazdy tanie (filtr metodą nazwaną): - Skoda (2012) - Fiat (2005) Pojazdy po 2015 roku (filtr lambdą): - BMW (2022) - Audi (2016) Pojazdy w budżecie 50000 PLN (Func + closure): - Skoda - Fiat - Audi Wykonanie serii akcji na obiekcie (Action multicast): [LOG] Analiza: Skoda... Status: OK