c#
Obsahová náplň květnové lekce programování v jazyce C# 3.0 se bude točit kolem datových typů a proměnných. Poté naprogramujeme první smysluplnou aplikaci a vysvětlíme si činnosti, které provádí.
Datové typy
Každý algoritmus pracuje s kvanty vstupních dat, která požadovaným způsobem transformuje na odpovídající kvanta výstupních dat. V programování lze pracovat s různými typy dat, kupříkladu s celočíselnými hodnotami, desetinnými čísly, logickými hodnotami, textovými znaky či řetězci. Abychom mohli v informatice jednoznačně odlišit jednu kategorii dat od jiné, došlo k zavedení tzv. datových typů. Datové typy představují kolekci typů dat, které na vyšší úrovni abstrakce implementují formu, rozsah a množinu manipulačních operací s daty. Povaha dat je různá, různé jsou proto také jednotlivé datové typy, které data determinují. Každý programovací jazyk pracuje se soustavou vestavěných datových typů, jež jsou označovány jako primitivní datové typy. Jelikož jsou primitivní typy pevně zasazeny do jazykové specifikace dotyčného programovacího jazyka, překladač je zná a dovede s nimi přímo pracovat. Na druhou stranu, vývojáři mají vždy možnost vytvářet své vlastní datové typy – těm se říká uživatelsky deklarované datové typy (UDDT). Vzhledem k tomu, že UDDT nejsou součástí jazykové specifikace programovacího jazyka, programátoři je musí nejprve náležitě deklarovat a až poté mohou s nimi přímo pracovat. Užití UDDT je velice vhodné, poněvadž nám dovolují modelovat složitější entity, které nejsou primitivní datové typy schopny popsat. Jak uvidíte, uživatelsky deklarované datové typy jsou složeny z množiny vhodně uspořádaných primitivních datových typů.
Když se zaměříme na jazyk C# 3.0 a vývojově-exekuční platformu Microsoft .NET Framework 3.5, dojdeme k poznání, že datové typy se i v tomto prostředí člení na primitivní typy a uživatelsky deklarované typy. Kromě toho jazyk C# 3.0 a rovněž i společný typový systém CTS klasifikují primitivní a uživatelsky deklarované datové typy do dvou kategorií, jimiž jsou hodnotové a odkazové datové typy. Přehlednou architektonickou skladbu datových typů jazyka C# 3.0 a společného typového systému CTS vidíte na obrázku. Hodnotové a odkazové datové typy se liší svou praktickou aplikací a svým paměťovým managementem. Detaily ovšem odložíme až na později, přinejmenším do té doby, než se seznámíme s proměnnými. Nejprve si představíme primitivní hodnotové datové typy jazyka C# 3.0. Můžeme je roztřídit do čtyř homogenních skupin:
? integrální (celočíselné) typy,
? reálné (desetinné) typy,
? logické typy,
? znakové typy.
Integrální primitivní hodnotové datové typy
Začněme první skupinou, tedy integrálními primitivními hodnotovými datovými typy. Přehled této kategorie datových typů uvádíme v tabulce 1.
Celočíselné typy jsou určeny pro reprezentaci celočíselných hodnot z vymezeného intervalu. Jak si můžete všimnout, v tabulce 1 uvádíme v prvním sloupci název primitivního typu jazyka C# 3.0. V C# 3.0 je každý z primitivních typů dosažitelný prostřednictvím specifického klíčového slova. Kupříkladu nejfrekventovaněji využívaný celočíselný typ sloužící k vyjádření 32bitových celočíselných hodnot se jmenuje int. Typ int je nutno formátovat ručně. Když budeme chtít v C# 3.0 použít některý z typů, jednoduše zapíšeme adekvátní klíčové slovo, které daný typ determinuje. Ve druhém sloupci tabulky 1 je zobrazen systémový typ, jenž je ekvivalentem primitivního typu jazyka C# 3.0 v bázi společného typového systému CTS. Ve skutečnosti mají všechny primitivní hodnotové a odkazové typy své protějšky v systémové vrstvě. Těmto typům se zkráceně říká „systémové typy“ a v souvislosti s jejich užíváním platí snadno zapamatovatelné pravidlo: Jakýkoliv primitivní hodnotový nebo odkazový datový typ jazyka C# 3.0 je přímo mapován na odpovídající systémový datový typ. To je velmi důležité, neboť jsou to právě systémové typy, s nimiž pracuje jazyk MSIL. Systémová vrstva je významná především z hlediska dosažení jazykové interoperability mezi různými .NET kompatibilními programovacími jazyky. Když už víme, že mezi primitivními typy jazyka C# 3.0 a jejími systémovými protějšky existuje přímé spojení, pak nás nejspíš nepřekvapí sdělení, že ve zdrojovém kódu jazyka C# 3.0 můžeme místo klíčových slov pro primitivní typy používat přímo ekvivalentní systémové typy.
Každý primitivní hodnotový typ má svůj rozsah, který je uveden ve třetím sloupci tabulky 1. Rozsah typu představuje množinu přípustných hodnot, které dovede daný typ reprezentovat. Rozsah typu je aproximován z obou stran uzavřeným intervalem celočíselných hodnot. Jak po prostudování celočíselných typů zjistíte, některé jsou schopné uchovat kladná i záporná čísla, zatímco jiné jenom kladná čísla. Podle toho, zda je typ schopen pracovat pouze s kladnými nebo i se zápornými hodnotami, můžeme celočíselné typy rozdělit na dvě skupiny: znaménkové (angl. signed) a neznaménkové (angl. unsigned) typy (někdy se těmto typům říká „typy se znaménkem“ a „typy bez znaménka“). Rozdíl mezi znaménkovými a neznaménkovými typy je zřetelný na první pohled: první sada typů dovede reprezentovat záporná čísla, zatímco druhá ne. K ryze neznaménkovým primitivním celočíselným hodnotovým datovým typům patří tyto: byte, ushort, uint a ulong. Kromě typu byte, byte je nutno formátovat ručně mají všechny ostatní neznaménkové typy ve svém názvu počáteční písmeno „u“, které nám již vizuálně napovídá, že se jedná o typ bez znaménka. Neznaménkové typy jsou užitečné zejména v těch situacích, kdy budeme chtít pracovat s hodnotami, které nebudou nikdy záporné. K takovým patří třeba fixní a variabilní náklady alokované při produkci jistého výrobku, nebo tržby za prodej zboží. Poslední sloupec tabulky 1 nás informuje o míře shody se společnou jazykovou specifikací (CLS). Ne všechny primitivní celočíselné typy jsou kompatibilní s pravidly specifikace CLS. Platí to hlavně o neznaménkových typech (ushort, uint a ulong) a o jednom znaménkovém typu (sbyte). Fakt, že konkrétní datový typ není CLS kompatibilní, samozřejmě neznamená, že jej nemůžeme při stavbě našich programů použít. Jenom se vystavujeme potenciálnímu riziku, že v jiných .NET kompatibilních programovacích jazycích nemusí být hodnoty CLS nekompatibilního typu přímo reprezentovány jiným odpovídajícím datovým typem. Jestliže jste pozorně sledovali tok našich myšlenek, pak se právem zeptáte, proč jsme přeskočili předposlední sloupec tabulky 1, jenž pojednává o alokační kapacitě instance primitivního celočíselného typu. Ne, vážení přátelé, nechceme nic zastírat. Instance typu je ve skutečnosti proměnnou tohoto typu a o proměnných budeme diskutovat až v jedné z nadcházejících částí dnešní lekce programování.
Reálné primitivní hodnotové datové typy
Ačkoliv bychom si nikdy nedovolili říct byť i jen jediné křivé slovo o celočíselných typech, je pravdou, že někdy si pouze s celými čísly prostě nevystačíme. Ať už počítáme míru návratnosti vynaložené investice, pravděpodobnost padnutí pěti šestek za sebou nebo výšku splátky pro splátkový kalendář, budeme potřebovat reálné datové typy. Jazyk C# 3.0 obsahuje celkem tři primitivní reálné hodnotové datové typy, jejichž charakteristiku můžete nalézt v tabulce.
Typy float, double a decimal jsou primitivní reálné typy, jejichž pomocí mohou programátoři provádět operace s desetinnými čísly. Reálné typy se liší svým rozsahem i alokační kapacitou svých instancí. Ovšem nejdůležitější je jejich přesnost. Ta je dána počtem signifikantních míst za desetinným oddělovačem, která překladač bere při manipulaci s hodnotami reálného typu v potaz. Je zřejmé, že nejmenší přesnost dosahuje typ float. I proto se mu často říká reálný typ s jednoduchou přesností. Dvojnásobnou přesnost dosahuje typ double, ovšem dvojnásobná je také alokační kapacita jeho instancí. Pokud diskutujeme o přesnosti, pak je králem typ decimal s minimálně osmadvaceti signifikantními místy za desetinným oddělovačem. Dovolujeme si ještě připojit jeden zajímavý postřeh: Přestože je typ decimal nejpřesnějším z reálných typů, není současně typem s největším rozsahem. Při práci s ohromnými reálnými hodnotami je proto vždy nutné stanovit prioritu: buď rozsah, nebo přesnost.
Logické primitivní hodnotové datové typy
Množina logických hodnotových typů je v jazyce C# 3.0 pouze jednoprvková, přičemž je tvořena primitivním typem bool. Datový typ bool je určen k vyjádření hodnot logických stavů podle klasické (Booleovy) algebry. Logická pravda je v jazyce C# 3.0 reprezentována klíčovým slovem true, zatímco logická nepravda je ztělesňována klíčovým slovem false.
Jestliže přicházíte z jazyků C/C++, pak vězte, že mezi hodnotami logického typu bool a hodnotami celočíselných typů neexistují žádné implicitní typové konverze. Bližší informace o primitivním logickém typu bool přináší tabulka.
Znakové primitivní hodnotové datové typy
Podobně jako u logických typů, rovněž znakové typy jsou v C# 3.0 zastoupeny pouze jediným primitivním datovým typem char. Více informací o typu char se dozvíte z tabulky 4. Instance typu char dovede uchovat jeden textový znak ze znakové sady Unicode. Pokud máte zkušenosti s jazykem C, pak v tomto prostředí byly textové znaky kódovány pomocí sady ASCII. Na uložení jednoho znaku v sadě ASCII stačil pouze jeden bajt. V sadě ASCII byl každý textový znak jednoznačně identifikován pomocí svého číselného kódu. Pokud označíme znak symbolem A a jeho ASCII kód symbolem AASCII, pak platí, že AASCII€<0, 255>. V jazyce C# 3.0 se vždy užívá pouze znaková sada Unicode, v níž jsou textové znaky interně kódovány jako 16bitová celá čísla bez znaménka. Libovolný textový znak sady Unicode je charakterizován svým číselným kódem. Označíme-li znak sady Unicode symbolem A a jeho číselný kód symbolem AUnicode, pak platí, že AUnicode€<0, 65535>. Z uvedeného rovněž vyplývá, že na absorpci jednoho znaku (neboli jedné hodnoty typu char) jsou zapotřebí 2 bajty paměťového prostoru. Typ char je nutno formátovat ručně.
Proměnné
Aby bylo možné uchovat data určitého datového typu, potřebujeme datový kontejner. V programování se takovémuto datovému kontejneru říká proměnná. Ačkoliv termín „proměnná“ se jako první objevil v matematice, v informatice a programování obecně je proměnná chápána jinak než v královně věd. Zatímco v matematice je proměnná zastupujícím symbolem pro množinu přípustných hodnot, které odpovídají řešení jistého matematického problému, v informatice je proměnná definována jako datový kontejner s určitou alokační kapacitou, do kterého lze umístit kvantum dat. Z technického pohledu je proměnná symbolickým pojmenováním pro paměťový prostor, jenž je určen pro uskladnění dat. Proměnná v programu jazyka C# 3.0 vzniká definičním příkazem, jehož generická podoba je následující:
kde:
? T je datový typ proměnné.
? I je identifikátor (název) proměnné.
Každá proměnná musí mít svůj identifikátor a datový typ. Identifikátor je název proměnné, který musí splňovat požadavky pro pojmenovávání entit v jazyce C# 3.0.
Datový typ proměnné přesně určuje, jaké hodnoty budou moci být do dotyčné proměnné uloženy. D atovým typem proměnné smí být jakýkoliv z dosud popsaných primitivních hodnotových datových typů. Kupříkladu následující ukázka předvádí, jak v těle hlavní metody Main vytvořit tři proměnné:
Definiční příkaz vytváří proměnnou a proces vytvoření proměnné je spojen s alokací paměťového prostoru o jisté kapacitě. Třeba první definovaná proměnná je celočíselného typu int, takže bude alokovat 4 bajty (32 bitů / 8 bitů = 4 bajty). Proces definice proměnné je často označován také jako instanciovaní datového typu. V procesu instanciace datového typu se vytváří jeho instance, která má symbolické jméno, jež je shodné se jménem definované proměnné.
Když zkusíme přeložit výše uvedený fragment zdrojového kódu jazyka C# 3.0, zjistíme, že překladač neohlásí žádnou chybu. Jenom označí identifikátory proměnných zelenou vlnovkou, která říká, že proměnná je sice definována, ale není nikde prakticky použita. Jelikož je naším cílem praktická demonstrace, pokročíme dál. Je-li proměnná definována, existuje v operační paměti počítače a jako taková je připravena k uchování hodnot. Hodnotu do proměnné uložíme přiřazovacím příkazem, jenž vypadá takto:
V přiřazovacím příkazu se vyskytuje přiřazovací operátor (=), jenž vizuálně segmentuje celý příkaz na levou a pravou stranu. Nalevo od operátoru pro přiřazení se nachází proměnná, která je příjemcem hodnoty, jež je specifikována napravo od přiřazovacího operátoru. Uvědomte si prosím, že toto je přiřazení, jehož smyslem je uložení hodnoty do proměnné. Na přiřazovací příkaz nesmíme proto nahlížet jako na matematickou formuli. V matematice představuje symbol = rovnost neboli shodnost dvou entit, což je něco zcela jiného. Jestliže přiřadíme do proměnné určitou hodnotu, pak říkáme, že jsme tuto proměnnou inicializovali.
Jazyk C# 3.0 nám umožňuje proměnnou v rámci její definice okamžitě inicializovat:
Tento příkaz se odborně nazývá definiční inicializace a spojuje v sobě sílu definice a inicializace proměnné. Definiční inicializace je vhodnou technikou zejména tehdy, když již při vytváření proměnné můžeme stanovit její inicializační hodnotu.
Proměnná však nemusí být inicializována pouze diskrétní celočíselnou hodnotou (konstantou), jako je tomu ve výpisu kódu. Do proměnné smíme uložit také hodnotu určitého výrazu. V zájmu zachování pozvolného tempa výkladu nabízíme ukázku, v níž je proměnná inicializována hodnotou aritmetického výrazu:
Nyní se na pravé straně definičně-inicializačního příkazu nachází aritmetický výraz, v němž vystupují celočíselné konstanty společně s aritmetickými operátory. Operátory jsou reprezentovány symboly, které předepisují provedení jisté (v tomto případě aritmetické) operace. Operátor * (hvězdička) slouží k vynásobení dvou hodnot a operátor – (minus) uskutečňuje rozdíl dvou hodnot. Entity, na něž jsou operátory aplikovány, se v programování označují jako operandy. Náš aritmetický výraz je tedy složen ze třech operandů (to jsou hodnoty 10, 2 a 3) a dvou operátorů (* a – ). Hodnotu aritmetického výrazu dovede překladač určit podle standardních matematických pravidel. Výraz 10 * 2 – 3 bude proto vyhodnocován jako výraz (10 * 2) – 3, jehož hodnota je 17. Po vyhodnocení výrazu je jeho hodnota přiřazena do proměnné, čímž se stává její inicializační hodnotou. Správnost vysvětleného postupu můžeme velice jednoduše ověřit. Stačí, abychom vypsali obsah proměnné na obrazovku:
Hodnotu uloženou v proměnné zobrazíme v dialogovém okně příkazového řádku pomocí metody WriteLine třídy Console, s níž jsme již měli tu čest se setkat. Metoda WriteLine zobrazí veškeré vstupní hodnoty, které ji poskytneme. Metodě předáváme dva textové řetězce (ano, ty jsou umístěny ve dvojitých uvozovkách) a identifikátor proměnné. Metoda zobrazí na výstupu nejprve první textový řetězec, pak hodnotu proměnné, poté druhý textový řetězec. Poslední příkaz volá metodu Read třídy Console, která čeká na stisknutí klávesy Enter (metoda nám umožňuje v klidu prohlédnout obsah konzolového okna programu). Výstup našeho miniprogramu vidíte na obrázku.
V jednom definičním příkazu můžeme vytvořit i více proměnných najednou. Podmínkou je, aby měly všechny zúčastněné proměnné stejný datový typ:
Vzhledem k tomu, že proměnné pro uskladnění gramáže základních živin jsou všechny typu int, smíme je zkonstruovat v jednom definičním příkazu. Ba co více, můžeme jít ještě dál a původní definice přepracovat na definiční inicializace:
Pokusme se rozvinout předcházející fragment zdrojového kódu a povězme, že budeme chtít napsat program, který dovede spočítat energetický obsah jídla měřený v kilokaloriích (kcal). Budeme přitom vycházet z biologického vzorce, podle něhož celkovou energetickou náročnost (E) pokrmu zjistíme takto:
kde:
? BG je množství bílkovin (v g)
obsažených v jídle.
? CG je množství cukrů (v g)
obsažených v jídle.
? TG je množství tuků (v g)
obsažených v jídle.
Vzorec vychází ze skutečnosti, že 1 gram živiny je schopen vyprodukovat jisté kvantum energie. Vycházíme z empiricky ověřených experimentů, podle nichž 1 gram bílkovin vydá za 4 kcal energie, podobně jako 1 gram cukrů. Energeticky nejnáročnější je ovšem 1 gram tuků, poněvadž ten spolkne až 9 kcal energie. Jak by tedy vypadal náš program? Jednodušší variantu bychom mohli navrhnout třeba takhle:
V programu jsme fixně zadali gramáže pro bílkoviny, cukry a tuky. Jídlo, jehož energetickou hodnotu bude analyzovat, je tedy složeno z deseti gramů bílkovin, dvaceti gramů cukrů a patnácti gramů tuků. Pro výpočet energetické náročnosti jídla definujeme novou proměnnou energie, která disponuje celočíselným datovým typem. Jak vidíme, definovaná proměnná je záhy inicializovaná hodnotou aritmetického výrazu. Zdejší aritmetický výraz se od toho předešlého liší pouze v jednom: jako operandy vstupují kromě celočíselných konstant také proměnné. Jakmile překladač vypočte hodnotu aritmetického výrazu, přiřadí ji do proměnné energie. V této proměnné se tedy bude nacházet hodnota určující energetický obsah jídla. Pokud program přeložíme a spustíme, zjistíme, že naše jídlo není příliš tučné – obsahuje pouze 255 kcal. 8 0202/CZ o
Poznámka: Uvedené informace nás přivádí k zajímavé implikaci: Jeden systémový datový typ může být v různých .NET kompatibilních jazycích reprezentován primitivními typy interpretovanými pomocí odlišných klíčových slov. Uveďme si příklad. Ve společném typovém systému CTS existuje typ System.Int32, který dovede pracovat s celočíselnými hodnotami. Již víme, že v C# 3.0 je odpovídajícím primitivním typem int, ovšem v jazyce Visual Basic 2008 je to typ Integer. Kdybychom chtěli naprogramovat svůj vlastní programovací jazyk pro platformu .NET, mohli bychom odpovídající celočíselný primitivní typ pojmenovat třeba CelaCisla, ale v zájmu zachování vzájemné spolupráce by byl i náš typ CelaCisla mapován na systémový typ System.Int32.Poznámka: V naprosté většině případů mohou být proměnné pojmenovávány podle logického úsudku programátora. Přesto existuje několik pravidel, která bychom měli mít na paměti:
1. Jméno proměnné nesmí začínat číslicí.
2. Jméno proměnné nesmí obsahovat mezeru. Pokud je smysluplné volit identifikátor proměnné
složením z více slov, pak mohou být aplikovány různé pojmenovací konvence. Jednotlivá slova
víceslovného názvu mohou být oddělena podtržítkem (_), kupříkladu moje_proměnná, resp.
Moje_Proměnná. Anebo je možné uplatnit tzv. velbloudí notaci, podle které bychom
proměnnou pojmenovali takto: mojeProměnná. Velbloudí notace říká, že pokud je jméno
proměnné tvořeno z více slov, pak první slovo je psáno s malým počátečním písmenem
a každé další slovo víceslovného názvu je zase psáno s velkým počátečním písmenem.
3. Jméno proměnné může obsahovat symboly diakritiky.
4. Jméno proměnné se nesmí shodovat se jménem dříve definované proměnné. Jestliže by
takováto situace vznikla, překladač by ji posuzoval jako redefinici proměnné, což je chyba.
5. Jméno proměnné se nesmí shodovat s některým z klíčových slov jazyka C# 3.0.