8. diel - Aréna s mágom (dedičnosť a polymorfizmus)
V minulej lekcii, Dedičnosť a polymorfizmus , sme si vysvetlili dedičnosť a polymorfizmus.
Dnes máme sľúbené, že si ich vyskúšame v praxi. Bude to opäť na našej
aréne, kde z bojovníka oddědíme mága. Tento C # .NET tutoriál už patrí k
tým náročnejším a bude tomu tak aj u ďalších. Preto si priebežne
precvičujte prácu s objektmi, skúšajte si naše cvičenia a tiež
vymýšľajte nejaké svoje aplikácie, aby ste si zažili základné veci. To,
že je tu prítomný celý online kurz neznamená, že ho celý naraz
prečítate a pochopíte
Snažte sa programovať priebežne.
![mág - Objektovo orientované programovanie v C #](images/5/csp/mag.png)
0
. Každé kolo sa bude
mana zvyšovať o 10 a mág bude podnikať len bežný útok. Akonáhle sa mana
úplne doplní, opäť bude môcť magický útok použiť. Mana bude zobrazená
grafickým ukazovateľom, rovnako ako život.
Vytvoríme teda triedu Mag.cs
, zdedíme ju z
Bojovnik
a dodáme ju atribúty, ktoré chceme oproti bojovníkovi
navyše. Bude teda vyzerať takto (opäť si ju okomentujte):
class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; }
V mágovi nemáme zatiaľ prístup ku všetkým premenným, pretože sú v
bojovníkovi nastavené ako privátne. Musíme triedu Bojovnik
ľahko upraviť. Zmeníme modifikátory private
u atribútov na
protected
. Budeme potrebovať len
kostka
a jmeno
, ale pokojne nastavíme ako
protected
všetky atribúty charakteru, pretože sa v budúcnosti
môžu hodiť, keby sme sa rozhodli oddědit ďalšie typy bojovníkov. Naopak
atribút zprava
nie je vhodné nastavovať ako
protected
, pretože nesúvisí s bojovníkom, ale s nejakou
vnútornou logikou triedy. Trieda teda bude vyzerať nejako takto:
protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected Kostka kostka; private string zprava; ...
Prejdime ku konstruktoru.
Konštruktor potomka
C # nededia konstruktory! Je to pravdepodobne z toho dôvodu, že predpokladá, že potomok bude mať navyše nejaké atribúty a pôvodné konštruktor by u neho bol na škodu. To je aj náš prípad, pretože konštruktor mága bude brať oproti tomu z bojovníka navyše 2 parametre (mana a magický útok).
Definujeme si teda konštruktor v potomkovi, ktorý berie parametre potrebné pre vytvorenie bojovníka a niekoľko parametrov navyše pre mága.
V konštruktor potomkov je nutné vždy volať konštruktor predka. Je to z toho dôvodu, že bez volania konstruktoru nemusí byť inštancie správne inicializovaná. Konštruktor predka nevoláme iba v prípade, že žiadny nemá. Náš konštruktor musia mať samozrejme všetky parametre potrebné pre predka plus tie nové, čo má navyše potomok. Niektoré potom odovzdáme predkovi a niektoré si spracujeme sami. Konštruktor predka sa vykoná pred naším konstruktoru.
VC # .NET existuje kľúčové slovo base
,
ktoré je podobné nami už známemu this
. Na rozdiel od
this
, ktoré odkazuje na konkrétnu inštanciu triedy,
base
odkazuje na predka. My teda môžeme zavolať
konštruktor predka s danými parametrami a potom vykonať navyše
inicializáciu pre mága. VC # sa volanie konstruktoru predka píše do
hlavičky metódy.
Konštruktor mága bude teda vyzerať takto:
public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; }
Rovnako môžeme volať aj iný konštruktor v tej istej triede (nie predka),
len namiesto base použijeme this
.
Presuňme sa teraz do Program.cs
a druhého bojovníka (Shadow)
zmeňme na mága, napr. Takto:
Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45);
Zmenu samozrejme musíme urobiť aj v riadku, kde bojovníka do arény
vkladáme. Všimnite si, že mága ukladáme do premennej typu
Bojovnik
. Nič nám v tom nebráni, pretože bojovník je jeho
predok. Rovnako tak si môžeme typ premennej zmeniť na Mag
. Keď
aplikáciu teraz spustíme, bude fungovať úplne rovnako, ako predtým. Mág
všetko dedí z bojovníka a zatiaľ teda funguje ako bojovník.
Polymorfizmus a prepisovanie metód
Bolo by výhodné, keby objekt Arena
mohol s mágom pracovať
rovnako ako s bojovníkom. My už vieme, že takémuto mechanizmu hovoríme
polymorfizmus. Aréna zavolá na objekte metódu
Utoc()
so súperom v parametri. Nestará sa o to, či bude útok
vykonávať bojovník alebo mág, bude s nimi pracovať rovnako. U mága si teda
prepíšeme metódu Utoc()
z predka. Prepíšeme
zdedenú metódu tak, aby útok pracoval s mannou, hlavička metódy však
zostane rovnaká.
Aby sme mohli nejakú metódu prepísať, musí byť v predkovi označená
ako virtuálny. Nehľadajte za tým žiadnu vedu, jednoducho
pomocou kľúčového slova virtual
C # oznámime,
že si želáme, aby potomok mohol túto metódu prepísať. Hlavičku metódy v
Bojovnik.cs
teda zmeníme na:
public virtual void Utoc(Bojovnik souper)
Keď sme pri metódach, budeme ešte určite používať metódu
NastavZpravu()
, tá je však privátne. Označme ju ako
protected
:
protected void NastavZpravu(string zprava)
Pri návrhu bojovníka sme samozrejme mali myslieť na to, že sa z neho bude
dediť a už označiť vhodné atribúty a metódy ako protected
,
prípadne metódy ako virtuálne. Kľúčovým slovom virtual
je
označená metóda, ktorú možno v potomkovi prepísať, inak to nie je
možné. V tutoriále k bojovníkovi som vás tým však nechcel zbytočne
zaťažovať, preto musíme modifikátory zmeniť až teraz, kedy im rozumieme *
Metóda Utoc()
v bojovníkovi bude teda
public virtual
. Teraz sa vráťme do potomka a poďme ju
prepísať. Metódu normálne definujeme v Mag.cs
tak, ako sme
zvyknutí. Za modifikátorom public
však ešte použijeme
kľúčové slovo override
, ktoré značí, že
si sme vedomí toho, že sa metóda zdedila, ale prajeme si zmeniť jej
správanie.
public override void Utoc(Bojovnik souper)
Podobne sme prepisovali metódu ToString()
u našich objektov.
Každý objekt v C # je totiž odděděný od System.Object
,
ktorý obsahuje 4 metódy, jedna z nich je aj ToString()
. Pri jej
implementácii teda musíme použiť override
.
Správanie metódy Utoc()
nebude nijako zložité. Podľa
hodnoty many buď vykonáme bežný útok alebo útok magický. Hodnotu many
potom buď zvýšime o 10 alebo naopak znížime na 0
v prípade
magického útoku.
public override void Utoc(Bojovnik souper) { int uder = 0; // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočí s úderem za {1} hp", jmeno, uder)); } else // Magický útok { uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); mana = 0; } souper.BranSe(uder); }
Kód je asi zrozumiteľný. Všimnite si obmedzenia many na
maxMana
, môže sa nám totiž stať, že túto hodnotu presiahne,
keď ju zvyšujeme o 10. Keď sa nad kódom zamyslíme, tak útok vyššie v
podstate vykonáva pôvodnej metóda Utoc()
. Iste by bolo
prínosné zavolať podobu metódy na predkovi namiesto toho, aby sme správanie
odpisovali. K tomu opäť použijeme base
:
{CSHARP_OOP} class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; } public override void Utoc(Bojovnik souper) { // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; base.Utoc(souper); } else // Magický útok { int uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); souper.BranSe(uder); mana = 0; } } } {/CSHARP_OOP}
{CSHARP_OOP} {CSHARP_MAIN_BLOCK} // vytvorenie objektov Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena = new Arena(zalgoren, gandalf, kostka); // zápas arena.Zapas(); Console.ReadKey(); {/CSHARP_MAIN_BLOCK} {/CSHARP_OOP}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } public int hod() { return random.Next(1, pocetSten + 1); } public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
{CSHARP_OOP} class Bojovnik { protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected Kostka kostka; private string zprava; public Bojovnik(string jmeno, int zivot, int utok, int obrana, Kostka kostka) { this.jmeno = jmeno; this.zivot = zivot; this.maxZivot = zivot; this.utok = utok; this.obrana = obrana; this.kostka = kostka; } public bool Nazivu() { return (zivot > 0); } public string GrafickyZivot() { string s = "["; int celkem = 20; double pocet = Math.Round(((double)zivot / maxZivot) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; } public virtual void Utoc(Bojovnik souper) { int uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočia s úderom za {1} hp", jmeno, uder)); souper.BranSe(uder); } public void BranSe(int uder) { int zraneni = uder - (obrana + kostka.hod()); if (zraneni > 0) { zivot -= zraneni; zprava = String.Format("{0} utrpel poškodenie {1} hp", jmeno, zraneni); if (zivot <= 0) { zivot = 0; zprava += " a zomrel"; } } else zprava = String.Format("{0} odrazil útok", jmeno); NastavZpravu(zprava); } protected void NastavZpravu(string zprava) { this.zprava = zprava; } public string VratPosledniZpravu() { return zprava; } public override string ToString() { return jmeno; } } {/CSHARP_OOP}
using System.Threading; {CSHARP_OOP} class Arena { private Bojovnik bojovnik1; private Bojovnik bojovnik2; private Kostka kostka; public Arena(Bojovnik bojovnik1, Bojovnik bojovnik2, Kostka kostka) { this.bojovnik1 = bojovnik1; this.bojovnik2 = bojovnik2; this.kostka = kostka; } private void Vykresli() { Console.Clear(); Console.WriteLine("-------------- Aréna -------------- \n"); Console.WriteLine("Zdravie bojovníkov: \n"); Console.WriteLine("{0} {1}", bojovnik1, bojovnik1.GrafickyZivot()); Console.WriteLine("{0} {1}", bojovnik2, bojovnik2.GrafickyZivot()); } private void VypisZpravu(string zprava) { Console.WriteLine(zprava); Thread.Sleep(500); } public void Zapas() { // pôvodné poradie Bojovnik b1 = bojovnik1; Bojovnik b2 = bojovnik2; Console.WriteLine("Vitajte v aréne!"); Console.WriteLine("Dnes sa stretnú {0} s {1}! \n", bojovnik1, bojovnik2); // prehodenie bojovníkov bool zacinaBojovnik2 = (kostka.hod() <= kostka.VratPocetSten() / 2); if (zacinaBojovnik2) { b1 = bojovnik2; b2 = bojovnik1; } Console.WriteLine("Začínať bude bojovník {0}! \nZápas môže začať...", b1); Console.ReadKey(); // cyklus s bojom while (b1.Nazivu() && b2.Nazivu()) { b1.Utoc(b2); Vykresli(); VypisZpravu(b1.VratPosledniZpravu()); // správa o útoku VypisZpravu(b2.VratPosledniZpravu()); // správa o obrane if (b2.Nazivu()) { b2.Utoc(b1); Vykresli(); VypisZpravu(b2.VratPosledniZpravu()); // správa o útoku VypisZpravu(b1.VratPosledniZpravu()); // správa o obrane } Console.WriteLine(); } } } {/CSHARP_OOP}
Opäť vidíme, ako môžeme znovupoužívat kód. S dedičnosťou je spojené naozaj mnoho techník, ako si ušetriť prácu. V našom prípade to ušetrí niekoľko riadkov, ale u väčšieho projektu by to mohlo mať obrovský význam.
Aplikácia teraz funguje tak, ako má.
Konzolová aplikácia
-------------- Aréna --------------
Zdravie bojovníkov:
Zalgoren [############# ]
Gandalf [################# ]
Gandalf použil mágiu za 52 hp
Zalgoren utrpel poškodenie 36 hp
Aréna nás však neinformuje o mane mága, poďme to napraviť. Pridáme
mágovi verejnú metódu GrafickaMana()
, ktorá bude obdobne ako u
života vracať string
s grafickým ukazovateľom many.
Aby sme nemuseli logiku so zložením ukazovatele písať dvakrát, upravíme
metódu GrafickyZivot()
v Bojovnik.cs
. Pripomeňme
si, ako vyzerá:
public string GrafickyZivot() { string s = "["; int celkem = 20; double pocet = Math.Round(((double)zivot / maxZivot) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; }
Vidíme, že nie je výnimkou premenných zivot
a
maxZivot
na živote nijako závislá. Metódu premenujeme na
GrafickyUkazatel()
a dáme ju 2 parametre: aktuálnu hodnotu a
maximálnu hodnotu. Premenné zivot
a maxZivot
v tele
metódy potom nahradíme za aktualni
a maximalni
.
Modifikátor bude protected
, aby sme metódu mohli v potomkovi
použiť:
protected string GrafickyUkazatel(int aktualni, int maximalni) { string s = "["; int celkem = 20; double pocet = Math.Round(((double)aktualni / maximalni) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; }
Metódu GrafickyZivot()
v Bojovnik.cs
naimplementujeme znovu, bude nám v nej stačiť jediný riadok a to zavolanie
metódy GrafickyUkazatel()
s príslušnými parametrami:
public string GrafickyZivot() { return GrafickyUkazatel(zivot, maxZivot); }
Určite som mohol v tutoriálu s bojovníkom urobiť metódu
GrafickyUkazatel()
rovno. Chcel som však, aby sme si ukázali, ako
sa rieši prípady, keď potrebujeme vykonať podobnú funkčnosť viackrát. S
takouto parametrizáciou sa v praxi budete stretávať často, pretože nikdy
presne nevieme, čo budeme v budúcnosti od nášho programu požadovať.
Teraz môžeme vykresľovať ukazovateľ tak, ako sa nám to hodí. Presuňme
sa do Mag.cs
a naimplementujme metódu GrafickaMana()
:
public string GrafickaMana() { return GrafickyUkazatel(mana, maxMana); }
Jednoduché, že? Teraz je mág hotový, zostáva len naučiť arénu
zobrazovať manu v prípade, že je bojovník mág. Presuňme sa teda do
Arena.cs
.
Rozpoznanie typu objektu
Keďže sa nám teraz vykreslenie bojovníka skomplikovalo, urobíme si na
neho samostatnú metódu VypisBojovnika()
, jej parametrom bude
daná inštancie bojovníka:
private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Zivot: "); Console.WriteLine(b.GrafickyZivot()); }
Teraz poďme reagovať na to, či je bojovník mág. Minule sme si povedali,
že k tomu slúži operátor is
:
private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Život: "); Console.WriteLine(b.GrafickyZivot()); if (b is Mag) { Console.Write("Mana: "); Console.WriteLine(((Mag)b).GrafickaMana()); } }
Bojovníka sme museli na mága pretypovať, aby sme sa k metóde
GrafickaMana()
dostali. Samotný Bojovnik
ju totiž
nemá. To by sme mali, VypisBojovnika()
budeme volať v metóde
Vykresli()
, ktorá bude vyzerať takto:
using System.Threading; {CSHARP_OOP} class Arena { private Bojovnik bojovnik1; private Bojovnik bojovnik2; private Kostka kostka; public Arena(Bojovnik bojovnik1, Bojovnik bojovnik2, Kostka kostka) { this.bojovnik1 = bojovnik1; this.bojovnik2 = bojovnik2; this.kostka = kostka; } private void Vykresli() { Console.Clear(); Console.WriteLine("-------------- Aréna -------------- \n"); Console.WriteLine("Bojovníci: \n"); VypisBojovnika(bojovnik1); Console.WriteLine(); VypisBojovnika(bojovnik2); Console.WriteLine(); } private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Život: "); Console.WriteLine(b.GrafickyZivot()); if (b is Mag) { Console.Write("Mana: "); Console.WriteLine(((Mag)b).GrafickaMana()); } } private void VypisZpravu(string zprava) { Console.WriteLine(zprava); Thread.Sleep(500); } public void Zapas() { // pôvodné poradie Bojovnik b1 = bojovnik1; Bojovnik b2 = bojovnik2; Console.WriteLine("Vitajte v aréne!"); Console.WriteLine("Dnes sa stretnú {0} s {1}! \n", bojovnik1, bojovnik2); // prehodenie bojovníkov bool zacinaBojovnik2 = (kostka.hod() <= kostka.VratPocetSten() / 2); if (zacinaBojovnik2) { b1 = bojovnik2; b2 = bojovnik1; } Console.WriteLine("Začínať bude bojovník {0}! \nZápas môže začať...", b1); Console.ReadKey(); // cyklus s bojom while (b1.Nazivu() && b2.Nazivu()) { b1.Utoc(b2); Vykresli(); VypisZpravu(b1.VratPosledniZpravu()); // správa o útoku VypisZpravu(b2.VratPosledniZpravu()); // správa o obrane if (b2.Nazivu()) { b2.Utoc(b1); Vykresli(); VypisZpravu(b2.VratPosledniZpravu()); // správa o útoku VypisZpravu(b1.VratPosledniZpravu()); // správa o obrane } Console.WriteLine(); } } } {/CSHARP_OOP}
{CSHARP_OOP} class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int VratPocetSten() { return pocetSten; } public int hod() { return random.Next(1, pocetSten + 1); } public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
{CSHARP_OOP} class Bojovnik { protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected Kostka kostka; private string zprava; public Bojovnik(string jmeno, int zivot, int utok, int obrana, Kostka kostka) { this.jmeno = jmeno; this.zivot = zivot; this.maxZivot = zivot; this.utok = utok; this.obrana = obrana; this.kostka = kostka; } public bool Nazivu() { return (zivot > 0); } protected string GrafickyUkazatel(int aktualni, int maximalni) { string s = "["; int celkem = 20; double pocet = Math.Round(((double)aktualni / maximalni) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; } public string GrafickyZivot() { return GrafickyUkazatel(zivot, maxZivot); } public virtual void Utoc(Bojovnik souper) { int uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočia s úderom za {1} hp", jmeno, uder)); souper.BranSe(uder); } public void BranSe(int uder) { int zraneni = uder - (obrana + kostka.hod()); if (zraneni > 0) { zivot -= zraneni; zprava = String.Format("{0} utrpel poškodenie {1} hp", jmeno, zraneni); if (zivot <= 0) { zivot = 0; zprava += " a zemřel"; } } else zprava = String.Format("{0} odrazil útok", jmeno); NastavZpravu(zprava); } protected void NastavZpravu(string zprava) { this.zprava = zprava; } public string VratPosledniZpravu() { return zprava; } public override string ToString() { return jmeno; } } {/CSHARP_OOP}
{CSHARP_OOP} class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; } public string GrafickaMana() { return GrafickyUkazatel(mana, maxMana); } public override void Utoc(Bojovnik souper) { // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; base.Utoc(souper); } else // Magický útok { int uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); souper.BranSe(uder); mana = 0; } } } {/CSHARP_OOP}
{CSHARP_OOP} {CSHARP_MAIN_BLOCK} // vytvorenie objektov Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena = new Arena(zalgoren, gandalf, kostka); // zápas arena.Zapas(); Console.ReadKey(); {/CSHARP_MAIN_BLOCK} {/CSHARP_OOP}
}
Hotovo
Konzolová aplikácia
-------------- Aréna --------------
Bojovníci:
Zalgoren
Život: [########## ]
Gandalf
Život: [##### ]
Mana: [############# ]
Zalgoren útočia s úderom za 28 hp
Aplikáciu ešte môžeme dodať krajší vzhľad, vložil som ASCIIart
nadpis Aréna, ktorý som vytvoril touto aplikáciou: http://patorjk.com/software/taag .
Navyše som zafarbil ukazovatele pomocou farby pozadia a popredia. Metódu k
vykreslenie ukazovatele som upravil tak, aby vykreslovala plný obdĺžnik
miesto #
(ten napíšete pomocou Alt + 2
1 9). Výsledok môže vyzerať takto:
Konzolová aplikácia
__ ____ ____ _ _ __
/__\ ( _ \( ___)( \( ) /__\
/(__)\ ) / )__) ) ( /(__)\
(__)(__)(_)\_)(____)(_)\_)(__)(__)
Bojovníci:
Zalgoren
Život: <span style="color:red">████</span><span style="color:maroon">████████████████</span>
Gandalf
Život: <span style="color:red">███████</span><span style="color:maroon">█████████████</span>
Mana: <span style="color:blue">█</span><span style="color:navy">███████████████████</span>
Gandalf použil mágiu za 48 hp
Zalgoren utrpel poškodenie 33 hp
Kód máte v prílohe. Ak ste niečomu nerozumeli, skúste si článok prečítať viackrát alebo pomalšie, sú to dôležité praktiky. V budúcej lekcii, Riešené úlohy k 5.-8. lekciu OOP v C # .NET , si vysvetlíme pojem statika.
V nasledujúcom cvičení, Riešené úlohy k 5.-8. lekciu OOP v C # .NET, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 3416x (40.04 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#