INF.04 · C# · lekcja 17
Klasy generyczne (szablony klas)
Jeden pojemnik na różne typy
- Zrozum
- Sprawdź
- Napisz sam
Po tej lekcji: jeden pojemnik na różne typy
Dzisiaj zrobisz konkretną rzecz
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.
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.
Sprawdź w działaniu
Jeden pojemnik na różne typy
Najpierw przewidź wynik. Zmień dane w modelu i porównaj efekt. Spróbuj także przypadku granicznego, a nie tylko pierwszego poprawnego przykładu.
Model pokazuje wybraną zasadę. Nie uruchamia ani nie ocenia wklejonego kodu C#.
1. Przygotuj i uruchom własny projekt
2. Najpierw mały przykład
Wykonaj tę próbę przed dłuższym rozwiązaniem. T jest parametrem typu. Pudelko<int> i Pudelko<string> powstają z tej samej definicji, ale kompilator pilnuje właściwych typów. To nie zamiana wszystkiego na object.
var liczby = new Pudelko<int>(7);
var napisy = new Pudelko<string>("Ola");
Console.WriteLine(liczby.Wartosc);
Console.WriteLine(napisy.Wartosc);
class Pudelko<T>
{
public T Wartosc { get; }
public Pudelko(T wartosc) { Wartosc = wartosc; }
}
7 Ola
- Co sprawdzić? Zmień jedną wartość i przed uruchomieniem zapisz przewidywany rezultat. Porównaj go z opisem wyniku; przy błędzie wróć do konkretnej instrukcji.
Jak to działa — 1
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>).
Jak to działa — 2
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.
3. Pełny przykład — czytaj od Main
Teraz zastąp Program.cs tym programem. Zanim uruchomisz, znajdź dane wejściowe, operację i miejsce prezentacji wyniku. W tym wariancie zachowujemy jawny Main. Nie wklejaj kilku pełnych programów do jednego pliku.
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)
}
}
- Prześledź kod
class Para<T1, T2>deklaruje klasę generyczną z DWOMA niezależnymi parametrami typu — przy tworzeniu obiektu (new Para<string, int>(...)) podajesz konkretne typy, którymiT1iT2zostaną zastąpione dla TEGO KONKRETNEGO obiektu. Kompilator pilnuje zgodności typów — próbafilmOcena.Pierwszy = 5;(liczba zamiast tekstu) zgłosiłaby błąd kompilacji, boT1zostało ustalone jakostring.
Sprawdź, zanim przepiszesz cały program
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ć.
Ćwiczenie z pomocą · samodzielna praca
4. Teraz Twoja zmiana
Zdefiniuj prostą klasę generyczną Pojemnik<T> z metodami Ustaw(T wartosc) i Pobierz(), i użyj jej dla typu int oraz string.
Spełnij warunki polecenia. Zapisz wejście, otrzymany wynik i wyjaśnij, która część kodu za niego odpowiada.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Wróć do podstawowej formy oraz objaśnienia: T jest parametrem typu. Pudelko<int> i Pudelko<string> powstają z tej samej definicji, ale kompilator pilnuje właściwych typów. To nie zamiana wszystkiego na object.
Krok 2 — pokaż podpowiedź
Zmień tylko część potrzebną do polecenia. Zachowaj działającą wersję przed zmianą i uruchamiaj po każdym małym etapie.
Krok 3 — pokaż podpowiedź
Sprawdź przypadek zwykły i graniczny; porównaj z regułą: 7 Ola
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
5. Sprawdź rozumienie, nie tylko uruchomienie
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →