06
Polimorfizm: metody wirtualne i nadpisywanie zachowań
Cel zadania

Zrozumienie koncepcji polimorfizmu dynamicznego i późnego wiązania (late binding). Student nauczy się korzystać ze słów kluczowych virtual oraz override, a także pozna operatory bezpiecznego rzutowania is oraz as do sprawdzania typów w czasie wykonywania programu.

Scenariusz problemowy

W Twoim prężnie działającym komisie "Auto-Premium" każdy pojazd, zanim trafi na wystawę, musi przejść procedurę przygotowania. Jednak proces ten wygląda zupełnie inaczej dla samochodu, który wymaga mycia tapicerki i woskowania karoserii, a inaczej dla motocykla, gdzie kluczowe jest sprawdzenie naciągu łańcucha i polerowanie chromów. Zamiast pisać oddzielne pętle dla każdego typu pojazdu, chcesz po prostu wywołać wspólną komendę "PrzygotujDoSprzedazy()" dla całej floty.

To właśnie tutaj polimorfizm pokazuje swoją potęgę: jeden komunikat wysłany do grupy różnych obiektów wywołuje u każdego z nich specyficzną, odpowiednią dla jego typu reakcję. System sam "wie", którą wersję metody wywołać, mimo że operujemy na ogólnej liście pojazdów. W tym zadaniu nauczysz się również, jak bezpiecznie sprawdzać, z jakim typem pojazdu masz w danej chwili do czynienia (używając operatorów is i as), aby móc wykonać dodatkowe operacje dostępne tylko dla konkretnych podtypów.

Opis wykonania
  • W klasie bazowej Pojazd zdefiniuj wirtualną metodę PrzygotujDoSprzedazy() używając słowa virtual.
  • W klasach pochodnych Samochod i Motocykl nadpisz tę metodę za pomocą słowa override.
  • Użyj base.PrzygotujDoSprzedazy() wewnątrz nadpisanych metod, aby zachować wspólną logikę (np. sprawdzanie poziomu płynów).
  • Stwórz listę List<Pojazd> i dodaj do niej obiekty różnych typów.
  • Przeiteruj po liście wywołując metodę PrzygotujDoSprzedazy() – zaobserwuj mechanizm późnego wiązania.
  • Użyj operatora is, aby sprawdzić czy dany obiekt jest samochodem i jeśli tak, wykonaj specyficzną dla niego akcję.
  • Użyj operatora as jako alternatywy dla bezpiecznego rzutowania.
  • Zademonstruj użycie właściwości GetType() oraz operatora typeof() do porównania dokładnych typów obiektów.
  • Wyjaśnij w komentarzach różnicę między nadpisaniem (override) a ukrywaniem metod (new) – choć w zadaniu skupiamy się na tym pierwszym.
Kod źródłowy
Program.cs
using System;
using System.Collections.Generic;

namespace LaboratoriumOOP
{
    public class Pojazd
    {
        public string Marka { get; protected set; }

        public Pojazd(string marka) { Marka = marka; }

        // VIRTUAL: Metoda wirtualna posiada domyślną implementację,
        // ale może zostać nadpisana w klasie pochodnej.
        public virtual void PrzygotujDoSprzedazy()
        {
            Console.WriteLine($"[POJAZD] {Marka}: Sprawdzanie poziomu płynów eksploatacyjnych...");
        }
    }

    public class Samochod : Pojazd
    {
        public Samochod(string marka) : base(marka) { }

        // OVERRIDE: Nadpisanie metody wirtualnej.
        public override void PrzygotujDoSprzedazy()
        {
            base.PrzygotujDoSprzedazy(); // Wywołanie logiki z klasy bazowej
            Console.WriteLine($"[SAMOCHÓD] {Marka}: Mycie karoserii i odkurzanie wnętrza.");
        }

        public void OtworzBiezacyProjekt() 
        {
            Console.WriteLine(" -> Specjalna funkcja dostępna tylko dla komputerów pokładowych aut.");
        }
    }

    public class Motocykl : Pojazd
    {
        public Motocykl(string marka) : base(marka) { }

        public override void PrzygotujDoSprzedazy()
        {
            base.PrzygotujDoSprzedazy();
            Console.WriteLine($"[MOTOCYKL] {Marka}: Smarowanie łańcucha i polerowanie chromowanych elementów.");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--- ZADANIE 06: Polimorfizm dynamiczny i rzutowanie ---\n");

            List placKomisowy = new List
            {
                new Samochod("Toyota"),
                new Motocykl("Honda"),
                new Samochod("Mercedes")
            };

            Console.WriteLine("1. Proces przygotowania całej floty (Polimorfizm):");
            foreach (var pojazd in placKomisowy)
            {
                // Wywołanie polimorficzne: program sam decyduje w czasie runtime,
                // którą wersję metody PrzygotujDoSprzedazy wywołać.
                pojazd.PrzygotujDoSprzedazy();
                Console.WriteLine("------------------------------------------");
            }

            Console.WriteLine("\n2. Wyszukiwanie specyficznych typów (Rzutowanie):");
            foreach (var p in placKomisowy)
            {
                // OPERATOR 'is': Sprawdza czy obiekt jest zgodny z typem
                if (p is Samochod s)
                {
                    Console.WriteLine($"Zaleziono auto: {s.Marka}. Wykonuję akcje dodatkowe...");
                    s.OtworzBiezacyProjekt();
                }

                // OPERATOR 'as': Bezpieczne rzutowanie (zwraca null jeśli typy nie pasują)
                Motocykl m = p as Motocykl;
                if (m != null)
                {
                    Console.WriteLine($"Znaleziono motocykl: {m.Marka}. Wykryto typ: {m.GetType().Name}");
                }
            }

            Console.WriteLine("\n3. Porównanie typów (typeof vs GetType):");
            if (placKomisowy[0].GetType() == typeof(Samochod))
            {
                Console.WriteLine("Pierwszy obiekt na liście jest dokładnie typu Samochod.");
            }

            Console.WriteLine("\nNaciśnij klawisz...");
            Console.ReadKey();
        }
    }
}
                    
Wynik w konsoli
--- ZADANIE 06: Polimorfizm dynamiczny i rzutowanie --- 1. Proces przygotowania całej floty (Polimorfizm): [POJAZD] Toyota: Sprawdzanie poziomu płynów eksploatacyjnych... [SAMOCHÓD] Toyota: Mycie karoserii i odkurzanie wnętrza. ------------------------------------------ [POJAZD] Honda: Sprawdzanie poziomu płynów eksploatacyjnych... [MOTOCYKL] Honda: Smarowanie łańcucha i polerowanie chromowanych elementów. ------------------------------------------ [POJAZD] Mercedes: Sprawdzanie poziomu płynów eksploatacyjnych... [SAMOCHÓD] Mercedes: Mycie karoserii i odkurzanie wnętrza. ------------------------------------------ 2. Wyszukiwanie specyficznych typów (Rzutowanie): Zaleziono auto: Toyota. Wykonuję akcje dodatkowe... -> Specjalna funkcja dostępna tylko dla komputerów pokładowych aut. Znaleziono motocykl: Honda. Wykryto typ: Motocykl Zaleziono auto: Mercedes. Wykonuję akcje dodatkowe... -> Specjalna funkcja dostępna tylko dla komputerów pokładowych aut. 3. Porównanie typów (typeof vs GetType): Pierwszy obiekt na liście jest dokładnie typu Samochod. Naciśnij klawisz...