Pojďte s námi programovat !

1. 11. 2008

Sdílet

V listopadové lekci programátorského seriálu jazyka C# 3.0 se naučíme pracovat s metodami, které budou definovány ...


V listopadové lekci programátorského seriálu jazyka C# 3.0 se naučíme pracovat s metodami, které budou definovány v tělech struktur.


Struktury a metody

Metoda je v programovacím jazyce C# 3.0 konstrukce, která seskupuje množinu programových příkazů. Tyto příkazy jsou zpravidla inteligentně navrženy tak, aby plnily vymezený úkol. Po technické stránce je metoda samostatnou entitou, která implementuje algoritmy provádějící jistou, předem naprogramovanou činnost. Jako programátoři v jazyce C# 3.0 jsme se už s jednou metodou setkali: v minulé lekci jsme se seznámili s konstruktorem, přičemž jsme si řekli, že konstruktor je speciální metoda inicializačního charakteru, která se může vyskytovat v tělech struktur a tříd (o třídách jako o abstraktních objektových odkazových datových typech pojednáme až v jedné z příštích lekcí tohoto seriálu). Nyní naše znalosti rozšíříme, poněvadž se naučíme, jak obohatit deklaraci struktury o naši vlastní, tedy uživatelsky definovanou metodu.
Předpokládejme, že budeme chtít naprogramovat jednoduchou počítačovou aplikaci, která bude simulovat ježdění s automobilem. Za tímto účelem deklarujeme strukturu Automobil s následujícím syntaktickým obrazem:

S naší dosavadní znalostní bází dovedeme spolehlivě přečíst deklaraci struktury. Na první pohled postřehnutelnou změnou je definice datových členů struktury jako soukromých (povšimněme si užití přístupového modifikátoru private). Struktura Automobil pracuje se třemi soukromými datovými členy, jimiž jsou: model automobilu, cena automobilu a výkon motoru automobilu měřený v kilowattech (KW). Když později založíme instanci struktury, tato bude virtuálním automobilem, jenž bude charakterizován svým modelem, cenou a motorickým výkonem. Je nasnadě, že u reálného automobilu bychom dokázali identifikovat desítky, ne-li stovky dalších atributů, ovšem pro potřeby výkladu a spřízněných praktických experimentů si vystačíme s uvedenou trojicí datových členů.
Zatímco v předcházející lekci jsme datové členy struktury ponechali veřejně přístupné, nyní je definujeme jako soukromé. Tímto úkonem spějeme k důsledné aplikaci jednoho z pilířů objektově orientovaného programování, jímž je ukrývání dat. Jelikož data instance struktury budou uskladněna v soukromých datových členech, budou rovněž soukromá. To znamená, že k těmto datům nebude mít přístup nikdo jiný, než instance struktury sama. Veřejně přístupný instanční konstruktor je nám také dobře známý, poněvadž s jeho významem, syntaktickou stavbou a praktickou aplikací jsme se již obeznámili. Konstruktor je parametrický (připomeňme, že u struktur se s jinou variantou konstruktoru ani nesetkáme), přičemž pracuje se třemi formálními parametry, které jsou připraveny uchovat stejný počet vstupních argumentů. Konstruktor je zodpovědný za korektní inicializaci založené instance struktury.

Když budeme instanciovat strukturu ope-rátorem new, získáme nový virtuální automobil se zadanými atributy:

V tuto chvíli na zásobníku programového vlákna existuje jeden automobil. Skutečnost, že datové členy instance struktury jsou soukromé, má citelné praktické implikace: není totiž možné přistupovat k nim přímo pomocí tečkového (.) operátoru.
Budeme-li chtít, můžeme samozřejmě vytvořit tolik aut, kolik budeme potřebovat:

Abychom poskytli klientům naší instance struktury možnost přistupovat k datové sekci instance, vložíme do deklarace struktury tzv. přístupové metody. Jelikož klientský kód bude moci nejenom získávat (neboli číst) hodnoty soukromých datových členů instance struktury, ale také je bezpečným způsobem upravovat (čili zapisovat do datových členů nové hodnoty), musíme každý datový člen asociovat s dvojicí přístupových metod. Jedna z metod bude sloužit k získání aktuální hodnoty soukromého datového členu, zatímco smysl existence druhé metody bude spočívat ve změně aktuální hodnoty datového členu podle přání klienta. Matematicky řečeno, vztah mezi soukromým datovým členem a s ním provázaných přístupových metod lze vyjádřit relací typu 1 : n, kde n = 2, tedy každému datovému členu budou příslušet právě dvě přístupové metody.
Upravená deklarace struktury Automobil s integrovanou množinou přístupových metod vypadá takto:

