Dataczwartek, 13 sierpnia 2026 Czas18:51:32
← Programowanie konsolowe (C#)

Lekcja 17. Klasy generyczne (szablony klas)

Trudny / egzaminacyjny

Po co się tego uczymy?

Generyki to mechanizm, z którego korzystałeś już wielokrotnie (List, Dictionary), nie wiedząc jeszcze, jak dokładnie działa — ta lekcja odsłania tę "kuchnię" i pokazuje, jak tworzyć własne, elastyczne klasy działające dla dowolnego typu danych, bez powielania kodu.

Teoria

W Lekcji 8 korzystałeś z List<string> i Dictionary<string, int> — te "ostre nawiasy" z typem w środku to GENERYKI (generics), mechanizm, który teraz nauczysz się tworzyć SAMODZIELNIE.

Klasa generyczna to szablon klasy, który działa dla RÓŻNYCH typów danych, bez pisania osobnej wersji klasy dla każdego typu z osobna. Zamiast konkretnego typu (np. int), w definicji klasy używasz "placeholdera" — zwyczajowo nazywanego T (od "Type"):
class Pojemnik<T> { private T zawartosc; public void Ustaw(T wartosc) { zawartosc = wartosc; } public T Pobierz() { return zawartosc; } }

Tej samej klasy Pojemnik<T> możesz użyć dla DOWOLNEGO typu, podając go przy tworzeniu obiektu:
Pojemnik<int> pojemnikLiczb = new Pojemnik<int>();
Pojemnik<string> pojemnikTekstow = new Pojemnik<string>();

Dlaczego to lepsze niż kopiowanie klasy dla każdego typu? Bez generyków musiałbyś napisać osobno PojemnikInt, PojemnikString, PojemnikFilm — ten sam kod, powtórzony wielokrotnie, tylko z innym typem. Generyki pozwalają napisać logikę RAZ i używać jej dla dowolnego typu, zachowując przy tym pełne bezpieczeństwo typów (kompilator nie pozwoli wstawić string do Pojemnik<int>).

Dokładnie w ten sposób zbudowane są List<T> i Dictionary<TKey, TValue>, których używałeś już wcześniej — teraz wiesz, jak działają "od środka" i potrafisz stworzyć własny, podobny mechanizm.

Przykład z życia

Uniwersalne pudełko transportowe w magazynie może pomieścić różne rodzaje towarów (książki, elektronikę, ubrania) — sam mechanizm pakowania i transportu jest identyczny, zmienia się tylko to, CO jest w środku. To dokładnie idea klasy generycznej.

Kod (C#)

using System;

class Para<T1, T2> // klasa generyczna z DWOMA parametrami typu
{
    public T1 Pierwszy;
    public T2 Drugi;

    public Para(T1 pierwszy, T2 drugi)
    {
        Pierwszy = pierwszy;
        Drugi = drugi;
    }

    public void Wyswietl()
    {
        Console.WriteLine("(" + Pierwszy + ", " + Drugi + ")");
    }
}

class Program
{
    static void Main()
    {
        // ta sama klasa Para<T1, T2> działa dla RÓŻNYCH kombinacji typów
        Para<string, int> filmOcena = new Para<string, int>("Diuna", 9);
        filmOcena.Wyswietl(); // (Diuna, 9)

        Para<string, double> produktCena = new Para<string, double>("Bilet", 24.99);
        produktCena.Wyswietl(); // (Bilet, 24.99)

        Para<int, int> wspolrzedne = new Para<int, int>(10, 20);
        wspolrzedne.Wyswietl(); // (10, 20)
    }
}

Komentarz i wyjaśnienie kodu

class Para<T1, T2> deklaruje klasę generyczną z DWOMA niezależnymi parametrami typu — przy tworzeniu obiektu (new Para<string, int>(...)) podajesz konkretne typy, którymi T1 i T2 zostaną zastąpione dla TEGO KONKRETNEGO obiektu. Kompilator pilnuje zgodności typów — próba filmOcena.Pierwszy = 5; (liczba zamiast tekstu) zgłosiłaby błąd kompilacji, bo T1 zostało ustalone jako string.

Ćwiczenie samodzielne

Zdefiniuj prostą klasę generyczną Pojemnik<T> z metodami Ustaw(T wartosc) i Pobierz(), i użyj jej dla typu int oraz string.

Plac zabaw — wypróbuj online

Poniższy kod wykonuje się od razu w Twojej przeglądarce — nic nie trzeba instalować. Zmień kod i kliknij „Uruchom”.

CSHARP

Zadania do pracy własnej

  1. Utwórz klasę generyczną Para<T> (z JEDNYM parametrem typu) przechowującą dwie wartości TEGO SAMEGO typu i metodę Zamien() zamieniającą je miejscami.

  2. Zdefiniuj generyczną klasę StosProsty<T> (uproszczony stos) z wewnętrzną List<T>, metodami Dodaj(T element) (dokłada na wierzch) i Zdejmij() (usuwa i zwraca ostatnio dodany element).

  3. Zbuduj generyczną klasę MagazynDanych<T> przechowującą listę elementów typu T, z metodami: Dodaj(T element), UsunPierwszy(), Liczba() zwracającą aktualną liczbę elementów, oraz WyswietlWszystkie() wypisującą całą zawartość. Przetestuj tę samą klasę dla typu string (magazyn tytułów filmów) i dla własnej klasy Produkt z wcześniejszych lekcji.

Typowe błędy

Częsty błąd to próba wykonania na parametrze typu T operacji, których nie każdy typ obsługuje (np. T + T nie zadziała, bo nie każdy typ da się dodawać operatorem +) — generyki bez dodatkowych ograniczeń (constraints, temat wykraczający poza ten kurs) muszą działać dla KAŻDEGO możliwego typu. Drugi błąd: mylenie nazwy parametru typu T z prawdziwą, konkretną klasą — T to tylko "placeholder", zastępowany dopiero przy użyciu klasy generycznej. Trzeci: tworzenie osobnych, niemal identycznych klas dla różnych typów zamiast jednej klasy generycznej — to dokładnie problem, który generyki mają rozwiązać.

Nawiązanie do egzaminu zawodowego

To bezpośrednia realizacja efektu INF.04.4.8 z podstawy programowej: "programuje szablony (wzorce) klas" — dosłowne sformułowanie oficjalnego tekstu to "generyki dla typów liczbowych", co ten przykład (i zadania) w pełni pokrywa.