Dziedziczenie w C# – nie pisz tego samego dwa razy
Nauczysz się tworzyć hierarchie klas, eliminować duplikację kodu i budować elastyczne, łatwe w utrzymaniu aplikacje. To jeden z filarów programowania obiektowego!
Problem – powtarzający się kod
Wyobraź sobie, że tworzysz grę z różnymi postaciami: Wojownik, Mag i Łucznik. Każda postać ma imię, punkty życia i może atakować. Zobaczmy, jak wyglądałby kod bez dziedziczenia:
// ❌ PROBLEM: Powtarzający się kod w każdej klasie! public class Wojownik { public string Imie { get; set; } public int PunktyZycia { get; set; } public int Poziom { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } public void Atakuj() { Console.WriteLine($"{Imie} atakuje mieczem!"); } } public class Mag { public string Imie { get; set; } // To samo co w Wojownik! public int PunktyZycia { get; set; } // To samo! public int Poziom { get; set; } // To samo! public int Mana { get; set; } // Tylko to jest unikalne public void PrzedstawSie() // To samo! { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } public void Atakuj() { Console.WriteLine($"{Imie} rzuca kulę ognia!"); } } public class Lucznik { public string Imie { get; set; } // Znowu to samo! public int PunktyZycia { get; set; } // Znowu! public int Poziom { get; set; } // Znowu! public int Strzaly { get; set; } public void PrzedstawSie() // Znowu! { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } public void Atakuj() { Console.WriteLine($"{Imie} strzela z łuku!"); } }
- Duplikacja – Imie, PunktyZycia, Poziom i PrzedstawSie() powtarzają się 3 razy!
- Trudne utrzymanie – Chcesz dodać pole „Doswiadczenie”? Musisz zmienić 3 klasy!
- Ryzyko błędów – Łatwo zapomnieć zmienić jedną z klas
- Rozrastanie się – Co jeśli będzie 20 typów postaci?
Ten problem rozwiązuje dziedziczenie – zamiast pisać to samo wiele razy, definiujemy wspólne cechy raz i pozwalamy innym klasom je „odziedziczyć”.
Co to jest dziedziczenie?
Dziedziczenie to mechanizm OOP, który pozwala klasie „przejąć” pola i metody innej klasy. Klasa dziedzicząca automatycznie otrzymuje wszystkie publiczne i chronione składowe klasy bazowej.
Pomyśl o dziedziczeniu jak o cechach rodzinnych:
- Klasa bazowa (rodzic) = Ssak – ma cechy wspólne: oddycha, je, porusza się
- Klasa pochodna (dziecko) = Pies – dziedziczy cechy ssaka + dodaje własne: szczeka, aportuje
- Klasa pochodna (dziecko) = Kot – dziedziczy cechy ssaka + dodaje własne: miauczy, mruczy
Pies i Kot nie muszą definiować „oddychania” – dostają to automatycznie od Ssaka!
PunktyZycia
Poziom
PrzedstawSie()
+ AtakMieczem()
+ RzucCzar()
+ Strzelaj()
↑ Wszystkie klasy automatycznie mają Imie, PunktyZycia, Poziom i PrzedstawSie()
Terminologia
| Termin | Inne nazwy | Opis |
|---|---|---|
| Klasa bazowa | Nadklasa, klasa rodzica, base class | Klasa, po której dziedziczymy (Postac) |
| Klasa pochodna | Podklasa, klasa dziecka, derived class | Klasa dziedzicząca (Wojownik, Mag) |
| Dziedziczenie | Inheritance | Relacja „jest rodzajem” (Wojownik jest Postacią) |
Dziedziczenie ma sens, gdy możesz powiedzieć „X jest rodzajem Y„:
- ✅ Wojownik jest rodzajem Postaci – TAK, dziedziczenie
- ✅ Pies jest rodzajem Zwierzęcia – TAK, dziedziczenie
- ❌ Samochód jest rodzajem Silnika – NIE! Samochód ma silnik (kompozycja)
Podstawowa składnia dziedziczenia
W C# dziedziczenie zapisujemy za pomocą dwukropka po nazwie klasy.
// Klasa BAZOWA (rodzic) public class Postac { public string Imie { get; set; } public int PunktyZycia { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}"); } } // Klasa POCHODNA (dziecko) – dziedziczy po Postac public class Wojownik : Postac { // Wojownik automatycznie MA: Imie, PunktyZycia, PrzedstawSie() // Dodajemy tylko to, co jest UNIKALNE dla Wojownika public int Sila { get; set; } public void AtakMieczem() { Console.WriteLine($"{Imie} atakuje mieczem z siłą {Sila}!"); } }
// Tworzenie obiektu klasy pochodnej Wojownik conan = new Wojownik(); // Mamy dostęp do składowych odziedziczonych z Postac: conan.Imie = "Conan"; // ← z klasy Postac conan.PunktyZycia = 100; // ← z klasy Postac conan.PrzedstawSie(); // ← z klasy Postac → "Jestem Conan" // I do składowych własnych Wojownika: conan.Sila = 15; // ← tylko w Wojownik conan.AtakMieczem(); // ← tylko w Wojownik → "Conan atakuje mieczem..."
C# nie obsługuje dziedziczenia wielokrotnego (jedna klasa nie może mieć dwóch rodziców). To ograniczenie zapobiega wielu problemom.
// ❌ TO NIE ZADZIAŁA: public class Centaur : Czlowiek, Kon // Błąd!
Jeśli potrzebujesz „cech” z wielu źródeł, użyj interfejsów (to temat na inny materiał).
Ewolucja kodu – przed i po dziedziczeniu
Zobaczmy pełną transformację kodu z sekcji 1. To najlepiej pokazuje korzyści dziedziczenia.
❌ PRZED – bez dziedziczenia (60+ linii powtórzonego kodu)
// 😰 3 klasy, każda powtarza te same pola i metody public class Wojownik { public string Imie { get; set; } public int PunktyZycia { get; set; } public int Poziom { get; set; } public int Sila { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } public void Atakuj() { Console.WriteLine($"{Imie} atakuje mieczem!"); } } public class Mag { public string Imie { get; set; } // DUPLIKAT public int PunktyZycia { get; set; } // DUPLIKAT public int Poziom { get; set; } // DUPLIKAT public int Mana { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } // DUPLIKAT public void Atakuj() { Console.WriteLine($"{Imie} rzuca zaklęcie!"); } } public class Lucznik { public string Imie { get; set; } // DUPLIKAT public int PunktyZycia { get; set; } // DUPLIKAT public int Poziom { get; set; } // DUPLIKAT public int Strzaly { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } // DUPLIKAT public void Atakuj() { Console.WriteLine($"{Imie} strzela z łuku!"); } }
✅ PO – z dziedziczeniem (DRY – Don’t Repeat Yourself)
// 😊 Wspólne cechy definiujemy RAZ w klasie bazowej public class Postac { public string Imie { get; set; } public int PunktyZycia { get; set; } public int Poziom { get; set; } public void PrzedstawSie() { Console.WriteLine($"Jestem {Imie}, poziom {Poziom}"); } // Metoda virtual – klasy pochodne mogą ją nadpisać public virtual void Atakuj() { Console.WriteLine($"{Imie} atakuje!"); } }
// Każda klasa pochodna definiuje TYLKO to, co jest unikalne public class Wojownik : Postac { public int Sila { get; set; } public override void Atakuj() { Console.WriteLine($"{Imie} atakuje mieczem z siłą {Sila}!"); } } public class Mag : Postac { public int Mana { get; set; } public override void Atakuj() { Console.WriteLine($"{Imie} rzuca kulę ognia! (Mana: {Mana})"); } public void Lecz(Postac cel) { cel.PunktyZycia += 20; Console.WriteLine($"{Imie} leczy {cel.Imie}!"); } } public class Lucznik : Postac { public int Strzaly { get; set; } public override void Atakuj() { if (Strzaly > 0) { Strzaly--; Console.WriteLine($"{Imie} strzela! (Zostało strzał: {Strzaly})"); } else { Console.WriteLine($"{Imie} nie ma strzał!"); } } }
Porównanie korzyści
❌ Bez dziedziczenia
- ~60 linii kodu
- 9 powtórzonych pól (3 × 3)
- 3 powtórzone metody PrzedstawSie()
- Zmiana wymaga edycji 3 klas
- Łatwo o niespójności
✅ Z dziedziczeniem
- ~40 linii kodu (-33%)
- Pola wspólne w jednym miejscu
- PrzedstawSie() napisane RAZ
- Zmiana w jednym miejscu
- Gwarancja spójności
Wystarczy dodać je tylko w klasie Postac – wszystkie klasy pochodne automatycznie je odziedziczą!
// Dodajemy w Postac:
public int Doswiadczenie { get; set; }
// Teraz KAŻDA postać (Wojownik, Mag, Łucznik) ma Doswiadczenie!
Konstruktory i słowo kluczowe base
Gdy klasa pochodna ma konstruktor, musi najpierw wywołać konstruktor klasy bazowej. Robimy to za pomocą słowa base.
Pomyśl o budowie domu:
- Klasa bazowa = fundamenty i ściany
- Klasa pochodna = wykończenie i meble
Nie możesz ustawić mebli, jeśli nie masz najpierw ścian! Dlatego zawsze najpierw budujemy klasę bazową.
public class Postac { public string Imie { get; set; } public int PunktyZycia { get; set; } // Konstruktor klasy bazowej public Postac(string imie, int punktyZycia) { Imie = imie; PunktyZycia = punktyZycia; Console.WriteLine($"Tworzę postać: {imie}"); } } public class Wojownik : Postac { public int Sila { get; set; } // Konstruktor klasy pochodnej wywołuje konstruktor bazowy public Wojownik(string imie, int punktyZycia, int sila) : base(imie, punktyZycia) { Sila = sila; Console.WriteLine($"Wojownik gotowy z siłą {sila}!"); } } public class Mag : Postac { public int Mana { get; set; } public Mag(string imie, int mana) : base(imie, 80) // Magowie zawsze mają 80 HP { Mana = mana; } }
// Tworzenie obiektów Wojownik conan = new Wojownik("Conan", 100, 15); // Wyświetli: // Tworzę postać: Conan ← konstruktor Postac // Wojownik gotowy z siłą 15! ← konstruktor Wojownik Mag gandalf = new Mag("Gandalf", 200); // Wyświetli: // Tworzę postać: Gandalf ← konstruktor Postac (HP=80 automatycznie)
↑ Najpierw wykonuje się konstruktor bazowy, potem pochodny
Jeśli klasa bazowa nie ma konstruktora bezparametrowego, musisz explicite wywołać base(...) z odpowiednimi argumentami. W przeciwnym razie dostaniesz błąd kompilacji.
Modyfikator protected
protected to modyfikator dostępu stworzony specjalnie dla dziedziczenia. Pole/metoda protected jest dostępna w tej klasie i wszystkich klasach pochodnych, ale nie z zewnątrz.
public class Postac { public string Imie { get; set; } protected int bazowePunktyAtaku = 10; // Dostępne dla klas pochodnych private string tajneHaslo = "abc"; // Tylko dla tej klasy } public class Wojownik : Postac { public int ObliczObrazenia() { return bazowePunktyAtaku * 2; // ✅ OK – protected jest dostępne // return tajneHaslo; // ❌ BŁĄD – private niedostępne! } } // Z zewnątrz: Wojownik w = new Wojownik(); // w.bazowePunktyAtaku = 20; // ❌ BŁĄD – protected niedostępne z zewnątrz! Console.WriteLine(w.ObliczObrazenia()); // ✅ OK – publiczna metoda
| Modyfikator | Ta klasa | Klasa pochodna | Inne klasy |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
Używaj protected gdy:
- Klasy pochodne potrzebują dostępu do tego pola/metody
- Ale nie chcesz, żeby było dostępne z zewnątrz
Przykłady: wewnętrzne liczniki, pomocnicze metody, wartości bazowe do obliczeń.
Nadpisywanie metod – virtual i override
Klasa pochodna może zmienić zachowanie metody odziedziczonej z klasy bazowej. To nazywamy nadpisywaniem (override).
Jak to działa?
- W klasie bazowej oznaczamy metodę słowem
virtual– „można mnie nadpisać” - W klasie pochodnej używamy
override– „nadpisuję tę metodę”
public class Zwierze { public string Nazwa { get; set; } // virtual = "klasy pochodne mogą to zmienić" public virtual void WydajDzwiek() { Console.WriteLine("Jakiś dźwięk..."); } // Metoda BEZ virtual – NIE można jej nadpisać public void Jedz() { Console.WriteLine($"{Nazwa} je."); } } public class Pies : Zwierze { // override = "zmieniam zachowanie metody bazowej" public override void WydajDzwiek() { Console.WriteLine("Hau hau!"); } } public class Kot : Zwierze { public override void WydajDzwiek() { Console.WriteLine("Miau!"); } } public class Krowa : Zwierze { public override void WydajDzwiek() { Console.WriteLine("Muuu!"); } }
Pies reksio = new Pies { Nazwa = "Reksio" }; Kot filemon = new Kot { Nazwa = "Filemon" }; Krowa mucka = new Krowa { Nazwa = "Mućka" }; reksio.WydajDzwiek(); // Hau hau! filemon.WydajDzwiek(); // Miau! mucka.WydajDzwiek(); // Muuu! // Metoda Jedz() jest odziedziczona bez zmian: reksio.Jedz(); // Reksio je.
- Metoda bez virtual → nie można nadpisać (błąd kompilacji)
- override bez virtual w klasie bazowej → błąd kompilacji
- Możesz też użyć
overridei dodaćvirtual, żeby kolejne klasy mogły nadpisywać dalej
Rozszerzanie metod bazowych (base.Metoda)
Czasem nie chcemy całkowicie zastąpić metodę bazową, ale tylko ją rozszerzyć. Używamy wtedy base.NazwaMetody() wewnątrz override.
public class Postac { public string Imie { get; set; } public int PunktyZycia { get; set; } public virtual void WyswietlStatystyki() { Console.WriteLine($"=== {Imie} ==="); Console.WriteLine($"HP: {PunktyZycia}"); } } public class Mag : Postac { public int Mana { get; set; } public int Inteligencja { get; set; } public override void WyswietlStatystyki() { // Najpierw wywołaj wersję bazową base.WyswietlStatystyki(); // Potem dodaj własne rzeczy Console.WriteLine($"Mana: {Mana}"); Console.WriteLine($"INT: {Inteligencja}"); } } public class Wojownik : Postac { public int Sila { get; set; } public int Pancerz { get; set; } public override void WyswietlStatystyki() { base.WyswietlStatystyki(); Console.WriteLine($"Siła: {Sila}"); Console.WriteLine($"Pancerz: {Pancerz}"); } }
Mag gandalf = new Mag { Imie = "Gandalf", PunktyZycia = 80, Mana = 200, Inteligencja = 18 }; gandalf.WyswietlStatystyki(); // Wyświetli: // === Gandalf === ← z base.WyswietlStatystyki() // HP: 80 ← z base.WyswietlStatystyki() // Mana: 200 ← dodane przez Mag // INT: 18 ← dodane przez Mag
HP: …
INT: …
Gdy chcesz rozszerzyć (dodać coś do) zachowania bazowego, a nie je całkowicie zastąpić.
- ✅ Wyświetlanie statystyk – bazowe + własne
- ✅ Zapisywanie do pliku – bazowe + własne pola
- ❌ Wydawanie dźwięku – pies nie mówi „Jakiś dźwięk… Hau hau!”
Hierarchia klas – dziedziczenie wielopoziomowe
Klasa pochodna może być klasą bazową dla kolejnej klasy. Tworzy to hierarchię dziedziczenia.
// Poziom 1 – najbardziej ogólna klasa public class Istota { public string Nazwa { get; set; } public bool CzyZyje { get; set; } = true; } // Poziom 2 – Postac dziedziczy po Istota public class Postac : Istota { public int PunktyZycia { get; set; } public int Poziom { get; set; } public virtual void Atakuj() { } } // Poziom 3 – Wojownik dziedziczy po Postac (i pośrednio po Istota) public class Wojownik : Postac { public int Sila { get; set; } public override void Atakuj() { Console.WriteLine($"{Nazwa} atakuje mieczem!"); } } // Poziom 4 – Paladyn dziedziczy po Wojownik public class Paladyn : Wojownik { public int SwietaMoc { get; set; } public void UzyjSwietejMocy() { Console.WriteLine($"{Nazwa} używa świętej mocy!"); } }
Paladyn arthas = new Paladyn(); // Paladyn ma dostęp do WSZYSTKICH odziedziczonych składowych: arthas.Nazwa = "Arthas"; // z Istota arthas.CzyZyje = true; // z Istota arthas.PunktyZycia = 150; // z Postac arthas.Poziom = 20; // z Postac arthas.Sila = 18; // z Wojownik arthas.SwietaMoc = 50; // własne Paladyn arthas.Atakuj(); // z Wojownik arthas.UzyjSwietejMocy(); // własne Paladyn
Zbyt głęboka hierarchia (5+ poziomów) staje się trudna do zrozumienia i utrzymania. Zazwyczaj 2-3 poziomy wystarczają.
Polimorfizm – wiele form, jeden interfejs
Polimorfizm oznacza, że możesz traktować obiekty różnych klas jednakowo, jeśli dziedziczą po tej samej klasie bazowej. To potężna technika!
Jeden pilot może obsługiwać różne telewizory różnych marek. Dla użytkownika interfejs jest ten sam (przyciski), ale każdy telewizor reaguje po swojemu.
Podobnie: zmienna typu Postac może przechowywać Wojownika, Maga lub Łucznika – i każdy reaguje na Atakuj() po swojemu!
// Tablica POSTACI – ale przechowuje różne typy! Postac[] druzyna = new Postac[3]; druzyna[0] = new Wojownik { Imie = "Conan", Sila = 15 }; druzyna[1] = new Mag { Imie = "Gandalf", Mana = 200 }; druzyna[2] = new Lucznik { Imie = "Legolas", Strzaly = 30 }; // Każdy atakuje PO SWOJEMU, mimo że wywołujemy tę samą metodę! foreach (Postac postac in druzyna) { postac.Atakuj(); } // Wyświetli: // Conan atakuje mieczem z siłą 15! ← Wojownik.Atakuj() // Gandalf rzuca kulę ognia! (Mana: 200) ← Mag.Atakuj() // Legolas strzela! (Zostało strzał: 29) ← Lucznik.Atakuj()
Praktyczne zastosowanie – metoda przyjmująca klasę bazową
// Ta metoda działa z KAŻDĄ postacią! public void WykonajTure(Postac postac) { Console.WriteLine($"--- Tura: {postac.Imie} ---"); postac.PrzedstawSie(); postac.Atakuj(); // Wywoła odpowiednią wersję! } // Użycie: Wojownik w = new Wojownik { Imie = "Conan" }; Mag m = new Mag { Imie = "Gandalf" }; WykonajTure(w); // Działa z Wojownikiem WykonajTure(m); // Działa z Magiem // Jedna metoda obsługuje wszystkie typy postaci!
Nie musisz pisać osobnej metody WykonajTureWojownika(), WykonajTureMaga(), itd. Jedna metoda działa z wszystkimi klasami pochodnymi!
Kiedy używać dziedziczenia?
✅ Używaj dziedziczenia gdy:
| Sytuacja | Przykład |
|---|---|
| Relacja „jest rodzajem” (is-a) | Pies jest Zwierzęciem |
| Wspólne cechy wielu klas | Wszystkie pojazdy mają prędkość |
| Różne zachowania tej samej akcji | Każde zwierzę inaczej je |
| Hierarchia naturalna | Ssak → Pies → Labrador |
❌ NIE używaj dziedziczenia gdy:
| Sytuacja | Co zamiast? |
|---|---|
| Relacja „ma” (has-a) | Samochód ma Silnik → kompozycja |
| Tylko jedna wspólna metoda | Interfejs |
| Chcesz „dziedziczyć” po wielu | Interfejsy |
| Klasy nie są logicznie powiązane | Nie twórz sztucznej hierarchii |
✅ Dobre przykłady
- Postac → Wojownik, Mag
- Pojazd → Samochód, Motocykl
- Kształt → Koło, Prostokąt
- Pracownik → Programista, Manager
- Kontrolka → Button, TextBox
❌ Złe przykłady
- Samochód → Silnik (ma, nie jest!)
- Człowiek → Adres (ma adres)
- Ptak → Samolot (oba latają, ale…)
- String → StringBuilder (różne cele)
Częste błędy
❌ Błąd 1: Brak virtual przy metodzie bazowej
❌ Źle
public void Atakuj() { }
// W klasie pochodnej:
public override void Atakuj()
// BŁĄD! Nie można nadpisać
// metody bez virtual
✅ Dobrze
public virtual void Atakuj() { }
// W klasie pochodnej:
public override void Atakuj()
// OK!
❌ Błąd 2: Brak wywołania base w konstruktorze
❌ Źle
// Klasa bazowa bez konstruktora
// bezparametrowego:
public Postac(string imie) { }
// Klasa pochodna:
public Wojownik(string imie)
{
// BŁĄD! Brak : base(imie)
}
✅ Dobrze
public Postac(string imie) { }
public Wojownik(string imie)
: base(imie) // Wywołanie base
{
// OK!
}
❌ Błąd 3: Próba dostępu do private z klasy pochodnej
❌ Źle
// W klasie bazowej: private int sekret = 42; // W klasie pochodnej: int x = sekret; // BŁĄD!
✅ Dobrze
// W klasie bazowej: protected int sekret = 42; // W klasie pochodnej: int x = sekret; // OK!
❌ Błąd 4: Dziedziczenie wielokrotne
❌ Źle
public class Syrena : Ryba, Czlowiek
{
// BŁĄD! C# nie pozwala
}
✅ Dobrze
public class Syrena : Istota
{
// Jedna klasa bazowa
}
// Użyj interfejsów dla
// wielu "umiejętności"
Podsumowanie
- Dziedziczenie = klasa przejmuje składowe innej klasy
- Klasa bazowa = rodzic, nadklasa
- Klasa pochodna = dziecko, podklasa
- : = operator dziedziczenia (
class Pies : Zwierze) - base = odwołanie do klasy bazowej
- virtual = metoda może być nadpisana
- override = nadpisuję metodę bazową
- protected = dostępne dla klasy i jej dzieci
- Polimorfizm = jedna zmienna, wiele zachowań
Korzyści z dziedziczenia
| Korzyść | Opis |
|---|---|
| DRY | Don’t Repeat Yourself – kod wspólny w jednym miejscu |
| Łatwiejsze zmiany | Zmiana w klasie bazowej propaguje się do wszystkich |
| Polimorfizm | Jeden kod obsługuje wiele typów |
| Rozszerzalność | Łatwo dodać nową klasę pochodną |
| Czytelność | Hierarchia odzwierciedla rzeczywistość |
Schemat składni
public class KlasaBazowa { public PoleDostepneWszedzie; protected PoleDlaDzieci; private PoleTylkoTutaj; public KlasaBazowa(parametry) { } public virtual void MetodaDoNadpisania() { } } public class KlasaPochodna : KlasaBazowa { public WlasnePole; public KlasaPochodna(parametry) : base(parametryDoBazowej) { } public override void MetodaDoNadpisania() { base.MetodaDoNadpisania(); // opcjonalnie // własna implementacja } }
Zadania praktyczne
📝 Zadanie 1: Hierarchia pojazdów
Utwórz hierarchię klas pojazdów:
- Pojazd (bazowa): Marka, Model, RokProdukcji, metoda virtual
Jedz() - Samochod: LiczbaKol (4), override
Jedz()→ „Jadę samochodem” - Motocykl: LiczbaKol (2), override
Jedz()→ „Jadę motocyklem” - Rower: CzyMaTrzySilnik (bool), override
Jedz()→ „Jadę rowerem”
Utwórz tablicę Pojazd[] z różnymi pojazdami i wywołaj Jedz() dla każdego.
💡 Podpowiedź: Użyj foreach i zobacz polimorfizm w akcji!
📝 Zadanie 2: System pracowników
Utwórz hierarchię pracowników:
- Pracownik: Imie, Nazwisko, PensjaBase, metoda virtual
ObliczPensje() - Programista: JezykProgramowania, override
ObliczPensje()= PensjaBase * 1.5 - Manager: LiczbaPodwladnych, override
ObliczPensje()= PensjaBase + (LiczbaPodwladnych * 200) - Stażysta: MiesiacStazu, override
ObliczPensje()= PensjaBase * 0.5
💡 Podpowiedź: Użyj konstruktorów z base()
📝 Zadanie 3: Kształty geometryczne
Utwórz hierarchię:
- Ksztalt: Nazwa, Kolor, virtual
ObliczPole(), virtualObliczObwod() - Prostokat: Szerokosc, Wysokosc
- Kolo: Promien (użyj Math.PI)
- Trojkat: BokA, BokB, BokC, Podstawa, Wysokosc
Dodaj metodę WyswietlInfo() w klasie bazowej używając base.WyswietlInfo() w klasach pochodnych.
💡 Podpowiedź: Pole koła = π * r², obwód = 2 * π * r
⭐ Zadanie 4: Refaktoryzacja kodu
Masz trzy klasy z powtarzającym się kodem. Zrefaktoryzuj je używając dziedziczenia:
public class Pies {
public string Imie;
public int Wiek;
public void Jedz() { Console.WriteLine("Jem"); }
public void Szczekaj() { Console.WriteLine("Hau!"); }
}
public class Kot {
public string Imie;
public int Wiek;
public void Jedz() { Console.WriteLine("Jem"); }
public void Miaucz() { Console.WriteLine("Miau!"); }
}
public class Papuga {
public string Imie;
public int Wiek;
public void Jedz() { Console.WriteLine("Jem"); }
public void Mow() { Console.WriteLine("Witaj!"); }
}
💡 Podpowiedź: Utwórz klasę bazową Zwierze z wspólnymi elementami
⭐⭐ Zadanie 5: System kont bankowych
Utwórz rozbudowaną hierarchię:
- Konto: NumerKonta (readonly), Saldo (protected), Wlasciciel
- Konstruktor z base
- virtual
Wplac(kwota) - virtual
Wyplac(kwota)– z walidacją - virtual
ObliczOdsetki()– domyślnie 0%
- KontoOszczednosciowe: OprocentowanieRoczne, override
ObliczOdsetki() - KontoFirmowe: LimitDebetu, override
Wyplac()– można wejść w debet - KontoStudenckie: DataWaznosci, override
Wplac()– bonus 5% do wpłat
💡 Podpowiedź: W Wyplac() dla KontoFirmowe sprawdź Saldo + LimitDebetu >= kwota