class Potomek : Rodic, base(...), virtual, override, base.Metoda(), abstract, polymorfismusPrisera a z ní odvodíš skřeta, vlkodlaka a obra.abstract, aby nešlo vyrobit „obecnou příšeru".foreach obslouží celý bestiář.Tenhle blok je jen pro vás — dětem ho nepromítejte.
| Fáze | Co se děje | Čas |
|---|---|---|
| Rozjezd | Zvířata na tabuli: co mají společné, čím se liší | 10 min |
| Kopírka | Blok 2 — tři třídy, jeden kód | 10 min |
| Dědičnost | Bloky 3–4 + ukázky 1–2 | 20 min |
| virtual/override | Bloky 5–6 + ukázka 3 | 20 min |
| abstract a polymorfismus | Bloky 7–8 + ukázky 4–5 | 20 min |
| Cvičení | Děti pracují samy, vy obcházíte | 55 min |
| Závěr | Kdo chce, ukáže svůj bestiář | 5 min |
Dnešek má jedinou myšlenku: „Skřet JE příšera." Ne „skřet má příšeru", ne „skřet používá příšeru" — je to příšera, jen zvláštní druh.
Rozjezd dělejte bez počítače. Napište na tabuli tři zvířata (pes, kočka, papoušek)
a nechte třídu vypsat, co mají společné (jméno, věk, jí, spí) a čím se liší
(zvuk, počet nohou). Společné věci zakroužkujte — to je základní třída.
Rozdíly podtrhněte — to jsou override.
Až budou děti umět odpovědět na otázku „je tohle 'je', nebo 'má'?", je hotovo. Syntaxe je zbytek hodiny jen technický detail.
interface). Je to blízké téma, ale dnes by se pletlo
s abstraktní třídou. Zmínka pro nejrychlejší je v učitelském bloku na konci.Jak pracovat se smíšenou úrovní. Cvičení 1 a 2 jsou mechanická — zvládne je každý.
Cvičení 3 (virtual/override) je jádro hodiny, tam se zdržte nejdéle.
Cvičení 4 (abstract) je náročnější a slabším stačí, když ho jen opíšou a spustí.
Cvičení 5 je odměna — funguje efektně a děti si u něj vymýšlejí vlastní nestvůry.
Nejčastější zádrhel hodiny: CS0534 — potomek neimplementoval abstraktní metodu.
Napište to na tabuli i s vysvětlením: „rodič slíbil, že to každý potomek umí.
Ty to neumíš, takže tě překladač nepustí."
Přečti si obě třídy a najdi rozdíl. Není žádný. Kromě názvu je to dvakrát ten samý kód.
Co se stane, až budeš chtít přidat vlastnost Odmena?
Musíš ji dopsat na obě místa. A až přidáš třetího a čtvrtého netvora,
tak na čtyři místa. Jednou zapomeneš a hra se začne chovat divně.
Je tu ještě horší problém: nejde je dát do jednoho seznamu.
List<Skret> umí jen skřety. Takže bys potřeboval seznam skřetů, seznam
vlkodlaků, seznam obrů… a pak tři skoro stejné cykly.
C# na to má jedno klíčové slovo. Vejde se na jeden řádek a vyřeší oboje.
Dvojtečka znamená „je to". class Skret : Prisera čti jako
„skřet je příšera".
Skřet nemá nikde napsané Jmeno, Zivoty, Sila ani Popis() —
a přesto je má. Zdědil je. Všechno, co je v Prisera veřejné,
má automaticky každý potomek.
Slovník, který se ti bude hodit:
Prisera,Skret, Vlkodlak.Operátor is se ptá „je tenhle objekt tohoto druhu?" a vrátí true/false.
Skřet je zároveň Skret i Prisera — a právě díky tomu půjde dát
skřety i vlkodlaky do jednoho List<Prisera>.
: base(...) zavolá konstruktor rodiče. Čti to jako „nejdřív ať se
postará příšera o to svoje, a pak dodělám já to svoje".
Pořadí je vždycky takové: nejdřív rodič, pak potomek. Proto může
Vlkodlak v konstruktoru rovnou nastavit VUplnku — v tu chvíli už má
jméno, životy i sílu vyplněné.
Bez base(...) by C# hledal u rodiče konstruktor bez parametrů, nenašel by ho
a ohlásil CS7036. To je dnes druhá nejčastější chyba.
Potomek smí mít věci navíc. VUplnku má jen vlkodlak — skřet o něm neví
a nepotřebuje ho. Dědičnost je jednosměrná: potomek vidí na rodiče,
rodič na potomka ne.
virtual je povolení. Rodič říká: „takhle to dělám já, ale klidně
to udělej po svém." Bez virtual potomek metodu přepsat nesmí.
override je využití toho povolení. Potomek napíše metodu se stejným
názvem a stejnými parametry a C# bude u jeho objektů volat tuhle verzi.
Obě slova musí sedět k sobě:
virtual chybí u rodiče → CS0506,override chybí u potomka → varování a metoda rodiče se schová,
což je skoro vždycky chyba,CS0115 (není co přepisovat).Tohle je chvíle, kdy začne být dědičnost užitečná: jeden název metody, tolik různých chování, kolik máš druhů nestvůr.
base.Karta() zavolá verzi rodiče. Potomek tak nemusí opisovat,
co už rodič umí — jen k tomu přidá své.
Přečti si vlkodlakovu metodu nahlas: „vezmi kartu, jakou by udělala obyčejná příšera, a připiš k ní řádek o úplňku."
Kdyby vlkodlak napsal Karta() místo base.Karta(), volal by sám sebe
pořád dokola a program by spadl na StackOverflowException.
Tečka za base je tu opravdu důležitá.
Skřet žádný override nemá, takže mu zůstala rodičovská verze.
Dědičnost je vždycky nabídka, ne povinnost — dokud nepřijde abstract.
abstract class se nedá vyrobit. Řádek new Prisera(...) by skončil
chybou CS0144. A to je přesně to, co chceme: „obecná příšera" ve hře neexistuje,
existuje jen skřet, vlkodlak nebo obr.
abstract metoda nemá tělo — má jen středník. Je to slib:
„každý potomek tohle umí, ale každý jinak."
Kdo slib nesplní, nepřeloží se:
CS0534: ... does not implement inherited abstract member.
Všimni si, že Karta() klidně volá Zvuk(), i když rodič vůbec neví,
co vrátí. Za běhu se použije verze toho objektu, který metodu zavolal.
virtual = rodič má rozumné výchozí chování, potomek ho může změnit.
abstract = rodič žádné rozumné chování nemá a nechce hádat.
Tohle je odměna za celou hodinu. V seznamu jsou tři různé druhy nestvůr,
ale cyklus je jeden jediný a nemá v sobě žádné if.
Jak to, že každá vypsala jiný zvuk? Protože C# se při volání Zvuk()
podívá, co je objekt doopravdy zač, a zavolá jeho verzi metody.
Tomu se říká polymorfismus — mnohotvarost.
List<Prisera> unese cokoliv, co je příšera. Až příště přidáš
class Drak : Prisera, stačí ho přidat do seznamu — cyklus měnit nemusíš.
To je ten pravý důvod, proč dědičnost existuje.
Operátorem is se dá kdykoliv doptat na konkrétní druh:
p is Vlkodlak vrátí true jen u vlkodlaků.
Proč virtual a override explicitně. Java má virtuální metody implicitně,
C# ne. Je to schválně: v C# musí autor rodiče dovolit přepsání.
Dětem stačí „rodič musí dát svolení", ale je dobré vědět, že za tím stojí
snaha zabránit nechtěnému přepsání metody, na kterou se spoléhá zbytek knihovny.
new místo override. Kdo napíše metodu se stejným názvem bez override,
dostane varování CS0108 a metoda se jen schová (method hiding).
Polymorfismus pak nefunguje — přes proměnnou typu Prisera se zavolá verze rodiče.
Je to klasická past a stojí za to ji dětem jednou ukázat naživo.
protected. Mezi public a private je třetí úroveň: vidí na ni potomci,
ale ne okolní kód. Používáme ji u konstruktoru abstraktní třídy. Do výkladu
ji netlačte, ale kdo se zeptá, ať ji zná — v reálném kódu je běžná.
sealed. Opak dědičnosti: sealed class Drak říká „ze mě už nikdo dědit
nebude". Používá se hlavně kvůli výkonu a bezpečnosti návrhu.
interface. Alternativa k abstraktní třídě: čistý seznam schopností bez
jakéhokoliv kódu, a třída jich smí mít víc. Až budou děti psát větší projekty,
narazí na IComparable nebo IEnumerable. Dnes by to ale téma rozmělnilo.
Kompozice místo dědičnosti. Profesionální pravidlo zní „preferuj kompozici". Skřet má zbraň, ale je příšera. Dobrá kontrolní otázka na dítě: „dává větší smysl 'je', nebo 'má'?" Kdo řekne „skřet má příšeru", ten to ještě nemá.
Pozor na hloubku. Víc než dvě úrovně dědičnosti (Prisera → Skret)
v téhle hře nepotřebujeme. Kdo si postaví pětipatrovou hierarchii, ztratí se v ní —
a je to dobrá příležitost mu ukázat, že jednoduchost je vlastnost, ne lenost.
Pořadí v souboru. Jako minule: všechny třídy patří pod top-level příkazy. Na pořadí tříd mezi sebou nezáleží — potomek může být napsaný nad rodičem.
Netopyr.Prisera novou vlastnost Odmena a sleduj, že ji mají hned všichni.Popis() a vypiš znovu — změní se u všech tří.Jmeno a zjisti, že už ho má.Co si z toho odnést: tři druhy nestvůr, ale Popis() je napsaný jednou.
Dvojtečka za názvem třídy udělala všechnu práci.
VUplnku na false a sleduj rozdíl.Brneni a odečti ji od poškození.Zbran s textem a vypiš ji.VUplnku — a přečti si chybu.Co si z toho odnést: : base(...) vyplní společné údaje a potomek se pak
stará jen o to, čím je zvláštní.
override a přečti si varování.base.Utok() za Utok() a zjisti, co se stane (pak to vrať!).Co si z toho odnést: Utok() má každý jiný, ale volá se pořád stejně.
A base.Karta() znamená „to, co by udělal rodič — a ještě něco navíc".
new Prisera("X", 10, 1) a přečti si chybu CS0144.Zvuk() a přečti si chybu CS0534.Odmena().Karta() normální (neabstraktní) metodu — už to tak je, projdi si proč.Co si z toho odnést: abstract je slib, který překladač vymáhá.
Díky němu nemůže v bestiáři vzniknout nestvůra, která neumí to, co má.
new Random(5) a uvidíš jiný průběh výpravy.OrderBy(p => p.Utok())).Co si z toho odnést: hlavní smyčka neví, jestli bojuje se skřetem, nebo s obrem.
Ptá se pořád stejně — nestvura.Utok(), nestvura.Odmena() — a každý druh
odpoví po svém. Přidat čtvrtou nestvůru znamená přidat třídu, ne měnit hru.
error CS0534: 'Skret' does not implement inherited
abstract member 'Prisera.Zvuk()'
Chyba číslo jedna dnešní hodiny. Rodič je abstract a slíbil, že každý
potomek umí Zvuk(). Skřet ho ale nenapsal.
Oprava je vždycky stejná — doplnit do potomka metodu se stejným názvem,
stejnými parametry i návratovým typem a se slovem override:
public override string Zvuk()
{
return "Grrrmbl!";
}
Druhá nejčastější chyba je CS7036 u konstruktoru: potomek zapomněl
: base(...) a rodič nemá konstruktor bez parametrů. Doplň dvojtečku
a předej rodiči, co po tobě chce.
A pozor na StackOverflowException: uvnitř override metody musí být
base.Neco(), ne Neco() — jinak metoda volá sama sebe donekonečna.
| Hláška / projev | Co to znamená | Jak to opravit |
|---|---|---|
CS0534 |
potomek neimplementoval abstraktní metodu | dopiš ji s override |
CS0144 Cannot create an instance of the abstract type |
new na abstraktní třídě |
vyrob konkrétního potomka |
CS7036 v konstruktoru potomka |
chybí : base(...) |
public Skret(...) : base(...) |
CS0115 no suitable method found to override |
název nebo parametry nesedí s rodičem | zkontroluj podpis metody znak po znaku |
CS0506 cannot override inherited member ... not virtual |
rodičovská metoda není virtual |
dopiš virtual k rodiči |
CS0108 (varování) |
potomek metodu jen schoval | dopiš override |
CS1729 u rodiče |
rodič nemá konstruktor s takovými parametry | uprav base(...) podle rodiče |
StackOverflowException |
override volá sám sebe |
použij base.Metoda() |
CS0122 u vlastnosti rodiče |
je private |
uděl z ní protected nebo public |
| potomek dělá totéž co rodič | chybí override nebo se volá přes špatný typ |
zkontroluj dvojici virtual + override |
CS0266 u List<Prisera> |
do seznamu dáváš něco, co není potomek | třída musí mít : Prisera |
abstract) je nejnáročnější — nespěchej.abstract metodu rodiči,
musíš ve stejném kroku dopsat override všem potomkům —
jinak se program nepřeloží.Zadání:
Postav základní třídu
Priseraa odvoď z ní tři druhy nestvůr.
- Napiš třídu
Priseras jménem, životy, silou a metodouPopis()💡 Poradit
Zatím obyčejná třída, jako minulou hodinu — a jako vždycky až pod příkazy:class Prisera{public string Jmeno { get; set; }public int Zivoty { get; set; }public int Sila { get; set; }public string Popis() { return $"{Jmeno}: {Zivoty} HP"; }}- Odvoď z ní
Skreta vyrob prvního skřeta💡 Poradit
Dvojtečka znamená „je to":class Skret : Prisera{}
Skřet hned umíJmeno,Zivoty,SilaiPopis(), i když je nemá napsané.- Přidej
VlkodlakaKamennyObra vypiš všechny tři💡 Poradit
Úplně stejně jako skřet — jen jiný název třídy.
Ověř si dědičnost operátoremis:Console.WriteLine(vlk is Prisera);- Dej rodiči konstruktor a všem potomkům
: base(...)💡 Poradit
Jakmile dá rodič konstruktor s parametry, musí ho zavolat každý potomek — jinakCS7036. Uprav proto všechny tři najednou:public Skret(string jmeno, int zivoty, int sila) : base(jmeno, zivoty, sila){}- Přidej každému potomkovi jeden vlastní údaj navíc
💡 Poradit
Napříkladpublic string Zbran { get; set; }u skřeta,public bool VUplnku { get; set; }u vlkodlaka,public int Brneni { get; set; }u obra.
Nastav je v konstruktoru potomka za volánímbase(...).
| Úroveň | Co má zvládnout |
|---|---|
| Minimum | Body 1–3, tři potomci dědí Popis() |
| Rozšíření | Bod 4, konstruktor rodiče a : base(...) |
| Výzva | Bod 5 a k tomu čtvrtý druh nestvůry s vlastním údajem |
Očekávaný výsledek: tři druhy nestvůr, ani jeden nemá vlastní Popis(),
a přesto ho všichni umí.
Kde se zaseknou:
class Skret : Prisera a : base(...) vypadají
podobně, ale znamenají něco jiného. Napište obojí na tabuli vedle sebe:
první je „jsem to a to", druhé je „rodiči, vyplň si své".Skret zopakuje Jmeno.
Program se přeloží (a rozbije polymorfismus). Ptejte se: „a proč to tam máš,
když už to je v rodiči?"Rychlíkům zadejte čtvrtý druh (např. Prizrak s vlastností Neviditelny).
Toto je jedna z možných variant. Tvoje řešení se může lišit a to je v pořádku.
Zadání:
Nestvůry z minula zatím útočí a mluví úplně stejně. To spravíme pomocí
virtualaoverride.
- Dej rodiči
virtual int Utok(), který vrátíSila💡 Poradit
Slovovirtualznamená „potomek to smí přepsat":public virtual int Utok(){return Sila;}
Rodič má normální tělo, takže zatím funguje všem.- Vlkodlak v úplňku ať útočí dvojnásobně
💡 Poradit
Do třídyVlkodlakpřidej:public override int Utok(){return VUplnku ? Sila * 2 : Sila;}
Stejné jméno, stejné parametry, stejný návratový typ — jinakCS0115.- Přepiš
Utok()i skřetovi a obrovi💡 Poradit
Skřet má bonus za zbraň, obr počítá i brnění:return Sila + 2;return Sila + Brneni / 2;
Každý potomek si sahá na své vlastní údaje — rodič o nich nic neví.- Přidej
virtual string Zvuk()a přepiš ho ve všech třech💡 Poradit
Rodič vrátí něco neutrálního ("...něco zavrčí..."), potomci svoje:public override string Zvuk() { return "Gruaah!"; }
Protože je rodičovská verzevirtual(neabstract), nemusíš přepsat všechny naráz.- Přidej
Karta()a u obra použijbase.Karta()💡 Poradit
base.Karta()zavolá rodičovskou verzi a ty k ní jen něco přilepíš:public override string Karta(){return base.Karta() + $"\n brnění: {Brneni}";}
Nepišthis.Karta()— to by se zacyklilo donekonečna.
| Úroveň | Co má zvládnout |
|---|---|
| Minimum | Body 1–2, vlkodlak útočí jinak než rodič |
| Rozšíření | Body 3–4, všichni tři mají svůj Utok() i Zvuk() |
| Výzva | Bod 5, base.Karta() a k tomu vlastní řádek u skřeta |
Očekávaný výsledek: stejné volání Utok() dává u každého druhu jiné číslo
a base.Karta() ukáže, že potomek může rodiče doplnit, ne jen nahradit.
Kde se zaseknou:
virtual u rodiče. Překladač hlásí CS0506 („metoda není
virtual"). Napište na tabuli dvojici: virtual = rodič dovolí, override
= potomek využije. Bez prvního nefunguje druhé.public override string Utok() místo int → CS0508.
Ať si podpisy porovnají znak po znaku.this.Karta() místo base.Karta() v kroku 5. Program se přeloží,
ale zacyklí se (v prohlížeči to vypadá jako „stránka zamrzla").
Ukažte to schválně — je to nezapomenutelný zážitek — a hned vysvětlete proč.Rychlíkům zadejte Zvuk() u skřeta, který se mění podle životů
(pod 15 HP kňourá, jinak řve).
Toto je jedna z možných variant. Tvoje řešení se může lišit a to je v pořádku.
Zadání:
virtualje nabídka,abstractje příkaz. Dnes rodič přestane být sám o sobě použitelný.
- Udělej z
Priseraabstraktní třídu a zkus ji vyrobit💡 Poradit
Stačí jedno slovo předclass:abstract class Prisera
Řádeknew Prisera(...)teď skončí chybouCS0144. To je správně — „příšera obecně" neexistuje, existuje jen skřet, vlkodlak nebo obr.- Přidej
public abstract int Odmena();a hned ji doplň všem třem💡 Poradit
Pozor: abstraktní metoda nemá tělo, jen středník — a od té chvíle ji musí mít každý potomek, jinak se nepřeloží nic. Přidej proto rodiči i všem třem potomkům naráz:public abstract int Odmena();
a u potomkapublic override int Odmena() { return 12; }- Udělej konstruktor rodiče
protected💡 Poradit
protected Prisera(string jmeno, int zivoty, int sila)
Znamená to „smí volat jen potomek přesbase(...)". Zvenku už se k němu nikdo nedostane.- Předělej
Zvuk()zvirtualnaabstract💡 Poradit
V rodiči smaž tělo a nech jen:public abstract string Zvuk();
Jestli má některý potomekZvuk()ještě nepřepsaný, musíš mu ho ve stejném kroku dopsat. JinakCS0534.- Přidej čtvrtou nestvůru
Prizrak💡 Poradit
Překladač ti přesně řekne, co jí chybí —Odmena()iZvuk(). Přidej obojí:class Prizrak : Prisera
a v konstruktoru nezapomeň na: base(...).
| Úroveň | Co má zvládnout |
|---|---|
| Minimum | Body 1–2, abstract třída a povinná Odmena() |
| Rozšíření | Body 3–4, protected konstruktor a abstraktní Zvuk() |
| Výzva | Bod 5 a k tomu pátý druh nestvůry na jeden pokus bez chyby |
Očekávaný výsledek: rodič, který se nedá vyrobit, a čtyři potomci, kteří
všichni splnili oba sliby (Zvuk() a Odmena()).
Kde se zaseknou — a tohle je dnes to hlavní:
public abstract int Odmena();, přestanou se překládat všichni potomci
naráz (CS0534). Studenti si myslí, že něco rozbili. Řekněte předem:
„teď uděláme jednu změnu na čtyřech místech a mezitím to bude červené."
Tohle je nejčastější důvod paniky v celé hodině.new Prisera(...) v kroku 1. CS0144 je záměr, ne chyba studenta.
Nechte je to schválně zkusit a chybu si přečíst nahlas.abstract metoda s tělem → CS0500. Abstraktní = jen podpis a středník.virtual vs. abstract shrňte jednou větou:
„virtual = rodič něco umí a ty to smíš změnit; abstract = rodič to neumí
a ty to musíš dodělat."Rychlíkům zadejte abstraktní string Slabina() — a ať ji doplní všem čtyřem.
Toto je jedna z možných variant. Tvoje řešení se může lišit a to je v pořádku.
Zadání:
Teď přijde odměna za všechnu tu práci: jeden cyklus obslouží všechny druhy nestvůr.
- Dej všechny nestvůry do
List<Prisera>💡 Poradit
Do seznamu typu rodič se vejde kterýkoli potomek:List<Prisera> bestiar = new List<Prisera>();bestiar.Add(new Skret("Skřet zvěd", 40, 6));bestiar.Add(new Vlkodlak("Vlkodlak", 60, 9));- Projdi je jedním
foreacha vypiš kartu každé💡 Poradit
foreach (Prisera p in bestiar){Console.WriteLine(p.Popis());}
Ani jednoif, ani jeden dotaz na typ. C# si sám najde tu správnou verzi metody.- Spočítej, kolik je v bestiáři vlkodlaků
💡 Poradit
Operátorisse ptá „jsi tenhle druh?":int vlku = bestiar.Count(p => p is Vlkodlak);
nebo klasicky přesforeachaif (p is Vlkodlak) pocet++;- Zjisti celkovou odměnu a nejnebezpečnější nestvůru
💡 Poradit
int zlato = bestiar.Sum(p => p.Odmena());Prisera nejhorsi = bestiar.OrderByDescending(p => p.Utok()).First();
Metody z 13. hodiny fungují i na objektech.- Přidej
Draka nesahej na cyklus💡 Poradit
Napiš tříduDrak : Prisera, doplň povinné metody, přidej ji do seznamu — a výpis nech úplně beze změny. To je celý smysl dnešní hodiny.
| Úroveň | Co má zvládnout |
|---|---|
| Minimum | Body 1–2, jeden foreach přes celý bestiář |
| Rozšíření | Body 3–4, počty přes is a součty přes LINQ |
| Výzva | Bod 5 — nový druh bez jediné změny ve výpisu |
Očekávaný výsledek: pět druhů nestvůr, jeden foreach, žádné if podle typu
a celková odměna 359 zlatých.
Kde se zaseknou:
List<Skret> místo List<Prisera>. Do seznamu potomka se rodič ani
sourozenec nevejde (CS1503). Pravidlo: seznam se typuje na rodiče.p.Zbran na proměnné typu Prisera
je CS1061 — rodič o ní neví. Tady se hodí ukázat if (p is Skret s)
(pattern matching), ale jen rychlíkům; pro většinu stačí konstatování,
že přes rodiče vidíme jen společné věci.Count bez using System.Linq. V našem prostředí je LINQ zapnutý,
ale ve Visual Studio Code doma si ho musí přidat — zmiňte to.ifů by museli přidat bez dědičnosti.Rychlíkům zadejte bestiar.Where(p => p.Odmena() >= 25).ToList() a výpis
jen „hodnotných" cílů.
Toto je jedna z možných variant. Tvoje řešení se může lišit a to je v pořádku.
Zadání:
Finále hodiny: hrdina se vydá bestiářem a bude bojovat se vším, co potká. A ty nenapíšeš ani jedno
ifpodle druhu nestvůry.
- Přidej třídu
Hrdinaa postav proti ní bestiář💡 Poradit
Hrdinanení potomekPrisera— je to samostatná třída se svýmJmeno,Zivoty,SilaaZlato.
Hodí se i vlastnostpublic bool Zije { get { return Zivoty > 0; } }.- Napiš jeden souboj s jednou nestvůrou
💡 Poradit
Cykluswhile, dokud oba žijí, s pojistkou na počet kol:while (hrdina.Zije && prisera.Zivoty > 0 && kolo <= 20)
Uvnitř: hrdina uberehrdina.Utok(), pak nestvůra ubereprisera.Utok().- Přesuň souboj do lokální funkce a projdi celý bestiář
💡 Poradit
void Souboj(Hrdina h, Prisera p, Random r)napiš mezi příkazy (ne do třídy) a pak jen:foreach (Prisera p in bestiar) { Souboj(hrdina, p, kostka); }
Parametr má typ rodiče — proto funguje na všechny druhy.- Přičítej zlato a ukonči výpravu, když hrdina padne
💡 Poradit
Po vítězstvíhrdina.Zlato += p.Odmena();
a vforeach:if (!hrdina.Zije) break;
Nezapomeň, žebreakpatří do cyklu s bestiářem, ne do soubojovéhowhile.- Přidej závěrečnou bilanci a jednu zvláštní schopnost
💡 Poradit
Na konci vypiš zlato, přeživší nestvůry a hodnocení.
Schopnost udělej přesoverride— třeba zuřivý vlkodlak:public override int Utok() { return Zivoty < 20 ? Sila * 2 : Sila + 3; }
Žádnéifv hlavním cyklu nepřibude.
| Úroveň | Co má zvládnout |
|---|---|
| Minimum | Body 1–2, jeden funkční souboj |
| Rozšíření | Body 3–4, výprava celým bestiářem a počítání zlata |
| Výzva | Bod 5 a k tomu vlastní druh nestvůry se zvláštní schopností |
Očekávaný výsledek: hrdina projde bestiář, v hlavním cyklu není ani jedno
if podle druhu nestvůry, a přesto se každá chová jinak. Se new Random(5)
vychází výsledek všem stejně — což je fajn pro společnou kontrolu.
Kde se zaseknou:
Hrdina : Prisera. Někdo hrdinu podědí od příšery, protože „mají to samé".
Zeptejte se: „je hrdina příšera?" Dědičnost se řídí větou „je to",
ne tím, že dvě třídy mají podobné vlastnosti.while. Bez kolo <= 20 umí souboj běžet věčně
(například když útok vyjde 0). V prohlížeči to vypadá jako zaseknutá stránka.
Trvejte na pojistce ve všech soubojových cyklech.Souboj patří mezi příkazy nahoře,
ne dovnitř class Hrdina. Připomeňte pravidlo: příkazy nahoře, třídy dole.break ve špatném cyklu. Bod 4 přeruší výpravu, ne souboj.
Ať si nakreslí dva obdélníky do sebe a ukážou, kde break skáče.Souboj se opravdu mění ten samý
hrdina, co je venku. Pro někoho je to překvapení — navažte na 15. hodinu.Rychlíkům: Prizrak, který se po prvním zásahu jednou „uhne"
(bool vlastnost + override Utok()), nebo lektvar mezi souboji.
Toto je jedna z možných variant. Tvoje řešení se může lišit a to je v pořádku.
Postav ze svého CsQuestu bestiář na dědičnosti.
- abstraktní (nebo aspoň společná) třída
Priseras údaji, které mají všichni,- alespoň tři různí potomci, každý s
: base(...)konstruktorem,- alespoň dvě metody přepsané přes
override(např.Utok()aZvuk()),- jedno místo, kde použiješ
base.Metoda()a k výsledku rodiče něco přidáš,- celý bestiář v
List<Prisera>a jedinýforeach, který ho vypíše.Až to budeš mít, zkus přidat čtvrtý druh nestvůry. Pokud jsi nemusel sáhnout do výpisu, je to hotové správně.
Kontrolní otázka při odevzdání zní vždycky stejně: „kolik ifů máš
v hlavním cyklu?" Správná odpověď je nula. Když jich tam je pět,
student napsal dědičnost, ale používá ji jako switch podle typu.
Co uznat jako splněné: i verze bez abstract (jen virtual s výchozím
tělem) je v pořádku. Pointa úkolu je jeden cyklus nad různými typy,
ne formální dokonalost.
Čekejte jednu typickou chybu: potomek s vlastní kopií Jmeno a Zivoty.
Program běží, ale rodičovské metody vidí prázdné hodnoty. Je to skvělá
příležitost si znovu nakreslit, že potomek má jen jednu sadu vlastností —
tu zděděnou.
Toto je jedna z možných variant, jak mohl úkol dopadnout. Tvoje řešení se může lišit a to je v pořádku.
Oficiální dokumentace je místo, kam se programátoři chodí podívat, když si chtějí něco ověřit nebo zjistit víc detailů. Je většinou v angličtině, ale to nevadí — zkus se orientovat podle názvu příkazu a podle ukázek kódu.
| Co jsme použili | Oficiální dokumentace | Co tam najdeš |
|---|---|---|
dědičnost : Rodic |
Dědičnost | jak potomek přebírá vlastnosti a metody |
virtual |
Klíčové slovo virtual |
kdy rodič dovolí přepsání |
override |
Klíčové slovo override |
pravidla pro přepsání metody |
abstract |
Klíčové slovo abstract |
třída i metoda bez těla |
base |
Klíčové slovo base |
volání rodičovské verze a : base(...) |
| polymorfismus | Polymorfismus | proč jeden cyklus stačí na všechny druhy |
protected |
Modifikátory přístupu | co vidí jen potomek |
is |
Operátor is |
dotaz na typ objektu |
| abstraktní a zapečetěné třídy | Abstract a sealed | souhrn obojího na jednom místě |
List<T> |
List<T> |
seznam objektů a jeho metody |
LINQ (Sum, Count, Where) |
Přehled LINQ | dotazy nad seznamy objektů |
Stránka o polymorfismu má hned v úvodu ukázku se tvary (Shape, Circle,
Rectangle). Je to přesně náš bestiář, jen bez příšer — dobrý důkaz,
že to, co jsme dnes dělali, není školní vymyšlenost, ale standardní postup.
Na stránce o virtual si všimněte tabulky rozdílů virtual / abstract /
override / new. Slovo new u metod zatím neučte — jen řekněte,
že existuje a že se mu vyhýbáme.
Pro rychlejší studenty, kteří dokončí cvičení dřív, nebo na posledních 10–15 minut.
| Cvičení | Odkaz | Proč se hodí |
|---|---|---|
| Programování — základy | umimeinformatiku.cz | čtení cizího kódu a předvídání výstupu |
| Funkce a procedury | umimeinformatiku.cz | která verze metody se vlastně zavolá |
| Seznamy a pole | umimeinformatiku.cz | průchod seznamem, dnes seznamem objektů |
Nejlepší bonus dneška je ale tužka a papír: ať studenti nakreslí strom dědičnosti pro něco úplně jiného než pro hru — dopravní prostředky, zvířata v ZOO, hudební nástroje. U každé úrovně musí platit věta „potomek je rodič". Kdo to zvládne bez chyby, pochopil dnešní hodinu líp než z jakéhokoli kódu.
Osvědčený trik na závěr: napište na tabuli špatný strom
(Auto : Kolo, protože „obojí má kola") a nechte třídu, ať ho vyvrátí.
Věta „je to" je jediné pravidlo, které si z dědičnosti musí odnést všichni.