Ze syntaktického hlediska se přístupová metoda přenáší na konstruktor, ovšem s jedním rozdílem: přístupové metody (a metody obecně) mohou pracovat s tzv. návratovou hodnotou (to konstruktor nedokáže). Návratová hodnota je hodnota jistého datového typu, kterou nám metoda nabídne poté, co vykoná svou činnost. Generický model jakékoliv metody (a tedy i přístupové metody) smíme formalizovat následujícím způsobem:

kde:

PM je přístupový modifikátor metody.

TNH je datový typ návratové hodnoty metody.

IM je identifikátor metody.

T1, T2, …, Tn jsou datové typy formálních parametrů metody.

Fp1, Fp2, …, Fpn jsou identifikátory formálních parametrů metody.

[ ] jsou symboly, které uvádějí nepovinnou syntaktickou součást metody.

Při analýze libovolné metody se zaměřujeme na dvě hlavní syntaktické části: hlavička metody a tělo metody. Jak již víme, hlavička metody představuje první řádek v definici metody. Tělo metody je tvořeno blokem programových příkazů, jenž je ohraničen složenými závorkami ({ }).
Každá metoda má svůj identifikátor (název), který volíme tak, abychom již z pojmenování metody mohli vydedukovat její pracovní zaměření. Obor dosažitelnosti (neboli viditelnosti) metody je dán použitým přístupovým modifikátorem. Nebude-li stanoveno jinak, pak budeme chtít definovat naše metody jako veřejné, s přístupovým modifikátorem public. Jestliže je metoda veřejná, může k ní přistupovat jakýkoliv klientský programový kód.
U metod, jež pracují s návratovou hodnotou, specifikujeme v hlavičce metody datový typ návratové hodnoty. Tímto datovým typem smí být cokoliv z:

primitivních hodnotových datových typů,

primitivních odkazových datových typů,

uživatelsky deklarovaných hodnotových datových typů,

uživatelsky deklarovaných odkazových datových typů.

Jestliže styl práce metody nezakládá přirozenou potřebu pro vypočet a vrácení návratové hodnoty, sestavíme metodu bez návratové hodnoty.

Když budeme chtít v programovacím jazyce C# 3.0 vytvořit metodu bez návratové hodnoty, vložíme na místo datového typu návratové hodnoty (TNH) v hlavičce metody klíčové slovo void. Toto klíčové slovo říká, že metoda nevrací žádnou návratovou hodnotou.
Podle toho, zda metody pracují s kvanty vstupních dat (argumenty), nebo ne, je dělíme na parametrické a bezparametrické. Věnujme se nejprve bezparametrickým metodám. Je-li metoda bezparametrická, pak se za jejím identifikátorem nacházejí prázdné závorky. Za těchto okolností říkáme, že metoda disponuje prázdnou signaturou. Vzhledem k absenci formálních parametrů u bezparametrických metod je zřejmé, že tato kategorie metod nepotřebuje pro svou práci žádné argumenty. Na druhou stranu, pokud je metoda parametrická, pracuje s množinou formálních parametrů. Formální parametr, jak je známo, reprezentuje lokální proměnnou determinovaného datového typu, která slouží pro úschovu argumentu. O metodě mluvíme jako o parametrické v okamžiku, kdy obsahuje aspoň jeden správně definovaný formální parametr. Ačkoliv ve všeobecnosti uvažujeme o kolekci formálních parametrů jako o konečné a neprázdné množině entit, v praktickém nasazení nejsou obvyklé metody s příliš mnoha formálními parametry. Pro úplnost podotkněme, že mezi argumenty a formálními parametry existuje relace typu 1 : 1.
V těle metody se mohou nacházet jakékoliv programové příkazy, jejichž prostřednictvím jsou implementovány požadované algoritmy.
Sada přístupových metod umístěných v deklaraci naší struktury Automobil obsahuje jak bezparametrické, tak i parametrické metody. Všechny metody jsou veřejně přístupné (modifikátor public). S každým datovým členem je spřízněna dvojice metod s identifikátory Získat_X, resp. Nastavit_X, kde X je název datového členu. Práci přístupových metod objasníme na metodách ZískatModel a Na-stavitModel.
První metoda nám na požádání poskytne textovou reprezentaci modelu automobilu. Z uvedeného plyne, že metoda bude pracovat s návratovou hodnotou, jejímž typem bude string. Poněvadž smyslem metody je nabídnout nám informace o modelu auta, nepotřebuje žádné formální parametry, tudíž je bezparametrická. V těle metody je zapsán příkaz return, jehož pomocí specifikujeme návratovou hodnotu (tou je hodnota uložená v datovém členu model). Druhá metoda (NastavitModel) nám umožňuje pozměnit model automobilu. Aby mohla metoda pozměnit stávající model auta, musí vědět, jakým modelem bude nahrazen ten aktuální. Nový model auta tedy představuje argument (kvantum vstupních dat). Za účelem archivace argumentu potřebuje metoda formální parametr, z čehož vyplývá, že jde o parametrickou metodu. V těle metody poté dochází k reinicializaci soukromého datového členu: do datového členu je přiřazen nový název automobilu. Přístupová metoda NastavitModel nevrací žádnou návratovou hodnotu, což indikujeme užitím klíčového slova void nalevo od identifikátoru metody. Podle nastíněného schématu pracují rovněž zbývající dvojice metod ZískatCenu ? NastavitCenu a ZískatVýkon ? NastavitVýkon.

