Dataczwartek, 13 sierpnia 2026 Czas18:34:17
← Programowanie konsolowe (C#)

Lekcja 14. Polimorfizm i metody wirtualne

Trudny / egzaminacyjny

Po co się tego uczymy?

Polimorfizm to trzecia z czterech głównych zasad OOP (po hermetyzacji i dziedziczeniu) i jeden z tematów, w których uczniowie najczęściej czują się niepewnie na egzaminie — a jednocześnie jedna z najbardziej praktycznych technik, eliminująca długie łańcuchy warunków sprawdzających typ obiektu.

Teoria

Polimorfizm (z greckiego "wiele form") pozwala, żeby TO SAMO wywołanie metody zachowywało się RÓŻNIE, w zależności od tego, na jakim konkretnie obiekcie zostało wywołane — nawet jeśli w kodzie operujesz na nich przez wspólny typ bazowy.

Aby to działało, klasa bazowa musi oznaczyć metodę jako virtual ("może zostać nadpisana"), a klasa pochodna nadpisuje ją słowem override:
class Pracownik { public virtual double ObliczPremie() { return Pensja * 0.05; } }
class Kierownik : Pracownik { public override double ObliczPremie() { return Pensja * 0.20; } }

Dzięki temu możesz mieć tablicę czy listę typu bazowego (Pracownik[]), przechowującą obiekty RÓŻNYCH klas pochodnych, i wywołać na każdym tę samą metodę — C# sam "wie", którą wersję uruchomić, sprawdzając RZECZYWISTY typ obiektu, nie typ zmiennej.

Bez polimorfizmu musiałbyś sprawdzać typ każdego obiektu ręcznie (if (p is Kierownik) { ... } else if (p is Programista) { ... }) — z polimorfizmem ten cały łańcuch warunków znika, bo każdy obiekt "sam wie", jak się zachować.

To jedna z najważniejszych korzyści programowania obiektowego: możesz dodać NOWĄ klasę pochodną (np. Stazysta) w przyszłości, bez zmiany ani jednej linii kodu, który już korzysta z polimorficznego wywołania ObliczPremie().

Schemat

Pracownik[] zespol = { programista, kierownik, stażysta };

foreach (Pracownik p in zespol)
    p.ObliczPremie();     <- WYWOŁANIE JEST TAKIE SAMO dla każdego,
                             ale KAŻDY typ wykonuje SWOJĄ WŁASNĄ wersję:

    Programista.ObliczPremie()  -> premia = pensja * 0.10
    Kierownik.ObliczPremie()    -> premia = pensja * 0.20
    Stazysta.ObliczPremie()     -> premia = 0

Przykład z życia

Naciśnięcie "drukuj" działa tak samo niezależnie od tego, jaka konkretnie drukarka jest podłączona (laserowa, atramentowa, etykiet) — każda "wie", jak zinterpretować to samo polecenie na swój własny sposób. To właśnie polimorfizm.

Kod (C#)

using System;

class Pracownik
{
    public string Imie;
    public double Pensja;

    public Pracownik(string imie, double pensja)
    {
        Imie = imie;
        Pensja = pensja;
    }

    public virtual double ObliczPremie() // "virtual" = może być nadpisana
    {
        return Pensja * 0.05;
    }
}

class Kierownik : Pracownik
{
    public Kierownik(string imie, double pensja) : base(imie, pensja) { }

    public override double ObliczPremie() // "override" = własna wersja
    {
        return Pensja * 0.20;
    }
}

class Stazysta : Pracownik
{
    public Stazysta(string imie, double pensja) : base(imie, pensja) { }

    public override double ObliczPremie()
    {
        return ; // stażyści na razie bez premii
    }
}

class Program
{
    static void Main()
    {
        Pracownik[] zespol = {
            new Pracownik("Ala", 5000),
            new Kierownik("Bartek", 9000),
            new Stazysta("Celina", 3000)
        };

        foreach (Pracownik p in zespol)
        {
            // to samo wywołanie - RÓŻNY wynik dla każdego typu!
            Console.WriteLine(p.Imie + ": premia = " + p.ObliczPremie() + " zł");
        }
    }
}

Komentarz i wyjaśnienie kodu

Tablica Pracownik[] zespol przechowuje TRZY RÓŻNE typy obiektów (Pracownik, Kierownik, Stazysta) — to możliwe właśnie dzięki dziedziczeniu (Lekcja 13), bo każdy z nich JEST RODZAJEM Pracownik. Pętla foreach wywołuje p.ObliczPremie() identycznie dla każdego elementu, ale C# w czasie DZIAŁANIA programu sprawdza rzeczywisty typ obiektu i uruchamia WŁAŚCIWĄ, nadpisaną wersję metody — to się nazywa wiązanie dynamiczne.

Ćwiczenie samodzielne

Dodaj do przykładu z lekcji nową klasę pochodną Dyrektor : Pracownik z premią 30%, bez zmiany kodu w Main() poza dodaniem nowego obiektu do tablicy.

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. Zdefiniuj klasę bazową Zwierze z metodą wirtualną WydajDzwiek() zwracającą tekst "...", oraz dwie klasy pochodne Pies (nadpisującą na "Hau!") i Kot (na "Miau!") — wywołaj metodę na tablicy obiektów obu typów.

  2. Rozbuduj hierarchię Pojazd z wcześniejszej lekcji o metodę wirtualną OpisPojazdu() zwracającą różny tekst dla każdej klasy pochodnej (np. Motocykl, Samochod), i wypisz opisy wszystkich pojazdów z listy przez pętlę.

  3. Zbuduj system naliczania opłat za wypożyczenie w wypożyczalni: klasa bazowa Produkt z metodą wirtualną ObliczOplateZaDzien(), oraz klasy pochodne Film (2 zł/dzień), Konsola (15 zł/dzień) i Rower (5 zł/dzień). Utwórz listę różnych wypożyczonych produktów i oblicz łączną opłatę za wypożyczenie wszystkich na 3 dni.

Typowe błędy

Najczęstszy błąd to zapomnienie słowa virtual w metodzie klasy bazowej — bez niego override w klasie pochodnej zgłosi błąd kompilacji, bo metoda nie jest oznaczona jako "nadpisywalna". Drugi błąd: użycie new zamiast override przy nadpisywaniu metody — to UKRYWA metodę bazową zamiast ją nadpisać, co psuje polimorfizm (przy wywołaniu przez typ bazowy uruchomi się stara wersja). Trzeci: ręczne sprawdzanie typu obiektu (if (p is Kierownik)) tam, gdzie polimorfizm załatwiłby sprawę prościej i bezpieczniej.

Nawiązanie do egzaminu zawodowego

To realizacja efektu INF.04.4.5 z podstawy programowej ("stosuje zasady programowania obiektowego") w zakresie polimorfizmu, oraz efektu INF.04.4.7 ("definiuje klasy pochodne") w części dotyczącej metod wirtualnych.