List<int>, List<string>, Dictionary<TKey, TValue> — nawiasy kątowe widziałeś już wielokrotnie, korzystając z gotowych kolekcji. Ten artykuł pokazuje, co się za nimi kryje i jak napisać własny typ generyczny, nie tylko używać cudzych.
Problem, który typy generyczne rozwiązują
// ❌ bez generyków — osobna klasa dla każdego typu danych
public class PudelkoInt
{
public int Wartosc { get; set; }
}
public class PudelkoString
{
public string Wartosc { get; set; }
}Te dwie klasy różnią się tylko typem Wartosc — cała reszta jest identyczna. Bez generyków musiałbyś pisać taką klasę osobno dla każdego typu, który chcesz “opakować”.
Własna klasa generyczna
public class Pudelko<T>
{
public T Wartosc { get; set; }
public Pudelko(T wartosc) => Wartosc = wartosc;
}
var pudelkoInt = new Pudelko<int>(42);
var pudelkoTekst = new Pudelko<string>("Cześć");T to parametr typu — placeholder, który wypełniasz konkretnym typem dopiero w momencie użycia (Pudelko<int>, Pudelko<string>). Jedna definicja klasy obsługuje dowolny typ, zamiast pisania osobnej klasy na każdy przypadek — dokładnie ten sam mechanizm stoi za List<T>, który widziałeś wcześniej.
Metoda generyczna — parametr typu na poziomie metody, nie klasy
static T Wiekszy<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) > 0 ? a : b;
}
int wiekszaLiczba = Wiekszy(5, 10); // działa dla int
string dluzszyTekst = Wiekszy("ab", "abc"); // ta sama metoda działa dla stringJedna metoda Wiekszy obsługuje dowolny typ, który da się porównać — bez pisania osobnej wersji dla int, osobnej dla string, osobnej dla DateTime.
Ograniczenia typu (constraints) — where T : …
where T : IComparable<T> w przykładzie powyżej to ograniczenie typu — mówi kompilatorowi: “T może być dowolnym typem, ale musi implementować IComparable<T>” (czyli musi umieć się z sobą porównywać przez CompareTo). Bez tego ograniczenia kompilator nie pozwoliłby wywołać a.CompareTo(b), bo nie miałby gwarancji, że dowolny typ T w ogóle ma taką metodę.
Najczęstsze ograniczenia:
where T : class // T musi być typem referencyjnym
where T : struct // T musi być typem wartościowym
where T : new() // T musi mieć publiczny konstruktor bezparametrowy
where T : IComparable<T> // T musi implementować ten interfejs
where T : BazowaKlasa // T musi dziedziczyć po tej klasieDlaczego to się opłaca — bezpieczeństwo typów bez rzutowania
// ❌ przed generykami — ArrayList przechowuje wszystko jako object
ArrayList lista = new ArrayList();
lista.Add(5);
lista.Add("tekst"); // kompilator na to pozwoli — błąd wyjdzie dopiero w runtime
// ✅ z generykami — kompilator pilnuje typu już na etapie kompilacji
List<int> liczby = new List<int>();
liczby.Add(5);
liczby.Add("tekst"); // ❌ błąd kompilacji — natychmiast, nie w runtimeTo sedno korzyści z generyków: błędne użycie typu wychodzi na jaw podczas kompilacji, nie dopiero gdy ktoś uruchomi program i trafi na wyjątek InvalidCastException.
Typy generyczne to fundament większości kolekcji w .NET — zobacz je w akcji w List<T>, Dictionary<TKey, TValue> i HashSet<T>.
Podsumowanie
Typy generyczne to nie tylko List<T> do konsumowania — to mechanizm języka pozwalający pisać własne klasy (Pudelko<T>) i metody (Wiekszy<T>), które działają dla dowolnego typu bez powielania kodu. Ograniczenia typu (where T : ...) pozwalają zawęzić, jakie typy są dozwolone, gdy metoda potrzebuje od nich konkretnej funkcjonalności (np. porównywalności). Efekt uboczny, który odczuwasz na co dzień: błędy typów wychodzą na jaw w czasie kompilacji, nie dopiero podczas działania programu.
🚀 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ń.
1 comment
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.
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ę →
UX Twojego bloga jest optymalne i nowatorskie, ostylowanie CSS sprawia wrażenie świeżości fajnie też że zadbałeś o detale to “kuje” w oczy. Długo pracowałeś nad jego realizacją ?