Přejděme nyní k ukázce použití přístupových metod:

Zrozená instance struktury je inicializovaná implicitním bezparametrickým konstruktorem, který automaticky vygeneruje kompilátor. Tento konstruktor zabezpečí východiskovou inicializaci soukromých datových členů instance struktury podle následujícího schématu: datový člen mo-del bude obsahovat nulový odkaz (null) a členům cena a výkon budou přiřazeny nulové inicializační hodnoty. Přestože implicitní inicializace nevytváří žádné kon-krétní auto, není situace ztracena, neboť uživatelsky definovanou inicializaci provedou přístupové metody pro nastavení datových členů instance struktury. Za zmínku stojí, že k veřejným metodám instance struktury přistupujeme pomocí tečkového operátoru. Zmíněné metody jsou parametrické, a proto do závorek za jejich identifikátory pokaždé zapisujeme argument, jenž odpovídá modelu, ceně nebo výkonu. Příkazu mojeAuto.M(A);, kde M je název metody a A je argument, říkáme volání (ekvivalentně též aktivace nebo spuštění) metody M. Když je metoda zavolána, jsou provedeny všechny programové příkazy, které se nacházejí v jejím těle. Samozřejmě, jestliže je volaná metoda parametrická, dochází ještě před zpracováním příkazů situovaných v jejím těle k inicializaci všech přítomných formálních parametrů (tyto jsou inicializovány argumenty specifikovanými v příkazu, jenž metodu volá).
Jakmile je instance struktury díky přístupovým metodám náležitě inicializována, smíme s ní dále pracovat. Pro zobrazení základních informací o automobilu na obrazovce počítače voláme bezparametrické přístupové metody.
Když se rozhodneme definovat datové členy struktury jako soukromé (což bychom měli až na několik triviálních případů dělat vždy), musíme s každým členem provázat dvojici přístupových metod. Jenom tak mohou klienti objektu (instance struktury) pracovat s jeho daty. Samozřejmě, metody instancí struktur nemusí být pouze přístupové. Kdepak, ve skutečnosti smíme vytvářet různorodé metody, které modelují činnosti virtuálního objektu. Kupříkladu když chceme použít automobil, musíme jej nastartovat. Této aktivitě nechť odpovídá metoda Nastartovat, která má tuto podobu:

Metoda Nastartovat je veřejně přístupná, bezparametrická a nevrací žádnou návratovou hodnotu. Je to jedna z nejjednodušších metod, s jakými se můžete při programování setkat. V těle metody je umístěn jeden příkaz, který oznamuje, že motor auta byl úspěšně uveden do chodu. Máme-li auto nastartované, můžeme se vydat na projížďku. Svezení v našem imaginárním voze obstarává metoda VydatSeNaCestu:

Metoda VydatSeNaCestu je parametrická metoda bez návratové hodnoty. První formální parametr (rychlost typu int) bude uchovávat průměrnou rychlost, kterou pojede náš vůz. Druhý formální parametr (čas typu int) pojednává o délce projížďky.
Praktické použití právě definovaných metod je potom už snadné:

Na výstupu zjistíme, že pokud pojedeme průměrnou rychlostí 60 km/h, urazíme za 2 hodiny 120 kilometrů cesty.

Tentokrát naleznete doplňkový materiál k tomuto kurzu. Jedná se o test, díky němuž si můžete ověřit své znalosti algoritmizace a programování v jazyce C# 3.0. Pokud máte o téma programování hlubší zájem, rozhodně tomuto materiálu věnujte pozornost. 8 0632/CZ ?


