10. diel - Vlastnosti
V predchádzajúcom cvičení, Riešené úlohy k 9. lekcii OOP v C # .NET, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Riešené úlohy k 9. lekcii OOP v C # .NET , sme si vysvetlili statiku. V dnešnom C # .NET tutoriálu sa pozrieme na ďalšie prvky tried, ktoré ešte nepoznáme. Začnime prisľúbeným vlastnosťami.
Vlastnosti
Veľmi často sa nám stáva, že chceme mať kontrolu nad zmenami nejakého
atribútu objektu zvonku. Budeme chcieť atribút nastaviť ako read-only alebo
reagovať na jeho zmeny. Založme si nový projekt (názov
Vlastnosti
) a vytvorme nasledujúce triedu Student
,
ktorá bude reprezentovať študenta v nejakom informačnom systéme.
class Student { public string jmeno; public bool muz; public int vek; public bool plnolety; public Student(string jmeno, bool pohlavi, int vek) { this.jmeno = jmeno; this.muz = pohlavi; this.vek = vek; plnolety = true; if (vek < 18) plnolety = false; } public override string ToString() { string jsemPlnolety = "som"; if (!plnolety) jsemPlnolety = "nie som"; string pohlavi = "muž"; if (!muz) pohlavi = "žena"; return String.Format("Som {0}, {1}. Je mi {2} rokov a {3} plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } }
Trieda je veľmi jednoduchá, študent sa nejako volá, je nejakého pohlavia
a má určitý vek. Podľa tohto veku sa nastavuje atribút
plnolety
pre pohodlnejšie vyhodnocovanie plnoletosti na rôznych
miestach systému. Na uloženie pohlavia používame hodnotu bool
,
či je študent muž. Konštruktor podľa veku určí, či je študent
plnoletý. Metóda ToString()
je navrhnutá pre potreby tutoriálu
tak, aby nám vypísala všetky informácie. V reáli by vrátila pravdepodobne
len meno študenta. Pomocou konstruktoru si nejakého študenta vytvorme:
{CSHARP_CONSOLE} Student s = new Student("Pavel Hora", true, 20); Console.WriteLine(s); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Student { public string jmeno; public bool muz; public int vek; public bool plnolety; public Student(string jmeno, bool pohlavi, int vek) { this.jmeno = jmeno; this.muz = pohlavi; this.vek = vek; plnolety = true; if (vek < 18) plnolety = false; } public override string ToString() { string jsemPlnolety = "som"; if (!plnolety) jsemPlnolety = "nie som"; string pohlavi = "muž"; if (!muz) pohlavi = "žena"; return String.Format("Som {0}, {1}. Je mi {2} rokov a {3} plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } } {/CSHARP_OOP}
výstup:
Konzolová aplikácia
Som Pavel Hora, muž. Je mi 20 rokov a som plnoletý.
Všetko vyzerá pekne, ale atribúty sú prístupné ako na čítanie, tak na zápis. Objekt teda môžeme rozbiť napríklad takto (hovoríme o nekonzistentnom vnútorným stave):
{CSHARP_CONSOLE} Student s = new Student("Pavel Hora", true, 20); s.vek = 15; s.muz = false; Console.WriteLine(s); Console.ReadKey(); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Student { public string jmeno; public bool muz; public int vek; public bool plnolety; public Student(string jmeno, bool pohlavi, int vek) { this.jmeno = jmeno; this.muz = pohlavi; this.vek = vek; plnolety = true; if (vek < 18) plnolety = false; } public override string ToString() { string jsemPlnolety = "som"; if (!plnolety) jsemPlnolety = "nie som"; string pohlavi = "muž"; if (!muz) pohlavi = "žena"; return String.Format("Som {0}, {1}. Je mi {2} rokov a {3} plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } } {/CSHARP_OOP}
výstup:
Konzolová aplikácia
Som Pavel Hora, žena. Je mi 15 rokov a som plnoletý.
Určite musíme ošetriť, aby sa plnoletosť obnovila pri zmene veku. Keď
sa zamyslíme nad ostatnými atribúty, nie je najmenší dôvod, aby sme ich
taktiež umožňovali modifikovať. Študent si za normálnych okolností asi
len ťažko zmení pohlavia alebo meno. Bolo by však zároveň vhodné ich
vystaviť na čítanie, nemôžeme je teda iba iba nastaviť ako
private
. V skorších lekciách C # kurzu sme na tento účel
používali metódy, ktoré slúžili na čítanie privátnych atribútov. Ich
názov sme volili ako VratVek()
a podobne. Na čítanie vybraných
atribútov vytvoríme tiež metódy a atribúty označíme ako privátne, aby sa
nedali modifikovať zvonku. Trieda by novo vyzerala napr. Takto (vynechal som
konštruktor a ToString()
):
class Student { private string jmeno; private bool muz; private int vek; private bool plnolety; ... public string VratJmeno() { return jmeno; } public bool VratPlnoletost() { return plnolety; } public int VratVek() { return vek; } public bool Muz() { return muz; } public void NastavVek(int hodnota) { vek = hodnota; // prehodnotenie plnoletosti plnolety = true; if (vek < 18) plnolety = false; } }
Metódy, čo hodnoty len vracajú, sú veľmi jednoduché. Nastavenie veku
má už nejakú vnútornú logiku, pri jeho zmene musíme totiž prehodnotiť
atribút plnolety
. Zaistili sme, že sa do premenných nedá
zapisovať inak, ako my chceme. Máme teda pod kontrolou všetky zmeny
atribútov a dokážeme na ne reagovať. Nemôže sa stať, že by nám niekto
vnútorný stav nekontrolovane menil a rozbil.
Metódam na vrátenie hodnoty sa hovorí Getter a metódam
pre zápis setter. Pre editáciu ostatných atribútov by sme
urobili jednu metódu EditujStudenta
, ktorá by bola podobná
konstruktoru. Meno, vek a podobne by sa teda menili pomocou tejto metódy, tam
by sme mohli napr. Kontrolovať, či hodnoty dávajú zmysel, opäť by sme
odchytili všetky pokusy o zmenu na jedinom mieste.
Ručné písanie Getter a setter je určite veľmi prácne. Nemohol by to urobiť niekto za nás? Áno, C # nám ich vie vygenerovať. Potom už nehovoríme o atribútoch, ale o vlastnostiach. Syntax vlastnosti je veľmi podobná atribútu:
public string Jmeno { get; set; }
Sprvu to vyzerá, ako by sme deklarovali atribút. Meno vlastnosti je však veľkým písmenom, jedná sa totiž o metódu (presnejšie 2 metódy). V zložených zátvorkách potom špecifikujeme, ktoré metódy si prajeme vygenerovať. Za vlastností nepíšeme bodkočiarka! V ukážke vyššie sa vygeneruje setter aj getter, vlastnosť pôjde teda normálne čítať aj modifikovať:
Console.WriteLine(objekt.Jmeno); // čítať objekt.Jmeno = "Jan Malý"; // zapisovať
Jediný rozdiel oproti atribútu je zvonku v tom, že začiatočné písmeno je veľké. C # v skutočnosti vygeneruje privátnej atribút ak nemu dve metódy, ktoré podľa kontextu volá (spozná podľa situácie či čítame alebo zapisujeme). Keď do vlastnosti nevygeneruje setter, nepôjde meniť zvnútra ani zvonku. Ak si želáme, aby vlastnosť nešla mimo triedu meniť, označíme setter ako privátne:
public string Jmeno { get; private set; }
Tohto budeme hojne využívať a práve takto bude vyzerať väčšina vlastností našich budúcich tried.
Ak si prajeme, aby sa v Getter alebo Setter dialo viac, než len načítanie / zápis hodnoty, môžeme si ho definovať ručne. Ukážme si to na našom príklade s dosiahnutím plnoletosti, ktorá sa musí po zmene veku prehodnotiť:
private int vek; public int Vek { get { return vek; } set { vek = value; // kontrola plnoletosti Plnolety = true; if (vek < 18) Plnolety = false; } }
Sprvu je nutné si vytvoriť privátne atribút vek
s malým
písmenom, v ktorej bude hodnota v skutočnosti uložené. V Getter a
setter potom pracujeme s týmto atribútom, ak použijete v get{}
alebo set{}
Vek
, program sa zacyklí! . Nie
je možné definovať len getter alebo setter, buď sa obaja vygenerujú samy
alebo oba definujeme ručne. Pre prístup k zadanej hodnote je nám v setter k
dispozícii kľúčové slovo value
. Takto sa v C
# do verzie 3.0 museli definovať všetky vlastnosti, až potom Microsoft
zaviedol tzv. Autoimplementaci a skrátený zápis, aký sme si uviedli
vyššie. U drvivej väčšiny vlastností totiž v metódach nepotrebujeme
žiadnu logiku. S Vek
teraz pracujeme opäť rovnako, ako s
atribútom, len s veľkým písmenom. Nenápadné priradenie do veku vnútorne
spustí ďalšiu logiku k prehodnoteniu vlastnosti Plnolety
:
objekt.Vek = 15; // teraz sa zmení aj plnoletosť
Rovnako môžeme pochopiteľne implementovať aj getter a napríklad niečo niekam logovať.
Upravíme si našu triedu Student
tak, aby používala
vlastnosti. Vyzerala by takto:
class Student { public string Jmeno { get; private set; } public bool Muz { get; private set; } public bool Plnolety { get; private set; } private int vek; public int Vek { get { return vek; } set { vek = value; // kontrola plnoletosti Plnolety = true; if (vek < 18) Plnolety = false; } } public Student(string jmeno, bool pohlavi, int vek) { EditujStudenta(jmeno, pohlavi, vek); } public void EditujStudenta(string jmeno, bool pohlavi, int vek) { Jmeno = jmeno; Muz = pohlavi; Vek = vek; } public override string ToString() { string jsemPlnolety = "som"; if (!Plnolety) jsemPlnolety = "nie som"; string pohlavi = "muž"; if (!Muz) pohlavi = "žena"; return String.Format("Som {0}, {1}. Je mi {2} rokov a {3} plnoletý.", Jmeno, pohlavi, Vek, jsemPlnolety); } }
To je o veľa lepšie, že? Vlastnosti budeme odteraz používať stále,
umožňujú nám totiž objekty dokonale zapouzdřit. V .NET sú všetky
verejné atribúty tried vlastnosti (napr. Nám známa vlastnosť
Length
na string
u), platí pravidlo, že čo
ide von je vlastnosť, čo sa používa len vo vnútri je
privátne atribút. Verejný atribút sa defakto príliš nepoužíva.
Celú triedu is ukážkovým programom si samozrejme opäť môžete stiahnuť
pod článkom. Kontrolu plnoletosti môžeme z konstruktoru teraz vybrať,
akonáhle totiž dosadíme do vlastnosti Vek
, nastaví sa
plnoletosť sama. Ešte si opäť vyskúšajme problémový príklad:
{CSHARP_CONSOLE} Student s = new Student("Pavel Hora", true, 20); s.Vek = 15; // s.Muz = false; // tento riadok teraz spôsobí chybu a musí byť odobratá Console.WriteLine(s); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Student { public string Jmeno { get; private set; } public bool Muz { get; private set; } public bool Plnolety { get; private set; } private int vek; public int Vek { get { return vek; } set { vek = value; // kontrola plnoletosti Plnolety = true; if (vek < 18) Plnolety = false; } } public Student(string jmeno, bool pohlavi, int vek) { EditujStudenta(jmeno, pohlavi, vek); } public void EditujStudenta(string jmeno, bool pohlavi, int vek) { Jmeno = jmeno; Muz = pohlavi; Vek = vek; } public override string ToString() { string jsemPlnolety = "som"; if (!Plnolety) jsemPlnolety = "nie som"; string pohlavi = "muž"; if (!Muz) pohlavi = "žena"; return String.Format("Som {0}, {1}. Je mi {2} rokov a {3} plnoletý.", Jmeno, pohlavi, Vek, jsemPlnolety); } } {/CSHARP_OOP}
A výstup:
Konzolová aplikácia
Som Pavel Hora, muž. Je mi 15 rokov a nie som plnoletý.
Ak celú vlastnosť označíme ako private
, nemožno potom
setter alebo Getter označiť ako public
.
V budúcej lekcii, Dátum a čas v C # , sa pozrieme ako sa v .NET pracuje s dátumom a časom.
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é 1384x (26.85 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#