Referenční typy - třída
| Stránky: | Moodle ZČU |
| Kurz: | KPV/TI Technická informatika ve strojírenství - šablona |
| Kniha: | Referenční typy - třída |
| Vytiskl(a): | Nepřihlášený host |
| Datum: | neděle, 8. listopadu 2026, 16.21 |
1. Třída
Abychom pochopili rozdíl mezi hodnotovým a referenčním datovým typem, je nejprve objasnit pojem třída (ta je referenčního typu).
Jazyk C# je objektově orientovaný jazyk (dále budeme využívat zkratku „OOP“ – anglicky „Object Oriented Programming“ - objektově orientované programování). Když jsme poprvé analyzovali základní program vygenerovaný pro konzoli, náš Program.cs definoval třídu Program a metodu Main, která náležela této třídě (anglicky „Class“). Třída – klíčové slovo class - je kořenem slova klasifikace anglicky „Classification“. V C# je veškerý spustitelný kód definován v rámci metody a všechny metody musí patřit do třídy nebo struktury.
Microsoft .NET Framework obsahuje tisíce tříd, z nichž už jsme si některé vyzkoušeli např. Console a Exception. Třídy poskytují mechanismus pro modelování tzv. entit (anglicky „Entity“), které odrážejí prvky reálného světa, ale i abstraktní věci. Součástí návrhu systému je vždy analýza, jejímž cílem je vymezit důležité entity, které jsou ve vztahu s procesy, které daný systém implementuje a správně definovat data, která mají tyto entity obsahovat a jaké funkce mají provádět. Informace, které třída uchovává, jsou ukládány ve formě datových složek (anglicky "Fields" - nazývaných též atributy) a operace jsou implementovány jako metody (anglicky "Methods"). [6]
Když definujeme třídu, musíme systematicky uspořádat informace a chování do smysluplné entity – jedná se tedy o akt klasifikace, který v podstatě děláme každý a nejen programátoři. Ukažme klasifikaci na příkladu auta. Automobily mají společné vlastnosti, můžeme je řídit, zastavit, rozjet apod – představme si tyto vlastnosti jako metody. Mají také společné atributy – kola, volant, motor apod. Vidíme, že auta mají společné prvky, lze tedy odvodit jistý předpis, který je společný pro podstatnou většinu aut – třída. Třída je předpisem pro vytvoření objektu. Pokud je objekt vytvořen na základě definované třídy, lze ho také obecně nazvat tzv. instancí příslušné třídy. Instance je vytvářena za běhu programu. Instance má stejné rozhraní jako třída, podle které je vytvořena. Jednotlivé objekty třídy se od sebe navzájem liší svými atributy. Jako příklad nám poslouží dva objekty třídy Auto jakými jsou např. autoOctavia a autoFord. Tato dvě auta (objekty) se budou se lišit svými atributy, tzn., že mají různé parametry např. SPZ, typ, objem [cm3] a výkon [kW] motoru, spotřeba [l/100 km], cena [Kč], rok výroby apod. Tyto atributy jsou uloženy pomocí tzv. datových složek, které využíváme pro uložení informací např. u objektu autoOctavia budou nastaveny parametry auta, tj. atributy objektu jako SPZ = "4P49462"; typMotoru = "TDI"; objemMotoru = 1.9; vykonMotoru = 81, spotrebaMotoru = 5, atd. Asi cítíme, že to jiné nastavení parametrů auta můžeme udělat i u objektuFord.
Další důležitou vlastností objektově orientovaného programování je zapouzdření (anglicky "Encapsulation"). Atributy a metody objektu tvoří nedělitelný celek a vzájemně se ovlivňují. Díky zapouzdření může objekt své metody a atributy skrýt před okolím. Představme si, že bychom u automobilu měli volně přístupnou metodu vypusť nádrž. Asi bychom nechtěli, aby jiný programátor nám omylem vypustil právě natankované auto. Členy přístupné okolí se nazývají rozhraní (anglicky "Interface") objektu.
V programování jsme se již s něčím podobným setkali. Využívali jsme proměnné, které byly jistého datového typu a pomocí metod jsme mohli ovlivňovat hodnoty uložené v těchto proměnných. Objekt reprezentuje složitější proměnnou složitějšího datového typu – třídy (v našem příkladu Auto). Když jsme potřebovali vytvořit proměnnou, deklarovali jsme ji. Když potřebujeme vytvořit objekt, použijeme konstruktor (v našem příkladu auta si můžeme představit konstruktor jako metodu, která nastaví parametry auta např. typMotoru = "TDI"; objemMotoru = 1.9; vykonMotoru = 81). Když jsme potřebovali zacházet s proměnnými, použili jsme metody. K zacházení s jednotlivými objekty (popřípadě jeho datovými složkami) budeme používat také metody (v našem příkladu metoda Zastav, Rozjed, Zpomal atd.). Pokud mluvíme o objektovém programu, skládá se pouze z objektů, kde si mezi sebou objekty navzájem posílají zprávy, tzn., že volají (spouštějí) své metody (v našem případě auto může komunikovat s jiným autem např. předání informací o stavu na vozovce).
2. Zapouzdření
Důležitým principem používaným v OOP je princip zapouzdření (anglicky „Encapstulation“), tzn., že se program využívající nějakou třídu se nemá starat o to, jak tato třída interně funguje. Program pouze vytvoří instanci této třídy a poté volá její metody. Třída si někdy musí uchovávat informace o jejím interním stavu, které jsou nutné pro provádění metod. Takové informace jsou však také před programem skryty. Vidíme, že zapouzdření představuje „určité skrývání“ vlastních informací před vnějškem.
Zapouzdření má dva hlavní účely:
- Kombinovat metody a data (ve formě datové složky) uvnitř třídy
- Kontrolovat přístupnost metod a dat – kontrola použití třídy [6]
3. Deklarace třídy
Zjednodušeně můžeme deklaraci třídy popsat následujícím způsobem:
modifikátorPřístupu class identifikátorTřídy předekARozhraní { těloTřídy }
kde:
- class - klíčové slovo uvozující identifikátor třídy (název třídy)
Další atributy deklarace budou popsány v následujících podkapitolách.
3.1. Modifikátor přístupu třídy
Spojení „určité skrývání“ vlastních informací třídy před vnějškem bylo použito záměrně (např. u našeho příkladu auto, bychom nechtěli, aby po natankování plné nádrže někdo zavolal metodu vypustNadrz). U některých datových složek můžeme specifikovat přístupová práva tzv. modifikátor přístupu (anglicky „Access Modifiers“). Modifikátor přístupu specifikovaný pro třídu lze rozdělit do dvou základní kategorií:
Modifikátor přístupu značí přístupová práva ke třídě, která mohou využívat následující klíčová slova:
- public – veřejně přístupná třída – můžeme ji využívat a vytvářet její instance, volat její metody apod. - kdokoliv ve kterékoliv části programu (pokud to dovolí práva v datových složkách), v němž jsme ji deklarovali a v jakémkoliv jiném programu, který obsahuje odkaz na seskupení (viz kapitola Seskupení), jež tuto třídu obsahuje
- internal – třídu lze použít pouze v rámci aktuálního programu. Jestliže nevyužijeme ani public a ani internal, bude se třída chovat jako bychom použili internal
- abstract – abstraktní třída – nelze od ní vytvořit instanci. Abstraktní třída typicky shrnuje společné vlastnosti svých potomků
- sealed – zapečetěná třída – nelze od ní odvozovat potomky. Je v jistém smyslu opakem klíčového slova abstract. Abstraktní třída nemůže být zapečetěná. Lze uvést pouze jedno klíčové slovo, buď abstract nebo sealed [3]
3.2. Identifikátor třídy
Identifikátor třídy, tedy název třídy (včetně konstruktoru třídy užívaného pro vznik instance třídy), budeme uvozovat velkým písmenem. Pokud je název složen z více slov, použijme velbloudí notaci („CamelCase“) – každé slovo začíná velkým písmenem. Pro identifikátor třídy platí stejná pravidla jako pro obecný identifikátor – viz kapitola Identifikátor.
3.3. Předek a rozhraní
Vzhledem k tomu, že jsem teprve v začátcích popisu funkcionalit třídy, berme následující specifikace předka a rozhraní třídy prozatím jako doplňující informaci. U specifikace se jedná o určení předka a implementovaných rozhraní pro danou třídu. Tato specifikace může mít tvar:
: jménoBázovétřídy
: jménoBázovétřídy, seznamRozhraní
: seznamRozhraní
kde:
- jménoBázovétřídy - identifikátor třídy, kterou chceme použít jako předka právě deklarované třídy. Je-li to nutné, připojíme k identifikátoru bázové třídy kvalifikaci jménem prostoru jmen. Pokud specifikaci předka vynecháme, doplní si překladač jako předka třídu object, která je společným předkem všech tříd v C#
- seznamRozhraní - představuje jména implementovaných rozhraní oddělená čárkami [3]
3.4. Tělo třídy
Tělo třídy je uzavřeno ve složených závorkách. Tělo obsahuje deklaraci datových složek, metod, vlastností atd. Pro deklarace, které tvoří tělo třídy, budeme používat souhrnné označení složky. Pro každou ze složek třídy, tj. pro datové složky i metody musíme určit přístupová práva – modifikátory přístupu. Po definování úrovně přístupu pojmenujme datovou složku „S“ ve třídě „T“. V jazyku C# rozlišuje u složek následující modifikátory přístupu:
- public – tzv. veřejně přístupná datová složka S je dostupná všude, kde lze použít třídu T. O veřejně přístupných metodách hovoříme také jako o rozhraní třídy
- protected internal – složka S je dostupná z T a ze všech tříd, které jsou potomkem třídy T
- protected – tzv. chráněná složka S je dostupná pouze ve třídě T anebo u jejích potomků
- internal – tzv. interní složka S je dostupná pouze ze seskupení (například z nějaké dynamické knihovny – viz kapitola Seskupení)
- private – tzv. soukromá složka S je dostupná pouze uvnitř třídy T. Tento modifikátor přístupu je implicitní, tzn., že pokud neuvedeme žádný modifikátor přístupu, bude brán přístup jako soukromý
Identifikátory složek, které jsou public budeme uvozovat na začátku velkým písmenem (tento přístup je v angličtině označován jako „PascalCase“, protože byl poprvé použit v jazyce Pascal). Ostatní složky, které nejsou public budeme označovat malým písmenem.
V následující tabule jsou zobrazena přístupová práva u složek pro jednotlivé modifikátory přístupu - viz Tabulka 1. Slovo Ano znamená, že složka s odpovídajícím modifikátorem je dostupná v rámci téže třídy, v tomtéž seskupení, téhož potomka atd.
|
Modifikátor přístupu |
Třída |
Seskupení |
Potomci |
Ostatní |
|
public |
Ano |
Ano |
Ano |
Ano |
|
protected internal |
Ano |
Ano |
Ano |
Ne |
|
protected |
Ano |
Ne |
Ano |
Ne |
|
internal |
Ano |
Ano |
Ne |
Ne |
|
private |
Ano |
Ne |
Ne |
Ne |
4. Statická třída
Další vlastností jazyka C # je možnost deklarovat třídu jako statickou. Statická třída může obsahovat pouze statické složky (všechny objekty, které vytvoříte pomocí této třídy, sdílejí jednu kopii těchto složek). Účelem statické třídy je pouze držet užitkové metody a datové složky. Statická třída nemůže obsahovat žádná data nebo metody instance a nemá smysl pokusit se vytvářet instance statické třídy pomocí operátoru new (pokud to zkusíme, kompilátor ohlásí chybu). Pokud potřebujeme provést jakoukoli inicializaci, statická třída může mít výchozí konstruktor. Ten ale musí být deklarován jako statický. Jakékoli jiné typy konstruktoru kompilátor ohlásí jako nepovolené. [6]
Pokud budeme volat statickou datovou složku, nebo statickou metodu, musíme vždy uvádět jméno statické třídy, které tyto složky náleží (vzpomeňte si, že když jsme deklarovali nějakou metodu a volali jsme ji v hlavní statické metodě Main, musela být tato metoda také statická). Pokud nechceme pokaždé vypisovat název statické třídy, můžeme do jmenného prostoru uvést klíčové slovo using a static a definovat jmenný prostor, ve kterém se statická třída nachází. Pomocí tečkové notace vybereme statickou třídu, na níž se chceme odkazovat.
Pokud je používáno příliš statických tříd definovaných ve jmenném prostoru pomocí using, usnadní nám to psaní kódu, ale na druhou stranu je těžké spravovat takový kód. Není totiž jisté, která metoda do které třídy patří, a to může být problémem při jejich správě.
Ilustrativní příklad StatickaMetodaJinaTrida
Program StatickaMetodaJinaTrida
- Popis: Deklarace dvou statických tříd, použití jmenného prostoru se statickou třídou
- Zadání: Deklarujte nestatickou třídu Trida, která bude obsahovat hlavní metodu Main ve které budete volat statickou metodu StatickaMetoda z jiné statické třídy StatickaTrida. StatickaMetoda bude pouze obsahovat výpis textu "Nyní probíhá statická metoda v jiné statické třídě". Obě třídy jsou deklarovány ve společném jmenném prostoru StatickaMetodaJinaTrida
- Řešení: Bude postupně probráno v následujícím textu
V následujícím kódu vidíme, volání
statické metody jiné statické třídy z hlavní statické metody Main určité třídy – viz Kód 1. Pokud
bychom neuvedli odkaz na statickou třídu ve jmenném prostoru, museli bychom u
prvního příkazu statické metody Main
museli před statickou metodu StatickaMetoda uvést
název statické třídy, tj. StatickaTrida.Metoda()
ve které je metoda umístěna. Tím, že jsme ve jmenném prostoru definovali using static StatickaMetodaJinaTrida.StatickaTrida;
nemusíme u volání metodu StatickaMetoda vypisovat třídu, které patří.
using System; //Použití jmenného prostoru, kde je deklarována statická třída using static StatickaMetodaJinaTrida.StatickaTrida; namespace StatickaMetodaJinaTrida { class Trida { static void Main(string[] args) { //Volání statické metody jiné statické třídy StatickaMetoda(); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); } } //Deklarace statické třídy static class StatickaTrida { //Deklarace veřejné statické metody jiné třídy public static void StatickaMetoda() { //Výpis Console.WriteLine("Nyní probíhá statická metoda v jiné statické třídě"); } } }
Kód 1: Příklad volání statické metody statické třídy v jiné nestatické třídě – uvedení použití statické třídy ve jmenném prostoru
Na výpisu v konzole vidíme, že v rámci
metody Main proběhlo volání statické
metody jiné statické třídy – viz Výstup programu - Kód 1.
Nyní probíhá statická metoda v jiné statické třídě
Výstup programu - Kód 1: Ukázka výsledku volání statické metody statické třídy v jiné
nestatické třídě
5. Datové složky
Ve třídě můžeme použít dvě základní skupiny datových složek (anglicky „Fields“):
- Statické – uvozené klíčovým slovem static
- Nestatické – ostatní datové složky, tj. neuvozené klíčovým slovem static
Oba tyto typy datových složek mohou být uvozeny klíčovým slovem:
- const - konstantní, tj. hodnota datové složky je inicializována pouze v rámci deklarace, hodnotu nikde v kódu již nelze měnit
- readonly – pouze ke čtení, tj. hodnotu takové složky lze inicializovat buď v deklaraci, nebo v konstruktoru. Pak už nelze hodnotu v kódu nikde měnit
5.1. Nestatické datové složky
Obecná syntaxe nestatické datové složky může vypadat následovně:
modifikátorPřístupu modifConstRead datovýTyp identifikátor;
modifikátorPřístupu modifConstRead datovýTyp identifikátor = inicializace;
kde:
- modifikátorPřístupu - specifikuje přístupová práva k datové složce pomocí některé z modifikátoru přístupu
- modifConstRead - značí buď použití klíčového slova const nebo readonly
- Identifikátor - značí identifikátor datové složky. Lze také využít druhou variantu, kde použijeme inicializaci datové složky přiřazením počáteční hodnoty. inicializace proběhne bezprostředně po vytvoření instance třídy, ještě před voláním konstruktoru. Pokud ji neuvedeme, bude inicializovaná hodnota datové složky záviset na jejím datovém typu – viz Tabulka 1.
|
Datový typ |
Výchozí hodnota |
|
sbyte, byte, short, ushort, int, uint, long, ulong |
0 |
|
char |
'\x0000' |
|
float |
0.0f |
|
double |
0.0d |
|
decimal |
0.0m |
|
bool |
false |
|
object |
null |
|
string |
null |
Tabulka 1: Výchozí hodnoty podle datového typu datové složky [12]
Ilustrativní příklad AutoNestatickeDatoveSlozky
Program AutoNestatickeDatoveSlozky
- Popis: Deklarace nestatické třídy, nestatických datových složek, zpřehlednění kódu - oblasti
- Zadání: Deklarujte nestatickou třídu Auto v rámci jmenného prostoru Trida, která bude obsahovat následující datové složky:
- pocetKol – počet kol auta (konstantní volně přístupný atribut)
- kapacitaNadrze – kapacita nádrže auta (pouze pro čtení)
- sPZ – státní poznávací značka auta
- stavNadrze – aktuální stav objemu paliva v nádrži auta
- ujetoKm – aktuální stav počtu ujetých kilometrů
- spotreba – průměrná spotřeba litrů paliva na 100 km
Pro zpřehlednění kódu využijte značku oblasti (region), abyste oddělili datové složky od zbývajícího kódu - Řešení: Bude postupně probráno v následujícím textu (zdrojový kód tohoto příkladu bude využit pro sestavení konečné podoby třídy Auto)
Pojďme si zkusit deklarovat třídu Auto v rámci jmenného prostoru Trida – viz Kód 1. V této třídě využijeme rozdělení kódu do jednotlivých oblastí, abychom kód ve Visual Studio zpřehlednili. K tomu využijeme značku pro specifikace začátku oblasti #region a značku #endregion k označení konce oblasti. Tuto oblast pak můžeme v kódu sbalit (značka – nalevo od zdrojového kódu), nebo následně rozbalit (značka +) – viz kapitola Přehlednost kódu. Pokud máme delší zdrojový kód, použití oblastí může kód velmi zpřehlednit.
U datových složek
neuvádíme klíčové slovo private,
protože to je implicitně přednastaveno u každé datové složky, pokud není uveden
její modifikátor přístupu. K takovým složkám lze tedy přistoupit uvnitř
třídy. Tato třída bude obsahovat pro začátek pouze pár datových složek. pocetKol – počet kol auta je uveden pro ilustraci použití klíčového slova const,
tzn., že počet kol nesmí být změněn. Inicializace musí být provedena v rámci deklarace. kapacitaNadrze – kapacita nádrže
auta je datová složka pouze pro čtení, tzn. použití klíčového slova readonly. Tuto složku jsme inicializovali v deklaraci. Složka však nemusí být inicializována přímo v deklaraci. Tuto hodnotu lze změnit i v rámci konstruktoru.
using System; //Deklarace třídy namespace Trida { //Deklarace třídy 'Auto' class Auto { //Značka začátku oblasti s datovými složkami #region Deklarace datových složek třídy //Deklarace konstantní datové složky public const int pocetKol = 4; //Deklarace datové složky pouze pro čtení readonly double kapacitaNadrze = 60; //Deklarace privátních datových složek string sPZ; double stavNadrze; double ujetoKm; double spotreba; //Značka konce oblasti s datovými složkami #endregion //Deklarace hlavní metody static void Main(string[] args) { } } }
Kód 1: Příklad deklarace nestatických datových složek uvnitř třídy Auto včetně využití rozdělení kódu do oblasti
5.2. Statické datové složky
Statické datové složky nejsou součástí jednotlivých instancí, ale pouze třídy jako takové. Datové složky jsou společné pro všechny instance (lze je vnímat jako globální proměnné uvnitř třídy) a jsou uvozeny klíčovým slovem static. Obecná syntaxe nestatické datové složky může vypadat následovně:
modifikátorPřistupu static modifConstRead datovýTyp identifikátor;
modifikátorPřistupu static modifConstRead datovýTyp identifikátor = inicializace;
kde:
- static - klíčové slovo označující, že jde o statickou datovou složku
- Podstata u ostatní částí je stejná jako v případě nestatické datové složky – viz kapitola Nestatické datové složky.
Protože jsou statické datové složky společné pro všechny instance, existují nezávisle na tom, kolik instancí vytvoříme. Existují od okamžiku spuštění programu, před vytvořením první instance a existují i po zániku poslední instance. [3]
Ilustrativní příklad AutoStatickaDatovaSlozka
Program AutoStatickaDatovaSlozka
- Popis: Deklarace nestatické třídy, statické datové složky
- Zadání: V rámci předchozí nestatické třídy Auto v rámci jmenného prostoru Trida, deklarujte statickou celočíselnou datovou složku delkaGrafUkazatele
- Řešení: Bude postupně probráno v následujícím textu (zdrojový kód tohoto příkladu bude využit pro sestavení konečné podoby třídy Auto)
V případě třídy Auto bychom si mohli graficky zobrazit aktuální stav paliva
v nádrži auta. Tento stav zobrazíme v hranatých závorkách, mezi nimiž
umístíme znaky "#", které budou zobrazovat aktuální stav paliva uvnitř nádrže. Počet celkových dílků grafického ukazatele stavu paliva tedy pro jednotlivé instance třídy Auto bude konstantní, ale zároveň
chceme mít možnost jej modifikovat kvůli různým typům aut - objem nádrže je různý. Proto tuto datovou složku třídy Auto reprezentující počet znaků
v grafickém ukazateli budeme deklarovat jako statickou – viz Kód 1.
//Deklarace statické datové složky static int delkaGrafUkazatele;
Kód 1: Příklad deklarace statické datové složky uvnitř třídy Auto
Dalším typem statické složky je veřejná konstanta deklarovaná ve třídě – hodnota konstanty se nezmění. Datová složka sice nepoužívá klíčové slovo static, ale přesto je svojí podstatou statická. [6]6. Metody
Metody reprezentují operace, které lze provádět s instancemi nebo přímo s třídou. Volání metody představuje poslání zprávy instanci (princip OOP) nebo třídě. Metody mohou být statické nebo nestatické. U metod lze využívat různé modifikátory přístupu, stejně jako u datových složek – viz kapitola Tělo třídy. Jedná se o stejné metody práce, které jsme již dříve probírali (metody byly deklarovány v rámci třídy) – viz kniha Metody.
6.1. Statické metody
Volání v případě statické metody představuje zprávu odeslání celé třídě, a ne konkrétní instanci. I to je občas zapotřebí např. při vytvoření nového objektu, kolik bylo již vytvořeno instancí třídy apod.
Ilustrativní příklad TridaStatickaMetoda
Program TridaStatickaMetoda
- Popis: Deklarace nestatické třídy, deklarace statické metody uvnitř této třídy
- Zadání: Deklarujte nestatickou třídu Trida, která bude obsahovat hlavní metodu Main ve které budete volat statickou metodu StatickaMetoda. StatickaMetoda bude pouze obsahovat výpis textu "Nyní probíhá statická metoda ve stejné třídě"
- Řešení: Bude postupně probráno v následujícím textu
Volání statické metody z jiné metody
v rámci téže třídy není zapotřebí uvádět třídu. V následujícím kódu
vidíme, volání statické metody z hlavní statické metody Main nestatické třídy – viz Kód1. Takto
jsme volali statické metody, které jsme doposud vytvářeli.
//Deklarace třídy class Trida { //Deklarace soukromé statické metody třídy static void StatickaMetoda() { Console.Write("Nyní probíhá statická metoda ve stejné třídě"); } static void Main(string[] args) { //Volání statické metody v rámci stejné třídy StatickaMetoda(); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); } }
Kód 1: Příklad volání statické metody ve stejné nestatické třídě
Na výpisu v konzoli vidíme, že proběhlo volání statické metody v rámci stejné nestatické třídy – viz Výstup programu - Kód 1.
Nyní probíhá statická metoda ve stejné třídě
Výstup programu - Kód 1: Ukázka výsledku volání statické
metody ve stejné nestatické třídě
Ilustrativní příklad StatickaMetodaJinaTrida
Program StatickaMetodaJinaTrida
- Popis: Deklarace jiné nestatické třídy, deklarace statických metod, volání statické metody jiné nestatické třídy
- Zadání: Deklarujte dvě nestatické třídy Trida a JinaTrida. Trida bude obsahovat hlavní metodu Main, ve které budete volat statickou metodu StatickaMetoda. StatickaMetoda bude pouze obsahovat výpis textu "Nyní probíhá statická metoda ve stejné třídě"
- Řešení: Bude postupně probráno v následujícím textu
Pokud bychom ale volali metodu jiné třídy, třídu volané metody musíme uvést – viz Kód 2.
namespace StatickaMetodaJinaTrida { //Deklarace nestatické třídy class Trida { static void Main(string[] args) { //Volání statické metody z jiné třídy JinaTrida.StatickaMetoda(); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); } } //Deklarace jiné nestatické třídy class JinaTrida { //Deklarace soukromé statické metody jiné třídy public static void StatickaMetoda() { Console.WriteLine("Nyní probíhá statická metoda v jiné třídě"); } } }
Kód 2: Příklad volání statické metody jiné nestatické třídy v metodě Main implementované uvnitř nestatické třídy
Na výpisu v konzoli vidíme, že proběhlo volání metody z jiné třídy – viz Výstup programu - Kód 2.
Nyní probíhá statická metoda v jiné tříděVýstup programu - Kód 2: Ukázka výsledku volání statické metody jiné nestatické třídy
6.2. Metoda Main
Neměli bychom také opomenout důležitou statickou metodu Main, která představuje vstupní bod programu. Musí ji obsahovat alespoň jedna třída v programu, jinak kompilátor ohlásí chybu. Může ji obsahovat ale i více tříd. Pak musíme specifikovat, pomocí parametru
/main:jménoTřídykterou metodu Main má použít jako vstupní bod.
U metody Main platí následující pravidla:
Metoda Main je vstupní bod programu C#, odkud začíná provádění programu
Metoda Main musí být statická, protože se jedná o metodu na úrovni třídy. Nestatická metoda Main způsobí chybu při kompilaci
Metodu Main nelze překrýt, protože se jedná o statickou metodu. Statická metoda také nemůže být virtual nebo abstract
Přetížení metody Main je povoleno. V takovém případě pouze jedna metoda Main() se však považuje za jeden vstupní bod pro spuštění provádění programu [13]
Metoda Main musí mít jednu z následujících podob:
public static void Main();
public static void Main(string[] args);
public static int Main();
public static int Main(string[] args);
kde:
- string[] args - představuje pole řetězců, které obsahuje parametry z příkazového řádku programu. Pokud tato metoda vrací int, musí obsahovat příkaz return. Vrácená hodnota se interpretuje jako kód ukončení programu; 0 podle konvence znamená, že je vše v pořádku, nenulová čísla představují kód chyby. [3]
6.3. Nestatické metody
Nestatickou metodu (není deklarována klíčovým slovem static) lze volat pouze na konkrétní instanci třídy. Tato metoda může pracovat s datovými složkami této instance. Volání metody představuje zprávu poslanou instancí. Instanci třídy vytváříme pomocí konstruktoru – viz kapitola Konstruktory. Pokud voláme v těle nestatické metody další nestatickou metodu pro tutéž instanci, jméno instance nemusíme uvádět.
Nestatické metody třídy lze volat v nestatických metodách pomocí klíčového slova this. Jedná se o odkaz na konkrétní instanci třídy. Pomocí this lze také přistoupit k nestatickým datovým složkám.
Ilustrativní příklad TridaStatickaNestatickaSlozka
Program TridaStatickaNestatickaSlozka
- Popis: Deklarace nestatické třídy, deklarace statické a nestatické složky uvnitř této třídy
- Zadání: Deklarujte nestatickou třídu Trida, která bude obsahovat hlavní metodu Main, ve které vypíšete hodnoty deklarované statické celočíselné složky a a nestatické datové složky b, které jsou deklarovány uvnitř třídy. Deklarujte soukromou (privátní) nestatickou metodu Soucet uvnitř této třídy. Tato metoda navrací součet obou datových složek. Zkuste v metodě Main vypsat hodnoty obou datových složek, aniž by byly inicializovány, nebo by jim byly přiřazeny nějaké hodnoty
- Řešení: Bude postupně probráno v následujícím textu
V následujícím příkladu si zkusíme ve
statické metodě Main použít
statickou a nestatickou datovou složku – viz Kód 1. Jak
vidíme v prvním příkazu metody Main, šlo přistoupit k soukromé
statické datové složce. Ve druhém případu přístupu k soukromé nestatické složce bychom úspěšní nebyli. Kompilátor by zahlásil chybu. V kódu jsme také
deklarovali nestatickou soukromou metodu Soucet,
která navrací součet hodnoty ve statické a nestatické složce třídy. Jak vidíme,
tuto metodu nelze volat v rámci statické metody Main, i když je metoda Main
deklarována rámci téže třídy.
//Deklarace třídy class Trida { //Deklarace soukromé statické datové složky static int a; //Deklarace soukromé nestatické datové složky int b; //Deklarace soukromé nestatické metody třídy int Soucet() { return (a + this.b); } static void Main(string[] args) { //Použití soukromé statické datové složky Console.WriteLine(a); //Nelze použít soukromou nestatické datovou složku -> K.O. Console.WriteLine(b); //Nelze volat nestatickou metodu v rámci stejné třídy -> K.O. Console.WriteLine(Soucet()); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); } }
Kód 1: Příklad volání nestatické metody ve statické metodě, použití soukromé statické a nestatické složky
Vraťme se k příkladu našeho auta.
Ilustrativní příklad AutoUjedTrasu
Program AutoUjedTrasu
- Popis: Deklarace veřejné metody třídy Auto
- Zadání: Deklarujte veřejnou metodu bez návratu s názvem UjedTrasu ve třídě Auto, která simuluje jízdu auta. Využijte deklarovaných datových složek v předchozím programu AutoNestatickeDatoveSlozky. Tato metoda přijímá jako vstupní reálný formální parametr pocetKm, který vyjadřuje počet km, které má auto ujet. V rámci této metody je volána další metoda SpotrebujPalivo, která zajistí spotřebu paliva pro jízdu auta (datová složka stavNadrze). Je zároveň nutné zvýšit počet ujetých kilometrů auta (datová složka ujetoKm). Deklarujte metodu StavNadrzeGraficky pro zobrazení grafického prvku, který ukazuje aktuální stav paliva v nádrži (návrat metody ve formě textového řetězce). Grafický ukazatel stavu paliva budou reprezentovat znaky "#" uzavřené v hranatých závorkách
- Řešení: Bude postupně probráno v následujícím textu (zdrojový kód tohoto příkladu bude využit pro sestavení konečné podoby třídy Auto)
Účelem našeho auta je jezdit různé trasy.
K tomu využijeme veřejnou metodu bez návratu s názvem UjedTrasu - viz Kód 2.
Vzdálenost, kterou má auto ujet, je specifikována pomocí formálního parametru pocetKm. Tento vstupní parametr je předán
hodnotou. V metodě zavoláme další metodu třídy spotrebujPalivo, která zajistí, že bude spotřebováno palivo v nádrži v závislosti na počtu ujetých kilometrů. Pro volání metody jsme využili
klíčového slova this a tečkové
notace (anglicky „Dot Notation“) , ale vzhledem k tomu, že metodu deklarujeme ve stejné třídě, to
není nutné. Mohli jsme pouze uvést název metody. Ujeté kilometry se nám také
musí odrazit v celkovém počtu ujetých kilometrů auta. Musíme tedy zvýšit hodnotu soukromé datové složky ujetoKm
o hodnotu počtu ujetých kilometrů na trase pomocí operátoru +=.
//Deklarace veřejné metody pro ujetí určité trasy public void UjedTrasu(double pocetKm) { //Volání metody pro spotřebu paliva this.SpotrebujPalivo(pocetKm); //Zvýšení celkového počtu ujetých kilometrů this.ujetoKm += pocetKm; }
Kód 2: Příklad deklarace veřejné metody UjedTrasu ve třídě Auto – spotřeba paliva na ujetí trasy
Naše auto by mělo spotřebovat pohonnou látku,
pokud uskuteční jízdu. Za tímto účelem byla deklarována soukromá metoda bez návratu s názvem SpotrebujPalivo, která přijímá počet ujetých
kilometrů na jízdu auta jako formální parametr pocetKm - viz Kód 3. Na základě výpočtu
spotřebovaného paliva pomocí spotřeby a ujetého počtu kilometrů spočteme
spotřebované palivo na jízdu (vypočtenou hodnotu uložíme do lokální proměnné spotrebovanePalivo). Vypočtenou hodnotu
spotřebovaného paliva musíme zároveň odebrat z nádrže pomocí operátoru -= (z datové složky stavNadrze).
//Deklarace soukromé metody pro výpočet spotřeby a snížení množství paliva private void SpotrebujPalivo(double pocetKm) { //Výpočet spotřebovaného paliva double spotrebovanePalivo = pocetKm / 100 * this.spotreba; //Odebrání spotřebovaného paliva z nádrže this.stavNadrze -= spotrebovanePalivo; }
Kód 3: Příklad deklarace soukromé metody SpotrebujPalivo ve třídě Auto – spotřeba paliva
Další veřejnou metodou StavNadrzeGraficky kterou budeme využívat, je zobrazení grafického ukazatele, který nám ukáže, jaký je aktuální stav paliva v nádrži - viz Kód 4. Grafický ukazatel ve formě textového řetězce bude zobrazen v hranatých závorkách, mezi nimiž jsou umístěny znaky "#", které zobrazují aktuální stav paliva v nádrži auta.
Počet
dílků tedy pro jednotlivé instance třídy Auto
bude konstantní, proto je tato datová složka deklarována jako statická (datová složka delkaGrafUkazatele). Pro
přístup k této datové složce jsme využili třídu Auto a pomocí tečkové notace přistoupili k této statické
složce (vzhledem k tomu, že jsme v rámci téže třídy, lze vynechat Auto.). V další příkazu jsme ve
výrazu využili funkci Round třídy Math, která nám zaokrouhlí na celé
číslo vypočtenou procentuální hodnotu objemu paliva v nádrži vzhledem k
celkové kapacitě nádrže. Pro zapsání počtu znaků "#" (reprezentujících
objem paliva v nádrži) jsme využili cyklus s krokem, protože víme,
kolik znaků má být zapsáno uvnitř hranatých závorek ukazatele. Pro připojení
znaků do textového řetězce zprava jsme využili operátor +=. Po vypsání znaků "#" musíme doplnit mezery do konce
hranaté závorky (mezery budou zobrazovat prázdnou nádrž). K tomu jsme
využili příkaz PadRight datového
typu string, který navrací řetězec
zadané délky, ve kterém je konec řetězce vyplněn mezerami. K výslednému
textovému řetězci připojíme konečnou hranatou závorku a pomocí příkazu return
předáme výsledkem do metody, ze které byla tato metoda volána.
//Deklarace veřejné metody pro grafiku zobrazující aktuální stav paliva v nádrži public string StavNadrzeGraficky() { //Deklarace grafického výstupu - textový řetězec - počátek hranaté závorky string grafickyUkazatel = "["; /*Výpočet procentuálního objemu paliva v nádrži a výpočet počtu dílků * v grafickém ukazateli, tj. počet znaků # uvnitř hranaté závorky*/ double pocetDilku = Math.Round((this.stavNadrze / this.kapacitaNadrze) * Auto.delkaGrafUkazatele); //Cyklus na připojení znaků do grafického výstupu for (int i = 0; i < pocetDilku; i++) grafickyUkazatel += "#"; //Doplnění mezer do celkové délky grafického ukazatele grafickyUkazatel = grafickyUkazatel.PadRight(Auto.delkaGrafUkazatele + 1); //Návrat grafického textového ukazatele stavu paliva v nádrži return (grafickyUkazatel += "]"); }
Kód 4: Deklarace veřejné metody StavNadrzeGraficky ve třídě Auto – grafický ukazatel spotřeby
paliva
7. Konstruktory
Konstruktor je metoda třídy, pomocí níž vytváříme instance za běhu programu. V konstruktoru jsou inicializovány všechny datové složky, kromě těch, které byly inicializovány přímo v deklaraci datových složek třídy (za běhu programu se vytýčí část paměti operačního systému a zaberou je datovými složkami třídy a poté je zavolán konstruktor). Syntaxe deklarace konstruktoru vypadá následovně:
hlavičkaKonstruktoru inicializátor těloKonstruktoru
kde:
- hlavičkaKonstruktoru - se podobá hlavičce metody a obsahuje modifikátory přístupu kromě modifikátoru sealed nebo abstract! Hlavička nesmí obsahovat datový typ vrácené hodnoty a ani void. Název (identifikátor) konstruktoru je stejný jako název třídy, které náleží, budeme ho tedy vždy značit velkým počátečním písmenem. Pokud je deklarováno více konstruktorů, musí se lišit počtem, typem nebo pořadím typů parametrů – přetížení
- inicializátor - je nepovinný. Inicializátor může mít jednu z následujících podob:
:base(skutečnéParametry)
:this(skutečnéParametry)
U první možnosti se jedná o vyvolání konstruktoru předka (více se dozvíme později). Druhý typ inicializátoru zavolá jiný konstruktor téže třídy, před provedením těla tohoto konstruktoru. [3]
Některá třída může být docela velká, proto ji lze rozdělit do několika menších celků, které jsou uloženy v různých souborech. Toto dělení se provádí pomocí klíčového slova partial. Díky tomu lze oddělit i jednotlivé datové složky, metody a konstruktory jedné třídy.
7.1. Implicitní konstruktor
Pokud ve třídě nedefinujeme vlastní konstruktor, vytvoří si ho kompilátor sám a my ho můžeme používat. Tento výchozí konstruktor nepřijímá žádné parametry. Takový konstruktor má prázdné tělo, a pokud je veřejně přístupný (public), můžeme vytvářet instance z jiné třídy.
Konstruktor voláme při vytváření nové instance pomocí operátoru new. Syntaxe volání konstruktoru vypadá následovně:
new identifikátorTřídy()
Hodnotou takového výrazu konstruktoru je odkaz na vytvořenou instanci třídy. Konstruktory se nedědí.
Vraťme se k příkladu třídy Auto.
Ilustrativní příklad AutoImplicitniKonstruktor
Program AutoImplicitniKonstruktor
- Popis: Vytvoření instance třídy pomocí implicitního konstruktoru
- Zadání: Vytvořte novou instanci třídy autoOctavia předchozí nestatické třídy Auto
- Řešení: Bude postupně probráno v následujícím textu
Pomocí následujícího kódu
vytvoříme instanci této třídy pomocí zavolání implicitního (výchozího)
konstruktoru – viz Kód 1. Tento
typ konstruktoru voláme, pokud si nedeklarujeme vlastní konstruktor ve třídě.
Pokud datové složky nebyly přímo inicializovány při deklaraci, jsou jim
automaticky nastaveny výchozí předdefinované hodnoty – viz kapitola Nestatické datové složky - Tabulka 1.
Instanci pojmenujeme autoOctavia.
//Vytvoření instance třídy Auto pomocí volání implicitního konstruktoru Auto autoOctavia = new Auto();
Kód 1: Příklad vytvoření instance třídy Auto pomocí implicitního konstruktoru
7.2. Vlastní konstruktor
V minulém případě implicitního konstruktoru jste asi vnímali, že by bylo vhodné si vytvořit vlastní konstruktor, kterému mu předáme skutečné parametry a těmi naplníme jednotlivé datové složky vytvářené instance. K jednotlivým datovým složkám třídy a zároveň k jejím metodám přistupujeme pomocí tečkové notace (anglicky „Dot Notation“). Pomocí klíčového slova this přistupujeme do obsahu třídy.
Syntaxe volání vlastního konstruktoru vypadá následovně:
new identifikátorTřídy(skutečnéParametry)
kde:
- identifikátorTřídy - název třídy, podle které bude generována instance
- skutečnéParametry - uvádění skutečných parametrů není povinné (konstruktor nemusí přijímat žádné parametry, záleží na nás, jak budeme specifikovat vlastní konstruktor). Hodnotou takového výrazu konstruktoru je odkaz na vytvořenou instanci třídy. Implicitní ani vlastní konstruktory se nedědí.
Ilustrativní příklad AutoVlastniKonstruktor
Program AutoVlastniKonstruktor
- Popis: Vytvoření instance třídy pomocí vlastního konstruktoru, inicializace datových složek třídy
- Zadání: Vytvořte novou instanci třídy autoOctavia předchozí nestatické třídy Auto pomocí vlastního konstruktoru. Konstruktor bude přijímat jako formální parametry kapacitaNadrze, stavNadrze, spotreba (datové typy těchto parametrů jsou shodné s datovými typy datových složek třídy Auto). Nastavte délku grafického ukazatele paliva (nádrž je plná) na 20 znaků. Počáteční stav najetých km (ujetoKm) je 0. V hlavní metodě vytvořte instanci třídy Auto s názvem autoOctavia se skutečnými parametry 55, 50, 0 (kapacitaNadrze, stavNadrze, spotreba). Vypište v Main kapacitu nádrže, stav ujetých km, objem nádrže a zobrazte objem nádrže pomocí grafického prvku. Ujeďte s autem 100 km a proveďte znovu všechny výpisy
- Řešení: Auto_Main
(spojení programů AutoNestatickeDatoveSlozky + AutoStatickaDatovaSlozka + AutoUjedTrasu + AutoVlastniKonstruktor)
V konstruktoru nazvaným stejným jménem jako třída, tedy Auto, jsme využili formální
parametry pro inicializaci datových složek. První parametr kapacitaNadrze je určen k definování skutečného objemu nádrže auta. Výchozí kapacitu nádrže jsme již dříve inicializovali přímo v deklaraci této
datové složky (přiřadili jsme jí hodnotu 60 litrů). Každé auto však bude mít jinou kapacitu
nádrže, proto tato datová složka má modifikátor přístupu typu readonly, takže ji lze přiřadit jinou
hodnotu v konstruktoru, což právě provedeme pomocí hodnoty ve formálním parametru
kapacitaNadrze. Dalším formálním
parametrem stavNadrze jsme nastavili
objem paliva v nádrži auta. Jako poslední jsme použili parametr spotreba a tím jsme nastavili deklarovanou
spotřebu litrů paliva na 100 km jízdy autem.
//Deklarace vlastního konstruktoru třídy public Auto(double kapacitaNadrze, double stavNadrze, double spotreba) { //Inicializace soukromých datových složek this.stavNadrze = stavNadrze; this.ujetoKm = 0; this.spotreba = spotreba; //Inicializace statické datové složky delkaGrafUkazatele = 20; //Inicilazace datové složky (readonly) pomocí parametru this.kapacitaNadrze = kapacitaNadrze; }
Kód 1: Příklad deklarace vlastního konstruktoru třídy Auto
Pokud v kódu používáme stejný název
parametru, jako je název datové složky, nelze vynechat klíčové slovo this – viz Kód 2. Kompilátor
oznámí: Přiřazení proběhlo u stejné proměnné. Kompilátor totiž neví, že
identifikátor vlevo od přiřazení byl zamýšlen jako datová složka a vpravo jako
hodnota parametru. Formální parametr se stejným názvem jako datová složka totiž
skryje datovou složku uváděnou v příkazech metody (je upřednostněn
formální parametr – lokálnost).
/*Inicilazace datové složky (readonly) pomocí parametru * s vynecháním this. Kompilátor neví, že se jedná o přiřazení * hodnoty parametru do datové složky -> K.O. */ kapacitaNadrze = kapacitaNadrze;
Kód 2: Příklad důsledku nepoužití klíčového slova this při stejném názvu datové složky a formálního parametru
V následujícím kódu je volán vlastní
konstruktor Auto třídy Auto, kterému jsou předány skutečné parametry 55, 50, 5, jejichž hodnota je
přiřazena do příslušných datových složek (kapacitaNadrze, stavNadrze, spotreba). Pomocí klíčového slova new je vytvořena instance třídy, která je přiřazena objektu autoOctavia třídy Auto. Po proběhnutí těla
konstruktoru tedy náš objekt autoOctavia
nebude mít kapacitu nádrže 60 litrů, ale jen 55 litrů. Dále jsme nastavili objem nádrže na 50 litrů a definovali spotřebu 5 litrů paliva na 100 km – viz Kód 3.
//Deklarace hlavní metody static void Main(string[] args) { /*Vytvoření instance třídy Auto pomocí volání vlastního konstruktoru * včetně předání skutečného parametru*/ Auto autoOctavia = new Auto(55, 50, 5); Console.WriteLine("Kapacita nádrže: {0}", autoOctavia.kapacitaNadrze); Console.WriteLine("Ujeto km: {0}", autoOctavia.ujetoKm); Console.WriteLine("Stav nádrže: {0}", autoOctavia.stavNadrze); Console.WriteLine("Stav nádrže: {0}", autoOctavia.StavNadrzeGraficky()); autoOctavia.UjedTrasu(100); Console.WriteLine("Auto Octavia:"); Console.WriteLine("Ujeto km: {0}", autoOctavia.ujetoKm); Console.WriteLine("Stav nádrže: {0}", autoOctavia.stavNadrze); Console.WriteLine("Stav nádrže: {0}", autoOctavia.StavNadrzeGraficky()); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); }
Kód 3: Deklarace hlavní metody třídy Auto - vytvoření instance autoOctavia a výpis hodnot datových složek
V následujícím výpisu v konzole vidíme stav autoOctavia před jízdou a po ujetí 100 km - viz Výstup programu - Kód Auto_Main.
Kapacita nádrže: 55
Ujeto km: 0
Stav nádrže: 50
Stav nádrže: [################## ]
Auto Octavia:
Ujeto km: 100
Stav nádrže: 45
Stav nádrže: [################ ]
Výstup programu - Kód Auto_Main
7.3. Statický konstruktor
Deklarace třídy může obsahovat také jeden statický konstruktor (použití modifikátoru static). Takový konstruktor slouží k inicializaci statických složek třídy (včetně složek s modifikátorem readonly), které nejsou inicializovány přímo v deklaraci. Takové konstruktory nesmí obsahovat žádné parametry. Ani statický konstruktor se nedědí. [3]
8. Dekonstrukce instance
Konstruktor jsme použili pro vytvoření instance třídy a inicializaci datových složek, které instance obsahuje. Dekonstrukce nám umožní prozkoumat instanci a extrahovat hodnoty z jejích datových složek. Obecná syntaxe pro dekonstrukci je následující:
modifikátorPřístupu void Deconstruct (výstupníParametry) { těloDekonstrukce }
kde:
modifikátorPřístupu - je stejný jako metod a datových složek
void - klíčové slovo říkající, že metoda nemá žádný výstup
Deconstruct - povinný název metody pro dekonstrukci instance
výstupníParametry - musí existovat minimálně jeden výstupní parametr. výstupníParametry musí být uvozeny klíčovým slovem out, tzn., že hodnoty jim budou přiřazeny teprve v metodě – viz kapitola Výstupní parametry. Všem výstupním parametrům musí být přiřazena hodnota
těloDekonstrukce - je uzavřeno do složených závorek
Pro ilustraci uveďme jednoduchý příklad metody dekonstrukce instance bodu na ploše.
Ilustrativní příklad DekonstrukceInstance
Program DekonstrukceInstance
- Popis: Dekonstrukce objektu třídy
- Zadání: Vytvořte instanci bodu na ploše bodNaPlose třídy Bod pomocí vlastního konstruktoru. Instance je definována pomocí dvou reálných souřadnic x a y (soukromé datové složky). V hlavní metodě Main konstruktoru předejte x = 5 a y = 7. Dekonstruujte instanci a vypište v Main hodnotu jejích datových složek
- Řešení: Bude postupně probráno v následujícím textu
Třídu jsme nazvali Bod. Každá instance (každý bod) je definována pomocí dvou
souřadnic: x,y. Tyto reálné hodnoty
souřadnic x-ové a y-ové souřadnice bodu uložíme do
soukromých datových složek – viz Kód 1.
//Deklarace třídy pro generování bodu na ploše class Bod { //Deklarace soukromých datových složek - souřadnice bodu double x, y; }
Kód 1: Deklarace třídy Bod a deklarace soukromých datových složek třídy
Pro třídu bod jsme deklarovali konstruktor,
který přijímá jako formální parametry x-ové
a y-ové souřadnice bodu. Pomocí
těchto hodnot provedeme inicializaci datových složek instance – viz Kód 2.
//Deklarace konstruktoru třídy public Bod(double x, double y) { //Incializace datových složek pomocí hodnoty parametrů this.x = x; this.y = y; }
Kód 2: Deklarace vlastního konstruktoru třídy Bod
V následujícím kódu je zobrazena metoda
dekonstrukce instance třídy Bod.
Jedná se o extrahování hodnot uložených v jednotlivých datových složkách
instance. Tyto hodnoty jsou v těle metody přiřazeny do výstupních
parametrů metody – viz Kód 3.
//Metoda dekonstrukce instance třídy - extrahování datových složek public void Deconstruct(out double x, out double y) { //Přiřazení hodnoty datové složky do výstupního parametru x = this.x; y = this.y; }
Kód 3: Deklarace dekonstruktoru třídy Bod
Zkusme si nyní ve statické metodě Main pomocí konstruktoru vygenerovat bod na ploše o
reálných souřadnicích. Nejprve v metodě Main deklarujeme lokální proměnné x a y stejného datového typu jako datové složky třídy. Instanci třídy Bod pojmenujeme bodNaPlose. Tuto instanci vytvoříme
pomocí deklarovaného konstruktoru. V dalším kroku použijeme metodu
dekonstrukce instance, pomocí níž získáme hodnoty souřadnic bodu, které uložíme
do lokálních proměnných x a y. Tyto hodnoty následně vypíšeme – viz Kód 4.
static void Main(string[] args) { //Deklarace reálných proměnných double x, y; //Zavolání konstruktoru a předání parametrů Bod bodNaPlose = new Bod(5, 7); //Metoda dekonstrukce instance bodNaPlose.Deconstruct(out x, out y); Console.WriteLine("Bod na ploše - dekonstrukce:"); //Výpis hodnot získaných z datových složek instance Console.WriteLine("Hodnota x: {0}", x); Console.WriteLine("Hodnota y: {0}", y); //Čekání na stisk klávesy -> ukončení programu Console.ReadKey(); }
Kód 4: Hlavní metoda Main - generování instance bodNaPlose třídy Bod a zavolání dekonstrukce objektu
Na výpisu v konzoli vidíme, že byly vypsány hodnoty datových složek instance bodNaPlose zkopírované do lokálních proměnných – viz Výstup programu - Kód 4.
Bod na ploše - dekonstrukce:
Hodnota x: 5
Hodnota y: 7
Výstup programu - Kód 4: Výpis hodnot lokálních proměnných obsahující hodnoty datových
složek
K dekonstrukci instance lze také využít
již nám známý postup návratu více výstupů – viz kapitola Návrat více výstupů. V kódu
vidíme, že vpravo od operátoru přiřazení je umístěna instance dané třídy. Vlevo
jsou umístěny lokální proměnné, do nichž bude uložena hodnota. C# totiž sám na
pozadí spustil dekonstruktor (tato metoda musí být deklarována, jinak metoda
s více výstupy nebude fungovat) a naplnil hodnoty do proměnných. Proměnné jsme
na začátku před použitím dekonstrukce instance ještě vynulovali, abychom si byli
jisti, že metoda návratu více výstupů funguje – viz Kód 5.
//Volání metody pro návrat více výstupů (x, y) = bodNaPlose; //Výpis hodnot získaných z datových složek instance Console.WriteLine("Bod na ploše - více výstupů:"); Console.WriteLine("Hodnota x: {0}", x); Console.WriteLine("Hodnota y: {0}", y);
Kód 5: Příklad volání metody pro návrat více výstupů
9. Destruktor
Každá třída může obsahovat pouze jeden destruktor (anglicky „Destructor“). Tato metoda má za úkol připravit instanci k zániku – tedy uvolnit systémové prostředky, které instance v průběhu své existence získala např. uzavřít databázová spojení, zavřít otevřený soubor apod.
Deklarace destruktoru má tvar:
~jménoTřídy( ) těloDestruktoru
kde:
- vlnovka (neboli tilda – na české klávesnici Alt+126) se umísťuje před jméno třídy. Destruktor nesmí mít parametry a jeho deklarace nesmí obsahovat žádný modifikátor přístupu a ani typ návratové hodnoty. Destruktor nelze volat ve zdrojovém kódu. Prostředí .NET Framework si ho zavolá těsně před tím, než automatická správa paměti (Garbage Collector) odstraní instanci z paměti. Destruktory se nedědí. [3]
10. Anonymní třída
Anonymní třída je třída, která nemá jméno. Sice to může znít zvláštně, ale tento typ třídy je využíván u dotazových výrazů. Anonymní třída se vytváří pomocí klíčového slova new a do složených závorek se umístí definice datových složek a jejich hodnot. U anonymních tříd platí následující pravidla:
- Anonymní třídy musí obsahovat pouze veřejné datové složky
- Datové složky musí být inicializované
- Datové složky nesmí být statické a nelze specifikovat metody [6]
Ilustrativní příklad AnonymniTrida
Program AnonymniTrida- Popis: Deklarace anonymní třídy, vytvoření instance, výpis datových složek
- Zadání: Deklarujte anonymní třídu se dvěma datovými složkami - Jmeno (textový řetězec) a Vek (celé číslo). Do datových složek anonymní instance s názvem anonymniInstance uložte hodnoty "Pavel", 38
- Řešení: AnonymniTrida_Main
Následující kód vytváří
anonymní instanci se dvěma datovými složkami - viz Kód 1. Vzhledem k tomu, že jsme nedefinovali žádné datové typy, kompilátor
si je musí odvodit sám z jejich inicializace. Při definici anonymní třídy
vygeneruje kompilátor pro tuto třídu její vlastní název (ten se však
nedozvíme). Asi si říkáte, jak tedy máme deklarovat instanci této třídy, když
neznáme její jméno? Odpověď zní, využijeme deklaraci implicitně typované
proměnné, tzn., že použijeme klíčové slovo var
– viz kapitola Proměnná
– přiřazovací příkaz, výraz. Deklarace takové instance
by tedy vypadala následovně. Klíčové slovo var
způsobí, že kompilátor si vytvoří proměnnou stejného datového typu, jako je
výraz použitý při její deklaraci (v tomto případě je typem výrazu jakékoliv
jméno, které použil kompilátor pro vygenerování naší anonymní třídy).
//Deklarace instance anonymní třídy včetně inicializace datových složek anonymní instance var anonymniInstance = new { Jmeno = "Pavel", Vek = 38 };
Kód 1: Vytvoření instance anonymní třídy
K datovým složkám instance lze přistoupit pomocí tečkové notace –
viz Kód 2.
//Výpis hodnoty první datové složky instance anonymní třídy Console.WriteLine("Jméno: {0}", anonymniInstance.Jmeno); //Výpis hodnoty druhé datové složky instance anonymní třídy Console.WriteLine("Věk: {0}", anonymniInstance.Vek);
Kód 2: Výpis hodnot v datových složkách instance anonymní třídy
Na výpisu v konzoli můžeme vidět vypsané hodnoty obou datových složek instance anonymní třídy - viz Výstup programu - Kód AnonymniTrida_Main.
Anonymní instance:
Jméno: Pavel
Věk: 38
Výstup programu - Kód AnonymniTrida_Main: výpis hodnot datových složek instance anonymní třídy
Další anonymní instance této třídy lze
vytvářet stejným způsobem – viz Kód 3. Kompilátor
používá názvy, typy, počet a pořadí datových složek ke stanovení, zda dvě
instance anonymní třídy mají stejný typ.
//Deklarace další instance anonymní třídy včetně inicializace datových složek var jinaAnonymniInstance = new { Jmeno = "Petr", Vek = 39 };
Kód 3: Příklad vytvoření další instance anonymní třídy
Pořadí plnění datových složek jsme záměrně
prohodili za účelem testování – viz Kód 4. To je
však jedno v tomto případě jedno, protože jednotlivé datové složky jsou
pojmenovány a mají shodný datový typ.
//Prohození datových složek instance anonymní třídy var jinaAnonymniInstance = new { Vek = 39, Jmeno = "Petr" };
Kód 4: Příklad vytvoření další instance anonymní třídy – prohození datových složek
Anonymní instanci lze přiřadit jiné anonymní
instanci – viz Kód 5. Pokud
se ale myslíte, že se vytvoří kopie takové instance, není to pravda! Vytvoří se
pouze odkaz na jednu stejnou instanci. Vše co provádíte anonymniInstance se provádí i s jinaAnonymniInstance.
//Přiřazení instance jiné instanci jinaAnonymniInstance = anonymniInstance;
Kód 5: Příklad přiřazení instance třídy jiné instanci anonymní třídy
11. Stručné shrnutí
| Pro ... | ... uveďte následující |
|---|---|
| Deklarace soukromé datové složky třídy | V těle třídy uveďte modifikátor přístupu datové složky, její datový typ a jméno datové složky, např.: class Bod { private int x; } |
| Přístup k soukromé datové složce třídy | Pro přístup k seznamu atributů třídy, např. datových složek třídy, použijte klíčové slovo this následovaný tečkou a uveďte název datové složky, např.:this.x = x; |
| Deklarace soukromé statické datové složky třídy | V těle třídy uveďte modifikátor přístupu datové složky, za ním uveďte klíčové slovo static, datový typ datové složky a jméno datové složky, např.: class Bod { ... private static int pocetBodu = 0; } |
| Přístup k soukromé statické datové složce třídy | Pro přístup k seznamu atributů třídy, např. statické datové složky třídy, použijte název třídy následovaný tečkou a uveďte název statické datové složky, např.:Bod.pocetBodu++;
|
| Deklarace konstantní datové složky | V těle třídy uveďte modifikátor přístupu datové složky, za ním uveďte klíčové slovo const, dále uveďte datový typ složky a jméno této datové složky, např.: class Bod { ... public const double PI = ...; } |
| Deklarace konstruktoru | Uveďte metodu, jejíž název je shodný s názvem třídy. Tato metoda nemá žádný návratový typ (ani void), např.:class Bod { ... public Bod(int x, int y) { ... } } |
| Zavolání konstruktoru | Uveďte klíčové slovo new a název konstruktoru s příslušnými parametry, např.:Bod novyBod = new Bod(0, 0); |
| Deklarace statické metody | Uveďte před deklarací metody klíčové slovo static, např.: class Bod { public static int PocetBodu() { ... } } |
| Zavolání statické metody | Uveďte název třídy následovaný tečkou a za ní uveďte název statické metody, např.:int pocetObjektu = Bod.PocetBodu(); |