Věnujme prosím pozornost volání metody WriteLine třídy Console. V textovém řetězci, jenž je obklopen dvojitými uvozovkami, detekujeme tři zástupné symboly {x}, kde x ? <0, 2>. Tyto zástupné symboly budou nahrazeny hodnotami argumentů (proměnných), které zadáváme za určením textového řetězce. Prvním argumentem je proměnná rychlost, druhým proměnná čas a třetím argumentem je hodnota aritmetického výrazu rychlost * čas. U prvých dvou argumentů je proces jednoduchý: prostě se použijí aktuální hodnoty dotyčných proměnných. U třetího argumentu se nejprve vyhodnotí aritmetický výraz, přičemž předmětem dalšího zpracování bude jeho hodnota.
Typ návratové hodnoty uvedený v hlavičce metody se nemusí vždy nezbytně shodovat s typem hodnoty, která bude metodou vrácena. Pro lepší orientaci označme typ návratové hodnoty situovaný v hlavičce metody identifikátorem T1, a typu hodnoty, která bude vrácená metodou, přisuďme identifikátor T2. Pokud dovede kompilátor implicitně konvertovat typ T2 na typ T1, tak je vše v pořádku, lze užít různé, i když vzájemně kompatibilní datové typy. Kupříkladu typem T2 hodnoty, která vzejde z aritmetického výrazu, může být short. Pokud je typem T1 typ int, nevzniknou žádné kolizní stavy. Vždyť proč by také měly: jakákoliv platná hodnota typu short může být interpretována rovněž pomocí typu int. V programování se metody dělí na dvě základní kategorie:

1. Metody s návratovou hodnotou.
2. Metody bez návratové hodnoty.

Pokud budeme uvažovat o metodě, která nám po splnění svého úkolu podá informaci o průběhu celého výpočetní procesu, pak ji navrhneme tak, aby nám návratovou hodnotu vracela. Pomozme si praktickým příkladem: řekněme, že chceme vypočítat medián z posloupnosti pseudonáhodných celých čísel. V zájmu automatizace výpočtu sestrojíme metodu s názvem VypočítatMedián, kterou opatříme logikou pro zjištění mediánu jako jedné ze středních hodnot analyzovaného statistického souboru dat. Když metoda medián určí, vrátí nám jej v podobě své návratové hodnoty. Poskytnutou návratovou hodnotu metody VypočítatMedián následně uložíme do proměnné a můžeme ji použít například při dalších matematických operacích.
Ze sémantického hlediska jsou metody bez návratové hodnoty totožné s procedurami, které se vyskytují v jiných programovacích jazycích (například ve Visual Basicu to jsou procedury Sub). O nemožnosti přímého přístupu k soukromým datovým členům instance struktury se můžeme přesvědčit velice jednoduše. Stačí, když do těla hlavní metody Main, ihned za instanciační příkaz, vložíme nový řádek a pokusíme se zapsat identifikátor strukturové proměnné (mojeAuto) s tečkovým operátorem (mojeAuto.). Přestože senzitivní technologie Intellisense zobrazí seznam členů, nenajdeme mezi nimi žádné datové členy.
Zkušenější programátoři, kteří přicházejí z jazyků C/C++, se mohou zeptat, zda můžeme v C# 3.0 založit instanci struktury i bez explicitního užití operátoru new. Programátoři v C/C++ jsou zvyklí zakládat instance struktury následujícím příkazem: X s;

kde:
X je identifikátor struktury.

s je strukturová proměnná, v níž je uskladněna instance struktury.

Fyzickou reprezentaci instancí struktury Automobil v zásobník vidíte na obrázku. Uvedený instanciační příkaz je v jazycích C/C++ docela běžný, přičemž představuje tzv. zásobníkovou sémantiku. Vzniklá instance struktury bude umístěna na zásobníku, z čehož je zřejmé, že užití zásobníkové sémantiky má stejné důsledky jako instanciace struktury v jazyce C# 3.0. (Dodejme, že instance struktury jazyka C# 3.0 bude standardně vždy umístěna v zásobníku.) Datové členy instance struktury založené pomocí zásobníkové sémantiky v C/C++ mohou, ale nemusí být korektně inicializovány (zpracování inicializačních aktivit je závislé na tom, zda deklarace struktury X disponuje explicitním konstruktorem či nikoliv). V jazyce C# 3.0 je možné vytvořit instanci struktury i pomocí zásobníkové sémantiky známé
z jazyků C/C++. Rozdíl mezi příkazem

X s;

a příkazem

X s = new X();

jazyka C# 3.0 spočívá v tom, že v prvním případě nebude automaticky volán bezparametrický instanční konstruktor struktury. To znamená, že sice vytvoříme instanci struktury, ale tato nebude vůbec (ani implicitně) inicializována. Navíc kompilátor nám nedovolí použít instanci struktury, jež byla sestrojena v prvním instanciačním příkazu (poznamenejme, že překladač zakazuje užití neinicializovaných proměnných). Ve druhém instanciačním příkazu bude instance struktury inicializována, a to bezparametrickým konstruktorem.

Autor článku