Poznávame C# a Microsoft.NET

Rozměr: px
Začít zobrazení ze stránky:

Download "Poznávame C# a Microsoft.NET"

Transkript

1 Poznávame C# a Microsoft.NET

2 Poznáváme C# a Microsoft.NET 1.díl...5 Důvody vzniku...5 Princip běhového prostředí...5 Klíčové vlastnosti...6 Typy aplikací...6 Poznáváme C# a Microsoft.NET 2. díl...7 Vlastnosti jazyka...7 Požadavky na běh...7 Vývojová prostředí...8 První program Ahoj světe...8 Základní datové typy...9 Rozdělení datových typů...9 Příklad na konec...9 Poznáváme C# a Microsoft.NET 3. díl...10 Proč objekty? Objekty a třídy...10 Zapouzdření objektů...11 Implementace v C# Konstruktor Atributy a vlastnosti Metody Specifikátory přístupu...13 Ukázková třída žárovka...14 Poznáváme C# a Microsoft.NET 4. díl...16 Jednoduchá dědičnost v C#...16 Využití dědičnosti...16 Implementace dědičnosti v C# Specifikátor přístupu protected...17 Virtuální metody a jejich překrytí pomocí new a override Metody a konstruktory se vstupními parametry Přetěžování metod a konstruktorů...18 Příklad se zaměstnanci v C#...18 Poznáváme C# a Microsoft.NET 5. díl...21 Statické členy tříd...21 Statické atributy Statické metody Statické konstruktory Polymorfismus Poznáváme C# a Microsoft.NET 6. díl...25 Abstraktní třídy...25 Rozhraní Rozhraní a dědičnost...28 Vícenásobná implementace Poznáváme C# a Microsoft.NET 7. díl...29 Uzavřené třídy...29 Privátní konstruktory Specifikátor přístupu internal...30 Vnořené třídy Konstanty Proměnné pouze pro čtení...32 Poznáváme C# a Microsoft.NET 8. díl...33 Relační operátory Logické operátory Podmínkové příkazy...35 Příkaz if Ternární operátor Příkaz switch Přetěžování operátorů...37 Poznáváme C# a Microsoft.NET 9. díl...38 Prefixový versus postfixový zápis inkrementace/dekrementace...38 Příkazy pro vytváření cyklů...39 Příkazy break a continue Příkaz while Příkaz do-while...40 Příkaz for Poznáváme C# a Microsoft.NET 10. díl...42 Struktury v C# Struktury a konstruktory Výčtové typy Číselná reprezentace a bázové typu výčtů

3 Poznáváme C# a Microsoft.NET 11. díl...45 Co je pole? Deklarace pole Další možnosti inicializace Vlastnost Length Vícerozměrná pole...47 Pole polí Pole jako parametr metody Poznáváme C# a Microsoft.NET 12. díl...50 System.Array Zjištění počtu rozměrů pole...50 Další možnost vytvoření pole...50 Převrácení pořadí prvků pole...50 Získáni indexů okrajů pole...51 Mělká kopie pole Vyhledání prvku v jednorozměrném poli...52 Cyklus foreach Poznáváme C# a Microsoft.NET 13. díl výjimky...53 Co jsou výjimky? Proč výjimky? Vyhození výjimky Chráněné bloky a handlery výjimek...54 Handlery pro více typů výjimek...55 Závěrečné bloky Poznáváme C# a Microsoft.NET 14. díl výjimky po druhé...56 Předávání výjimek Vlastnost Message třídy System.Exception...57 Vlastnost StackTrace Vytváření vlastních výjimek...58 Omezení při definici více handlerů...59 Opakované vyhození výjimky Zabalení výjimky Poznáváme C# a Microsoft.NET 15. díl delegáty...61 K čemu delegáty? Deklarace delegáta Vytvoření instance delegáta...62 Volání delegáta Skládání delegátů...64 Poznáváme C# a Microsoft.NET 16. díl události...65 Co jsou události? Deklarace události Vyvolání události Ukázková třída vyvolávající událost...66 Obsluha událostí Události a dědičnost...68 Poznáváme C# a Microsoft.NET 17. díl události podruhé...69 Doporučený způsob implementace tříd...69 Delegát typu System.EventHandler...69 Přenášení dodatečných údajů o události...71 Poznáváme C# a Microsoft.NET 18. díl indexery...72 Indexery Použití cyklu foreach se třídou s indexery...75 Poznáváme C# a Microsoft.NET 19. díl převody číselných typů...77 Konverze v C# Explicitní číselné konverze...77 Klíčové slovo checked a kontrolované převody Konverze pomocí třídy System.Convert...79 Poznáváme C# a Microsoft.NET 20. díl konverze referenčních typů...80 Explicitní konverze Příklady s obrazci Přetypování na proměnnou rozhraní...82 Referenční proměnná typu abstraktní třída Operátor is Operátor as Poznáváme C# a Microsoft.NET 21. díl komentování a dokumentace...84 Komentování kódu Běžné komentáře v C#...85 Dokumentace v XML Kontextová nápověda ve Visual C#.NET...88 Poznáváme C# a Microsoft.NET 22. díl uživatelsky definované konverze

4 Uživatelské konverze Konverze mezi třídami a strukturami...91 Poznáváme C# a Microsoft.NET 23. díl direktivy pre-procesoru...94 Direktivy určené k podmíněnému vykonání určité sekce kódu Direktiva #line Diagnostické direktivy Direktivy pro určení specifických bloků kódu...97 Poznáváme C# a Microsoft.NET 24. díl speciální případy metod...98 Metody s proměnným počtem parametrů Modifikátor ref Modifikátor out Poznáváme C# a Microsoft.NET 25. díl - třídy kolekcí Třídy kolekcí a jmenný prostor System.Collections Třída ArrayList Poznáváme C# a Microsoft.NET 26. díl třídy kolekcí II Třída Stack Třída Queue Slovníky a rozhraní IDictionary Třída HashTable Poznáváme C# a Microsoft.NET 27. díl třídy kolekcí III Definice vlastního porovnání instance Definice více možných způsobů porovnání Poznáváme C# a Microsoft.NET 28. díl HashProvidery a Klonování Komplexnější využití hešových tabulek a vlastní HashProvidery Kopírování instancí a Rozhraní ICloneable Poznáváme C# a Microsoft.NET 29. díl řetězce Bližší pohled na třídu System.String Poznáváme C# a Microsoft.NET 30. díl StringBuilder a Regulární výrazy Třída StringBuilder Úvod do regulárních výrazů v C# Poznáváme C# a Microsoft. NET 31. díl regulární výrazy Libovolné znaky v regulárních výrazech Intervaly znaků Skupiny v regulárních výrazech Poznáváme C# a Microsoft.NET 32. díl I/O a streamy I/O operace v rámci.net Operace se soubory Třídy BinaryWriter a BinaryReader Poznáváme C# a Microsoft.NET 33. díl I/O podruhé Třídy TextReader a TextWriter Práce s pamětí pomocí proudů Třída File Poznáváme C# a Microsoft.NET 34. díl informace o adresářích a sledování souborového systému Třída Directory Třídy FileInfo a DirectoryInfo Sledování změn souborového systému Poznáváme C# a Microsoft.NET 35. díl izolovaná úložiště Třídy StringReader a StringWriter Izolovaná úložiště Poznáváme C# a Microsoft.NET 36. díl úvod do reflexe Aplikace v.net frameworku Reflexe v.net Poznáváme C# a Microsoft.NET 37. díl použití reflexe Třída System.Type Dynamické vytvoření instance třídy Reflexe datových členů Reflexe metod Poznáváme C# a Microsoft.NET 38. díl atributy a jejich reflexe Proč atributy? Jak atributy použít? Poziční a pojmenované parametry atributů Cílené použití atributů Vytváření vlastních atributů Získání uživatelských atributů při použití reflexe Poznáváme C# a Microsoft.NET 39. díl další použití reflexe Použití třídy ConstructorInfo Reflexe a polymorfizmus Dynamické vytváření nových elementů aplikace pomocí reflexe

5 Poznáváme C# a Microsoft.NET 40. díl serializace Co je to serializace? Jak učinit typy serializovatelnými? Jak instanci serializovat? Poznáváme C# a Microsoft.NET 41. díl - pokročilé využití serializace Selektivní serializace členů Rozhraní IDeserializationCallback Vlastní řízení serializace Poznáváme C# a Microsoft.NET 42. díl úvod do použití XML XML? Co je to? Jak na zpracování XML v.net frameworku Jednosměrné čtení XML dokumentu Použití třídy XMLTextReader Poznáváme C# a Microsoft.NET 43. díl práce s XML Jak na atributy v XML Zápis do XML dokumentu Poznáváme C# a Microsoft.NET 44. díl zpracování XML pomocí DOM Document Object Model Použití DOM v prostředí.net Modifikace XML pomocí DOM Poznáváme C# a Microsoft.NET 45. díl validace XML dokumentů XML schémata Validace podle XSD v.net frameworku Poznáváme C# a Microsoft. NET 46. díl použití XPath Jazyk XPath Užití XPath v.net Rozhraní XPath a třída XPathNavigator Poznáváme C# a Microsoft.NET 47. díl použití XSL transformací XSL transformace? O co jde? Použití XSLT v.net frameworku Předávání parametrů transformačnímu jádru Poznáváme C# a Microsoft. NET 48. díl úvod do použití vláken Vlákna? O co jde? Použití vláken v.net Poznáváme C# a Microsoft.NET 49. díl použití vláken Zjištění informací o vláknu Uspání vlákna Vlákna s parametry Poznáváme C# a Microsoft.NET 50. díl použití vláken II Spojení vláken Synchronizace vláken Klíčové slovo lock Atomické operace Poznáváme C# a Microsoft.NET 51.díl použití vláken III Test na získání zámku objektu Notifikace vláken a čekání na zámek Poznáváme C# a Microsoft. NET 52. díl ThreadPool Vlákna typu démon Použití třídy ThreadPool Informace o thread poolu Poznáváme C# a Microsoft.NET 53. díl Timer a asynchronní delegáti Asynchronní delegáti Třída Timer

6 Poznáváme C# a Microsoft.NET 1.díl Nový seriál o jazyku C# a s ním spjaté platformy Microsoft.NET začneme úvodním seznámením s ní a jejími důležitými vlastnostmi. I když úvod do světa.netu byl již několikrát v našich končinách napsán, nemohu si tuto důležitou část dovolit vynechat, protože se určitě mezi zájemci o vývoj pro tuto platformu najdou tací, kteří ještě neměli tu čest se s ní seznámit. Důvody vzniku Platforma.NET byla oficiálně představena firmou Microsoft v roce 2000 jako klíčový produkt, jehož rozvoj a propagace je součástí dlouhodobé strategie firmy. Microsoft.NET znamená novou generaci systému vývoje aplikací pro operační systémy Windows založeném na řízeném běhovém prostředí, obohaceném o neskromnou sadu základních tříd, nesoucím jméno.net framework. Hlavními důvody vedoucí k více než čtyřletému vývoji, jehož výsledkem je.net, byly: nekompatibilita jednotlivých programovacích jazyků a s tím související obtížná spolupráce mezi programy/knihovnami napsanými v odlišných jazycích (např. C++ a Visual Basic) vysoká chybovost aplikací (chyby v práci s pamětí, neplatné konverze datových typů) problémy s verzemi knihoven (obtížná práce s provozem více verzí knihoven) zastaralý a nepřehledný způsob vývoje dosavadních webových aplikací Všechny tyto problémy efektivně řeší platforma.net a to použitím již zmíněného řízeného běhového prostředí, systémem assemblies, což jsou základní stavební prvky aplikací, a novou technologií ASP.NET pro vývoj webových aplikací. Princip běhového prostředí V předchozím odstavci jsem zmínil, že pro Microsoft.NET jsou aplikace vyvíjeny pod řízeným běhovým prostředím.net framework. Bylo by dobré objasnit co se skrývá pod pojmem řízené běhové prostředí. Většina dnešních aplikací, vytvořených například v jazyce C++, Visual Basicu nebo Delphi, jsou zkompilovány přímo pro danou platformu, nejčastěji je to pro platformu Win32 operačních systémů Windows, ale mohou to samozřejmě být i jiné. To znamená, že zdrojový kód je kompilací převeden do strojového kódu počítače. To ve výsledku přináší velmi dobrou rychlost běhu výsledné aplikace. Avšak na druhou stranu z toho plynou i některé nevýhody nepřenositelnost mezi jednotlivými platformami, popřípadě verzemi operačních systémů a nezřídka jsou k vidění chyby v přístupech do operační paměti. Princip řízených běhových prostředí, použitý právě u platformy.net, ale i u velmi známé platformy Java firmy Sun Microsystems, na to jde trochu jinak a přidává k převodu zdrojového kódu do kódu strojového ještě jednu vrstvu. Tuto vrstvu představuje mezikód, do kterého jsou zdrojové kódy zkompilovány, a tento mezikód je běhovým prostředím na cílové platformě (Windows, Linux) převeden do strojového kódu. Tento převod je na cílové platformě realizován vždy při spouštění konkrétní aplikace. Mínusem tohoto překladu je vyšší náročnost na výkon uživatelského počítače, a z tohoto důvodu se tento způsob nepoužívá pro vývoj výpočetně náročných aplikací (např. počítačové hry). S jeho častým použitím se naopak můžete setkat spíše u obchodních 5

7 aplikací, které přeci jen nejsou tak náročné na výpočetní výkon a rychlost běhu daných aplikací je naprosto vyhovující. Poznámka: Předchozí větu si prosím nevyložte tak, že aplikace pod těmito platformami jsou nepoužitelně pomalé. U spousty úloh (přístup k databázi, souborům atd.) uživatel snížení rychlosti aplikace ani nepocítí. Navíc je dobré dodat, že u těchto běhových prostředí při spuštění aplikace nedochází k překladu celé aplikace najednou, ale používá se JIT (Just-in-Time) kompilace. JIT kompilace znamená, že do strojového kódu je převedena pouze potřebná část mezikódu a při opětovném použití této (již přeložené) části se spouští její zkompilovaná forma, což se příznivě projeví na rychlosti, která si již nezadá s během neřízeného programu. Klíčové vlastnosti Tolik k jemnému osvětlení řízeného běhového prostředí. Nyní přejdeme ke klíčovým vlastnostem platformy Microsoft.NET, která tento způsob běhu aplikací přináší. Mezikód zmíněný o pár řádků výše se ve světe této platformy nazývá MSIL, tedy Microsoft Intermediate Language. Tento jazyk relativních adres je spouštěn klíčovou součástí.net frameworku pojmenovanou CLR (Common Language Runtime neboli společné běhové prostředí) a firma Microsoft jej dala ke standardizaci organizaci ECMA. V prostředí CLR existuje věc, která programátorům velmi usnadňuje práci s operační pamětí Garbage Collector. Jedná se o sadu složitých algoritmů pro uvolňování nepotřebných programových objektů z paměti. Díky Garbage Collectoru se již vývojáři nemusejí starat o přiřazování nebo uvolňování operační paměti a odpadá tak riziko již zmíněné nekorektní práce s ní, která ve většině situací končí pádem aplikace. Velmi důležitou vlastností, kterou dal Microsoft své platformě do vínku, je CLS Common Language Specification (Společná jazyková specifikace). S ní souvisí CTS neboli Common Type System (společný typový systém). Výsledkem použití CLS a CTS je rovnocennost programovacích jazyků. Jinými slovy pro vývoj.net aplikací je možné použít jeden z několika programovacích jazyků vyšší úrovně. Může se jednat například o: C#, nový jazyk vyvinutý pro.net, Visual Basic.NET, nová generace oblíbeného jazyku Visual Basic, J#, což je jazyk se syntaxí rozšířeného jazyka Java, managed C++, kde slovíčko managed označuje možnost psát řízený kód pro.net dokonce i v tak od svého počátku neřízeném jazyce S tím souvisí i další výhoda této platformy výrobcům třetích stran nic nebrání ve vývoji dalších jazyků Jediné, co stačí, je, aby tento nový jazyk měl kompilátor se schopností kompilovat zdrojové kódy do mezijazyku MSIL; to znamená splnit specifikaci CLS danou Microsoftem. Typy aplikací Nyní se pomalu dostáváme k otázce, která určitě napadla většinu čtenářů, kteří se dočetli až sem: Co všechno můžu v.netu vytvořit? Platforma Microsoft.NET vývojářům nabízí široké možnosti. Začít můžete u klasických konzolových aplikací, které pro vstup a výstup používají příkazový řádek. Daleko zajímavější jsou aplikace s využitím knihoven Windows.Forms, interně využívající Microsoft Win32 API. Výsledkem jejich použití jsou známé formulářové aplikace pro 6

8 Windows. Možné je také vytvořit aplikaci běžící jako proces na pozadí systému službu Windows. Dalším odvětvím jsou webové aplikace nahrazující zastaralé ASP 2.0; a to jejich nová generace označovaná jako ASP.NET, za kterou si dle mého názoru Microsoft zaslouží velkou pochvalu, protože tím posunul tvorbu dynamických webů o pořádný kus dál. Technologií ASP.NET se budeme blíže zabývat v některém z příštích dílů tohoto seriálu. Neméně zajímavým typem aplikací jsou takzvané Webové služby, které umožňují pomocí všudypřítomného http protokolu na vzdáleném serveru volat metody. A samozřejmě nesmíme zapomenout na možnost tvorby knihoven tříd, bez které by vyspělá platforma, kterou Microsoft.NET bezesporu je, neměla velký smysl. V příštím díle našeho seriálu se začneme přímo zabývat jazykem C# a názorně si ukážeme, jak napsat první velmi jednoduchou aplikaci. Poznáváme C# a Microsoft.NET 2. díl Po minulém jemném úvodu do světa platformy Microsoft.NET se dnes již zaměříme na programovací jazyk C#. Jde o nově vyvinutý jazyk pro.net, který kombinuje vlastnosti známých a oblíbených programovacích jazyků a přidává k nim některé nové. I přes to, že si jsou jednotlivé programovací jazyky pro tuto platformu rovny, je C# Microsoftem prosazován jako jazyk hlavní. Vlastnosti jazyka Jak jsem napsal o pár řádků výše, C# je nově vyvinutý jazyk pro Microsoft.NET. Je navržen pro maximální využití této rychle se rozvíjející platformy. Jedná se o silně objektově orientovaný jazyk vycházející z programovacích jazyků Java a C++, takže pokud nějaký z těchto jazyků znáte, nebude pro Vás C# velký problém. Stejně jako tyto jazyky je i C# case-sensitive, což znamená, že významově odlišuje velká a malá písmena ve výrazech (zarovka a Zarovka jsou brány jako dva rozdílné pojmy). V tomto jazyce je realizováno 80% základních knihoven.net frameworku. I přesto, že je koncipován hlavně pro psaní řízeného kódu, na jehož užití je platforma.net postavena, lze jej v případě potřeby využít i pro tvorbu kódu neřízeného(bloky unsafe). Použití neřízeného kódu znamená, že běhové prostředí CLR neověřuje zda-li je napsaný kód bezpečný (například se neověřuje jinak vyžadovaná typová bezpečnost). Nyní si dovolím uvést výčet několika vlastností jazyku C#, které můžete při tvorbě aplikací použít. Jedná se o: Třídy základní stavební prvek při tvorbě objektově orientovaných aplikací obsahující akce (metody) a atributy Struktury lze je chápat jako zjednodušené třídy, jejich užitím jsou nejčastěji popisovány vlastní datové struktury. Výčtové typy Vlastnosti někdy označované jako chytré proměnné Pole a jejich chytrá verze nazývaná indexery Zástupci typově bezpečné ukazatele na funkce Události druh zástupců sloužící ke zpracování asynchronních operací Požadavky na běh 7

9 Základním požadavkem pro běh.net aplikací je samozřejmě již několikrát zmíněné běhové prostředí.net framework, které si můžete stáhnout na adrese Pro vývoj doporučuji stáhnout také.net framework SDK (Software Development Kit), které mimo jiné obsahuje obsáhlou dokumentace k základním třídám.net frameworku. Vývojová prostředí Pro vývoj.net aplikací máte na výběr z několika vývojových prostředí. Samozřejmě pokud z nějakého důvodu specializované vývojové prostředí používat nechcete, můžete psát kód aplikace klidně v poznámkovém bloku a následně jej kompilovat v prostředí příkazového řádku. To bych vám ovšem příliš nedoporučoval. Za asi nejvhodnější vývojové prostředí pokládám Visual Studio.NET respektive Visual C#, což je jeho součást. Bohužel toto vývojové prostředí není zdarma, takže pokud nemáte v plánu vyvíjet aplikace komerčně asi to pro vás nebude nejvhodnější volba. Za účelem studia vidím jako dobrou volbu C# Builder od firmy Borland, který je ve své verzi personal volně ke stažení na adrese Ovšem tuto verzi není dovolené používat pro vývoj komerčních aplikací. Pokud máte zájem vyvíjet komerční aplikace a nechce se vám utrácet existuje open source vývojové prostředí SharpDevelop a to najdete na adrese První program Ahoj světe Je nepsaným zvykem začít učení programovacího jazyka tím nejjednodušším programem, který nedělá nic jiného než, že vypíše uživateli na obrazovku slova Ahoj světe. Tento prográmek se zrealizuje v jazyku C# takto: using System; namespace ukazky_zive class AhojSveteApp public static void Main(string[] args) Console.WriteLine("Ahoj svete!"); Console.ReadLine(); Na prvním řádku si příkazem using importujeme knihovnu System, která obsahuje třídu Console, jejíž metody budeme používat. Řádek s klíčovým slovem namespace určuje do kterého jmenného prostoru třída patří (v našem případě patří do jmenného prostoru ukazky_zive). Jmenné prostory slouží v.netu k oddělení tříd do k sobě logicky patřících částí. Výsledkem kompilace je knihovna DLL s názvem jmenného prostoru. Řádek s klíčovým slovem class určuje název vytvářené třídy (AhojSveteApp). Po něm následuje řádek definující metodu Main s identifikátory přístupu public a static,která nevrací žádnou hodnotu což je dáno slovíčkem void. Do metody jako parametr vstupuje pole řetězců(string [] args). Všechny tyto podmínky musí metoda splňovat, aby třída AhojSveteApp byla spustitelná konzolová aplikace. No a příkaz Console.WriteLine vypíše předaný parametr (v našem případě je to řetězec Ahoj svete! ) na obrazovku. V některých vývojových prostředích (například Borland C# Builder) je ještě vhodné uvést příkaz Console.Readline(), který zařídí nezavření okna aplikace ihned po výpisu našeho pozdravu. V prostředích Visual C# a SharpDevelop toto nutné není, protože ty si to zařídí 8

10 sami. Jistě jste si všimli, že jednotlivé bloky programu jsou jako ve všech C-like jazycích uzavřeny do složených závorek. Poznámka: Pokud jste nováčci ve světe objektově orientovaného programování, pak vám jsou pojmy jako třída či identifikátor přístupu nejspíše cizí. Ale nelekejte se, hned v příštím díle seriálu si tyto pojmy rozebereme. Základní datové typy Nyní si projdeme základní datové typy, použitelné v jazyku C#. Každý datový typ v C#,stejně jako u ostatních jazyků pro.net, je reprezentací datového typu.net frameworku a to kvůli splnění specifikace CTS. Jsou to tedy: Int 32-bitový celočíselný typ s hranicemi -2,147,483,648 až 2,147,483,647 Uint Int bez znaménka s hranicemi 0 až 4,294,967,295 Byte 8-bitový celočíselný typ bez znaménka s hranicemi 0 až 255 Sbyte byte se znaménkem s hranicemi -128 až 127 Short 16-bitový celočíselný typ s hranicemi -32,768 až 32,767 Ushort short bez znaménka s hranicemi 0 až 65,535 Long celočíselný typ s hranicemi -9,223,372,036,854,775,808 až 9,223,372,036,854,775,807 Ulong long bez znaménka s hranicemi 0 až 18,446,744,073,709,551,615 Float číselný typ s desetinnou čárkou s přesností na 7 míst Double číselný typ s desetinnou čárkou s přesností na 15 nebo 16 míst Char vyjadřuje Unicode znak String reprezentuje řetězec znaků ve znakové sadě Unicode Bool logický typ, který nabývá pouze dvou hodnot : true(pravda)/false(nepravda) Rozdělení datových typů V.NET frameworku se datové typy dělí na dvě skupiny. Jsou to: Hodnotové typy (value types) do této skupiny patří všechny číselné datové typy, typ char a ostatní struktury. U těchto jednoduchých typů se jejich hodnota ukládá přímo do proměnné místa v paměti určené pro uložení hodnoty. Referenční typy (reference types) do této skupiny patří typ String a všechny třídy. Na rozdíl od hodnotových typů se jejich hodnota uloží do oblasti paměti nazývané halda. Do proměnné se uloží pouze adresa paměti, kde je hodnota uložena reference. Příklad na konec To byl tedy výčet základních typů jazyka C# a na konec předložím velmi jednoduchý příkladek, který ilustruje použití číselného typu int a řetězce znaků (string). using System; namespace ukazky_zive class ConsoleSecteniApp public static void Main(string[] args) //deklarace promennych int a; 9

11 int b; int v; //deklarace promenne i s jeji definici string vysl_zprava = "Vysledek scitani je "; /* Ulozeni hodnot do promennych * ze standartniho vstupu pomoci metody ReadLine * objektu Console ze systemove knihovny trid System * a nasledne pouziti metody Int32.Parse pro prevod * retezce na cislo */ Console.Write("Prvni cislo:"); a = Int32.Parse(Console.ReadLine()); Console.Write("Druhe cislo:"); b = Int32.Parse(Console.ReadLine()); /*secteni hodnot promennych a,b a ulozeni vysledku * operace do promenne v */ v = a + b; Console.WriteLine(vysl_zprava + v); V příštím díle našeho seriálu se budeme zaobírat základy objektového programování a jeho aplikací v C#. Poznáváme C# a Microsoft.NET 3. díl V tomto díle se začneme zaobírat základy objektového programování a jeho aplikací v jazyku C#. Vysvětlíme si co je to třída, její instance a základní pojmy s tím související. Je důležité tyto pojmy a s tím spojené principy chápat, protože na nich je postaven vývoj aplikací pro platformu Microsoft.NET. Proč objekty? Objektově orientovaný přístup k vývoji softwaru je v dnešní době velmi využívaný. Na těchto principech staví své systémy a technologie všechny moderní společnosti IT světa. Důvody použití tohoto přístupu na rozdíl od přístupu strukturovaného, používaný například v jazycích Pascal nebo C, mimo jiné jsou: přehlednější zdrojové kódy vyšší znovupoužitelnost vytv Objekty a třídy Objekty ve světě vývoje aplikací často představují abstrakci objektů reálného světa. To s sebou přináší, větší možnosti při návrhu struktury jednotlivých částí aplikace. Každý objekt je instancí neboli výskytem třídy. Třídu lze chápat jako šablonu pro vytváření objektů. Pro lepší pochopení si jako třídu lze představit například auto a jako objekt její instanci třeba Audi A4, Volvo S40 nebo Ford Mondeo. Všechny tyto objekty jsou konkrétní typy aut. To nám přináší důležitou informaci a tou je, že při vlastním programování nepíšeme přímo objekty, ale třídy a podle ní pak budou jednotlivé objekty vytvářeny. 10

12 Každá třída může obsahovat definici pro: metody operace, které může třída nebo pomocí ní vytvořený objekt provádět atributy představují stavy/data třídy nebo vytvořeného objektu Zapouzdření objektů Zapouzdření je jednou ze základních vlastností objektově orientovaného přístupu a existuje z důvodu, že data a operace objektu se vzájemně ovlivňují tvoří nedělitelný celek. Zapouzdření znamená, že objekt má některé své členy (metody/atributy) před okolím skryty. Členy přístupné okolí se nazývají rozhraní objektu. 11

13 Se zapouzdřením souvisí i vlastnosti nazývané: selektory slouží k získání hodnoty nějakého skrytého atributu, ale neumožňují tuto hodnotu modifikovat modifikátory slouží ke změně hodnoty nějakého skrytého atributu Implementace v C# Na ukázku si v jazyku C# napíšeme jednoduchou třídu představující žárovku, která bude mít atributy identifikující její výkon a stav zda-li svítí. Konstruktor Každá třída musí mít konstruktor, který představuje předpis na vytvoření nového objektu. Konstruktor nejčastěji obsahuje kód pro inicializaci (první nastavení hodnoty) některých, nebo všech atributů nově vznikajícího objektu. Pokud ho ve své třídě neuvedeme, použije se implicitní (výchozí) konstruktor, který žádné takovéto přípravné operace neprovádí. Zápis definice konstruktoru je následující: public Nazev_Tridy() //kod operaci, ktere se provedou vzdy pri vytvareni noveho objektu podle teto tridy Konstruktoru mohou být také předány parametry se kterými bude při vytváření nového objektu pracovat. Definice konstruktoru očekávajícího parametry se zapisuje takto: public Nazev_Tridy(datový_typ Název_parametru, datový_typ Název_parametru2) //kod operaci, ktere se provedou vzdy pri vytvareni noveho objektu podle teto tridy a vyuzivaji predane //parametry Atributy a vlastnosti Atributy třídy se v C# definují zápisem: Specifikátor_přístupu datový_typ Nazev_Atributu; V našem ukázkovém příkladu má atribut výkon implementován selektor i modifikátor a atribut svítí pouze selektor. K deklaraci (vyjádření) selektorů a modifikátorů slouží v jazyku C# takzvané vlastnosti (properties). Vlastnosti tedy určují co se provede za akce při čtení atributu (blok get) a při jeho modifikaci (blok set). Jejich zápis je ve tvaru: Specifikátor_přístupu datový_typ Nazev_Vlastnosti get // v tomto bloku se může nalézat provádění operací pred vrácenim hodnoty //například nějaký výpočet, tento blok musí obsahovat klíčove slovo return, //které určuje, že specifikátor vrátí výslednou hodnotu return výsledná_hodnota; 12

14 set //v tomto bloku se nacházejí operace potřebné k nastavení hodnoty skrytého //atributu, klíčové slovo value představuje přiřazovanou hodnotu název_atributu = value; Získání hodnoty se následně provádí zápisem: nazev_promenne = Nazev_Objektu.Nazev_Vlastnosti; Nastavení hodnoty pak: Nazev_Objektu.Nazev_Vlastnosti = hodnota; Metody Výsledkem toho, že atribut svítí nebude mít uveden modifikátor, nebude moci být pomocí vlastnosti nastaven. Změna atributu svítí bude implementována v metodách (operacích) rozsvítit a zhasnout. Zápis metod vypadá takto: Specifikátor_přístupu datový_typ Nazev_metody() //kód metody return návratová_hodnota; V případě, že je jako datový typ metody uveden void, metoda žádnou hodnotu nevrací a slovíčko return není poviné. Také je možné, aby byli metodě při volání předány nějaké parametry. Zápis metody by v tomto případě byl následovný: Specifikátor_přístupu datový_typ Nazev_metody(datový_typ Nazev_parametru, datový_typ Nazev_parametru2) //kód metody return návratová_hodnota; Specifikátory přístupu Pro určení viditelnosti členů vně třídy nebo vytvořeného objektu slouží specifikátory přístupu, které jsou v C# představovány klíčovými slovy: private specifikuje, že člen je privátní (soukromý) - přístupný pouze uvnitř třídy public specifikuje, že člen je veřejný přístupný z jiných tříd protected specifikuje, že člen je přístupný pouze potomkům třídy Specifikátor protected souvisí s problematikou dědičnosti, kterou si probereme v příštím díle. Ve výčtu je tento specifikátor přístupu uveden pouze pro úplnost seznamu základních specifikátorů. Na obrázku níže je zobrazena třída Žárovka pomocí jazyku UML (Unified Modeling Language), což je jazyk užívaný pro vizuální vyjádření objektových konceptů. 13

15 Ukázková třída žárovka Následující zdrojový kód implementuje třídu Žárovka a její členy. namespace priklady_zive public class Zarovka // deklarace soukromych atributu tridy private int vykon; private bool sviti; //deklarace vlastnosti Vykon pres kterou lze precist ci nastavit //atribut vykon public int Vykon //definice selektoru get return vykon; //definice modifikatoru set vykon = value; //deklarace vlastnosti Sviti pres kterou lze pouze precist atribut sviti public bool Sviti //definice selektoru get return sviti; //konstruktor bez vstupnich parametru tridy zarovka - pri vytvoreni je zarovka //zhasnuta public Zarovka() sviti = false; //metoda, ktera nastavi atribut sviti na hodnotu true public void Rozsvitit() 14

16 sviti = true; //metoda, ktera nastavi atribut sviti na hodnotu false public void Zhasnout() sviti = false; Tímto máme vytvořenou naši novou třídu a teď budeme potřebovat vytvořit instanci této třídy konkrétní objekt. Vytvoření nové instance a uložení reference na ni do proměnné se provádí v C# následujícím způsobem: Datový_typ Nazev_promenne = new Datový_typ(); Tento řádek zařídí zavolání bezparametrického konstruktoru třídy. Zdrojový kód konzolové aplikace s vytvořením nové instance třídy žárovka a využitím členů vzniklého objektu by tedy mohl vypadat takto: namespace priklady_zive class ZarovkaTest public static void Main(string[] args) //zavolani konstruktoru pro vytvoreni instance tridy zarovka Zarovka nasezarovka = new Zarovka(); //nastaveni atributu vykon přes vlastnost Vykon nasezarovka.vykon = 120; Console.WriteLine("Vykon zarovky je " + nasezarovka.vykon); Console.WriteLine("Atribut sviti zarovky = " + nasezarovka.sviti); //zavolání metody Rozsvitit nasezarovka.rozsvitit(); Console.WriteLine("Atribut sviti zarovky po zavolani metody Rozsvitit = " + nasezarovka.sviti); //zavolání metody Zhasnout nasezarovka.zhasnout(); Console.WriteLine("Atribut sviti zarovky po zavolani metody Zhasnout = " + nasezarovka.sviti); Console.ReadLine(); Výstup aplikace by vypadat takto: Vykon zarovky je 120 Atribut sviti zarovky = False Atribut sviti zarovky po zavolani metody Rozsvítit = True Atribut sviti zarovky po zavolani metody Zhasnout = False Příště si rozebereme problematiku dědičnosti tříd v C#, blíže se podíváme se na metody a konstruktory s parametry a také se dozvíme co se skrývá pod pojmem přetížení. 15

17 Poznáváme C# a Microsoft.NET 4. díl Dědičnost a s ním spojené řízení verzí jsou klíčové vlastnosti v objektově orientovaném přístupu k vývoji aplikací. Pojmy jako předek, potomek či překrytí jsou pojmy které s dědičností souvisí a v tomto díle se seznámíme s jejich významem. Také se dozvíme co to je přetěžování metod a jak je možné jej naimplementovat v C#. Jednoduchá dědičnost v C# Pojem dědičnost v oblasti objektově orientovaného programování znamená možnost odvodit nějakou třídu z třídy jiné. Potřeba využít dědičnosti nastává tehdy, je-li jedna třída speciálním případem jiné třídy. Třída, která slouží jako základ pro odvozenou třídu nese označení bázová třída. Bázová třída je také často nazývána předkem odvozené třídy. V jazyku C# může být pro každou třídu určen pouze jeden předek jednoduchá dědičnost. To je rozdíl oproti jazyku C++, kde existovala několikanásobná dědičnost. Autoři jazyka C# se ale několikanásobné dědičnosti vyvarovali, kvůli problémům, které při jejím užívání vznikaly. Nepřítomnost několikanásobné dědičnosti řeší jazyk C# stejně jako Java použitím rozhraní, kterými se budeme zabývat později. Využití dědičnosti Představme si nějaký fiktivní informační systém, kde vytvoříme například třídu zaměstnanec. Tato třída bude implementovat základní operace a vlastnosti společné pro všechny druhy zaměstnanců ve firmě. Ve firmě pracuje několik druhů zaměstnanců. V té naší fiktivní to jsou programátor, traktorista a účetní (přiznávám, že je to zajímavá kombinace, ale v tomto případě to nijak nevadí). Každý vykonává pro svou profesi specifické operace a proto je zde vhodné vytvořit pro jejich reprezentaci vlastní třídy odvozené od třídy Zaměstnanec. Diagram tříd v jazyku UML by pro tuto situaci vypadal následovně. Diagram zobrazuje bázovou třídu Zaměstnanec, která má atribut HodinovaSazba datového typu int, vlastnost, která k němu zprostředkovává přístup, pak operaci VypisTyp která ve formě řetězce vrací typ zaměstnance a nakonec operaci VypoctiMzdu, která spočte mzdu podle počtu odpracovaných hodin a hodinové mzdy. Počet odpracovaných hodin bude metodě VypoctiMzdu předán ve formě vstupního parametru. 16

18 Odvozené třídy Programator, Traktorista a Ucetni mají operace patřící k jejich pracovnímu zařazení. Z důvodu, že tyto třídy dědí z třídy Zamestnanec obsahují také všechny její členy. Jinými slovy můžeme říci, že tyto členy zdědily po svém předkovi. Implementace dědičnosti v C# Zápis definice třídy, která dědí od jiné třídy se zapisuje v C# následujícím způsobem: public class NazevOdvozeneTrid y: NazevBazoveTridy //implementace tridy Zápis konstruktoru, který volá konstruktor implementovaný v bázové třídě takto: public NazevOdvozeneTridy() : base() //implemetace dodatecnych operaci Pokud v metodě, která je implementována potřebujeme zavolat metodu implementovanou na bázové třídě, opět využijeme klíčového slova base a to tímto způsobem: public NazevMetody () base.nazevbazovemetody(); Specifikátor přístupu protected S dědičností souvisí specifikátor přístupu protected. Tento specifikátor přístupu použijeme v případě, kdy chceme, aby daný člen byl přístupný pouze odvozeným třídám. V našem příkladu se zaměstnanci je tento specifikátor přístupu použit u atributu hodinovasazba. Byl použit místo specifikátoru private, protože privátní členy se nedědí a bylo by nutné tento atribut ve všech třídách znovu deklarovat. Virtuální metody a jejich překrytí pomocí new a override S implementací dědičnosti se často setkáváme s potřebou předefinovat funkčnost nějaké metody v odvozené třídě. Tomuto předefinování se také říká překrytí. V našem příkladě, jak níže uvidíme, překrýváme u každé odvozené třídy metodu VypisTyp. Pokud máme v úmyslu nějakou metodu ve třídě později v odvozených třídách překrývat, musíme tuto metodu nadefinovat jako virtuální. K tomu slouží v C# klíčové slovo virtual. Zápis virtuální metody tedy vypadá takto: specifikátor_přístupu virtual datový_typ NazevMetody() //vykonny kod metody K překrývání metod v odvozených třídách slouží klíčová slova override a new. Override použijeme v případě, kdy nová metoda v odvozené třídě plní logicky stejnou funkci, má stejný specifikátor přístupu a stejný návratový typ jako metoda v bázové třídě. Slovíčko new logicky použijeme v opačném případě, tedy když nová metoda plní logicky jinou funkci nebo mění specifikátor přístupu a nebo má jiný návratový typ. Používání těchto 17

19 slov správně také velmi zvýší čitelnost zdrojového kódu, protože bude zřejmé jestli se jedná o překrytí a jaký smysl toto překrytí má. Metody a konstruktory se vstupními parametry V minulém díle jsem se zmiňoval o možnostech implementovat metody se vstupními parametry. Nejjednodušší na pochopení bude, když uvedu příklad metody pro součet dvou čísel typu int. Zápis metody by mohl vypadat takto: public int Secti(int a, int b) int vysledek = a + b; return vysledek; Volání metody by pak probíhalo tímto způsobem: int soucet = Nazev_tridy.Secti(5,6); Uvnitř metody pracujeme s hodnotami předaných parametrů pomocí proměnných s názvy deklarovanými v hlavičce metody (v ukázkové metodě sečti jsou to proměnné a, b). Proměnné s těmito hodnotami jsou platné pouze v rámci metody, stejně jako pomocné proměnné definované v ní ( v našem případě je to proměnná výsledek). Stejně tak je to i konstuktorů s parametry. Přetěžování metod a konstruktorů Metody a konstruktory mohou být v rámci třídy definovány v několika verzích, do kterých vstupuje rozdílný počet parametrů. Takovéto metody/konstruktory jsou nazývány přetížené. V připadě metod musejí mít všechny verze stejný návratový typ. Pro pochopení si přetížíme naši metodu Secti. Po tomto přetížení budeme moci při volání této metody použít dva nebo tři vstupní parametry. public int Secti(int a, int b) int vysledek = a + b; return vysledek; public int Secti(int a, int b, int c) int vysledek = a + b + c; return vysledek; Příklad se zaměstnanci v C# Po vysvětlení potřebných pojmů se vrátíme k našemu příkladu se zaměstnanci. Výše jsme mohli vidět UML diagram tříd popisující naši modelovou situaci, teď zbývá k tomuto diagramu ještě přidat zdrojový kód. using System; namespace PrikladyZive public class Zamestnanec 18

20 protected int hodinovasazba; //konstuktor bez parametru, ktery nastavi atribut hodinovasazba //vuzitim klicoveho slova thi0073, //ktere oznacuje aktualne vytvarenou instanci public Zamestnanec() this.hodinovasazba = 60; //vlastnost zajistujici pristup k atributu hodinovasazba public int HodinovaSazba get return hodinovasazba; set hodinovasazba = value; //virtualni metoda ktera vrati ve forme retezce typ zamestance, //v tomto pripade vraci nazev obecneho zamestance public virtual string VypisTyp() return "Obecny zamestnanec"; //metoda pro vypocet mzdy - pocet odpracovanych hodin je //predavan metode formou vstupni parametru public int VypoctiMzdu(int pocethodin) //definice promenne pro ulozeni vysledku - tato promenna //se po vykonani metody odstrani z pameti int vysledek = pocethodin * hodinovasazba; return vysledek; using System; namespace PrikladyZive public class Traktorista : Zamestnanec //volani konstruktoru predka, ktery zaridi nastaveni hodnoty //atributu hodinovasazba na 60 public Traktorista() : base() //prekryti metody VypisTyp public override string VypisTyp() return "Traktorista"; public void OrejPole() 19

21 //zde by se nachazel vykonny kod using System; namespace PrikladyZive public class Ucetni : Zamestnanec public Ucetni() hodinovasazba = 100; public override string VypisTyp() return "Ucetni"; public void ZauctujPolozku(string polozka) //zde by se nachazel vykonny kod Nyní už zbývá jen vytvořit nějakou jednoduchou testovací aplikaci na demonstraci, toho jak dědičnost funguje. public class ZamestnanciTestApp public static void Main(string[] args) Zamestnanec zam = new Zamestnanec(); //pouziti konstruktoru s parametrem Programator prog = new Programator(150); Traktorista trak = new Traktorista(); Ucetni ucetni = new Ucetni(); Console.WriteLine(zam.VypisTyp() + " - Hodinova sazba: " + zam.hodinovasazba); Console.WriteLine(prog.VypisTyp() + " - Hodinova sazba: " + prog.hodinovasazba); Console.WriteLine(trak.VypisTyp() + " - Hodinova sazba: " + trak.hodinovasazba); Console.WriteLine(ucetni.VypisTyp() + " - Hodinova sazba: " + ucetni.hodinovasazba); Console.WriteLine("Mzdy po 120 odpracovanych hodinach"); Console.WriteLine(zam.VypisTyp() + " - Mzda: " + zam.vypoctimzdu(120)); Console.WriteLine(prog.VypisTyp() + " - Mzda: " + prog.vypoctimzdu(120)); Console.WriteLine(trak.VypisTyp() + " - Mzda: " + trak.vypoctimzdu(120)); Console.WriteLine(ucetni.VypisTyp() + " - Mzda: " + ucetni.vypoctimzdu(120)); Console.ReadLine(); Výstup demonstrativní aplikace by měl vypadat následovně: 20

22 Obecny zamestnanec Hodinova sazba: 60 Programator Hodinova sazba: 150 Traktorista Hodinova sazba: 60 Ucetni Hodinova sazba: 100 Mzdy po 120 odpracovanych hodinach Obecny zamestnanec Mzda: 7200 Programator Mzda: Traktorista Mzda: 7200 Ucetni Mzda: V příštím díle se dozvíme co to jsou statické členy a polymorfizmus. Poznáváme C# a Microsoft.NET 5. díl Po dědičnosti a řízení verzí s tímto dílem přichází další problematika spojená s objektově orientovaným přístupem k vývoji aplikací. Jedná se o statické členy s jejichž použitím se v tomto díle seznámíme. Také si vysvětlíme pojem polymorfizmus, který je také s objektovým programováním silně spjat. Statické členy tříd V minulých dílech, ve kterých jsme se zaobírali modelováním tříd, jsme se dozvěděli, že třídy mohou mít atributy a operace. Zatím jsme v našich příkladech používali pouze členy, které mohli být využity až po vytvoření instance třídy objektu. Tyto členy jsou nazývány instanční a jsou tedy svázány až s konkrétním objektem. Existují však ještě členy třídy, které využijeme v případě, když potřebujeme, aby nebyly spojeny s nějakou konkrétní instancí dané třídy, ale s třídou jako takovou. Takovéto členy označujeme jako členy statické. K těmto členům potom přistupujeme nikoliv prostřednictvím jména instance, ale prostřednictvím jména třídy. Statické atributy Nejjednodušším druhem statického člena je statický atribut třídy. Statický atribut se v jazyku C# deklaruje klíčovým slovem static. Školáckým příkladem využití statického atributu třídy je atribut pro ukládání počtu vytvořených instancí třídy. Zdrojový kód by mohl vypadat takto: using System; namespace PrikladyZive5 public class NaseTrida private static int pocetinstanci; //definice vlastnosti, která zpristupnuje atribut pocetinstanci public static int PocetInstanci getreturn pocetinstanci; public NaseTrida() //pri kazdem vytvareni noveho objektu podle nasi tridy se // hodnota atributu zvysi pocetinstanci++; 21

23 Poznámka: Výraz pocetinstanci++, zařídí zvýšení hodnoty proměnné pocetinstanci o jedna. Je to stejné jako kdybychom napsali : pocetinstanci = pocetinstanci + 1; Analogicky k tomu by výraz pocetinstanci-- zařídil to samé jako výraz pocetinstanci = pocetinstanci 1; Jak jsem zmínil výše, k statickým členům přistupujeme prostřednictvím jména třídy. Takže přečtení atributu počtu instancí, uložení této hodnoty do proměnné a její vypsání na obrazovku bysme zapsali takto: public class Zive5TestApp public static void Main(string[] args) NaseTrida nase1 = new NaseTrida(); NaseTrida nase2 = new NaseTrida(); int pocet = NaseTrida.PocetInstanci; Console.WriteLine(pocet); Console.ReadLine(); Statické metody Stejně jako atributy můžeme i metody pomocí klíčového slova static označit jako statické. Použití statickým metod s sebou nese jedno nezanedbatelné omezení a tím je, že tyto metody nemohou být virtuální. Pokud bychom se statickou metodu pokusili nadefinovat jako virtuální, kompilátor zahlásí chybu. Zápis naší ukázkové třídy obohacené o statickou metodu by vypadal takto: using System; namespace PrikladyZive5 public class NaseTrida private static int pocetinstanci; public static int PocetInstanci getreturn pocetinstanci; public NaseTrida() pocetinstanci++; public static string VypisPozdrav() return "Ahoj"; Následné vyvolání statické metody a uložení její návratové hodnoty do proměnné by se zapsalo tímto způsobem : string textpozdravu = NaseTrida.VypisPozdrav(); 22

24 Statické konstruktory Tak jako mohou existovat i jiné druhy statických členů, mohou existovat i statické konstruktory. Statické konstruktory se využívají ke spuštění inicializačních operací pracujícími se statickými členy. Tyto konstruktory jsou volány před vytvořením první instance třídy nebo před prvním použitím jiného statického členu. Zdrojový kód naší ukázkové třídy se statickým konstruktorem: public class NaseTrida private static int pocetinstanci; private static string jmeno; public static string Jmeno getreturn jmeno; //staticky konstruktor static NaseTrida() jmeno = "Petr"; Pokud se pokusíme přečíst hodnotu atributu jméno, aniž bychom ho předtím nastavovali, dostaneme hodnotu Petr. To je zapříčiněno právě tím, že před prvním přístupem k němu je zavolán statický konstruktor. Polymorfismus Polymorfismus, neboli mnohotvárnost je spolu se znovuvyužitelností kódu nejdůležitější aspekt v objektově orientovaném programování. Polymorfismus znamená možnost použít mnoho druhů typů se stejným rozhraním (veřejně přístupnými operacemi a atributy) bez ohledu na detaily jejich implementace. Polymorfismus tedy zajiš uje to, že nám stačí znát rozhraní bázové třídy a použitím tohoto rozhraní volat operace a atributy, které jsou různě naimplementovány v odvozených třídách. Z tohoto tvrzení plyne, že polymorfismus je svázán s použitím dědičnosti. Použití polymorfizmu si demonstrujeme na jednoduchém příkladu z obrazci. Nadefinujeme si třídu Obrazec, která bude implementovat virtuální metodu pro vykreslení. Třída Obrazec bude sloužit jako bázová pro třídy Čtverec, Obdélník a Kruh. Tyto třídy budou překrývat metodu Vykresli svoji odpovídající verzí a navíc implementují metodu DejObsah. Diagram příkladu s použitím známé notace UML je realizován takto: 23

25 Implementace těchto tříd není ničím významným zajímavá, protože využívá stejné principy jako minulý příklad se zaměstnanci. Zajímavější je ovšem spustitelná třída, která je využívá. V ní totiž využijeme výhody polymorfismu a to tak, že deklarujeme referenční proměnnou ( = proměnná pro referenční typ) pro objekt typu Obrazec, která bude odkazovat na objekt typu Kruh. Obrazec obrazecpromenna = new Kruh(); Kompilátor neohlásí chybu, že se snažíme referenční proměnnou odkazovat na objekt jiného typu, protože v objektově orientovaném programování platí, že potomek může nahradit předka, což se v tomto případě děje. Kruh tedy může nahradit obrazec, nebo je jeho potomkem. Polymorfismus zajistí, že pokud zavoláme metodu Vykresli, bude zavolána její verze implementovaná ve třídě Kruh a ne implementace ve třídě Obrazec jak by jistě mnozí čekali. Analogické chování bychom zaznamenali, pokud by tato proměnná odkazovala na objekt typu Obdélník nebo Čtverec. Takže pokud spustíme tento kód: Obrazec obrazecpromenna = new Kruh(); Console.WriteLine(obrazecPromenna.Vykresli());..výstup bude Kruh. Tento příklad demonstruje základní myšlenku polymorfismu a to tím, že každý typ obrazce umí provést operaci Vykresli(), ale každý z nich ji činí jinak má jinou implementaci metody. Následující obrázek zjednodušeně zobrazuje, jak tato situace vypadá v paměti. 24

26 To, že jsme nechali referenční proměnnou předka odkazovat na objekt potomka se v terminologii označuje jako upcasting. Jinými slovy se automaticky provedlo takzvané implicitní přetypování, které zapříčiní, že budeme moci na nově vzniklé instanci volat pouze členy tvořící rozhraní předka. To znamená,že členy, které jsou na odvozené třídě navíc (například metoda DejObsah), by v tomto případě nebyly k dispozici. To, které členy jsou k dispozici totiž závisí na typu referenční proměnné. Pokud bychom chtěli na této instanci volat všechny členy její třídy (v našem případě kruhu), museli bychom provést downcasting a to pomocí explicitního přetypování. Při explicitním přetypování musíme, na rozdíl od přetypování implicitního, uvést na jaký typ chceme objekt převést. Zápis by byl následující. Kruh kruhpromenna = (Kruh)obrazecPromenna; Po tomto kroku bude možno na instanci zavolat i metodu DejObsah. Ovšem pozor, pokud bychom se pokoušeli takto přetypovat instanci, která není typu Kruh, běh programu by skončil chybou. Uvedeného volání členů přes rozhraní jejich společného předka je hojně využíváno za účelem odstínění klienta využívajícího našich knihoven od skutečné implementace operací na potomcích. Jinak řečeno klienta vůbec nemusí zajímat, která konkrétní implementace metody se zavolá, protože díky polymorfismu bude použita ta správná. V příštím díle se budeme zaobírat abstraktními třídami a objasníme si princip použití rozhraní. Poznáváme C# a Microsoft.NET 6. díl Vysvětlení pojmu rozhraní a jeho principu v objektově orientované tvorbě aplikací v jazyku C# je to, co nás čeká v tomto díle. Spolu s tím se seznámíme s abstraktními třídami, jejichž význam je z rozhraními velmi úzce spojen. Abstraktní třídy Abstraktní třídy využijeme v situaci, kdy potřebujeme definovat dohodu o tom jaké členy bude odvozená třída muset implementovat. Abstraktní třídy se chovají stejně jako normální třídy až na to, že mají jednu nebo více členských metod definované jako abstraktní. Abstraktní metody jsou reprezentovány pouze hlavičkou funkce, tedy definicí jejího návratového typu, počtu vstupních parametrů a jejich typů. Tělo abstraktních metod je prázdné a jejich implementace je ponechána na odvozené třídy. Jelikož funkčnost abstraktních metod není naimplementována, není možné abstraktní třídy instancovat (nelze vytvořit objekt tohoto typu). Takže pokud bychom se o to pokusili: AbstraktniTrida abstrobj = new AbstraktniTrida(); 25

27 při kompilaci programu by došlo k chybě. Poznámka: Přestože abstraktní třídy nemouhou být instancovány, nic vám nebrání v definici jejich konstruktoru, poněvadž implicitní konstruktor (konstruktor bez parametrů) je volán na všech předcích instancované třídy. K definice třídy nebo její metody jako abstraktní slouží v jazyku C# klíčové slovo abstract. //definice abstraktni tridy public abstract class AbstaktniTrida //tato metoda musi byt v odvozene tride naimplementovana public abstract void AbstraktniMetoda(); public void NormalniMetoda() //implementace metody Z kódu je patrné, že metoda AbstraktniMetoda() je definována jako abstraktní a tudíž má pouze hlavičku a žádné tělo. V případě, že se pokusíme vytvořit odvozenou třídu v níže uvedené podobě, tedy bez implementace metody AbstraktniMetoda(), projekt nepůjde zkompilovat. public class OdvozenaTrida : AbstraktniTrida public OdvozenaTrida() Popis kompilační chyby by vypadal takto: PrikladyZive6\OdvozenaTrida.cs(5): `PrikladyZive6.OdvozenaTrida` does not implement inherited abstract member `PrikladyZive6.AbstraktniTrida.AbstraktniMetoda()` Takže pokud bychom chtěli dát vše do pořádku třída OdvozenaTrida by měla vypadat následujícím způsobem: public class OdvozenaTrida : AbstraktniTrida public OdvozenaTrida() public override void AbstraktniMetoda() //implementace metody Vyjádření této situaci by v notaci vizuálního modelovacího jazyku UML vypadalo následovně: 26

28 Rozhraní Rozhraní, podobně jako abstraktní třídy, umožňují vytvořit jakousi dohodu o tom, které členy bude muset nově vytvářená třída definovat. K tomu, aby nově vytvářená třída byla nucena definovat členy rozhraní,,musíme uvést, že třída konkrétní rozhraní implementuje. Na rozdíl od abstraktních tříd musí třída, která nějaké rozhraní implementuje definovat všechny členy uvedené v rozhraní. Z toho plyne, že rozhraní také můžeme chápat jako abstraktní třídu, která má všechny členy abstraktní. Pro vytváření rozhraní existuje v jazyku C# klíčové slovo interface. Definice rozhraní se zapisuje takto: public interface INaseRozhrani void PrvniMetodaRozhrani(); void DruhaMetodaRozhrani(int ciselnyparametr); Jistě jste si všimli, že u metod rozhraní není uveden žádný specifikátor přístupu. Je tomu tak z důvodu, že u rozhraní se toto neprovádí, protože neslouží k ničemu jinému, než k definici metod přístupných na implementujících třídách. Zápis, že třída nějaké rozhraní implementuje je naprosto stejný jako zápis pro určení bázové třídy. public class ImplementujiciTrida : INaseRozhrani public void PrvniMetodaRozhrani() // Implementace metody public void DruhaMetodaRozhrani(int ciselnyparametr) // Implementace metody Všechny definice metod, které jsou předepsány v rozhraní, musí mít v implementující třídě specifikátor přístupu public, jinak kompilátor při kompilaci ohlásí chybu. 27

29 Vyjádření v UML se realizuje následujícím způsobem: Když třída implementuje nějaké rozhraní je možné získat odkaz(referenční proměnnou) na rozhraní přetypováním instance třídy na toto rozhraní. Prostřednictvím tohoto odkazu se pak mohou volat metody z rozhraní. INaseRozhrani objekt = new ImplementujiciTrida(); Nejedná se o nic jiného než o použití polymorfismu, o kterém jsem se zmínil v předchozím díle. Rozhraní a dědičnost Při přetypování instance třídy na rozhraní se při volání metod prochází celá hierarchie dědičnosti, dokud není nalezena poslední třída implementující toto rozhraní. Nestačí tedy, když nějaký objekt pouze obsahuje správné metody samy o sobě. Uveďme si příklad: public class ImplementujiciTrida : INaseRozhrani public void PrvniMetodaRozhrani() Console.WriteLine("ImplementujiciTrida.PrvniMetodaRozhrani()"); public class NeimplementujiciTrida : ImplementujiciTrida public void PrvniMetodaRozhrani() Console.WriteLine("NeimplementujiciTrida.PrvniMetodaRozhrani()"); Máme zde dvě třídy. Třídu ImplementujiciTrida, která implementuje rozhraní INaseRozhrani a tím pádem i jeho metodu. A třídu NeimplementujiciTrida, která dědí z třídy ImplementujiciTrida, implementuje metodu uvedenou v rozhraní INaseRozhrani, ale nemá uvedeno, že toto rozhraní implementuje. Zkusme spustit následující kód: NeimplementujiciTrida neimpl = new NeimplementujiciTrida(); neimpl.prvnimetodarozhrani(); INaseRozhrani nase = (INaseRozhrani) neimpl; nase.prvnimetodarozhrani(); 28

30 Dostaneme výstup, který bude vypadat takto: NeimplementujiciTrida.PrvniMetodaRozhrani() ImplementujiciTrida.PrvniMetodaRozhrani() Vidíme, že pokud zavoláme prostřednictvím referenční proměnné rozhraní metodu PrvniMetodaRozhrani(), tak se nezavolá verze metody ze třídy NeimplementujiciTrida, a to přesto, že tato třída obsahuje tuto funkci ve správném tvaru. Děje se tak z důvodu, že třída NeimplementujiciTrida příslušné rozhraní neimplementuje. Vícenásobná implementace Hlavním důvodem pro vznik rozhraní bylo nahrazení vícenásobné dědičnosti, která je přítomna například v jazyku C++. Proto, na rozdíl od dědičnosti objektů, může třída implementovat více rozhraní. Následující příklad ukazuje využití násobné implementace rozhraní. public interface PrvniRozhrani void Pracuj1(); public interface DruheRozhrani void Pracuj2(); public class NasobImpl : PrvniRozhrani,DruheRozhrani public void Pracuj1() // Implementace metody public void Pracuj2() // Implementace metody Příště se seznámíme s uzavřenými třídami, vnořenými třídami, konstantami a proměnnými pouze pro čtení. Poznáváme C# a Microsoft.NET 7. díl V tomto díle si ještě doplníme znalosti související s modelováním tříd a seznámíme se s konstantami a proměnnými pouze pro čtení. Uzavřené třídy Uzavřené třídy se používají v případě, chceme-li zabránit tomu, aby třída byla použita jako bázová třída. Smyslem jejich použití je zabránit nežádoucímu odvozování nových tříd. Uzavřená třída se deklaruje pomocí klíčového slova sealed. public sealed class UzavrenaTrida //definice třídy 29

31 V případě pokusu o odvození nové třídy z této třídy dojde při kompilaci k chybě. //chybny priklad public class PotomekUzavrene : UzavrenaTrida //definice třídy Privátní konstruktory Použití privátních konstruktorů představuje v jazyku C# cestu k zabránění vytvoření instance dané třídy. To se může hodit v situacích kdy třída obsahuje pouze statické členy, protože pokud tomu tak je, není vůbec třeba instance takovéto třídy vytvářet. Následující příklad ukazuje možné využití. public class MatematickeKonstanty public static double Pi = ; //tim ze implicitni konstruktor deklarujeme jako privatni, //nebude mozne vytvaret instance teto tridy private MatematickeKonstanty() Následný pokus o vytvoření instance této třídy by skončil chybou. //chyba MatematickeKonstanty instancematem = new MatematickeKonstanty(); Poznámka: Další účinek použití privátního konstruktoru je nemožnost použít danou třídu jako bázovou, poněvadž každá třída při svém instancování volá implicitní konstruktor svých předků, což by v tomto případě nebylo možné. Specifikátor přístupu internal Specifikátor přístupu internal poskytuje možnost jak zviditelnit třídu nebo členy třídy širší množině tříd a současně zamezit viditelnosti pro všechny třídy. Tento specifikátor přístupu je předurčen pro psaní pomocných tříd respektive členů, které by měli být skryty koncovému uživateli tříd. Použití specifikátoru přístupu internal má za následek to, že třída nebo člen jsou viditelné pouze v rámci assembly, což je, jak jsem dříve uvedl v některém z předchozích dílů, knihovna DLL, v níž se ve zkompilované podobě (do metajazyku CIL) nacházejí třídy patřící do stejného jmenného prostoru. //deklarace interni tridy internal class InterniTrida public class TridaInterniClen //deklarace interniho clenu internal int interniclen; public int VerejnyClen; V případě, že použijeme tento specifikátor přístupu v kombinaci se specifikátorem protected bude člen třídy viditelný všem odvozeným třídám ve stejné assembly. 30

32 Vnořené třídy V některých případech může být užitečné vložit nějakou třídu dovnitř jiné třídy. Je to zpravidla vhodné, když se jedná o nějakou pomocnou třídu, která má být využita pouze ve třídě ve které je vložena. Za tímto účelem nám jsou v jazyku C# k dispozici vnořené třídy. Vnořené třídy také, kromě výše uvedeného důvodu, zvyšují čitelnost kódu a poskytují prostředek k lepší organizaci hierarchie tříd. Následující příklad ukazuje jak by se problém pomocné třídy řešil bez použití vnořené třídy. public class HlavniTrida private PomocnaTrida instancepomocna1; public class PomocnaTrida //definice pomocne tridy Toto řešení by samozřejmě bylo funkční, ale nebylo by správné, protože pomocná třída by byla přístupná všem ostatním třídám. O něco lepší by bylo pokud by pomocná třída měla specifikátor přístupu internal. Ale i tak by byla přístupná ostatním třídám v assembly, což je také nežádoucí. Pokud pomocnou třídu vnoříme do hlavní třídy jsme schopni ji skrýt před ostatními třídami a zviditelnit ji pouze hlavní třídě, která ji jako jediná potřebuje. public class HlavniTrida private PomocnaTrida instancepomocna1; //vnitrni trida private class PomocnaTrida //definice pomocne tridy Přístup k vnořeným třídam lze podobně jako u členů řídit pomocí specifikátorů přístupu. Díky tomu tedy lze deklarovat vnořenou pomocnou třídu jako soukromou(private) a zamezit viditelnosti z ostatních tříd. Pokud chceme z nějakého důvodu zviditelnit vnořenou třídu určité množině ostatních tříd, použijeme odpovídající specifikátor přístupu. Takže změníme-li příklad do této podoby: public class HlavniTrida private PomocnaTrida instancepomocna1; //deklarace vnorene tridy public class PomocnaTrida //definice pomocne tridy..budeme schopni k této třídě přistupovat jako ke každému veřejnému členu. HlavniTrida.PomocnaTrida instancepomocna = new HlavniTrida.PomocnaTrida(); 31

33 Vyjádření vnitřních tříd v notaci vizuálního modelovacího jazyku UML je znázorněno na obrázku níže. Třídy nejsou jediný typ který lze vnořovat. Kromě tříd lze vnořovat i rozhraní, struktury a výčtové typy. Poznámka: Pojmy struktura a výčtový typ nebyli v seriálu ještě uvedeny a budou vysvětleny později. Konstanty V jazyku C# je možné definovat hodnoty jako konstanty. Aby hodnota mohla být konstantou, musí být v takovém tvaru, který lze zapsat v podobě konstanty. To s sebou přináší omezení v podobě možnosti definovat konstanty pouze pro vestavěné typy, které takto mohou být vyjádřeny. K vyjádření konstanty slouží v jazyku C# klíčové slovo const. Konstantám je při jejich deklaraci přiřazena hodnota, která později nemůže být změněna. Každá konstanta je automaticky statický člen. Příklady konstant: public class TridaKonstanty public const int ciselnakonstanta = 10; public const string retezcovakonstanta = "slovo"; Proměnné pouze pro čtení Kvůli tomu, že použití konstant je omezeno pouze na několik vestavěných typů, nelze konstanty v řadě situací použít. Mějme například třídu zaměstnanec. Pokud bysme v nějaké třídě chtěli nadefinovat konstantu tohoto typu, překlad programu by skončil chybou, protože třída zaměstnanec nemůže být vyjádřena jako konstanta. public class Zamestanec //definice tridy zamestnanec //nefunkcni priklad public class TridaKonst 32

34 public const Zamestanec zamkonst = new Zamestanec(); Pro tyto situace je v jazyku C# možnost definovat takzvané read-only proměnné, neboli proměnné pouze pro čtení. Ty s sebou omezení konstant již nenesou. Těmto proměnným lze hodnotu nastavit v konstruktoru nebo v inicializační deklaraci, ale později ji nelze změnit. Použití proměnné pouze pro čtení demonstruje následující příklad: public class Zamestanec //definice tridy zamestnanec public class TridaReadOnly public static readonly Zamestanec zam; //staticky konstruktor static TridaReadOnly() //prirazeni hodnoty, pozdeji ji jiz nelze zmenit zam = new Zamestanec(); V příštím díle se budeme zaobírat logickými operátory a příkazy pro větvení programu. Poznáváme C# a Microsoft.NET 8. díl V tomto díle se zaměříme na použití příkazů pro větvení toku programu, které nám jsou v jazyku C# k dispozici. Spolu s tím se něco dozvíme o relačních a logických operátorech, které jsou s jejich užíváním spojeny a také o přetěžování operátorů pro konkrétní třídy. Relační operátory Relační operátory využijeme v případech, kdy potřebujeme porovnat nějaké dvě hodnoty. Všechny operace, prováděné použitím relačních operátorů, mají výsledek logického typu bool. Typ bool může nabývat pouze dvou hodnot a to true (pravda) nebo false (nepravda). Jazyk C# definuje následující relační operátory: Operace Výsledek a == b true, pokud se hodnota a rovná hodnotě b a!= b true, pokud se hodnota a nerovná hodnotě b a < b true, pokud hodnota a je menší než hodnota b a <= b true, pokud hodnota a je menší než hodnota b, nebo je rovna hodnotě b a > b true, pokud hodnota a je větší než hodnota b 33

35 a >= b true, pokud hodnota a je vetší než hodnota b, nebo je rovna hodnotě b Výsledek operace můžeme znegovat použitím operátoru logické negace, který je v jazyku C# představován vykřičníkem (!). Negace znamená, že z hodnoty true se stane false a naopak. Následující příklad demonstruje možné použití operátoru logické negace. bool pokus =!(3 > 2); //vrati false Operátor == u odkazových typů, pokud ho daná třída nepřetěžuje, vrací true v případě, že se jedná o tentýž objekt a neporovnává jestli mají objekty stejnou hodnotu, jak se tomu děje v případě hodnotových typů. Pokud třída operátor přetěžuje, kompilátor použije jeho implementaci pro danou třídu. Logické operátory Logické operátory se používají k provádění logických nebo bitových operací nad hodnotami. Jazyk C# definuje následující logické operátory: Operátor Popis & bitový součin obou operandů bitový součet obou operandů ^ bitový výlučný součet obou operandů (XOR) && logický součin dvou operandů logický součet dvou operandů Operátor && se hodí, pokud potřebuje otestovat, zda oba výrazy splňují podmínku. int a = 2; int b = 3; int c = 4; int d = 5; bool vysledek = ( (a < b) && (d > c) ); //vysledek je true bool vysledek2 =( (a > b) && (d > c) ); //vysledek je false Operátor využijeme pokud mám stačí, že alespoň jeden výraz ze dvojice splňuje podmínku. Tím pádem, kdybychom v předchozím příkladu místo operátoru && použili operátor, oba výsledky by nabývali hodnoty true. bool vysledek2 =( (a > b) (d > c) ); //vysledek je true Operátory && a se od svých jednoznakových verzí liší také tím, že provádějí takzvané zkrácené vyhodnocování mezi výrazy. To znamená, že ve výrazu: a && b bude výraz b vyhodnocen pouze v případě, že je splněn výraz a. A ve výrazu: 34

36 a b bude výraz b vyhodnocen pouze pokud výraz a není splněn. Podmínkové příkazy Podmínkové příkazy se používají pro vykonání nějakých operací na základě hodnoty nějakého výrazu. Pro podmíněné vykonání operací slouží v jazyku C# příkazy if a switch. Příkaz if Podmínkový příkaz if je jeden z nejpoužívanějších příkazů. Jazyk C# disponuje možností jej použít jak v neúplné podmínce, kde je použito pouze klíčové slovo if, tak v úplné podmínce s použitím klíčových slov if a else. Parametrem příkazu if je booleovský výraz a pokud je tento výraz splňen jsou provedeny požadované operace. Syntaxe je: if (booleovský výraz) příkazy, které jsou provedeny pouze je li podmínka splněna Pokud má být za splněné podmínky vykonán pouze jeden příkaz, nejsou složené závorky nutné. Chceme-li zapsat úplnou podmínku, zápis bude vypadat následovně: if (booleovský výraz) příkazy, které jsou provedeny pouze je li podmínka splňena else příkazy, které jsou provedeny pouze není li podmínka splňena Následující jednoduchý příklad demonstruje použití příkazu if. if (a == 1) Console.WriteLine("Hodnota je rovna 1"); else Console.WriteLine("Hodnota neni rovna 1"); V případě, že potřebujeme definovat odlišné chování pro více hodnot výrazu, než jen jednu, můžeme využít příkazu else if, jak je znázorněno níže. if (a == 1) //prikazy, ktere budou provedeny pokud hodnota //promenne a je rovna 1 else if(a == 2) //prikazy, ktere budou provedeny pokud hodnota //promenne a je rovna 2 35

37 else //prikazy, ktere budou provedeny, pokud hodnota //promenne a neni rovna ani 1 ani 2 Ternární operátor Pomocí ternárního operátoru je nám umožněno v jazyku C# zapsat podmíněný výraz. Ternární operátor můžeme chápat jako zjednodušený zápis pro if-else, ale s tím rozdílem, že se jedná o výraz a nikoli příkaz jako je tomu u zmíněného if-else. Tím pádem je použití ternárního operátoru v některých situacích vhodnější. Syntaxe vypadá takto: identifikátor = (booleovský výraz)? výraz_při_splnění : výraz_při_nesplnění; Takže použití může vypadat následovně: int i = 4; //promenna stav nabude hodnoty "Je mensi" string stav = (i < 5)? "Je mensi" : "Je vetsi nebo rovno"; Příkaz switch Switch je příkaz pro mnohonásobné větvení programu. Když chceme definovat chování pro větší počet hodnot výrazu, je příkaz switch vhodnější než použití odpovídající posloupnosti příkazů if. V příkazu switch jsou jednotlivé větve pro hodnoty výrazu definovány pomocí klíčového slova case. Každá větev musí být ukončena příkazem break nebo goto. Příkaz break definitivně ukončí provádění příkazu switch a příkaz goto umožňuje skok na jiný blok case uvnitř příkazu switch. Také je možné použít větev default, která je provedena, když žádná z definovaných větví case nevyhovuje. Zápis je následující: switch (výraz) case hodnota_1 : prikazy pro hodnotu 1 break;... case hodnota_n : prikazy pro hodnotu n break; default : prikazy pro ostatni hodnoty break; Výraz podle kterého je rozhodováno musí být číselného typu nebo typu char a nebo typu string. Možné použití demostruje tento příklad: switch (a) case 1: case 2: //tento blok zpracovava hodnoty 1 i 2 Console.WriteLine("Hodnota je 1 nebo 2"); 36

38 break; case 3: Console.WriteLine("Hodnota je 3"); break; case 4: Console.WriteLine("Střední číslo"); //prikaz goto provede skok do vetve //pro hodnotu 3 goto case 3; default: Console.WriteLine("Jina hodnota"); break; Přetěžování operátorů V jazyku C# je umožněno přetěžování operátorů, jehož prostřednictvím lze definovat novou funkčnost operátorů nad třídami, takže je možné určité funkce zapisovat pomocí operátorů. Přetěžování operátorů je vhodné použít u takových datových typů u kterých je zřejmé, co daný operátor provádí. Výsledkem přetěžování operátorů je možnost pozdějšího úsporného vyjádření. Ne všechny operátory je možné ve třídě přetížit. Lze přetížit pouze některé unární, bitové nebo relační operátory. Operátor se ve třídě definuje použitím klíčového slova operator. Definice operátoru ve třídě musí být vždy označena jako statická mít specifikátor přístupu public. Následující třída představující komplexní číslo implementuje některé přetížitelné operátory. public class KomplexniCislo private int realnacast; private int imaginarnicast; public KomplexniCislo(int arealnacast, int aimaginarnicast) realnacast = arealnacast; imaginarnicast = aimaginarnicast; public int RealnaCast getreturn realnacast; setrealnacast = value; public int ImaginarniCast getreturn imaginarnicast; setimaginarnicast = value; //definice operatoru pro scitani dvou komplexnich cisel public static KomplexniCislo operator + (KomplexniCislo komplexni1, KomplexniCislo komplexni2) int lnovarealnacast = komplexni1.realnacast + komplexni2.realnacast; int lnovaimaginarnicast = komplexni1.imaginarnicast + komplexni2.imaginarnicast; return new KomplexniCislo(lNovaRealnaCast,lNovaImaginarniCast); //definice operatoru ekvivalence public static bool operator == (KomplexniCislo komplexni1, KomplexniCislo 37

39 komplexni2) return komplexni1.realnacast == komplexni2.realnacast && komplexni1.imaginarnicast == komplexni2.imaginarnicast; //definice operatoru non-ekvivalence public static bool operator!= (KomplexniCislo komplexni1, KomplexniCislo komplexni2) return komplexni1.realnacast!= komplexni2.realnacast && komplexni1.imaginarnicast!= komplexni2.imaginarnicast; //prekryti metody ToString tridy object public override string ToString() return (String.Format("0 1i",realnaCast,imaginarniCast)); Později je možné přetížené operátory použít při práci s instancemi třídy KomplexniCislo, jak demonstruje následující příklad. KomplexniCislo lkomplexnicislo1 = new KomplexniCislo(4,4); KomplexniCislo lkomplexnicislo2 = new KomplexniCislo(3,2); KomplexniCislo lsoucet = lkomplexnicislo1 + lkomplexnicislo2; Console.WriteLine("Soucet : " + lsoucet); Console.WriteLine("Ekvivalence : " + (lkomplexnicislo1 == KomplexniCislo2)); Poznámka: Při přetěžování relačních operátorů, musí být nová implementace uskutečněna v párech. To znamená, že při přetěžování operátoru == musí být naimplementován i operátor!= a naopak. Stejné pravidlo platí i pro přetěžované operátory <, > a pro <=,=>. V příštím díle na nás čekají cykly. Poznáváme C# a Microsoft.NET 9. díl Příkazy pro vytváření cyklů je problematika na kterou se zaměříme v tomto díle spolu s příkazy break a continue, které se při psaní cyklů nezřídka využívají. Zmíněn bude také rozdíl mezi prefixovým a postfixovým zápisem pro inkrementaci či dekrementaci hodnot proměnných. Prefixový versus postfixový zápis inkrementace/dekrementace Pro kratší zápis inkrementace nebo dekrementace hodnoty číselné proměnné se hojně používají speciální unární operátory složené ze dvou stejných znaků: ++ inkrement -- dekrement Oba tyto operátory mohou být použity v prefixovém zápisu (před operandem) nebo postfixovém zápisu (za operandem). Rozdíl mezi těmito dvěma druhy zápisu je v čase provedení inkrementace respektive dekrementace. ++operand hodnota je zvětšena o jedničku a je vrácena operand++ - hodnota je nejdříve vrácena a potom je zvětšena o jedničku 38

40 Příklady: int x = 5; int y = 2; x++; //x bude 6 y = ++x; // y bude 7, x bude 7 x = y++; // x bude 7, y bude 8 Příkazy pro vytváření cyklů Jazyk C# nabízí k využití tří příkazy pro vytváření cyklů, kterými jsou while, for a dowhile. S těmito příkazy jsou spojeny také příkazy break a continue, které slouží k ovlivnění průběhu cyklů. Příkazy break a continue Jak jsem zmínil výše tyto příkazy lze použít ve všech konstrukcích cyklu a ovlivňují jeho standardní průběh. break tento příkaz ukončuje nejvnitřnější neuzavřenou smyčku cyklu a ihned opouští cyklus continue zapříčiní skok na konec nejvnitřnější neuzavřené smyčky, což znamená, že zbytek těla cyklu je vynechán, a je pokračováno další iterací cyklu Příkaz while Tento iterační příkaz testuje výraz s návratovou hodnotou bool vždy před průchodem cyklu. Pokud je výraz splněn, tedy jeho výsledek je true, pak je cyklus vykonán.tím, že je testování logické podmínky prováděno na začátku, nemusí být cyklus vykonán ani jednou. Syntaxe příkazu while je: while (booleovský výraz) tělo cyklu Pokud je tělo cyklu tvořeno pouze jedním příkazem, tak složené závorky nejsou nutné. Následující příklad ukazuje použití cyklu while pro výpis čísel od 0 do 9. public static void VypisCisla() int x = 0; while (x < 10) Console.WriteLine(x++); Pokud chceme aby v nějakém případě byl zbytek těla cyklu přeskočen použijeme příkaz continue. public static void VypisCisla() int x = 0; while (x < 10) //pokud je x rovno 5, skoci se 39

41 //na konec cyklu a bude se pokracovat //dalsi iteraci if (x == 5) x++; continue; Console.WriteLine(x++); V tomto příkladu, se na obrazovku vypíšou všechna čísla od 0 do 9 kromě čísla 5, z důvodu použití příkazu continue. Kdybychom příkaz continue nahradili příkazem break, tak by se celý cyklus ukončil když bude hodnota proměnné x rovna 5 a následkem toho by se vypsala pouze čísla od 0 do 4. Protože je řídíci výraz cyklu typu bool, je možné vytvořit nekonečnou smyčku použitím hodnoty true. V tomto případě je nutné použít pro ukončení průběhu cyklu příkaz break. while (true) //telo cyklu Příkaz do-while Tento příkaz použijeme pro konstrukci cyklu s řídící podmínkou na konci. Protože je podmínka testována až pro průchodu cyklem, je zajištěno, že cyklus proběhne nejméně jednou. Stejně jako u příkazu while, cyklus probíha dokud je řídící výraz hodnoty true. Zápis příkazu do-while je: do tělo cyklu while(booleovský výraz); Následující příklad opět vypíše čísla od 0 do 9, ale nyní s využitím příkazu do-while. public static void VypisCisla() int i = 0; do Console.WriteLine(i++); while(i < 10); Poznámka: Pokud znáte programovací jazyk Pascal, dejte si pozor na důležitý rozdíl v implementaci cyklu s podmínkou na konci, který je v Pascalu vyjadřován příkazem repeat-until. V repeat-until je cyklus ukončen v případě splnění řídící podmínky a v C# u do-while je tomu naopak. Příkaz for 40

42 Vytváření cyklů pomocí příkazu for je vhodné v případě, kdy potřebujeme definovat počáteční hodnotu, ukončující podmínku a způsob změny řídící proměnné cyklu. Příkaz for nám umožňuje, na rozdíl od cyklu while, tyto věci zadat přehledně na jedno místo. Tak jako u obou předchozích konstrukcí cyklu je nutné tělo cyklu uzavřít do složených závorek, pokud obsahuje více než jeden příkaz. Obecný zápis konstrukce cyklu pomocí příkazu for je následující: for (start. výraz; booleovský výraz; změna řídící proměnné) tělo cyklu Typické použití příkazu for demonstruje následující příklad pro výpis čísel: public static void VypisCisla() for (int i = 0; i < 10; i++) Console.WriteLine(i); Při použití příkazu for je zpočátku vyhodnocen startovní výraz, který se používá pro inicializaci řídící proměnné. Dále se vyhodnotí booleovský výraz, který představuje řídící podmínku cyklu, provede se tělo cyklu a nakonec je vyhodnocen výraz pro změnu řídící proměnné. Při druhé a dalších iteracích cyklu se již začíná vyhodnocením booleovského výrazu. I přestože to není nutné, je dobrým zvykem deklarovat řídící proměnnou přímo v hlavičce příkazu for. Následující příklad by byl také funkční, ale proměnná i by byla platná i mimo cyklus což není úplně správné, protože je vhodné, aby proměnná byla platná pouze tam, kde je skutečně potřeba tj. v cyklu. public static void VypisCisla() //zde deklarovana promenna bude //platna i mimo cyklus int i; for (i = 0; i < 10; i++) Console.WriteLine(i); Do hlavičky cyklu není nutné zadávat všechny ze trojice výrazů, ale je nutné za chybějícím výrazem uvést středník aby bylo jednoznačně určeno, který výraz byl vynechán. Takovéto použití ovšem nepatři k těm nejšťastnějším, protože ubírá příkazu for jednu z jeho hlavních výhod, kterou je přehlednost. Použití příkazu for s vynecháním startovního výrazu můžete vidět na tomto příkladu: public static void VypisCisla () int i = 0; for ( ; i < 10; i++) Console.WriteLine(i); Také je možné uvést více než jeden startovní výraz a více než jeden výraz pro změnu řídící proměnné. Jednotlivé výrazy se od sebe oddělují pomocí čárky. Uvést více výrazů pro definici podmínky není možné. Ani toto použití příkazu for mu na jeho přehlednosti nepřidá, spíše naopak. Více výrazů do hlavičky příkazu může vypadat takto: 41

43 public static void VypisCisla4() int i, soucet; for (i = 0, soucet = 0; i < 10;i++, soucet += i) Console.WriteLine(soucet); Příště se něco dozvíme o polích a strukturách. Poznáváme C# a Microsoft.NET 10. díl Tento díl bude věnován implementaci struktur a výčtových typů v jazyku C#. Struktury v C# Jak bylo řečeno dříve, v prostředí.net frameworku jsou typy děleny do dvou skupin na hodnotové typy a referenční typy. Pokud vytváříme strukturu, vytváříme nový hodnotový typ. Použití struktur je vhodné při vytváření reprezentace jednoduchých objektů. Hlavní výhoda hodnotových typů je v jejich rychlé alokaci paměti a v nepřítomnosti režie, která je vlastní typům referenčním. Pokud totiž máme proměnnou referenčního typu, je jako její hodnota, která je alokována v části paměti jménem zásobník, pouze odkaz na objekt, který se nachází v části pamětí nazývané hromada. U hodnotových typu tomu tak není a hodnota je uložena přímo v proměnné, tedy na zásobníku, což má za následek vyšší rychlost práce s nimi. Zjednodušeně řečeno, alokace místa pro objekt na hromadě zabere běhovému prostředí další čas a také s objekty na hromadě je spojena další režie pro její údržbu. Poznámka: Předchozí výklad rozdílu mezi hodnotovými a referenčními (odkazovými) typy je zjednodušený a je zde pro pochopení toho nejdůležitějšího rozdílu, kterým tedy je, že použití struktur místo tříd s sebou přináší méně režie běhového prostředí. Podrobněji se problematikou referenčních a hodnotových typů zabýval kolega Milan Petřík v díle svého seriálu o VB.NET - Programujeme ve Visual Basic.NET díl - přehled datových typů. Jelikož proměnné pro hodnotové typy neobsahují žádný odkaz na objekt, ale hodnotu nemohou nabývat hodnoty null, které mohou nabývat proměnné pro referenční typy. Hodnota null znamená, že referenční proměnná neukazuje na žádnou instanci třídy. K vytváření struktur použijeme v jazyce C# klíčové slovo struct. Realizace struktury bod by mohla vypadat takto: public struct Bod public int x,y; public Bod(int x, int y) //jelikoz jsou parametry konstruktoru //pojmenovany stejne jako clenske promenne //je nutne pouzit klicove slovo this, ktere //predstavuje aktualni instanci objektu this.x = x; this.y = y; 42

44 Vytvořili jsme strukturu představující bod. Kromě toho, že je místo klíčového slova class použito slovo struct se implementace v tomto případě na pohled nijak neliší od implementace referenčního typu. Struktury s sebou, na rozdíl od tříd, nesou několik omezení. Struktury totiž nelze od žádného typu odvodit a žádný typ nemůže být odvozen od nich. Struktury a konstruktory Konstruktory u struktur fungují poněkud odlišně od konstruktorů u tříd. V případě tříd je to tak, že dokud nevytvoříme novou instanci pomocí new a zavoláním příslušného konstruktoru, referenční proměnná má hodnotu null, tedy na žádnou instanci neukazuje a tím pádem není možné v danou chvíli objekt použít. Protože u struktur neexistují odkazy, tak zavolání implicitního (bezparametrického) konstruktoru pomocí new v jejich případě zajistí vytvoření nové instance, která má všechny své datové členy vynulovány. To znamená, že není nutné použít new před použitím struktury, ale pokud jej nepoužijeme, je nutné, aby datové členy struktury byli inicializovány předtím než jsou použity, jinak program nebude možné zkompilovat. Bod b1; //zavolani konstruktoru neni nutne, //ale datove cleny musí pred pouzitim byt inicializovany b1.x = 4; b1.y = 5; //souradnice x je 4 Console.WriteLine(b1.x); //volani implicitniho konstruktoru //zapricini vytvoreni nove instance //s vynulovanymi datovymi cleny b1 = new Bod(); //souradnice x je nyni 0 Console.WriteLine(b1.x); Definovat implicitní konstruktor struktury je v.net frameworku zakázáno a to z důvodu, že by bylo možné datovým členům struktury nastavit odlišné výchozí hodnoty, než je počáteční vynulovaný stav. Pokud definujeme konstruktor struktury, tak tento konstruktor musí inicializovat všechny datové členy struktury. Pokud tak neučiníme, nebude možné kód přeložit. Je tomu tak proto, aby bylo zajištěno, že po zavolání konstruktoru je instance struktury plně připravena k použití. Výčtové typy Dalším druhem hodnotového typu jsou výčtové typy, jejichž využití je vhodné v případech, kdy potřebujeme definovat výčet nějakých konkrétních hodnot, které jsou představovány konstantami a proměnné v programu budou nabývat pouze hodnot těchto konstant. Pro vytváření výčtových typů je v C# k dispozici klíčové slovo enum. Následující příklad ukazuje možné použití výčtového typu pro výčet dnů v týdnu. public enum DnyTydnu Pondeli, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele 43

45 Později je možné deklarovat proměnnou typu DnyTydnu, která bude moci nabývat pouze stanovené hodnoty. DnyTydnu den = DnyTydnu.Nedele; Také je samozřejmě možné napsat metodu jejímž parametrem bude nějaký výčtový typ. public static void NejakaMetoda(DnyTydnu den) //implementace metody Číselná reprezentace a bázové typu výčtů Každý výčtový typ je založen na jednom ze základních celočíselných typů. Výchozím bázovým typem pro výčtové typy je int (neboli typem Int32 v CTS). První hodnota výčtu je implicitně představována hodnotou nula, která je vždy s definicí další konstanty o jedničku vyšší. V příkladu se dny v týdnu je tedy vlastně pondělí představováno nulou, úterý jedničkou a tak dále. O tom se může přesvědčit použitím explicitního přetypováni na typ int. int intden = (int)dnytydnu.nedele; //vystup bude 6 Console.WriteLine(intDen); Počáteční hodnotu číselné reprezentace výčtu je možné změnit následujícím způsobem: public enum DnyTydnu Pondeli = 1, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele Po této změně bude číselná reprezentace výčtu začínat hodnotou 1. Možné je specifikovat nejen počáteční hodnotu číselné reprezentace výčtu, ale i reprezentaci pro další hodnoty výčtu. public enum DnyTydnu Pondeli = 1, Utery = 3, Streda = 5, Ctvrtek = 7, Patek = 9, Sobota = 11, Nedele = 13 Pokud chceme ušetřit paměť nebo nám pro náš výčet nestačí počet hodnot typu int je možné explicitně definovat jiný bázový typ pro výčet. Možné bázové typy, které lze pro výčtové typy použít jsou pouze celočíselné typy. 44

46 Pokud bychom tedy chtěli snížit náročnost našeho výčtu na paměť použili bychom jako bázový například typ byte. public enum DnyTydnu : byte Pondeli, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele V příštím díle seriálu se budeme zabývat vytvářením a použitím polí v C#. Poznáváme C# a Microsoft.NET 11. díl Dnešní díl bude věnován hojně využívané datové struktuře, kterou je pole. Naučíme se jak pole vytvářet a jak je možné jej využít. Co je pole? Pole je datová struktura, která obsahuje určitý počet proměnných. Tyto proměnné jsou nazývány prvky pole. Prvky pole jsou indexovány a pomocí těchto indexů je později možné prvky z pole získávat. V jazyku C# jsou prvky pole indexovány od nuly. Všechny prvky pole jsou stejného typu. V prostředí.net frameworku je pole objektem referenčního typu, který je potomkem třídy System.Array. Prvky jsou v poli uloženy, podle toho jakého jsou typu. Jestliže jsou prvky představovány objekty referenčního typu (například typu string), jsou v poli uloženy odkazy na objekty. V případě, že prvky pole jsou hodnotového typu (například typu int), pole obsahuje přímo hodnoty objektů. Deklarace pole Předtím než je možné pole používat, musíme si jej nejdříve založit. Deklarace pole je složena ze dvou částí, které představují název pole a jeho typ. datový_typ[] identifikátor; Tímto jsme vytvořili referenční proměnnou typu pole, která má nyní hodnotu null, protože jsme objekt pole ještě nevytvořili. Pro vytvoření instance pole slouží, stejně jako u všech referenčních typů, operátor new, jehož pomocí jsme schopni inicializovat pole o specifikované velikosti. identifikátor = new datový_typ[velikost_pole]; Počáteční hodnota prvků pole po jeho inicializaci zavisí na tom, jestli se jedná o pole objektů hodnotového typu nebo referenčního typu. V případě objektů hodnotového typu inicializace pole zapříčiní vytvoření objektů o specifikovaném počtu a pokud se jedná o objekty referenčního typu jsou vytvořeny pouze odkazy s hodnotou null. Je také možné provést inicializaci pole už při jeho deklaraci. 45

47 datový_typ[] identifikátor = new datový_typ[velikost_pole]; Pokud tedy chceme vytvořit pole, jehož prvky budou představovat 32-bitová celá čísla (int) provedeme to následujícím způsobem : int[] ciselnepole = new int[4]; Jak bylo zmíněno, tak k prvkům pole se přistupuje pomocí jejich indexu, který je u prvního prvku roven nule. ciselnepole[0] = 1; ciselnepole[1] = 2; ciselnepole[2] = 3; ciselnepole[3] = 4; Pro lepší pochopení je možné si tuto datovou strukturu a její indexování představit, tak jak ukazuje následující obrázek: Další možnosti inicializace Někdy můžeme chtít určit hodnoty prvků pole už při jeho inicializaci. Když určujeme hodnoty prvků pole už při jeho inicializaci, není nutné specifikovat velikost pole. V jazyku C# je toto proveditelné takto: int[] ciselnepole = new int[] 1,2,3,4; a v případě pole obsahujícího řetězce takto: string[] dnytydnu = new string[] "Po","Ut","St","Ct","Pa","So","Ne"; Zápis lze provést ještě kratší cestou: int[] ciselnepole = 1,2,3,4; string[] dnytydnu = "Po","Ut","St","Ct","Pa","So","Ne"; Hodnoty prvků inicializovaného pole tímto způsobem, lze samozřejmě později normálně měnit. 46

48 Vlastnost Length Třída System.Array, tedy i každé námi vytvořené pole obsahuje vlastnost Length, která může být pouze čtena a je pomocí ní možné zjistit délku pole. int[] ciselnepole = new int[4]; int delkapole = ciselnepole.length; //je vracena hodnota 4 Následující příklad ukazuje vypsání všech prvků pole použitím známého cyklu for, kde je v jeho řídící části použita vlastnost Length. public static void VypisObsahPole() int[] ciselnepole = 1,2,3,4; for (int i = 0; i < ciselnepole.length; i++) Console.WriteLine((i+1) + ". prvek pole - " + ciselnepole[i]); Vícerozměrná pole Pole mohou nabývat více rozměrů než pouze jednoho. Následující deklarace vytvoří dvourozměrné pole o čtyřech řádcích a třech sloupcích: int[,] dvourozmernepole = new int[4,3]; K prvku, který se nachází na prvním řádku a druhém sloupci přistoupíme tímto způsobem: int prvek = dvourozmernepole[0,1]; Pole o více než dvou rozměrech by bylo deklarováno analogicky: int[,,] vicerozmernepole = new int[2,3,4]; Podobně jako u jednorozměrných polí je možné specifikovat počáteční hodnoty prvků pole při jeho inicializaci. int[,] dvourozmernepole = new int[,]1,2,3,4; nebo ještě kratší cestou: int[,] dvourozmernepole = 1,2,3,4; K získávání délky rozměrů ve vícerozměrných polí již nepoužijeme vlastnost Length, protože ta v případě vícerozměrných polí vrací součin délek jednotlivých rozměrů, ale využijeme metody GetLength, jejímž argumentem je číslo rozměru, jehož délku požadujeme získat. Rozměry jsou číslovány od nuly, takže když potřebujeme zjistit délku druhého rozměru, zápis bude vypadat následovně: int delkadruhehorozmeru = dvourozmernepole.getlength(1); Kód, zajišťující vypsání všech prvků dvourozměrného pole pomocí vnořeného cyklu for a metody GetLength by mohl vypadat takto: 47

49 public static void VypisObsahPole() int[,] vicerozmernepole = new int[3,3]; vicerozmernepole[0,0] = 1; vicerozmernepole[1,1] = 1; vicerozmernepole[2,2] = 1; for (int i = 0; i < vicerozmernepole.getlength(0); i++) for (int j = 0; j < vicerozmernepole.getlength(1); j++) Console.Write(viceRozmernePole[i,j]); Console.WriteLine(); Pole polí Pole polí, která jsou někdy nazývána zubatá jsou pole jejichž prvky jsou pole. Pole tvořící prvky tohoto druhu polí mohou mít různou velikost. Vytvoření pole o velikosti 3 prvků, jehož prvky tvoří pole typu int se zapisuje takto: int[][] zubatepole = new int[3][]; Jelikož po tomto vytvoření jsou prvky pole pouze prázdné odkazy na vnořená pole je vhodné vnořená pole inicializovat. zubatepole[0] = new int[3]; zubatepole[1] = new int[4]; zubatepole[2] = new int[5]; Pokud se vám zdá tento postup příliš zdlouhavý, existuje zde i možnost vnořená pole inicializovat a to včetně jejich počátečních hodnot. int [][] zubatepole = new int[][] new int[]1,2, new int[]1,2,3, new int[]1,2,3,4 ; Přiřazování hodnot prvkům vnořených polí se provádí dvojicí hranatých závorek, kde první označuje index vnořeného pole a druhá index prvku vnořeného pole. zubatepole[0][1] = 5; zubatepole[1][3] = 10; zubatepole[2][0] = 15; Velikost vnořených polí je možné zjistit pomocí vlastnosti Length takto: int delkavnorenehopole = zubatepole[0].length; Uvedený příklad ukazuje možný způsob, kterým lze vypsat všechny prvky vnořených polí: public static void VypisObsahPole () int [][] zubatepole = new int[][] new int[]1,2, new int[]1,2,3, 48

50 new int[]1,2,3,4 ; for (int i = 0; i < zubatepole.length; i++) for (int j = 0; j < zubatepole[i].length; j++) Console.WriteLine((j + 1) + ".prvek v " + (i + 1) + ".vnorenem poli - " + zubatepole[i][j]); Vnořená pole mohou být i vícerozměrná. Kolik mají vnořená pole rozměrů určíme při deklaraci pole. int[][,] zubatepole = new int[5][,]; //inicializace prvku zubatepole[0] = new int[3,4]; Pole jako parametr metody Jak jsme si řekli, pole jsou v prostředí.net frameworku objekty referenčního typu, tudíž nám nic nebrání v jejich předání metodám ve formě parametru. Jelikož se jedná o referenční typ, je metodě předán pouze odkaz na existující objekt a není do proměnné parametru metody zkopírována jeho hodnota (obsah), jak se tomu děje v případě hodnotových typů. Následující příklad ukazuje použití pole jako parametru metody: public class PrikladParametr public static void VypisObsahPole(string[] poleprovypsani) for (int i = 0; i < poleprovypsani.length; i++) Console.WriteLine((i+1) + ". prvek pole - " + poleprovypsani[i]); public static void NaplnPole(string[] polepronaplneni) for (int i = 0; i < polepronaplneni.length; i++) polepronaplneni[i] = (i+1) + ".prvek"; class Zive11Test public static void Main(string[] args) //deklarace a inicializace pole string[] pole = new string[10]; //predani pole jako parametru do metody pro jeho naplneni PrikladParametr.NaplnPole(pole); //predani pole jako parametru do metody pro jeho vypsani PrikladParametr.VypisObsahPole(pole); Console.ReadLine(); V příštím díle si doplníme naše znalosti o polích a blíže se podíváme na třídu System.Array. 49

51 Poznáváme C# a Microsoft.NET 12. díl Po minulém seznámení s poli se pokusím v tomto díle osvětlit třídu System.Array a její užitečné metody. Pozornost bude také věnována použití cyklu for each, který se pro procházení polí nezřídka užívá. System.Array Jak jsem zmínil v předcházejícím díle, každé námi vytvořené pole je instance třídy, která je odvozena od třídy System.Array. Díky tomu je nám umožněno na vzniklých instancích používat členy, definované v této třídě. Mimo to, obsahuje tato třída i několik statických metod, které nám v mnoha situacích mohou být nápomocné pří užívání polí. Na některé užitečné statické i instanční členy se nyní podíváme blíže. Zjištění počtu rozměrů pole Někdy při práci s poli může být užitečné zjistit počet rozměrů daného pole. K tomuto účelu slouží instanční vlastnost Rank. int[,,] trirozmernepole = new int[2,3,4]; //vysledek je tri Console.WriteLine(triRozmernePole.Rank); Další možnost vytvoření pole Protože jsou pole objekty (stejně jako vše ostatní v.net), je možné vytvořit instanci pole jinak než použitím zápisu pro deklaraci pole v daném jazyku. Můžeme toho dosáhnout použitím statické metody CreateInstance. //vytvoreni pole o delce 5 prvku int[] mojepole = (int[])array.createinstance(typeof(int32),5); //vytvoreni trirozmerneho pole int[] ldelkyrozmeru = new int[3]4,3,2; int[,,] trirozmernepole = (int[,,])array.createinstance(typeof(int32),ldelkyrozmeru); Při vytváření pole touto cestou, je nutné použít odpovídající explicitní přetypování na konkrétní typ pole. Typ pole, které má být vytvořeno je metodě CreateInstance předán jako parametr. Poznámka: Datový typ je v prostředí.net frameworku představován třídou System.Type. Právě instanci této třídy očekává mimo jiné metoda CreateInstance třídy System.Array. operátoru typeof jak je ukázáno v ukázce. Převrácení pořadí prvků pole V případě, že z nějakého důvodu potřebujeme převrátit pořadí prvků pole, použijeme statickou metodu Reverse, jíž je jako parametr předána proměnná pole. int[] ciselnepole = new int[]1,2,3,4,5; Array.Reverse(ciselnePole); for (int i = 0; i < ciselnepole.length;i++) Console.WriteLine(ciselnePole[i]); Výstup bude vypadat takto: 50

52 Získáni indexů okrajů pole V určitých případech může být užitečné mít k dispozici indexy spodního nebo vrchního okraje pole. Pro tyto případy tu jsou instanční metody GetLowerBound a GetUpperBound, jimž se ve formě parametru předá číslo rozměru pole, jehož spodní respektive vrchní index potřebujeme zjistit. Následující příklad demonstruje možné použití těchto metod pro výpis pole pomocí cyklu for. int[] ciselnepole = new int[]1,2,3,4,5; int spodek = ciselnepole.getlowerbound(0); int vrsek = ciselnepole.getupperbound(0); for (int i = spodek; i <= vrsek;i++) Console.WriteLine(ciselnePole[i]); Mělká kopie pole Při použití takzvané mělké kopie pole je do nově vzniklé kopie pole zkopírován pouze obsah prvků originálu. Následkem tohoto jevu jsou v případě klonování polí, jejichž prvky představují objekty referenčního typu, zkopírovány pouze reference ukazující na tentýž objekt na což je třeba brát ohledy. V případě polí obsahující prvky hodnotových typů jsou do kopie pole zkopírovány objekty. K vytvoření mělké kopie slouží instanční metoda Clone. public static void CloneIntPriklad() int[] prvnipole = new int[5]1,2,3,4,5; int[] druhepole = new int[5]; druhepole = (int[])prvnipole.clone(); //jelikoz jsou prvky hodnotoveho typu //zmena prvku v prvnim poli neovlivni //hodnotu prvku druheho pole prvnipole[0] = 10; for (int i = 0; i < druhepole.length;i++) Console.WriteLine(druhePole[i]); Změna hodnoty prvku prvního pole po uskutečnění klonování neovlivnila hodnotu v druhém poli, jak je možno vidět na výstupu: Jinak je to ovšem u prvků referenčního typu. public static void CloneRefPriklad() //vytvoreni poli zarovek Zarovka[] polezarovek = new Zarovka[5]; Zarovka[] druhepolezarovek = new Zarovka[5]; 51

53 //vytvoreni zarovky s vykonem 120 W //jako prvku prvniho pole polezarovek[0] = new Zarovka(120); druhepolezarovek = (Zarovka[])poleZarovek.Clone(); //zmena vykonu zarovky pres prvek prvniho pole polezarovek[0].vykon = 100; //vykon zarovky v druhem poli bude take 100 //protoze prvek referencuje tu samou instanci Console.WriteLine("Vykon zarovky v druhem poli : " + druhepolezarovek[0].vykon); Vykon zarovky v druhem poli: 100 Vyhledání prvku v jednorozměrném poli Pokud potřebujeme vyhledat určitý prvek jednorozměrného pole nabízí se nám možnost použití statické metody BinarySearch. Tato metoda vrací index hledaného prvku nebo nulu, když pole hledaný prvek neobsahuje. V ukázce můžete vidět použití jedné z přetížených verzí metody BinarySearch. public static void BinarySearchPriklad() int[] mojepole = new int[5]; //prirazeni hodnot prvku pole pouzitim //metody SetValue mojepole.setvalue(1,0); mojepole.setvalue(2,1); mojepole.setvalue(3,2); mojepole.setvalue(4,3); mojepole.setvalue(5,4); NajdiPrvek(mojePole,3); private static void NajdiPrvek(Array pole, Object objektpronalezeni) //prvni parametr je pole ve kterem se ma hledat //a druhy hledany objekt int lindex = Array.BinarySearch(pole,objektProNalezeni); if (lindex == 0) Console.WriteLine("Prvek nebyl v poli nalezen"); else Console.WriteLine("Prvek 0 se v poli nachazi na indexu 1",objektProNalezeni,lIndex); Výstup: Prvek 3 se v poli nachazi na indexu 2 Cyklus foreach Cylkus foreach je v jazyku C# předurčen k procházení polí a kolekcí. Z tohoto důvodu se o něm zmiňuji až v tomto díle a neuvedl jsem ho v díle, který pojednával o cyklech. Použití cyklu foreach s poli je jednoduché: foreach(datový_typ identifikátor in pole) 52

54 operace Cyklus provede definované operace pro každý prvek pole. K prvku je v těle cyklu přistupováno prostřednictvím identifikátoru, který je platný pouze v rámci cyklu. Pro pochopení použití uvedu příklad. public static void VypisPole(int[] poleprovypsani) foreach(int prvek in poleprovypsani) //každý prvek bude vypsán Console.WriteLine(prvek); Tento krátký zápis bude mít stejnou funkčnost jako tento cyklus for: public static void VypisPoleFor(int[] poleprovypsani) for (int i = 0; i < poleprovypsani.length;i++) Console.WriteLine(poleProVypsani[i]); Je patrné, že použití cyklu foreach pro procházení polí je elegantnější a méně náchylné k chybám. V příštím díle se zaměříme na výjimky. Poznáváme C# a Microsoft.NET 13. díl výjimky V tomto díle je mým úkolem objasnit princip systému výjimek, který je použit v prostředí MS.NET framework. Vysvětlíme si co se pod pojmem výjimka skrývá a jakým způsobem se výjimky používají. Co jsou výjimky? V prostředí Microsoft.NET frameworku je pro zpracovávání výjimečných - chybových stavů použit systém výjimek. Protože, jak již bylo několikrát poznamenáno, je vše v.net frameworku reprezentováno objekty, výjimky na tom nejsou jinak. Výjimka je objekt alokovaný na hromadě, který nese informaci o chybovém stavu, který v aplikaci nastal a pomocí zachycení tohoto objektu je možné nastalý stav nějakým způsobem zpracovat. Proč výjimky? Mnohé z Vás, kteří již v nějakém jazyce, který výjimky nepodporoval, vytvářeli aplikace možná napadlo proč používat výjimky, když je možné chybové stavy zjistit pomocí návratové hodnoty funkce (metody). Tedy v podáni jazyku C# například nějak takto : static void Main(string[] args) bool lresult = NejakaMetoda(); Tímto způsobem samozřejmě můžeme zjistit jestli v metodě nedošlo k chybě, ale při použití takovéhoto přístupu je nutné testovat návratovou hodnotu každé metody. 53

55 Výsledkem toho bude špatně čitelný kód, kde je logika smíchána se zjišťováním chyb. Jedním z hlavních důvodů pro použití systému výjimek je skutečnost, že chybové stavy jsou zjišťovány odchytávány v přesně vymezeném prostoru. Vyhození výjimky Pokud v průběhu programu dojde k výjimečné situaci, pro kterou v daném kontextu nemáme dostatek informací, abychom ji mohli hned na místě vyřešit a pokračovat v právě prováděném bloku programu, vyhodíme výjimku. Vyhození výjimky znamená ukončení prováděného bloku programu a možnost výjimku ve volajícím bloku, kde již mohou být informace v dostačujícím množství pro napravení chyby, ošetřit. Všechny výjimky v prostředí.net frameworku mají společného předka, který je představován třídou System.Exception. Každá odvozená třída představuje konkrétnější výjimečný stav. Například třída DivideByZeroException představuje výjimku dělení nulou. Pro vyhozeni výjimky slouží klíčové slovo throw, kterému je předána instance výjimky. Následující příklad ukazuje vyhození výjimky při pokusu dělit číslo nulou. public class DeleniNulou public static float Vydel(int a,int b) if (b == 0) //vyhozeni vyjimky throw new DivideByZeroException(); return a / b; Jak je možno ze zdrojového kódu vyčíst, v případě, že je dělitel roven nule vytvoří se nová instance třídy DivideByZeroException, která je následně pomocí throw vyhozena. Odpovědnost za její zpracování je ponechána volajícímu kódu, ale jakým způsobem se výjimka zpracovává? Chráněné bloky a handlery výjimek Odpověď na otázku položenou v předchozím odstavci představuje použití chráněných bloků v kombinaci s takzvanými handlery výjimek, které jsou předurčeny k zachycení určitého typu výjimky a provedení příslušných operací. Chráněný blok, jež se v jazyku C# uvozuje klíčovým slovem try, reprezentující blok ve kterém zkoušíme volat metody, které mohou vyhodit výjimku. Bezprostředně po tomto chráněném bloku může následovat jeden nebo více handlerů výjimky. Blok handleru je uvozen klíčovým slovem catch. U každého handleru je navíc uveden typ výjimky pro kterou je daný handler určen. try //kod, ktery muze vyhodit vyjimku //handler vyjimky catch(typvyjimky identifikator) //kod reakce na vyjimku Takže jak by vypadalo zpracováni výjimky z předchozího příkladu můžete vidět níže. 54

56 public static void MetodaVolajiciVydel() try float lvysledek = Vydel(4,0); //nasledujici radky se pri vyvolani //vyjimky v metode Vydel neprovedou Console.WriteLine("Vysledek deleni: " + lvysledek); catch(dividebyzeroexception ex) Console.WriteLine("Pri deleni doslo k deleni nulou! " + ex); Handlery pro více typů výjimek Za blokem try může následovat i více než jeden handler výjimky a to když v bloku try voláme metody, které mohou vyhazovat více typů výjimek. Běhové prostředí při vyhození výjimky použije nejvhodnější handler v závislosti na typu vyvolané výjimky. try //chraneny blok catch(typvyjimky1 identifikator) //obsluzny blok pro typvyjimky1 catch(typvyjimky2 identifikator) //obsluzny blok pro typvyjimky2 Takže pokud je vyvolána výjimka typu typvyjimky1 bude použit handler pro tento typ výjimky a při vyvoláni výjimky typu typvyjimky2 bude použit analogicky druhý handler. Také je možné místo vytváření handlerů pro jednotlivé typy výjimek, které provádějí tytéž operace vytvořit handler zachytávající jejich společného předka. A jelikož je společným předkem všech výjimek v.net frameworku třída System.Exception nabízí se nám možnost vytvořit handler pro tento typ výjimky a tím pádem zachytit všechny vzniklé výjimky na jednom místě. try //chraneny blok catch(system.exception ex) //spolecny obsluzny blok pro //vsechny vyvolane vyjimky Závěrečné bloky V řadě případů je vhodné využít takzvané závěrečné bloky. Na rozdíl od kódu uvnitř chráněného bloku try, jehož provádění se při vyvolání výjimky přeruší, tak závěrečné bloky jsou provedeny vždy, tedy ať k výjimce dojde nebo nikoliv. Z tohoto důvodu se v 55

57 těchto blocích nejčastěji vyskytují operace potřebné k uvedení objektu do korektního stavu, například uzavření spojení k datovému zdroji (relační databáze, soubor..). K uvozeni závěrečného bloku použijeme klíčové slovo finally. try //kod, ktery muze vyhodit vyjimku //handler vyjimky catch(typvyjimky identifikator) //kod reakce na vyjimku finally //zaverecne operace, jez //jsou provedeny vzdy V příštím díle se budeme dále zabývat problematikou výjimek. Dozvíme se například jakým způsobem lze vytvořit vlastní typ výjimky a co je předávání výjimek. Poznáváme C# a Microsoft.NET 14. díl výjimky po druhé V dnešním díle si rozšíříme znalosti o tématu, které jsem načal v předchozím díle, což byl systém výjimek v prostředí MS.NET framework. Mimo jiné se můžete těšit na seznámení s tvorbou vlastních typů výjimek. Předávání výjimek Systém předávání (propagace) výjimek umožňuje nezachycovat výjimku ihned při jejím vyhození volanou metodou. Tedy metoda ve které se výjimka vyskytla vlastně ponechává odpovědnost na volajících metodách. Vzniklá výjimka bude probublávat výše v hierarchii volaných metod dokud nedojde k jejímu zachycení. Pokud výjimka námi není nikde v hierarchii zachycena, je zachycena běhovým prostředím a uživateli se zobrazí okno s hlášení o vzniklé výjimce. Následující příklad demonstruje jak je možné předávat výjimky. Poznámka: To, že se při nezpracované výjimce zobrazí okno.net frameworku s popisem výjimky je nejběžnější reakce, ale není jediná možná. Co bude provedeno při výskytu nezpracované výjimky je možné ovlivnit. public class DeleniNulou public static float Vydel(int a,int b) if (b == 0) //vyhozeni vyjimky throw new DivideByZeroException(); return a / b; public static void MetodaVolajiciVydel() //dojde k vyhozeni vyjimky //ktera bude predana volajici metode 56

58 float lvysledek = Vydel(4,0); Console.WriteLine("Vysledek deleni: " + lvysledek); class App static void VolejVydel() //pripadna vyjimka neni zachytavana //zadnym handlerem DeleniNulou.MetodaVolajiciVydel(); static void Main(string[] args) try VolejVydel(); //zde dojde ke zpracovani pripadne vyjimky //vyvolane metodou MetodaVolajiciVydel catch (DivideByZeroException) Console.WriteLine("V metode VolejVydel nastala chyba, deleni nulou"); Console.ReadLine(); Jak je vidět ve třídě DeleniNulou, případná výjimka vyvolaná metodou Vydel není zachytávána ihned v metodě MetodaVolajiciVydel. Metodu MetodaVolajiciVydel volá aplikační třída App ve své metodě VolejVydel, kde také k zachycení nedojde, jelikož není přítomný žádný handler. Případně vyvolaná výjimka je zachycena až v metodě Main aplikační třídy App, která volá metodu VolejVydel, kde se nalézá handler přímo pro konkrétní typ výjimky DivideByZeroException. Z toho je zřejmé jak je vyvolaná výjimka předávána volajícím metodám, kde může být zpracována. Vlastnost Message třídy System.Exception Jak již víme s předchozího dílu, výjimka je objekt nesoucí informace o vzniklém výjimečném stavu. Informace o nastalém stavu je možné z objektu výjimky získat přečtením jeho vlastností. Často používanou vlastností je vlastnost Message, jejímž přečtením můžeme získat řetězec znaků představující vysvětlení důvodu nastalého stavu. Vlastnosti Message můžeme využít při vyhazování nové výjimky a to použitím přetížené verze konstruktoru třídy System.Exception, do kterého jako parametr zadáme řetězec představující tuto vlastnost. Po rozšíření našich vědomostí o tuto skutečnost napíšeme příklad s výjimkou představující dělení nulou třeba takto: public class DeleniNulou public static float Vydel(int a,int b) if (b == 0) //vyhozeni vyjimky pouzitim pretizene //verze konstruktoru pro zadani //vlastnosti Message throw new DivideByZeroException("Delitel je roven nule!"); return a / b; 57

59 class App static void Main(string[] args) try DeleniNulou.Vydel(5,0); catch (DivideByZeroException ex) //vypsani vlastnosti Message nastale vyjimky Console.WriteLine(ex.Message); Console.ReadLine(); Vlastnost StackTrace Přečtením této vlastnosti objektu výjimky získáme trasování zásobníku, což je hierarchický výpis volaných metod, který začíná u původce výjimky. Na základě této informace jsme schopni lépe odhalit vzniklý problém, protože vidíme ve které metodě byla výjimka vyhozena a jak byla předávána až k jejímu zachycení. Takže pokud bychom v předchozím příkladu použili místo výpisu vlastnosti Message vlastnost StackTrace vidělo bychom toto: at PrikladyZive14.DeleniNulou.Vydel(Int32 a, Int32 b) in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\prikladyzive14\deleninulou.cs:line 13 at Zive14TestApp.App.Main(String[] args) in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\zive14testapp\app.cs:line 19 Vytváření vlastních výjimek Postupem času, se při programování s používáním výjimek s velkou pravděpodobností dostanete do situace, kdy vám vestavěné typy výjimek.net frameworku přestanou stačit. Zajisté budete chtít vytvořit vlastní typ výjimky, který se pro danou výjimečnou událost hodí více než jakákoli z vestavěných, nebo se mezi nimi dokonce žádná, ani vzdáleně podobná, nevyskytuje. Toho lze později využít při vytváření handlerů pro více typů výjimek, protože na naši vlastní výjimku můžeme vytvořit speciální handler. Vlastní výjimku v.net frameworku vytvoříme odvozením od nějaké existující třídy reprezentující výjimku. Ve velkém počtu případů nám vhodně jako bázová třída poslouží společný předek všech výjimek, tedy třída System.Exception. Pokud je to však možné, tak použijte existujícího typu výjimky, který je významově blízký nově vytvářené výjimce. Jelikož hlavní důvod pro vytvoření vlastní výjimky je nejčastěji v získání nového typu, který bude později handlery zachytáván, stačí pouze uvést při deklaraci třídy předka a nepřekrývat žádné členy. public class MojeVyjimka : System.Exception 58

60 Po takovémto vytvoření vlastní výjimky ji již můžeme v našich třídách nesměle používat jako kteroukoliv jinou, jak zobrazuje následující příklad. class MojeVyjimkaPouziti public static void MetodaVyvolavajiciMojiVyjimku() //nejaky kod metody throw new MojeVyjimka(); static void MetodaHandler() try MetodaVyvolavajiciMojiVyjimku(); catch(mojevyjimka ex) //kod pro zpracovani vyjimky typu MojeVyjimka catch(exception ex) //kod pro zpracovani ostatnich vyjimek Omezení při definici více handlerů V.NET frameworku není povoleno při vytváření více handlerů, definovat handler pro obecnější typy výjimek nad handlery pro konkrétnější typy výjimek. Je to logické zabránění chybnému zachytávání výjimek, kde by handler pro obecnější typ výjimky zpracoval i výjimky, které by měly být správně zpracovány handlerem pro konkrétnější typ. Pokud bychom předcházející příklad změnili do následující podoby, program by nešel zkompilovat. static void MetodaHandler() try MetodaVyvolavajiciMojiVyjimku(); //tento handler by zabranil provedeni //konkretnejsiho handleru catch(exception ex) //kod pro zpracovani ostatnich vyjimek catch(mojevyjimka ex) //kod pro zpracovani vyjimky typu MojeVyjimka Opakované vyhození výjimky 59

61 Občas můžeme potřebovat při zachycení výjimky provést pouze určitou část akcí a zbylou část operací ponechat na volajících metodách. Toho lze docílit pomocí takzvaného opakovaného vyhození výjimky, kdy použijeme slovíčko throw na již zachycenou instanci výjimky. Tím dosáhneme požadovaného výsledku. public class Rethrow public static void MetodaVolajiciVyjimku() //nejaky kod metody throw new MojeVyjimka(); public static void MetodaHandler() try MetodaVolajiciVyjimku(); catch(mojevyjimka ex) //provedeni urcite casti akci //opakovane vyhozeni throw ex; Zabalení výjimky Kromě opakovaného vyhození výjimky je také možné vzniklý objekt výjimky zabalit do jiného objektu výjimky a ten následně vyhodit. To je vhodné v případě, když chceme k výjimce přidat nějaké dodatečné informace a vyhodit nový objekt výjimky, který může být i třeba jiného typu. K zabalení výjimky použijeme jednu z přetížených verzí konstruktoru, který je definován na třídě System.Exception. Tento konstruktor od nás očekává řetězec představující vlastnost Message a zabalovanou(vnitřní) výjimku. public class ExceptionBoxing public static void MetodaVolajiciVyjimku() try DeleniNulou.Vydel(5,0); catch(dividebyzeroexception ex) //zabaleni vyjimky throw new Exception("Delitel je roven nule!", ex); class App static void Main(string[] args) try 60

62 ExceptionBoxing.MetodaVolajiciVyjimku(); catch (Exception ex) //zobrazeni vyjimky pomoci metody ToString Console.WriteLine(ex.ToString()); Console.ReadLine(); Pro výpis výjimky jsem záměrně použil metodu ToString, která je překryta takovým způsobem, že nám v tomto případě zobrazí informace o přítomné vnitřní výjimce. Uvedený příklad tedy vygeneruje následující výstup: System.Exception: Delitel je roven nule! ---> System.DivideByZeroException: Delitel je roven nule! at PrikladyZive14.DeleniNulou.Vydel(Int32 a, Int32 b) in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\prikladyzive14\deleninulou.cs:line 13 at PrikladyZive14.ExceptionBoxing.MetodaVolajiciVyjimku() in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\prikladyzive14\exceptionboxing.cs:line End of inner exception stack trace --- at PrikladyZive14.ExceptionBoxing.MetodaVolajiciVyjimku() in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\prikladyzive14\exceptionboxing.cs:line 16 at Zive14TestApp.App.Main(String[] args) in c:\documents and settings\petr puš\dokumenty\visual studio projects\prikladyzive\zive14testapp\app.cs:line 19 Pokud bychom z nějakého důvodu potřebovali získat objekt vnitřní výjimky použijeme vlastnost InnerException na zachyceném objektu výjimky. class App static void Main(string[] args) try ExceptionBoxing.MetodaVolajiciVyjimku(); catch (Exception ex) Exception linnerex = ex.innerexception; Console.WriteLine(lInnerEx.ToString()); Console.ReadLine(); Příště se seznámíme s velmi zajímavou vlastností jazyku C#, kterou jsou delegáti. Poznáváme C# a Microsoft.NET 15. díl delegáty V tomto díle se zaměřím na, dle mého názoru, velmi zajímavou vlastnost jazyku C#, pomocí které je nám při vývoji aplikací umožněno tvořit objekty reprezentující reference na metody. Tyto objekty jsou v jazyce C# nazývány delegáty. 61

63 K čemu delegáty? Jak jsem nastínil o pár řádek výše, instance delegátů slouží k reprezentaci reference na metodu. Takto referencované metody mohou být jak instanční tak statické. Pokud vytváříme typ delegáta, vytváříme vlastně předpis pro signaturu metody. Při vytváření instance delegáta předáme konstruktoru naimplementovanou metodu, která má stejnou signaturu jakou předepisuje delegát. To nám umožňuje, podobně jako u rozhraní, oddělit specifikaci od vlastní implementace. My tedy při deklaraci delegáta tedy pouze specifikujeme požadovaný tvar a na uživateli našich knihoven je vytvoření příslušné metody požadovaného tvaru a její následné obsazení. Delegáty jsou někdy označovány jako bezpečné ukazatele na funkce, avšak na rozdíl od ukazatelů na funkce, známých z jazyku C++, jsou delegáty objektově orientované a typově bezpečné. Deklarace delegáta Chceme-li v námi vytvářeném jmenném prostoru respektive třídě, deklarovat delegáta použijeme k tomu klíčové slovo delegate. Specifikátor_přístupu delegate návratový_typ identifikátor(seznam_formálních_parametrů) Tímto vytvoříme nový typ delegáta. V případě, že máme v úmyslu vytvořit delegáta představující libovolnou matematickou operaci, která očekává dva parametry a její návratový typ je double uskutečníme tak tímto způsobem: public delegate double MatematickaOperace(int a, int b); Vytvoření instance delegáta Instanci námi deklarovaného delegáta vytvoříme stejně jako u všech ostatních objektů pomocí operátoru new. Při vytvoření objektu delegáta jej musíme asociovat s konkrétní metodu o předepsané signatuře. typ_delegáta identifikátor = new typ_delegáta(asociovaná_metoda); Takže pokud bychom chtěli vytvořit objekt delegáta MatemetickaOperace musíme mít k dispozici nějakou metodu, kterou bychom s instancí delegáta asociovali. Pro naše ukázkové účely bude vhodné napsat třídu obsahující základní matematické operace. public class MathOps public static double Soucet(int a, int b) return a + b; public static double Rozdil(int a, int b) return a - b; public static double Soucin(int a, int b) 62

64 return a * b; public static double Podil(int a, int b) if (b == 0) throw new DivideByZeroException("Pokus o deleni nulou"); return a / b; Jak můžete pozorovat, tak všechny metody implementované ve třídě MathOps mají signaturu odpovídající delegátovy MatematickaOperace. V tuto chvíli již máme splněny podmínky potřebné k vytvoření instance delegáta. Takže vytvoření zapíšeme následujícím způsobem. MatematickaOperace objektdelegata = new MatematickaOperace(MathOps.Soucet); Tímto zápisem tedy došlo k vytvoření objektu delegáta MatematickaOperace. Jelikož jsou všechny metody ve třídě MathOps deklarovány jako statické, není přístupu k nim potřebné vytvořit instanci třídy MathOps. V případě, že by metody byly deklarovány jako instanční postup vytvoření delegáta by se změnil do této podoby: MathOps instancemathops = new MathOps(); MatematickaOperace objektdelegata = new MatematickaOperace(instanceMathOps.Soucet); Poznámka: Uvedený způsob přístupu k typu delegáta je možné použít pouze v případě, že je delegát deklarován na úrovni jmenného prostoru. Delegáty je možné deklarovat i na úrovni konkrétní třídy a v těchto případech je k nim přistupováno stejně jako k vnitřním třídám, tedy jako by to byly statické členy. V případě deklarace na úrovni třídy, tedy logicky nemůže být delegát označen jako statický. Jak se nově vzniklá instance dá využít se dozvíme v následujícím odstavci. Volání delegáta Delegáty velmi často využijeme jako parametry nějaké metody. Metoda jednoduše očekává předání delegáta určitého typu a na nás tedy je objekt delegáta, asociovaného s nějakou metodou, předat. Mějme pro náš příklad třídu Calculator, která obsahuje metodu, jejímž parametrem je delegát typu MatematickaOperace. public class Calculator public void ProvedOperaci(int a, int b, MatemetickaOperace operace) Console.WriteLine(operace(a,b)); Metoda ProvedOperaci tedy kromě dvou operandů očekává objekt delegáta představující Matematickou operaci. Jediné co metoda provede je, že vypíše výsledek asociované matematické operace na konzoli a to pomocí volání delegáta skrze formální parametr metody: operace(a,b) 63

65 Jak tedy metodě předáme konkrétní matematickou operaci názorně ukazuje metoda Test ve třídě CalculatorApp, která vytvoří instanci třídy Calculator, následně je vytvořen objekt delegáta a ten je dosazen metodě ProvedOperaci. public class CalculatorTest public static void Test() Calculator lcalculator = new Calculator(); //vytvoreni instance delegata a asociace s metodou //Soucet tridy MathOps MatematickaOperace objektdelegata = new MatematickaOperace(MathOps.Soucet); lcalculator.provedoperaci(10,5,objektdelegata); Skládání delegátů Delegáty disponují jednou výbornou vlastností, kterou představuje možnost složit delegáta ze dvou nebo více existujících delegátů. Takto vytvořená instance delegáta svým voláním zapříčiní volání všech metod asociovaných s instancemi delegátů, z nichž byla složena. Pro složení delegátů použijeme operátor +. Skládání delegátů lze použít pouze na delegáty stejného typu. Tak jako mohou být delegáty komponovány pomocí operátoru +, mohou být i dekomponovány a to použitím operátoru -. Následující příklad ukazuje možné použití složeného delegáta. class MultiCast public delegate void PozdravDelegate(string jmeno); public static void Ahoj(string jmeno) Console.WriteLine("Zdravime " + jmeno); public static void Nashledanou(string jmeno) Console.WriteLine("Nashledanou " + jmeno); public class MultiCastTest public static void RunMultiCast() MultiCast.PozdravDelegate del1,del2,del3,del4; del1 = new MultiCast.PozdravDelegate(MultiCast.Ahoj); del2 = new MultiCast.PozdravDelegate(MultiCast.Nashledanou); //slozeni delegatu del3 = del1 + del2; del3("petr"); //dekomponovani delegatu del4 = del3 - del1; del4("petr"); 64

66 Instanci delegáta s identifikátorem del3 jsme vytvořili kompozicí instancí delegátů del1 a del2. Tím pádem se při volání tohoto delegáta zavolají dvě metody, jedna, která byla asociována s instancí delegáta del1 (MultiCast.Ahoj) a s ní také metoda MultiCast.Nashledanou, protože byla asociována s instancí del2. Po zavolání složeného delegáta del3 vypíše tento výstup: Zdravime Petr Nashledanou Petr Instance delegáta del4 byla vytvořena odebráním instance del1 od složeného delegáta del3, po této operaci tím pádem instance del4 obsahovala pouze instanci del2 a proto se nám po jejím zavolání objeví následující výstup: Nashledanou Petr V příštím díle se seznámíme s událostmi u kterých se nám nabyté informace o delegátech budou velmi hodit. Poznáváme C# a Microsoft.NET 16. díl události Po delegátech se budu v tomto díle věnovat další velmi zajímavé vlastnosti.net frameworku, která se často používá při zpracovávání asynchronních operací v tomto prostředí. K tomu nám v.net frameworku a tím pádem i v jazyku C# slouží členy třídy zvané události. Co jsou události? Události představují v jazyku C# cestu, která umožňuje třídě upozornit jinou třídu nebo třídy, že v ní došlo k něčemu zajímavému, na což by mohla tato třída respektive třídy určitým způsobem zareagovat. Časté použití můžeme nalézt v implementaci grafických uživatelských prostředí pro upozornění, že uživatel provedl nějakou akci, na níž by mohla být implementována reakce. Samozřejmě to není jediné možné použití události. Události se hodí všude tam, kde je potřeba upozornit okolí na to, že se v objektu respektive třídě došlo k nějaké změně stavu. Pro definici metod, které mohou být použity pro reakci na vzniklou událost slouží delegáty, kterými jsem se zaobíral v minulém díle, a proto by bylo vhodné abyste měli v implementaci delegátů jasno. Deklarace události Pro deklaraci nové události ve třídě použijeme klíčové slovo event. Musíme také určit typ delegáta, který asociujeme s touto události. To má za následek to, že třídy, které budou chtít na vzniklou událost reagovat, musí delegáta tohoto typu vytvořit a k tomuto delegátu asociovat obslužnou metodu(y). modifikátory event typ_delegáta identifikátor_události; Vyvolání události Tím, že jsme ve třídě deklarovali událost, je možné k ní přistupovat jako ke členu delegáta určeného typu. Pokud tento člen nabývá hodnoty null, znamená to, že žádná jiná třída si tuto událost takzvaně nepředplatila, jinými slovy, že nehodlá na nově vzniklou událost jakýmkoli způsobem reagovat. Proto je vhodné před vyvoláním události vždy zkontrolovat jestli člen události nenabývá hodnoty null. Jelikož se tedy událost vyvolá stejně jako volání delegáta obecný způsob vyvolání vypadá takto: 65

67 if (identifikátor_události!= null) identifikátor_události (seznam_případných_parametrů); Ukázková třída vyvolávající událost Po lehkém obecném úvodu bych rád demonstroval použití události na konkrétním případu. Na ukázku definujeme třídu Žárovka, která bude, na rozdíl od její v implementace v jednom s prvních dílů tohoto seriálu, při svém rozsvícení či zhasnutí vyvolávat událost oznamující změnu stavu. //deklarace delagata, ktery bude slouzit jako predpis metody //predstavujici reakci na udalost (handler) public delegate void ZmenaStavuHandler(); /// Trida predstavujici zarovku, ktera vyvolava udalost /// </summary> public class Zarovka private int vykon; private bool sviti; //deklarace udalosti public event ZmenaStavuHandler Zmeneno; public int Vykon get return vykon; set vykon = value; public bool Sviti get return sviti; public Zarovka(int vykon) this.vykon = vykon; //tato metoda vyvolava udalost protected void PriZmene() //kontrola, zda existuji klientske objekty //ktere si udalost predplatitili if (Zmeneno!= null) //vyvolani udalosti Zmeneno(); 66

68 public void Rozsvitit() sviti = true; //zavolani metody, ktera vyvolava udalost PriZmene(); public void Zhasnout() sviti = false; //zavolani metody, ktera vyvolava udalost PriZmene(); Na úrovni jmenného prostoru je deklarován delegát ZmenaStavuHandler, který je použit pro určení typu delegáta události Zmeneno ve třídě Žárovka. Delegát předepisuje, že obslužná metoda nebude vracet hodnotu a bude bez vstupních parametrů. Za zmínku stojí metoda PriZmene, která je určena k vyvolání události Zmeneno. Metoda zkontroluje jestli existuje nějaký klientský objekt, který si předplatil událost a pokud ano, dojde k vyvolání události. Metoda PriZmene je volána metodami Rozsviti a Zhasnout, které mění hodnotu soukromého atributu sviti. Obsluha událostí Pokud chceme nastalou událost nějaké instance třídy obsloužit, musíme nějakou třídu učinit takzvaným předplatitelem události. Zvenčí, lze k události třídy nebo její instance přistoupit jako ke členu, ale použití tohoto členu je velmi omezené. Jediné co je.net frameworkem dovoleno s členy typu událost provést je přidání instance delegáta do seznamu delegátů pro její obsluhu nebo naopak instancí delegáta z tohoto seznamu vyjmout. K přidání instance delegáta slouží operátor += a k jeho vyjmutí operátor -=. Následující kód třídy ZarovkaTest ukazuje způsob obsluhy událostí ve třídě: public class ZarovkaTest public static void Test() //vytvoreni instance tridy Zarovka Zarovka mojezarovka = new Zarovka(100); //vytvoreni instanci delegatu pro obsluhu //a jejich nasledne prirazeni do seznamu //instanci delegatu pro obsluhu udalosti mojezarovka.zmeneno += new ZmenaStavuHandler(StavZarovkyZmenenObsluha); mojezarovka.zmeneno += new ZmenaStavuHandler(StavZarovkyZmenenObsluha2); //metoda zapricini vyvolani udalosti mojezarovka.rozsvitit(); private static void StavZarovkyZmenenObsluha() Console.WriteLine("Stav zarovky byl zmenen"); private static void StavZarovkyZmenenObsluha2() Console.WriteLine("Druha metoda obsluhujici nastalou zmenu stavu zarovky"); 67

69 Jediné co se v metodě Test této třídy děje, je vytvoření instance třídy žárovka, následné vytvoření instancí delegátů typu ZmenaStavuHandler, které jsou asociovány s obslužnými metodami a ihned připojeny k obsluze události. Potom dochází k volání metody Rozsvit, která zapříčiní vyvolání události. Výstup po zavolání této metody bude tedy vypadat následovně, jak jistě mnozí předpokládají. Stav zarovky byl zmenen Druha metoda obsluhujici nastalou zmenu stavu zarovky Události a dědičnost Jednou ze specifických vlastností událostí, je že nemohou být přímo vyvolány z žádné jiné třídy, než ve které byly deklarovány, to znamená ani ve třídách odvozených, takže v případě, že bychom se pokusili zkompilovat následující kód, nebyli bychom úspěšní. public class BarevnaZarovka : Zarovka public BarevnaZarovka(int vykon) : base(vykon) public BarevnaZarovka(int vykon,system.drawing.color barva) : base(vykon) this.barva = barva; private System.Drawing.Color barva; public System.Drawing.Color Barva get return barva; set //chyba - udalost neni mozne v teto //tride udalost vyvolat Zmeneno(); barva = value; Vytvořil jsem třídu reprezentující barevnou žárovku, která je odvozena od třídy Zarovka. Nicméně pokud se v ní pokusím vyvolat událost Zmeneno, překlad programu díky tomu skončí chybou. Řešení v těchto situacích se naskýtá ve formě zavolání metody implementované na bázové třídě, která danou událost vyvolá. Pochopitelně tato metoda musí mít takový specifikátor přístupu, aby k ni bylo možné v odvozené třídě přistoupit. V našem případě se jedná o metodu PriZmene. Takže funkční řešení by vypadalo takto: public class BarevnaZarovka : Zarovka public BarevnaZarovka(int vykon) : base(vykon) public BarevnaZarovka(int vykon,system.drawing.color barva) : base(vykon) this.barva = barva; private System.Drawing.Color barva; 68

70 public System.Drawing.Color Barva get return barva; set PriZmene(); barva = value; Tím, že metodu vyvolávající událost zpřístupníme odvozeným třídam, tedy dosáhneme toho, že odvozené třídy budou pomocí této metody schopny danou událost vyvolat. Odvozeným třídám můžeme nechat i vetší volnost při vyvolávání události a to tím, že metodu vyvolávající událost definujeme jako virtuální a tím pádem budou odvozené třídy tuto metodu překrýt a naimplementovat po svém. V příštím díle si ještě prohloubíme znalosti o používání událostí v.net frameworku. Poznáváme C# a Microsoft.NET 17. díl události podruhé Po seznámení s událostmi, které proběhlo v minulém díle, se budu v tomto díle zabývat doporučeným způsobem pro implementaci tříd v.net frameworku, které mají vyvolávat události. Doporučený způsob implementace tříd Jak již víme, událost může mít libovolný typ delegáta, který předepisuje signaturu metody sloužící k reakci na ni. Ovšem v.net frameworku existuje určitý doporučený způsob implementace tříd, které mají vyvolávat události. Jedním z těchto doporučení je typ delegáta, který by měl být použit pro události. Podle tohoto doporučení by měl delegát pro událost předepisovat dva formální parametry. Prvním z nich by měl být objekt, který vyvolal událost a druhý má za úkol představovat objekt nesoucí dodatečné informace o vzniklé události. Objekt nesoucí ony dodatečné informace by měl podle těchto doporučení být instancí třídy, která je odvozena od třídy System.EventArgs. Delegát typu System.EventHandler Pro události, při jejichž vyvolání neexistuje potřeba předat jakékoliv dodatečné informace existuje v.net frameworku delegát typu System.EventHandler, který by měl být v těchto situacích použit. Tento typ delegáta předepisuje jako druhý parametr metody pro reakci na událost typ System.EventArgs. Jak bylo řečeno třída System.EventArgs je určena pro odvozování tříd pro reprezentaci dodatečných informací o nastalé události, takže instance této třídy žádné informace nenesou. Pro lepší pochopení jsem upravil třídu Zarovka, která se objevila v minulém díle. V této verzi třídy je jako typ delegáta události Zmeneno použit System.EventHandler, takže již splňuje ono výše uvedené doporučení. Zdrojový kód tedy vypadá následovně: public class Zarovka private int vykon; private bool sviti; 69

71 //deklarace udalosti public event EventHandler Zmeneno; public int Vykon get return vykon; set vykon = value; public bool Sviti get return sviti; public Zarovka(int vykon) this.vykon = vykon; //tato metoda vyvolava udalost protected virtual void PriZmene(EventArgs e) //kontrola, zda existuji klientske objekty //ktere si udalost predplatitili if (Zmeneno!= null) //vyvolani udalosti - je predana //reference na aktualni instanci a //instance tridy EventArgs Zmeneno(this,e); public void Rozsvitit() sviti = true; //zavolani metody, ktera vyvolava udalost PriZmene(EventArgs.Empty); public void Zhasnout() sviti = false; //zavolani metody, ktera vyvolava udalost PriZmene(EventArgs.Empty); Metoda PriZmene, sloužící k vyvolání události nyní očekává v podobě formálního parametru instanci třídy System.EventArgs, který je následně v jejím těle použit pro vyvolání události, spolu s referencí na aktuální instanci. Jelikož instance třídy System.EventArgs nemůže nést dodatečné informace, tak metody Rozsvitit a Zhasnout použijí pro vytvoření její instance statickou vlastnost Empty, která zapříčiní to samé jako zavolání implicitního konstruktoru. 70

72 Přenášení dodatečných údajů o události Pokud námi vytvářená třída vyvolává událost, u které je možné, že při jejím zpracovávání budou potřené nějaké dodatečné informace k nastalé události, musíme vytvořit třídu, jejíž instance tyto data ponesou. Vytvoříme tedy novou třídy, která je potomkem třídy System.EventArgs a definujeme členy pro přenos informací. Potom je ještě potřeba definovat delegáta, který jako druhý parametr pro přenos informací o události očekává právě instanci námi vytvořené třídy. Tím dosáhneme toho, že reakční metody budou mít k těmto dodatečným informacím přístup. Jako demonstrační příklad použiji novou verzi třídy BarevnaZarovka, kterou jste také mohli naleznout v příkladech z minulého dílu. Kromě této třídy definuji ještě třídu StavBarevneZarovkyZmenenEventArgs, jejíž instance budou představovat objekty nesoucí ony dodatečné informace. Instance této třídy budou ve formě druhého formálního parametru očekávány delegátem ZmenaBarevneZarovkyEventHandler. public class StavBarevneZarovkyZmenenEventArgs : EventArgs string kdozmenil; public StavBarevneZarovkyZmenenEventArgs(string kdozmenil) this.kdozmenil = kdozmenil; public string KdoZmenil get return kdozmenil; Jak je ze zdrojového kódu třídy zřejmé, jediná dodatečná informace, kterou její instance ponesou bude řetězec představující jméno člověka, který žárovku přebarvil. Následující kód již představuje novou verzi třídy BarevnaZarovka. public class BarevnaZarovka : Zarovka private System.Drawing.Color barva; public System.Drawing.Color Barva get return barva; //definice delegata pro zpracovani udalosti public delegate void ZmenaBarevneZarovkyEventHandler (object sender, StavBarevneZarovkyZmenenEventArgs e); //definice udalosti public event ZmenaBarevneZarovkyEventHandler BarvaZmenena; public BarevnaZarovka(int vykon) : base(vykon) 71

73 //metoda vyvolavajici udalost protected void PriZmeneBarvy(StavBarevneZarovkyZmenenEventArgs e) if (BarvaZmenena!= null) BarvaZmenena(this,e); //metoda slouzici ke zmene barvy zarovky public void Obarvit(System.Drawing.Color novabarva,string kdobarvi) barva = novabarva; //vytvoreni instance tridy pro neseni udaju o udalosti StavBarevneZarovkyZmenenEventArgs largs = new StavBarevneZarovkyZmenenEventArgs(kdoBarvi); //volani metody zapricini vyvolani udalosti PriZmeneBarvy(lArgs); Metoda Obarvi pomocí, které je umožněno žárovku přebarvit, očekává kromě nové barvy také řetězec indikující kdo je za změnu barvy žárovky zodpovědný. Na základě této informace je vytvořena instance třídy StavBarevneZarovkyZmenenEventArgs a ta je po té předána jako parametr metodě PriZmeneBarvy, která vyvolává událost. Tak třídy, které budou pomocí svých metod reagovat na tuto událost, budou schopny získat informaci o tom kým byla žárovka přebarvena. To, jakým způsobem je toto realizovatelné můžete shlédnout v následujícím zdrojovém kódu. public class BarevnaZarovkaTest public static void Test() BarevnaZarovka mojebarevnazarovka = new BarevnaZarovka(80); mojebarevnazarovka.barvazmenena += new BarevnaZarovka.ZmenaBarevneZarovkyEventHandler(ReakceNaZmenuBarvy); mojebarevnazarovka.obarvit(system.drawing.color.red, "Petr"); public static void ReakceNaZmenuBarvy(object sender, StavBarevneZarovkyZmenenEventArgs e) //z instance tridy StavBarevneZarovkyZmenenEventArgs //zjistime kdo zarovku prebarvil Console.WriteLine("Barvu zarovky zmenil " + e.kdozmenil); Další díl se budu zaobírat další zajímavou vlastností jazyku C#, kterou jsou indexery. Poznáváme C# a Microsoft.NET 18. díl indexery Dnešní díl seriálu bych chtěl věnovat popisu specifické vlastnosti jazyku C#, kterou jsou indexery, které se často používají v případech kdy v sobě třída obsahuje určitý počet objektů a umožňují nám k těmto objektům přistupovat jako v případě polí. 72

74 Indexery Někdy můžeme chtít dát možnost přistupovat k určitým členům třídy, způsobem, který známe s polí, tedy skrze indexy. K tomu, aby to bylo možné existují v jazyku C# takzvané indexery. Indexery se na úrovni třídy definují velmi podobně jako vlastnosti, takže umožňují definovat akce, které se mají provést při čtení indexované hodnoty respektive při jejím zápisu. Obecný zápis deklarace indexeru vypadá takto: Modifikátory datový_typ this[index(y)] Pro demonstraci si přestavme situaci, ve které máme za úkol naimplementovat jednoduchou datovou strukturu představující list osob. Pokud budeme chtít uživateli našich knihoven zpřístupnit osoby obsažené v listu, místo metody, právě zmíněným indexem, nadefinujeme ve třídě indexer, který osobu na požadovaném indexu zpřístupní. Implementace takovéhoto listu, kde je použit indexer by mohla vypadat následovně: public class Osoba private string jmeno; private string prijmeni; public Osoba(string jmeno, string prijmeni) this.jmeno = jmeno; this.prijmeni = prijmeni; public string Jmeno get return jmeno; set jmeno = value; public string Prijmeni get return prijmeni; set prijmeni = value; //prekryti metody ToString pro vraceni //textove reprezentace public override string ToString() return "Osoba: " + jmeno + " " + prijmeni; 73

75 public class ListOsob System.Collections.ArrayList osoby = new System.Collections.ArrayList(); public void PridejOsobu(Osoba osobapropridani) osoby.add(osobapropridani); public void OdeberOsobu(Osoba osobaproodebrani) osoby.remove(osobaproodebrani); //deklarace indexeru public Osoba this[int indexosoby] //definice akci pri cteni indexovane hodnoty get return (Osoba)osoby[indexOsoby]; //definice akci pri zapisu indexovane hodnoty set osoby.add(value); Nadefinoval jsem jednoduchou třídu pro reprezentaci osoby a třídu ListOsob, která slouži k jejich ukládání. Kromě metod, pro přidání a odebrání osoby z listu je zde deklarován indexer pro přístup k jednotlivým osobám v něm obsažených, pomocí celočíselného indexu. Jak můžete vidět, tak pomocí tohoto indexeru je možné hodnoty jak číst, tak zapisovat, protože obsahuje blok get i set. Poznámka: Prosím, nelekněte se toho, že pro ukládání osob je ve třídě ListOsob interně použita kolekce ArrayList, která na rozdíl od pole mění svou velikost dynamicky, podle potřeby. Kolekcím se budu určitě věnovat v jednom z příštích dílů tohoto seriálu. Protože, třída obsahuje indexer, tak nám již nebrání k tomu, abychom k agregovaným osobám přistupovali pomocí indexu, tak jak ukazuje následující zdrojový kód. public class ListOsobTest public static void Test() //vytvoreni instance listu ListOsob list = new ListOsob(); //vytvoreni objektu osob Osoba Petr = new Osoba("Petr","Pus"); Osoba Janna = new Osoba("Jana","Spanhielova"); Osoba Marketa = new Osoba("Marketa","Kecurova"); //pridani osob do listu list.pridejosobu(petr); list.pridejosobu(janna); list.pridejosobu(marketa); //vyzvednuti druhe osoby v poradi pomoci indexu Osoba lhledanaosoba = list[1]; 74

76 Console.WriteLine(lHledanaOsoba); Použití cyklu foreach se třídou s indexery Pokud lze k agregovaným objektům ve struktuře přistupovat jako v poli, je vhodné zajistit, aby bylo možné k procházení obsahu struktury použít oblíbený cyklus foreach. K tomu, aby tento cyklus mohl být k procházení agregovaných objektů použit, musí třída implementovat rozhraní IEnumerable z jmenného prostoru System.Collections. Toto rozhraní předepisuje pouze jednu metodu, kterou je GetEnumerator. Tato metoda musí vrátit odkaz na instanci třídy implementující rozhraní IEnumerator, které se taktéž nachází ve jmenném prostoru System.Collections. Metody této třídy pak slouží k procházení jednotlivými prvky datové struktury. To pro nás znamená, že pokud chceme, aby bylo možné k procházení naší datové struktury použít cyklu foreach, musíme implementovat třídu, které implementuje rozhraní IEnumerator a instance této třídy vracet pomocí metody GetEnumerator naší třídy. Pro pochopení této problematiky jsem upravil třídu ListOsob do podoby, která již splňuje dané podmínky a tudíž ji bude možné procházet pomocí cyklu foreach. public class ListOsobEnumerable : System.Collections.IEnumerable System.Collections.ArrayList osoby = new System.Collections.ArrayList(); //metoda predepsana v rozhrani IEnumerable //vraci instanci IEnumerator public System.Collections.IEnumerator GetEnumerator() return new ListOsobEnumerator(this); //Implementace rozhrani IEnumerator, slouzici k prochazeni //jendotlivymi prvky listu osob class ListOsobEnumerator : System.Collections.IEnumerator ListOsobEnumerable listosob; int index; //parametr konstruktoru predstavuje list osob //ktery ma byt prochazen public ListOsobEnumerator(ListOsobEnumerable list) index = -1; listosob = list; //vlastnost pro ziskani objektu na aktualnim indexu public Object Current get return listosob[index]; //Metoda, ktera zajisti posunuti indexu a //vraci true pokud je na danem indexu k dipozici prvek public bool MoveNext() 75

77 index++; return (index < listosob.osoby.count); //Metoda, ktera nastavi index na pocatecni pozici public void Reset() index = -1; public void PridejOsobu(Osoba osobapropridani) osoby.add(osobapropridani); public void OdeberOsobu(Osoba osobaproodebrani) osoby.remove(osobaproodebrani); //definice indexeru public Osoba this[int indexosoby] get return (Osoba)osoby[indexOsoby]; set osoby.add(value); Třída ListOsobEnumerator, která je implementována jako vnitřní soukromá třída, obsahuje implementaci tří metod, předepsaných v rozhraní IEnumerator, které zajistí, že pomocí instancí této třídy bude možné naším listem procházet. Vlastnost Current této třídy, slouží k navrácení aktuálního objektu. Pro posunutí se na další prvek listu slouží metoda MoveNext, která vrací hodnotu true pokud, se na další pozici nachází nějaký prvek. A konečně metoda Reset, zajišťuje nastavení pomyslného ukazatele na začátek listu. Podmínky dané.net frameworkem máme úspěšně splněny a je tedy možné listem osob iterovat pomocí pohodlného cyklu foreach, jak můžete vidět na zdrojovém kódu třídy ListOsobEnumerableTest. public class ListOsobEnumerableTest public static void Test() //vytvoreni instance listu ListOsobEnumerable list = new ListOsobEnumerable(); //vytvoreni objektu osob Osoba Petr = new Osoba("Petr","Pus"); Osoba Janna = new Osoba("Jana","Spanhielova"); Osoba Marketa = new Osoba("Marketa","Kecurova"); //pridani osob do listu list.pridejosobu(petr); list.pridejosobu(janna); list.pridejosobu(marketa); foreach(osoba aktualniosoba in list) 76

78 Console.WriteLine(aktualniOsoba); Jelikož, máme k dispozici objekt sloužící k procházení naší struktury (ListOsobEnumerator), můžeme s jeho pomocí k procházení použít i jiné cykly, například cyklus while. Mimochodem nějaký takovýto kód je vytvořen kompilátorem jazyka C# při použití cyklu foreach: System.Collections.IEnumerator enumerator = list.getenumerator(); while(enumerator.movenext()) Osoba aktualniosoba = (Osoba) enumerator.current; Console.WriteLine(aktualniOsoba); V následujícím dílu si povíme více o převodech mezi číselnými typy. Poznáváme C# a Microsoft.NET 19. díl převody číselných typů Dnešní díl bude podrobněji pojednávat o převodech mezi číselnými typy. Vysvětlíme si rozdíl mezi implicitním a explicitním převodem a jaká jsou možná rizika při jejich užití. Převody nebo jinými slovy konverze používáme v případech kdy z hodnoty proměnné nebo konstanty určitého typu potřebujeme udělat proměnnou jiného datového typu. Některé hodnoty totiž mohou být vyjádřeny pomocí několika typů a jediný rozdíl je ve způsobu uložení této hodnoty v paměti. Například číslo 5 může být uloženo jako 16- bitová hodnota bez znaménka,jako 32-bitová hodnota se znaménkem nebo i jako hodnota s plovoucí čárkou. Konverze v C# V jazyku C# jsou konverze rozděleny na dva druhy a to na implicitní a explicitní. Implicitní konverze jsou takové konverze, při kterých není možné, aby došlo ke změně hodnoty, protože je hodnota konvertována na typ s větším rozsahem. Naproti tomu při explicitní konverzi je hodnota převáděna na typ s menším rozsahem hodnot a tím pádem může ke změně hodnoty dojít. Pro explicitní konverzi se v jazyku C# používá operátor přetypování, implicitní konverze jsou prováděny automaticky. /*vsechny tyto prevody jsou implicitni protoze je hodnota prevadena na typ s vetsim rozsahem*/ sbyte b = 100; short s = b; int i = s; long l = i; /*prevod na datovy typ s mensim rozsahem musi byt vyjadreny explicitne pomoci operatoru pretypovani*/ i = (int) l; s = (short) i; b = (sbyte) s; Explicitní číselné konverze 77

79 U explicitních číselných konverzí, tedy u těch, které používají operátor přetypování v případech, kdy se hodnota původního typu nevejde do proměnné nového typu dojde ke změně hodnoty. Nová hodnota nabude podoby přebytku do velikosti rozsahu hodnot nového typu, popřípadě násobků velikosti tohoto rozsahu. Pro lepší pochopení se podívejme na tento příklad: public class ExplicitNumberReTyping public static void Retype() int inthodnota = 355; //po pretypovani je hodnota 99 ( (pocet hodnot rozsahu typu byte)) byte bytehodnota = (byte) inthodnota; Console.WriteLine("Nova hodnota : 0",byteHodnota); Po explicitní konverzi hodnoty typu int na typ byte je hodnota 355 změněna na hodnotu 99, což je dáno právě tím, že je do nové hodnoty uložen pouze přebytek nad velikost rozsahu hodnot typu byte. Takže původní hodnota byla 355 a od ní byla odečtena velikost rozsahu hodnot nového typu, což je v případě typu byte 256, tím pádem je nová hodnota rovna číslu 99. Klíčové slovo checked a kontrolované převody V určitých případech můžeme potřebovat kontrolovat, jesti při explicitním přetypování nedojde k tomu co jsme mohli vidět v předchozím případě, tedy k přetečení hodnoty. Jazyk C# nám jako cestu k řešeni tohoto problému nabízí kontrolované převody, které se realizuji použitím klíčového slova checked. Toto klíčové slovo umožňuje definovat kontrolovaný blok, v němž jsou všechny explicitní konverze kontrolovány, nebo je možné jeho pomocí učinit kontrolovanou pouze jednu explicitní konverzi. Následující příklad demonstruje použití klíčového slova checked pro definici kontrolovaného bloku. public static void CheckedBlockRetype() //definice kontrolovaneho bloku checked int inthodnota = 355; byte bytehodnota = (byte) inthodnota; V případě, že je přetypovávaná hodnota vyšší, než rozsah nového typu a dané explicitní přetypování je prováděno v kontrolovaném bloku dojde k vyvoláni výjimky System.OverflowException. Tím se nám samozřejmě nabízí, pomocí nám již dobře známe konstrukce try catch, na tuto situaci nějakým způsobem reagovat. public static void CheckedRetypeWithCatch() try checked int inthodnota = 355; byte bytehodnota = (byte) inthodnota; 78

80 catch(system.overflowexception) //zpracovani preteceni Console.WriteLine("Pri konverzi doslo k preteceni"); Předchozí příklady ukazovaly použití klíčového slova checked k definici kontrolovaného bloku, avšak je možné toto klíčové slovo použít i jako operátor pro explicitní konverzi, takže pokud v takovéto konverzi dojde k přetečení je opět vyvolána výjimka System.OverflowException. public static void CheckedOperatorRetype() int inthodnota = 355; byte bytehodnota = checked ((byte) inthodnota); Kompilátor jazyku C# nám umožňuje zajistit, aby všechny explicitní konverze v programu byly kontrolované. Lze toho docílit použitím přepínače /checked. Zajisté vás napadlo, jestli je v případě použití tohoto přepínače možné nějaké explicitní konverze definovat naopak jako nekontrolované. Samozřejmě, že to možné je a to pomocí klíčového slova unchecked. public static void UncheckedRetype() int inthodnota = 355; //tato konverze nebude kontrolovana ani //pri pouziti prepinace kompilatoru /checked byte bytehodnota = unchecked ((byte) inthodnota); Konverze pomocí třídy System.Convert Ke konverzím mezi číselnými typy lze použít také třídu System.Convert, která nabízí kompletní sadu metod pro podporované konverze. Je určena především pro konverze nezávislé na konkrétním použitém programovacím jazyce. V různých programovacích jazycích pro.net se totiž mohou techniky, použité pro jednotlivé konverze, lišit. Pomocí metod této třídy, lze provádět jak zužující či rozšiřující konverze tak i konverze nesouvisejících datových typů. Například lze provést konverzi z řetězce na číselnou hodnotu nebo, chcete-li, na logickou hodnotu. Je potřeba poznamenat, že všechny konverze prováděné pomocí této třídy jsou kontrolované, takže při zužujících konverzích může výt vyvolána výjimka. public class SystemConvertRetyping public static void ToInt32Test() decimal dechodnota = System.Decimal.MaxValue; //bude vyvolana vyjimka int inthodnota = System.Convert.ToInt32(decHodnota); Console.WriteLine(intHodnota); 79

81 //prevod z retezce na logickou hodnotu public static void FromStringToBoolean() string stringhodnota = "true"; //po provedeni teto konverze bude //hodnota promenne boolhodnota True bool boolhodnota = System.Convert.ToBoolean(stringHodnota); Console.WriteLine(boolHodnota); public static void FromInt64ToInt32() long int64hodnota = ; //vysledek je stejny jako pri pouziti implicitni konverze int inthodnota = System.Convert.ToInt32(int64Hodnota); Console.WriteLine(intHodnota); //prevod retezce na ciselnou hodnotu public static void FromStringToInt32() string stringhodnota = " "; int inthodnota = System.Convert.ToInt32(stringHodnota); Console.WriteLine(intHodnota); Příští díl se zaměřím na konverze mezi strukturami a referenčními typy. Poznáváme C# a Microsoft.NET 20. díl konverze referenčních typů V tomto díle se zaměřím na popis možných konverzí mezi referenčními typy. Dozvíme se o možnostech explicitního přetypování mezi instancemi a také o užitečných operátorech, které při konverzích nezřídka využijeme. V jazyce C# jsou možnosti přetypování spojeny s hierarchií dědičnosti použitých tříd. Stejně jako u číselných typů jsou konverze rozděleny na implicitní a explicitní. Rozdíl mezi nimi je v případě referenčních typů takový, že implicitní konverzi můžeme použít v případě, že chceme přetypovat nějaký konkrétnější typ na nějaký obecnější. Jinými slovy tento typ konverze použijeme, když chceme, aby na určitou instanci odkazovala referenční proměnná typu jejího předka. Tak jako u číselných typů v případě implicitních konverzí nehrozí, že by konverze proběhla neúspěšně. To je samozřejmě logické, neboť jelikož je referencovaná instance potomkem, lze na ní zavolat všechny veřejné metody a použít všechny veřejné vlastnosti definované na předkovi. Pokud je třída, jejíhož typu je referenční proměnná polymorfická, tedy má nějaké, metody definovány jako virtuální, bude při volání takovéto metody skrze referenční proměnnou použita verze metody definovaná na třídě odkazované instance. //referencni promenna typu Object bude odkazovat //na pole celych cisel Object lobject = new int[5]; 80

82 Jelikož je třída System.Object předkem všech tříd v MS.NET frameworku, není problém nechat referenční proměnnou tohoto typu odkazovat na jakoukoli instanci. V uvedeném přikladu referenční proměnná tohoto typu odkazuje na pole čísel typu int. Protože je referenční proměnná typu Object budeme moci používat pouze členy definované na ní. Jak ale dosáhneme toho, abychom k instanci mohli přistupovat opět jako k poli? Explicitní konverze K tomu abychom mohli instanci používat jako pole, musíme odkaz na Object explicitně přetypovat na referenční proměnnou typu pole. Zápis explicitní konverze známe již z minulého dílu. int[] lpole = (int[]) lobject; Console.WriteLine(lPole.Length); Po té, co se na instanci odkazujeme pomocí referenční proměnné typu int[] již můžeme využívat všech členů tohoto typu. Explicitní konverzi je nutné použít všude tam, kde se snažíme referenční proměnnou obecnějšího typu převést na referenční proměnnou specifičtějšího typ. Tedy když chceme z reference na předka udělat referenci na potomka za účelem použití specifických členů, které na předkovi nejsou k dispozici. U explicitních konverzí, na rozdíl od konverzí implicitních, ovšem existuje riziko, že neproběhne korektně. To se stane v případě, kdy chceme nechat referenční proměnnou odkazovat na instanci, ke které ve skutečnosti nemůže být přistupováno pomocí referenční proměnné onoho typu. K této situaci dojde ve chvíli, kdy instance není typu referenční proměnné, ani není potomkem tohoto typu a v případě pokusu přetypování na typ rozhraní, typ instance toto rozhraní neimplementuje. Pokud tedy tato situace nastane, běhové prostředí na ni zareaguje vyhozením výjimky System.InvalidCastException. Object lobject = new int[5]; //tato konverze skonci chybou string lretezec = (string)lobject; Tím, že spustíme uvedený příklad se o tom přesvědčíme. Referenční proměnná odkazuje na pole a pokus o její převedení na referenční proměnnou typu string je na už první pohled nesmyslný a samozřejmě skončí vyhozením zmiňované výjimky. Příklady s obrazci Pro lepší pochopení problematiky konverzí mezi referenčními typy použijeme jednoduchý příklad s obrazci. Implementaci tříd v článku nehledejte, můžete si ji však stáhnout. Uvedu zde pouze UML digram tříd. 81

83 Máme zde tři třídy představující určité obrazce, které mají společného předka představovaného abstraktní třídou Tvar. Třída Tvar obsahuje abstraktní metodu DejObsah pro vrácení obsahu konkrétního tvaru. Všechny tři třídy implementují rozhraní IVykreslitelne, takže musejí implementovat metodu Vykresli, pomocí které je konkrétní objekt vykreslen. Přetypování na proměnnou rozhraní Referenční proměnná nemusí být jen typu třídy, ale může být i typu rozhraní. Pomocí takovéto referenční proměnné můžeme odkazovat na jakoukoli instanci třídy, která toto rozhraní implementuje. Skrze tuto referenční proměnnou potom můžeme volat metody definované na rozhraní na konkrétní referencované instanci, která je implementuje. Takže v našem případě můžeme do proměnné typu IVykreslitelne obsadit, kterýkoli ze tří konkrétních tvaru a pomocí této proměnné na nich volat metodu vykresli, jak ukazuje následující příklad. public class RozhraniPriklad public static void PretypovaniTest() IVykreslitelne lvykreslitelnyobjekt; 82

84 Ctverec lctverec = new Ctverec(5); Kruh lkruh = new Kruh(5); Obdelnik lobdelnik = new Obdelnik(3,4); lvykreslitelnyobjekt = lctverec; lvykreslitelnyobjekt.vykresli(); lvykreslitelnyobjekt = lkruh; lvykreslitelnyobjekt.vykresli(); lvykreslitelnyobjekt = lobdelnik; lvykreslitelnyobjekt.vykresli(); Výstup po spuštění bude vypadat takto: Kreslim ctverec Kreslim kruh Kreslim obdélník Referenční proměnná typu abstraktní třída Referenční proměnná může bez problému být typu abstraktní třída, i když jak víme instanci abstraktních tříd nelze vytvořit. Do takovéto referenční proměnné můžeme díky existenci implicitní konverze obsadit každou instanci třídy, která je od ní odvozena a tím pádem můžeme využívat rozhraní typu abstraktní třídy včetně abstraktních metod, které již jsou na potomkovi naimplementovány. public class AbstraktniPriklad public static void PretypovaniTest() Tvar ltvar; Ctverec lctverec = new Ctverec(5); Kruh lkruh = new Kruh(5); Obdelnik lobdelnik = new Obdelnik(3,4); ltvar = lctverec; Console.WriteLine(lTvar.DejObsah()); ltvar = lkruh; Console.WriteLine(lTvar.DejObsah()); ltvar = lobdelnik; Console.WriteLine(lTvar.DejObsah()); Po spuštění tohoto příkladu budou vypsány obsahy jednotlivých instancí tvarů. Operátor is Operátor is nám umožňuje zjistit jestli lze referenční proměnnou odkazující na existující instanci převést na určitý typ. To se může hodit v situacích, kdy potřebujeme před uskutečněním konverze vědět, jestli je vůbec možná, aby později nedošlo k neplatnému 83

85 přetypování. Následující příklad demonstruje použití tohoto operátoru v kontextu našeho příkladu. public class OperatorIsPriklad public static void Vykresli(Object ObjektNaVykresleni) //v pripade ze predany objekt implementuje //rozhrani, bude vykreslen if (ObjektNaVykresleni is IVykreslitelne) ((IVykreslitelne) ObjektNaVykresleni).Vykresli(); else throw new Exception("Objekt nelze vykreslit"); Metoda vykresli pomocí operátoru is zkontroluje jestli předaná instance implementuje rozhraní IVykreslitelne a pokud ano dojde k přetypování a zavolání metody Vykresli. Operátor as Operátor as je podobný operátoru is, avšak na rozdíl od operátoru is pouze nezkontroluje jestli lze instanci na daný typ převést, ale navíc ono přetypování provede. Pokud existující instance nelze na daný typ převést, výsledek použití tohoto operátoru pro přetypování má hodnotu null, místo toho, aby byla vyhozena výjimka, jak je tomu v případě klasické explicitní konverze. Použití operátoru as je efektivnější než použití operátoru is spolu s následným přetypováním. Takto by mohla vypadat ukázková metoda z předchozího příkladu s použitím operátoru as : public class OperatorAsPriklad public static void Vykresli(Object ObjektNaVykresleni) IVykreslitelne lvykreslitelnytvar = ObjektNaVykresleni as IVykreslitelne; //pokud predany objekt neimplementuje rozhrani, //obsahuje promenna hodnotu null if (lvykreslitelnytvar!= null) lvykreslitelnytvar.vykresli(); else throw new Exception("Objekt nelze vykreslit"); Poznáváme C# a Microsoft.NET 21. díl komentování a dokumentace Dnešní díl se seznámíme s možnostmi komentování kódu napsaným v jazyce C# a také s možnosti vytváření dokumentace. Pro nováčky se mimo jiné pozastavím nad tím co vlastně komentování zdrojového kódu znamená a proč se dělá. Komentování kódu Ve zdrojovém kódu se kromě výkonného kódu tj. kódu, který vykonává operace, mohou nacházet i dodatečné informace, které slouží pouze pro lidi, které daný kód čtou. Tyto informace jsou nazývány komentáře a pokud tento seriál sledujete, tak se komentáře vyskytovali ve všech zdrojových kódech příkladů. 84

86 Komentáře, jak název napovídá, slouží k okomentování určité části kódu, k popisu toho co daný úsek kódu provádí, abychom my, nebo někdo jiný, kdo po nás zdrojový kód bude číst, funkčnost onoho bloku rychleji pochopil. Při kompilaci jsou komentáře pochopitelně ignorovány. Je známým programátorským zlozvykem přístup typu tohle okomentuji později, až to pořádně otestuji nebo v tom horším případě tohle si budu pamatovat a komentovat to nebudu. Bohužel výsledek prvního rozhodnutí je mnohdy stejný jako u rozhodnutí druhého a tím výsledkem je, že kód zůstane neokomentovaný, což ovšem není dobře, protože si představte situaci, kdy napíšete nějakou třídu, jejíž metody neprovádějí zrovna triviální operace a vy si po půl roce, kdy tuto třídu budete chtít upravit budete říkat co jsem to tady jenom dělal a nějaký čas vám pravděpodobně zabere, než onu funkčnost opět stoprocentně pochopíte. Nemluvě o projektech, na kterých pracuje více jak jeden člověk. Běžné komentáře v C# V C# jsou základní komentáře buď jednořádkové nebo víceřádkové. Jednořádkové komentáře: K vytvoření komentáře, který zabere pouze jeden řádek použijeme symbol dvou lomítek. //jednoradkovy komentar Víceřádkové komentáře: Pokud chceme vytvořit komentář, který zabere více než jeden řádek a nechceme před každý řádek komentáře psát ona dvě lomítka, použijeme víceřádkový komentář. /* komentar na vice radku */ Takto by tedy mohlo vypadat použití těchto komentářů v praxi: /* Tato metoda vraci soucet dvou cisel typu int */ public int Secti(int a, int b) //secteni hodnot a vraceni vysledne hodnoty return a + b; Dokumentace v XML Kromě komentování kódu je také možné vytvořit dokumentaci k námi vytvořeným třídám. K tomuto účelu jazyk C# podporuje dokumentační formát založený XML. Pro ty co o XML slyší poprvé uvedu jen, že se jedná o zkratku extensible Markup Language (Rozšířitelný značkovací jazyk). Je to značkovací jazyk na první pohled podobný jazyku 85

87 HTML, ale s podstatným rozdílem v tom, že XML definuje význam jednotlivých částí dokumentu, kdežto HTML definuje jejich vzhled. Kompilátor pro C# umožňuje s použitím parametru /doc vygenerovat XML dokument obsahující dokumentaci našim třídám. Některé tagy (značky) jsou při kompilaci kontrolovány, ostatní představují pouze doporučení dané.net frameworkem a při kompilaci jsou ignorovány. Výsledný XML dokument je velmi často pomocí XSL (extensible stylesheet language jazyk určený k převodu XML do jiného formátu) transformace převeden na HTML stránku. XML dokument je také často využíván vývojovými prostředími pro zobrazování kontextových popisků při psaní zdrojového kódu. Následující tabulka obsahuje seznam používaných tagů pro vytváření XML dokumentačních komentářů v C#. c code example exception include list para param paramref Text, který představuje zdrojový kód Text, který představuje více řádek zdrojového kódu Pro uvedení příkladu použití členu popř. třídy Slouží k uvedení výjimek, které mohou ve třídě nastat. Umožňuje se referencovat na jiný soubor s XML dokumentací, kde existuje popis k typům v našem zdrojovém kódu. Používá se pro uvedení výčtu hodnot Umožňuje strukturování text. Měl by se nacházet uvnitř tagů summary, remarks nebo returns Slouží k popisu parametru Indikuje, že uvedené slovo označuje parametr permission Určuje viditelnost člena remarks returns see seealso summary value Umožňuje uvést doplňující informace o typu nebo jeho členovi. Tato značka by měla uvádět popis návratové hodnoty Definuje odkaz na jiný programový element Definuje odkat na jiný programový element. Tento odkaz by měl být uveden v sekci See Also. Uvádí základní informace o typu nebo jeho členovi. Používá se popisu hodnoty vlastnosti. Značky exception, include, param, paramref, permission, see a seealso nebo přesněji některé jejich atributy jsou při kompilaci kontrolovány, ostatní jsou součástí doporučení pro dokumentaci v.net frameworku. U značek exception, permission, see a seealso je kompilátorem kontrolován atribut cref, který referencuje nějaký typ a kompilátor zjišťuje, zda takovýto typ existuje. Při použití značek param a paramref je kontrolováno zda existuje daný parametr a u značky include kompilátor ověří existenci referencovaného XML dokumentu. Pokud tomu tak není kompilace neskončí chybou, ale kompilátor vás na tuto skutečnost upozorní varováním. Jako příklad pro lepší pochopení si uveďme jednoduchou třídu zaměstnanec, na které demonstruji použití některých XML dokumentačních komentářů. using System; namespace PrikladyZive21 86

88 /// Trida predstavujici zamestnance /// slouzici k demonstraci XML /// dokumentacnich komentaru /// </summary> public class Zamestnanec private int vek; private string jmeno; private string prijmeni; private int hodinovasazba; /// <value> /// Vek zamestance /// </value> public int Vek get return vek; set vek = value; /// <value> /// Jmeno zamestnance /// </value> public string Jmeno get return jmeno; set jmeno = value; /// <value> /// Prijmeni zamestnance /// </value> public string Prijmeni get return prijmeni; set prijmeni = value; /// <value> /// Hodinova sazba zamestance /// </value> public int HodinovaSazba get 87

89 return hodinovasazba; set hodinovasazba = value; /// Konstruktor ocekavajici jmeno a prijmeni zamestance. /// Parametr <paramref name="jmeno">jmeno</paramref> i <paramref name="prijmeni">prijmeni</paramref> /// jsou typu <see cref="string"/> /// </summary> /// <param name="jmeno">jmeno zamestance</param> /// <param name="prijmeni">prijmeni zamestance</param> public Zamestnanec(string jmeno, string prijmeni) this.jmeno = jmeno; this.prijmeni = prijmeni; /// Metoda pro vypocet mzdy zamestance. /// <example> /// Priklad pouziti : /// <code> /// Zamestnanec instance = new Zamestnanec("Tomas","Kutin"); /// instance.hodinovasazba = 65; /// int lmzda = instance.vypocetmzdy(100); /// </code> /// </example> /// </summary> /// <param name="pocetodpracovanychhodin">pocet odpracovanych hodin</param> /// <returns>vysledna mzda</returns> public int VypocetMzdy(int pocetodpracovanychhodin) return pocetodpracovanychhodin * hodinovasazba; Jak jsem psal, tak některá vývojová prostředí umí vygenerovaného XML souboru s dokumentací využít pro zobrazování kontextové nápovědy při tvorbě kódu. Na obrázku můžete vidět, jak je vytvořená dokumentace zobrazována v prostředí Visual C#.NET. Kontextová nápověda ve Visual C#.NET K vygenerování dokumentace pro uživatele našich tříd nejčastěji použijeme XSL transformaci do formátu HTML. Sobory XSL můžete nalézt na internetu, nebo si je vytvořit sami. V souborech pro stažení jeden takovýto soubor naleznete. Na obrázku níže vidíte jak může vypadat XML soubor s naší dokumentaci po XSL transformaci do HTML. Další díl seriálu věnuji uživatelsky definovaným konverzím 88

90 Poznáváme C# a Microsoft.NET 22. díl uživatelsky definované konverze Tento díl bude věnován seznámení se s možností definovat vlastní konverze mezi jednotlivými typy. Jazyk C# nám toto nabízí v podobě uživatelsky definovaných konverzí. Uživatelské konverze V jazyce C# je nám umožněno definovat vlastní konverze mezi třídami nebo strukturami, takže je potom možné typy mezi sebou převádět stejně jako některé věstavěné typy v.net frameworku. Jelikož jsou základní typy v.net frameworku implementovány použitím struktur (hodnotových typů), je tím pádem bez problému možné definovat konverzi mezi základním typem a typem námi vytvořeným a naopak. Konverze jsou na úrovni typu definovány jako operátory, tedy jako statické funkce s využitím klíčového slova operator, jejichž název je stejný jako název typu, pro který je konverze definována a jejich vstupní parametr je hodnota typu ze kterého má být konverze provedena. Jak víme z jednoho z minulých dílů, tak konverze jsou v C# dvou druhů - implicitní a explicitní. Z tohoto důvodu je nám umožněno pomocí klíčových slov implicit a explicit určit jestli daná konverze je implicitní respektive explicitní. Implicitní konverze jsou prováděny automaticky a konverze explicitní vyžadují použít syntaxi přetypování. Při návrhu našeho typu definujeme jako explicitní ty konverze, při kterých může docházet ke zkrácení hodnoty, nebo při nichž může dojít k vyvolání výjimky. Následující příklad obsahuje definici pro strukturu představující jedno celé číslo s hodnotou 0 9 a jsou na jeho úrovni definovány uživatelské konverze pro převod tohoto typu z/na základní typ byte. /// Strukutra predstavujici jedno cislo (0-9) /// </summary> public struct Cislo private byte hodnota; public byte Hodnota get return hodnota; set hodnota = value; public Cislo(byte HodnotaCisla) if (HodnotaCisla > 9) throw new ArgumentException("Cislo nemuze nabyvat vetsi hodnoty nez 9"); hodnota = HodnotaCisla; 89

91 /// Explcitni konverze pro prevod hodnoty /// typu byte na hodnotu typu Cislo /// </summary> public static explicit operator Cislo(byte ByteHodnota) return new Cislo(ByteHodnota); /// Implicitni konverze pro prevod hodnoty /// typu Cislo na hodnotu typu byte /// </summary> public static implicit operator byte(cislo Cislo) return Cislo.hodnota; /// Prekryti metody ToString pro adekvatni /// retezcovou reprezentaci instance typu Cislo /// </summary> public override string ToString() return "Cislo s hodnotou " + hodnota; V konstruktoru struktury je kontrolována jestli hodnota čísla není vyšší než hodnota 9, pokud tato situace nastane je vyhozena výjimka System.ArgumentException. Protože je tento konstruktor volán při provádění explicitní konverze z typu byte na typ Cislo, je zajištěno, že pokud se pokusíme převést hodnotu typu byte větší než 9 na hodnotu typu Cislo, dojde k vyhození oné výjimky při provádění konverze. Po definici těchto konverzí již můžeme bez problémů konvertovat náš typ Cislo na typ byte a naopak jak můžete vidět v tomto zdrojovém kódu : /// Trida pro demonstraci funkcnosti uzivatelsky /// definovanych konverzi na strukture Cislo. /// </summary> public class CisloTest public static void CisloExplicitTest() byte lbcislo = 8; Cislo lcislo = (Cislo) lbcislo; Console.WriteLine(lCislo.ToString()); public static void CisloExplicitExceptionTest() byte lbcislo = 10; try //bude vyhozena vyjimka Cislo lcislo = (Cislo) lbcislo; Console.WriteLine(lCislo.ToString()); catch(argumentexception ex) 90

92 Console.WriteLine("Pri konverzi doslo k chybe : " + ex.message); public static void CisloImplicitTest() Cislo lcislo = new Cislo(5); byte lbcislo = lcislo; Console.WriteLine(lCislo.ToString()); Konverze mezi třídami a strukturami Uživatelsky definované konverze, které na rozdíl od základních typů pracují se třídami nebo strukturami, jsou vytvářeny obdobně, jen je třeba si uvědomit, že konverze může být definována jak pro výchozí tak pro cílový typ a tuto skutečnost brát v potaz při návrhu. Pro lepší pochopení a procvičení použití konverzí jsem vytvořil jednoduchý příklad, ve kterém figuruje i dříve použitá struktura Cislo. V příkladu existuje třída CiselnaHodnota, která se skládá z instancí typu Cislo. Třetím typem použitým v příkladu je struktura RimskeCislo, která obsahuje definici pro implicitní konverzi z hodnoty typu CiselnaHodnota a také pro opačný směr převodu. Následující UML diagram vizuálně popisuje tento příklad. Zdrojové kódy dvou nových typů vypadají takto: /// Trida predstavujici ciselnou hodnotu slozenou /// z instanci typu Cislo. /// </summary> public class CiselnaHodnota 91

93 private Cislo[] cisla; private int index; public CiselnaHodnota(int DelkaCiselneHodnoty) cisla = new Cislo[DelkaCiselneHodnoty]; index = -1; /// Metoda pro pridani cisla do ciselne hodnoty /// </summary> /// <param name="cislopropridani">instance typu cislo pro pridani do /// ciselne hodnoty</param> public void PridejCislo(Cislo CisloProPridani) cisla[++index] = CisloProPridani; /// Explicitni konverze hodnoty typu CiselnaHodnota /// na hodnotu typu int. /// </summary> public static explicit operator int(ciselnahodnota HodnotaProPrevod) return Int32.Parse((string)HodnotaProPrevod); /// Implicitni konverze hodnoty typu int /// na hodnotu typu CiselnaHodnota /// </summary> public static implicit operator CiselnaHodnota(int HodnotaProPrevod) string lhodnotastr = HodnotaProPrevod.ToString(); CiselnaHodnota lvysledek = new CiselnaHodnota(lHodnotaStr.Length); foreach (char lpismeno in lhodnotastr) Cislo lcislo = new Cislo(Convert.ToByte(lPismeno.ToString())); lvysledek.pridejcislo(lcislo); return lvysledek; /// Implicitni konverze hodnoty typu CiselnaHodnota /// na hodnotu typu string /// </summary> public static implicit operator string(ciselnahodnota HodnotaProPrevod) string lvysledek = String.Empty; for (int i = 0; i <= HodnotaProPrevod.index; i++) lvysledek += HodnotaProPrevod.cisla[i].Hodnota; return lvysledek; /// Struktura predstavujici rimske cislo. 92

94 /// </summary> public struct RimskeCislo private int hodnota; public int Hodnota get return hodnota; set hodnota = value; public RimskeCislo(int CiselnaHodnota) this.hodnota = CiselnaHodnota; /// Imiplicitni konverze hodnoty typu RimskeCislo na /// hodnotu typu String /// </summary> public static implicit operator string (RimskeCislo RimskaHodnota) string lvysledek = String.Empty; lvysledek += RimskaHodnota.CiselnyRetezec(1000,`M`); lvysledek += RimskaHodnota.CiselnyRetezec(500,`D`); lvysledek += RimskaHodnota.CiselnyRetezec(100,`C`); lvysledek += RimskaHodnota.CiselnyRetezec(50,`L`); lvysledek += RimskaHodnota.CiselnyRetezec(10,`X`); lvysledek += RimskaHodnota.CiselnyRetezec(5,`V`); lvysledek += RimskaHodnota.CiselnyRetezec(1,`I`); return lvysledek; /// Explicitni konverze hodnoty typu CiselnaHodnota /// na hodnotu typu RimskeCislo. /// <remarks> /// Protoze konverze typu ciselna hodnota na typ int muze vyvolat /// vyjimku je tato konverze explicitni. /// </remarks> /// </summary> public static explicit operator RimskeCislo(CiselnaHodnota CiselnaHodnotaProPrevod) //hodnota je nejdrive prevedena na typ int a po te predana konstruktoru return new RimskeCislo((int)CiselnaHodnotaProPrevod); /// Imiplicitni konverze hodnoty typu RimskeCislo na /// hodnotu typu CiselnaHodnota /// </summary> public static implicit operator CiselnaHodnota(RimskeCislo HodnotaProPrevod) 93

95 return ((int)hodnotaproprevod.hodnota); private string CiselnyRetezec(int rad, char pismeno) string lvysledek = String.Empty; while (hodnota >= rad) hodnota -= rad; lvysledek += pismeno; return lvysledek; Třída CiselnaHodnota obsahuje metodu PridejCislo slouží k přidání čísla do číselné hodnoty. Kromě této metody obsahuje také definici konverze na typ int, která je explicitní z důvodu, že hodnota představovaná instancí třídy CiselnaHodnota může být vyšší než je maximální možná hodnota typu Int32, což by při volání metody Parse, která je při konverzi použita, zapříčinilo vyvolání výjimky. Této konverze využívá uživatelská konverze definovaná ve struktuře RimskeCislo, která umožňuje převod z hodnoty typu CiselnaHodnota na hodnotu typu RimskeCislo. Tato konverze totiž nejdříve převede hodnotu typu CiselnaHodnota na typ int a tuto hodnotu předá konstruktoru struktury RimskeCislo. Kromě této konverze obsahuje struktura RimskeCislo i uživatelskou konverzi na typ String, která vrací řetězcovou reprezentaci konkrétní instance struktury RimskeCislo. Poznáváme C# a Microsoft.NET 23. díl direktivy pre-procesoru Dnešní díl se seznámíme s vlastností jazyku C#, pomocí které můžeme do jisté míry ovlivňovat kompilaci zdrojových kódů. Jedná se o takzvané direktivy pre-procesoru, s nimiž jste se v trochu jiné podobě mohli setkat v jazycích C či C++. Direktivy preprocesoru v C# Direktivy preprocesoru nám dávají možnost určit, které části zdrojových kódů budou kompilovány, produkovat chybová hlášení či upozornění a nebo umožňují rozdělit náš zdrojový kód do několika sekcí takzvaných regionů. Programátoři v jazycích C nebo C++ tento termín jistě znají. Ovšem chtěl bych upozornit na skutečnost, že onen termín je v jazyku C# použit pouze kvůli lepší návaznosti na programovací jazyky C/C++. V C# totiž žádný pre-procesor před kompilací spouštěn není, direktivy pre-procesoru jsou zpracovávány jako součást fáze lexikální analýzy. Pre-procesorové direktivy v jazyku C# zabírají vždy jeden řádek, který začíná znakem #, za kterým následuje název direktivy pre-procesoru. Ve zdrojovém kódu v jazyce C# můžeme použít následující direktivy pre-procesoru : #define, #undef, #if, #elif, #else, #endif, #line, #error, #warning, #region a #endregion. Direktivy určené k podmíněnému vykonání určité sekce kódu Použitím direktiv #define, #undef, #if, #elif, #else a #endif můžeme docílit toho, že některé části zdrojového kódu budou kompilovány a naopak. 94

96 Direktiva #define slouží k definici určitého symbolu, který později můžeme testovat za pomocí direktiv #if nebo #elif. Pokud je symbol definován a je testován ve výrazu s použitím direktiv #if nebo #elif je hodnota takovéhoto výrazu vyhodnocena jako true. Tímto způsobem je tedy možné vytvářet podmíněné provádění kompilace kódu, protože je-li výraz vyhodnocen kladně, tak je zkompilována určitá větev obalená do uvedených direktiv. Direktivu #define pro definici symbolu musíme použít před jakýmkoli výkonným kódem. Následující příklad demostruje použití těchto direktiv. #define LADENI #define VYPISY using System; namespace PrikladyZive23 /// Ukazkova trida s pouzitim direktiv preprocesoru /// pro vytvoreni podminecne kompilace kodu /// </summary> public class ConditionalPreDirecrivesExam public static void NejakaMetoda() #if (LADENI && VYPISY) Console.WriteLine("Aplikace je v ladicim rezimu a jsou zapnuty vypisy"); #elif (LADENI VYPISY) Console.WriteLine("Aplikace je v ladicim rezimu nebo jsou zapnuty vypisy"); #elif!(ladeni && VYPISY) Console.WriteLine("Ladeni ani vypisy nejsou zapnuty"); #endif Výstup po spuštění uvedeného zdrojového kódu v této podobě, tedy když jsme definovali oba dva symboly bude následující: Aplikace je v ladicim rezimu a jsou zapnuty vypisy Direktiva #define není jediným způsobem, kterým lze definovat symbol. Druhou možností, která je nám k dispozici se nabízí v podobě přepínače /define kompilátoru. Direktivu #undef užitečně využijeme právě při použití přepínače kompilátoru, kdy na úrovni jednoho zdrojového souboru určitý symbol oddefinujeme. Direktiva #line Použitím této direktivy můžeme dosáhnout toho, že kompilátor bude používat námi určené číslování řádek. To se hodí zejména u automaticky generovaných zdrojových kódů, kde chceme aby měl určitý řádek nebo skupina řádků námi určená čísla. Vlastní číslování uvedeme použitím této direktivy ve tvaru: #line číslo řádku Od uvedení direktivy budou řádky číslovány od inkriminovaného čísla dokud neuvedeme direktivu pro návrat k výchozímu číslování, která se zapisuje ve tvaru: 95

97 #line default Použití této direktivy ukazuje následující příklad: public class LinePreDirectiveExam public static void VypisObracenePole(Array Pole) //od tohoto radku jsou kompilatorem //radky pocitany od cisla 300 #line 300 Array.Reverse(Pole); foreach(object lprvek in Pole) String lprvekstr = lprvek.tostring(); //od tohoto radku je kompilatorem opet pouzito //vychozi radkovani #line default Console.WriteLine(lPrvekStr); Od řádku Array.Reverse(Pole); budou řádky číslovány od čísla 300 i přes to, že skutečná pozice řádku je 15. Výchozí číslování bude použito od řádku Console.WriteLine(lPrvekStr);, který již bude kompilátorem brán skutečně jako řádek s číslem 22. Takže pokud bychom například na řádku String lprvekstr = lprvek.tostring(); udělali nějakou chybu, kompilátor by nám zahlásil, že došlo k chybě na řádku 303. Další možností použití direktivy #line je skrýt určitou skupinu řádků před debuggerem a to použitím této direktivy ve tvaru: #line hidden public static void VypisObracenePoleHidden(Array Pole) //nasledujici radky budou debugerem ignorovany #line hidden Array.Reverse(Pole); foreach(object lprvek in Pole) String lprvekstr = lprvek.tostring(); //od tohoto radku budeme moci debugerem //opet krokovat #line default Console.WriteLine(lPrvekStr); Pokud bychom chtěli uvedenou metodu pomocí debuggeru krokovat, skočili bychom až na řádek nacházejícím se za direktivou #line default. Dokonce ani kdybychom do onoho skrytého bloku mezi direktivami umístili breakpoint, kýženého výsledku v podobě možnosti krokování bychom nedosáhli. Náš breakpoint by totiž byl debuggerem ignorován. 96

98 Diagnostické direktivy Do této skupiny direktiv pre-procesoru patří direktivy #warning a #error. Při použití direktivy #warning bude kompilátorem vygenerováno varovné hlášení k určitému řádku a jak název napovídá, tak direktiva #error zapříčiní, že kompilace skončí chybou. /// Ukazka pouziti direktivy #warning /// </summary> public class WarningPreDirectiveExam public static void NejakaMetoda() #warning Zkonzultovat s kolegou //nejaky vykonny kod metody Stejným způsobem jakým je v uvedeném případě použita direktiva #warning pro generování upozornění, že je potřeba kód zkonzultoval s kolegou by se použila direktiva #error pro vygenerování chyby při překladu. Direktivy pro určení specifických bloků kódu Tato skupina direktiv je tvořena dvojicí direktiv #region a #endregion a používají se pro rozdělení našeho zdrojového kódu na určité části. Direktiva #region označuje začátek bloku a direktiva #endregion jeho konec. Tuto možnost oceníte ze jména ve chvíli, kdy programujete v nějakém vývojovém prostředí, které tyto direktivy bere v potaz a reaguje na jejich výskyt tím, že nám nabídne možnost takto označený blok sbalit a tím zvýšit přehlednost kódu. /// Ukazka pouziti direktiv #region a #endregion /// </summary> public class RegionPreDirectiveExam #region Deklarace atributu private int prvniatribut; private int druhyatribut; #endregion #region Definice metod private void PrvniMetoda() private void DruhaMetoda() #endregion Na obrázku níže můžete vidět jak na tyto direktivy reaguje vývojové prostředí Visual C#. NET od Microsoftu. 97

99 Poznáváme C# a Microsoft.NET 24. díl speciální případy metod Dnešní díl bych rád věnoval seznámení s použitím a deklarací speciálních případů metod. Mezi takovéto druhy metod, které zatím nebyly v tomto seriálu představeny se řadí metody s proměnným počtem parametrů a metody, u jejichž parametrů je uveden modifikátor ref nebo out. Metody s proměnným počtem parametrů Někdy se může stát, že potřebujeme definovat metodu, u které chceme nechat počet jejích parametrů variabilní. K tomu, abychom tohoto cíle dosáhli nám jazyk C# nabízí k použití modifikátor params. Tento modifikátor, který je povoleno uvést pouze před posledním vstupním parametrem metody. Za tímto modifikátorem vstupního parametru se uvádí jako typ parametru pole určitého typu, který představuje typ vstupních parametrů o variabilním počtu. Z tohoto důvodu je pak v těle metody možné ony parametry získávat normální iterací pole. V deklaraci metody je povolen pouze jeden tento modifikátor. Následující ukázková třída Scitac, demonstruje použití modifikátoru params. /// Ukazka na pouziti metod s promennym poctem parametru /// </summary> public class Scitac public static int Secti(int a, int b) return a + b; //do teto metody muze vstoupit promenny pocet //parametru typu int. public static int Secti(params int[] Cisla) int lsuma = 0; for(int i = 0; i < Cisla.Length; i++) lsuma += Cisla[i]; return lsuma; 98

100 První verze metody Secti očekává standardně dva parametry a vrátí jejich součet na tom není nic nového. Ale druhá přetížená verze této metody již používá onen kouzelný modifikátor params, který zařídí to, že uživatel naší třídy bude moci metodě Secti předat libovolný počet hodnot typu int, tak jak ukazuje následující zdrojový kód. public class ScitacTest public static void Test() Console.WriteLine(Scitac.Secti(5,6)); //pouziti verze metody s promennym poctem parametru Console.WriteLine(Scitac.Secti(1,2,3,4,5)); V případě, že předaný počet parametrů metodě při jejím volání se přesně shoduje s nějakou deklarovanou verzí metody, je zavolána tato verze, pokud ovšem kompilátor nenalezne žádnou takovou verzi metody, která očekává tento přesný počet parametrů, použije verzi metody s modifikátorem params, samozřejmě pokud nějaká takováto verze metody existuje. Takže konkrétně v našem ukázkovém příkladě se při prvním zavoláním metody Secti použije verze deklarovaná právě pro dva vstupní parametry a při druhém volání metody, kdy jí je předáno parametrů pět, kompilátor nenalezne v definici třídy Scitac žádnou takovou verzi metody Secti, která by očekávala pět vstupních parametrů a zkusí najít nějakou verzi metody, kde se vyskytuje modifikátor params a pokud ji nalezne, tak ji použije. Modifikátor ref S velkou pravděpodobností se někdy při svém programování v C# dostanete do situace, kdy budete chtít, aby parametr hodnotového typu byl metodě předán nikoliv zkopírováním hodnoty (předání hodnotou), ale stejně jako v případě referenčních typů, tedy aby se změna hodnoty provedená v těle metody volaného objektu projevila i všude jinde. Pokud tedy chceme parametr hodnotového typu předat jakoby odkazem, použijeme modifikátor vstupního parametru metody ref. Po použití tohoto modifikátoru bude parametr metody reflektovat na stejnou proměnnou, která byla metodě předána při jejím volání. Toho se hodí využít v případech, kdy chceme aby naše metoda vracela více než jednu hodnotu. Samozřejmě metoda s těmito parametry může mít normální návratovou hodnotu. Pokud je v deklaraci metody uveden u nějakého parametru modifikátor ref, jsme nuceni při každém volání takovéto metody, také u předávaného parametru explicitně uvést modifikátor ref. Pokud předáváme metodě parametr s použitím modifikátoru ref, musíme předávanou proměnnou nejdříve inicializovat, jinak překlad programu skončí chybovým hlášením. V deklaraci metody je možné použít tento modifikátor i pro více než jeden vstupní parametr. Použití modifikátoru ref by mohlo vypadat například takto: /// Ukazka pouziti modifikatoru vstupniho parametru metody ref /// </summary> public class ScitacRef public static void Secti(int a, int b, ref int soucet) soucet = a + b; 99

101 Metoda Secti v tomto příkladu očekává kromě dvou hodnot pro sečtení i třetí parametr s modifikátorem ref do kterého bude uložen výsledek součtu. public class ScitacRefTest public static void Test() int lsoucet = 0; ScitacRef.Secti(5,4, ref lsoucet); //Bude vypsana hodnota 9 Console.WriteLine(lSoucet); Jak bylo napsáno, tak i při volání metody je potřeba uvést modifikátor ref, pokud je parametr metody s tímto modifikátorem uveden v deklaraci metody. Proměnná musela být před předáním metodě inicializována a její hodnota se po zavolání metody změnila na 9. Tím, že při deklaraci metody u nějakého parametru použijeme modifikátor ref, vytvoříme její novou verzi, jinými slovy ji přetížíme. Z toho plyne, že je možné provést něco takovéhoto: public class RefOverloadExam public void NejakaMetoda(int Cislo) //implementace metody public void NejakaMetoda(ref int Cislo) //implementace metody Modifikátor out Tento modifikátor vstupního parametru je velmi podobný modifikátoru ref, ale je zde jistý rozdíl v jejich užití. Zatímco u modifikátoru ref bylo požadováno, aby proměnná předávaná jako parametr s modifikátorem ref byla nejprve inicializována, tak u modifikátoru out toto nutné není. Avšak je nutné, aby parametr s modifikátorem out byl v metodě přiřazen. Taktéž jako u modifikátoru ref, tak i u použití tohoto modifikátoru dochází k vytvoření nové přetížené verze konkrétní metody, ale nelze vytvořit přetížení na základě toho, že se v metodě místo modifikátoru ref objeví modifikátor out a naopak. Jinak řečeno to znamená, že nemůžete provést něco takovéhoto: public class BadOverloadExam public void NejakaMetoda(ref int Cislo) //implementace metody public void NejakaMetoda(out int Cislo) 100

102 //implementace metody Poznáváme C# a Microsoft.NET 25. díl - třídy kolekcí Tento díl bude úvodem do problematiky o třídách představujících datové struktury pro ukládání různých hodnot, které nám jsou k dispozici v základní knihovně tříd prostředí Microsoft.NET framework. Tyto třídy jsou navrženy pro ulehčení práce programátorů a nazývají se třídy kolekcí. Dnes si povíme o co se vlastně jedná a seznámíme se s hojně využívanou třídou ArrayList. Třídy kolekcí a jmenný prostor System.Collections Během našeho poznávání světa Microsoft.NET frameworku jsme doposud narazili pouze na jedinou datovou strukturu, kterou jsme mohli využít pro ukládání různých druhů objektů a to pod takzvanými indexy. Nemám na mysli nic jiného, než základní datovou strukturu - pole. Určitě vás napadlo, že byste při vašem vývoji použili i nějaké jiné, řekněme flexibilnější, datové struktury do kterých byste si nastrkali objekty, a to ne nutně tak, aby byli přístupné pomocí indexů. Základní knihovna tříd (Base Class Library) prostředí Microsoft.NET frameworku v podobě tříd kolekcí, které jsou obsaženy ve jmenném prostoru System.Collections. V tomto jmenném prostoru tedy nalezneme třídy rozhraní představující rozličné listy, fronty, bitová pole, hešové tabulky a takzvané slovníky. Na třídách kolekcí lze provádět různá řazení hodnot v nich obsažených a také je nám umožňeno definovat vlastní způsoby řazení. Třída ArrayList Tato kolekce reprezentuje list hodnot, jehož kapacita je automaticky zvětšována podle potřeby. To znamená, že pokud přidáváme prvek do této struktury a stávající kapacita listu již není dostačující, tak je kapacita zvětšena. Třída ArrayList mimo jiné implementuje rozhraní IList z jmenného prostoru System.Collections, které definuje vlastnosti a operace pro kolekce objektů, k nimž lze přistupovat individuálně podle indexů. Počáteční kapacita může být určena při vytváření instance této třídy formou parametru konstruktoru, pokud tak neučiníme bude pro kapacita použita výchozí hodnota, která je 16. Kapacita listu může také být později explicitně nastavena využitím instanční vlastnosti Capacity. S kapacitou ArrayListu souvisí také metoda TrimToSize, která kapacitu listu změní na velikost potřebnou pro agregované prvky. Prvky jsou do této struktury přidávány pomocí metody Add, která očekává jako parametr instanci typu System.Object, což znamená, že tato datová struktura může obsahovat objekty jakéhokoli typu. K získávání, ale i k modifikaci, agregovaných objektů slouží vlastnost Item, která je v jazyce C# implementována jako indexer. Indexy jsou, stejně jako u pole, číslovány od nuly. Použitím metody Add jsou prvky přidávány na konec listu. Pokud potřebujeme vložit prvek na specifikovaný index, naskýtá se nám možnost použít metodu Insert, které kromě vkládaného prvku předáme onen index. Následující příklad ukazuje základní použití datové struktury ArrayList. public class ArrayListExam public static void Priklad() 101

103 file://inicialize listu bez udani kapacity ArrayList lmujlist = new ArrayList(); lmujlist.add("poznavame "); lmujlist.add(".net"); file://vlozeni prvku na urcity index lmujlist.insert(1,"microsoft"); VypisHodnotyPomociFor(lMujList); /// Vypise vsechny prvky listu pouzitim cyklu for a indexeru /// definovaneho na tride ArrayList /// </summary> public static void VypisHodnotyPomociFor(ArrayList ListProVypsani) for (int i = 0; i < ListProVypsani.Count;i++) Console.Write(ListProVypsani[i]); V uvedeném přikladu je obsah ArrayListu vypsán pomocí metody, která ho iteruje pomocí cyklu for a využívá zmíněného indexeru. Jelikož tato kolekce implementuje rozhraní System.Collections.IEnumerable je možné použít, dle mého názoru, elegantnější metodu průchodu a použít cyklu foreach. public class VypisKolekci public static void VypisHodnoty(IEnumerable KolekceProVypsani) foreach(object lzaznam in KolekceProVypsani) Console.Write("0, ",lzaznam); Console.WriteLine(); Využití metody VypisHodnoty této miniaturní třídy tedy představuje ono druhé, elegantnější řešení. Mimo jiné je možné, díky tomu, že metoda očekává parametr typu rozhraní, tuto metodu použít pro výpis obsahu jakékoli kolekce, která toto rozhraní implementuje. Prvky ArrayListu jsou odebírany použitím metody Remove, které jako vstupní parametr předáme objekt, který má být z instance listu vyjmut. Přesněji řečeno, bude z ArrayListu odebrán první výskyt specifikovaného objektu. V případě, že budeme chtít z listu odebrat prvek na požadovaném indexu použijeme k tomu metodu RemoveAt, která tento index očekává jako parametr. Metoda Clear této datové struktury odebere všechny elementy, které obsahuje, takže není nutné použití průchodu nějakým cyklem. Užitečná je také metoda Contains, vracející hodnotu typu bool, která je true v případě, že objekt předaný této metodě je v listu obsažen. public class ArrayListExam2 public static void Priklad() file://pocatecni kapacitu listu nastavime na 5 ArrayList lmujlist = new ArrayList(5); NaplnList(lMujList); lmujlist.add("dalsi prvek"); lmujlist.add("a zase dalsi prvek"); 102

104 VypisKolekci.VypisHodnoty(lMujList); lmujlist.remove("dalsi prvek"); lmujlist.removeat(0); Console.WriteLine("Obsah listu po vyjmuti prvku :"); VypisKolekci.VypisHodnoty(lMujList); file://odebrani vsech prvku z kolekce lmujlist.clear(); public static void NaplnList(ArrayList ListProNaplneni) for(int i = 0; i < ListProNaplneni.Capacity; i++) ListProNaplneni.Add("Prvek " + i); Po spuštění uvedeného příkladu uvidíme následující výstup: Prvek 0, Prvek 1, Prvek 2, Prvek 3, Prvek 4, Dalsi prvek, A zase dalsi prvek, Obsah listu po vyjmuti prvku: Prvek 1, Prvek 2, Prvek 3, Prvek 4, A zase dalsi prvek, V příštím díle se seznámíme s dalšími třídami kolekcí. Poznáváme C# a Microsoft.NET 26. díl třídy kolekcí II. Po minulém seznámení se s kolekcemi a hojně používaným ArrayListem, se dnes podíváme na další užitečné třídy kolekcí. Například na takové, které představují známé datové struktury typu fronta nebo zásobník. Seznámíme se také se s pojmem slovník a s jeho asi nejpoužívanější implementací v podobě hešové tabulky. Třída Stack Třída Stack je implementací jedné ze základních datových struktur zásobníku. Pro ty co o ni ještě neslyšely, tak tato struktura pracuje takzvaným principem last-in-first-out, což v překladu znamená, že prvek, který vložíte jako poslední, bude vyjmut jako první. Prvky jsou do této struktury přidávány pomocí metody Push, a získávány za použití metody Pop. Metoda Pop, kromě zmíněného získání posledně vloženého prvku, tento prvek ze zásobníku vyjme. Pokud je zásobník prázdný a pokusíme se zavolat tuto metodu, bude vyhozena výjimka System.InvalidOperationException. V situaci, kdy chceme získat posledně vložený prvek, ale nechceme aby tento prvek byl ze zásobníku vyjmut použijeme metodu Peek. Stejně jako u všech tříd kolekcí v základní knihovně tříd.net frameworku, lze počet obsažených prvků zjistit pomocí instanční vlastnosti Count, která je předepsána v rozhraní ICollection. Následující kód ukazuje použití třídy Stack. /// Priklad pouziti tridy Stack. /// </summary> public class StackPriklad public static void Priklad() Stack lmujzasobnik = new Stack(); 103

105 //pridani prvku do zasobniku lmujzasobnik.push("prvni"); lmujzasobnik.push("druhy"); lmujzasobnik.push("treti"); VypisZasobnik(lMujZasobnik); /// Tato metoda pouze pomoci metody Pop vyjme a vypise /// obsah zasobniku. /// </summary> /// <param name="zasobnik"></param> public static void VypisZasobnik(Stack Zasobnik) for (int i = 0; i < Zasobnik.Count;) Console.Write("0, ",Zasobnik.Pop()); Console.WriteLine(); Výstup bude vypadat takto: Treti, Druhy, Prvni, Poznámka: Určitě jste zaznamenali, že implementace metody VypisZasobnik není úplně nejšťastnější, protože při vypisování vyjme všechny prvky ze zásobníku. V Příkladu mi šlo jen u demonstraci funkčnosti metody Pop. Rozumnější řešení by bylo pro výpis obsahu zásobníku využit toho, že třída Stack implementuje rozhraní IEnumerable. Třída Queue Tato třída představuje implementaci další známe datové struktury, kterou je takzvaná fronta. Tato datová struktura je založena na principu first-in-first-out, což znamená, že první vložený prvek do struktury bude také jako první vrácen. Prvky jsou do instance této třídy zařazovány metodou Enqueue a k jejich vyřazování slouží metoda Dequeue, která zároveň vrací právě vyřazovaný objekt. Stejně jako u zásobníku, tak v případě, že tuto metodu zavoláme, když je fronta již prázdná, bude vyhozena výjimka System.InvalidOperationException. Pokud chceme jen přečíst prvek, který je na začátku fronty a nechceme jej vyjmout, tak zavoláme metodu Peek. Následující příklad demonstruje použití této kolekce. /// Priklad na pouziti kolekce Queue /// </summary> public class QueuePriklad public static void Priklad() Queue lmojefronta = new Queue(); lmojefronta.enqueue("prvni prvek"); lmojefronta.enqueue("druhy prvek"); lmojefronta.enqueue("treti prvek"); VypisKolekci.VypisHodnoty(lMojeFronta); string lprvek = (string) lmojefronta.dequeue(); Console.WriteLine("Prvni ziskany prvek z fronty je : 0", lprvek); 104

106 Po spuštění tohoto kódu byste měli vidět takovýto výstup: Prvni prvek, Druhy prvek, Treti prvek, Prvni ziskany prvek z fronty je : Prvni prvek K výpisu obsahu fronty jsem použil třídu VypisKolekci, známou s minulého dílu, která využívá implementace rozhraní IEnumerable na třídách kolekcí. Slovníky a rozhraní IDictionary Pod kolekci typu slovník si lze představit datovou strukturu, která uchovává své prvky v podobě dvojice klíč-hodnota. Jako společné rozhraní pro tento typ kolekcí je v.net frameworku použito rozhraní IDictionary. Ve slovnících je dvojice klíč-hodnota použita tak, že se pod unikátním klíčem, nachází určitá hodnota. Z toho logicky plyne, že hodnota,která v oné dvojici představuje klíč, nemůže nabývat hodnoty null. Nicméně část hodnota ve dvojici může hodnoty null bez problémů nabývat. Rozhraní IDictionary, mimo jiné obsahuje předpis pro vlastnost Item, která je v jazyce C# implementována formou indexeru. Tato vlastnost zpřístupňuje hodnotu asociované dvojice po zadání klíče, pod kterým je hodnota uložena. Rozhraní IDictionary předepisuje metodu GetEnumerator, kterou již známe s rozhraní IEnumerable. Ostatně rozhraní IDictionary toto rozhraní rozšiřuje. Avšak třídy implementující rozhraní IDictionary, vracejí po zavolání metody GetEnumerator instanci třídy implementující rozhraní IDictionaryEnumerator, které rozšiřuje rozhraní IEnumerator. Jeho instanční vlastnost Current nyní vrací instance třídy DictionaryEntry, které představují konkrétní asociovanou dvojici. V instanci třídy DictionaryEntry využijeme vlastnost Key pro získaní klíče a vlastnost Value pro získání hodnoty dvojice. Třída HashTable Tato třída je asi nejpoužívanější implementací výše zmíněného rozhraní. Organizace asociovaných dvojicí hodnot je založena na hešovém kódu hodnoty představující klíč. Možná se právě někteří z vás pozastavili nad pojmem hešový kód. Hešový kód je číselná hodnota představující instanci objektu. Mělo by platit, že pokud jsou dvě instance stejné, měli by mít stejný hešový kód. Dobře naimplementovaná hešová funkce by měla zajistit, aby měla každá různá instance i různý hešový kód. Základní implementace tvorby hešového kódu pro instanci se nachází na třídě System.Object a to ve formě metody GetHashCode. Jelikož je tato metoda virtuální, je možné a dokonce doporučené tuto metody na odvozených třídách překrývat vlastní implementací. HashTable neboli hešová tabulka vyhledává právě na základě hešových kódů objektů uložených jako klíče, umožňuje jí to tak efektivní a rychlý způsob vyhledávání, protože se vyhledává podle čísel. Nové prvky jsou do této struktury přidávány pomocí metody Add, která jako první parametr očekává instanci představující klíč a jako druhý hodnotu, která má být pod klíčem uchována. Odebráni prvku z této kolekce je realizovatelné pomocí metody Remove, které předáme klíč prvku dvojice, kterou chceme odstranit. Užitečné metody jsou také ContainsKey a ContainsValue, pomocí kterých, jak název napovídá, jsme schopni zjistit jestli se v hešové tabulce nachází určitý klíč respektive hodnota. Následující příklad demonstruje použití hešové tabulky. /// Ukazka pouziti tridy HashTable 105

107 /// </summary> public class HashTablePriklad public static void Priklad() Hashtable lmojetable = new Hashtable(); //pridani prvku do tabulky lmojetable.add("prvni", "Prvni prvek"); lmojetable.add("druhy", "Druhy prvek"); lmojetable.add("treti", "Treti prvek"); VypisTabulku(lMojeTable); //ziskani hodnoty ulozene pod klicem Prvni String lprvniprvek = (string)lmojetable["prvni"]; Console.WriteLine("Pod klicem Prvni je v tabulce ulozena hodnota : 0",lPrvniPrvek); Console.WriteLine("V tabulce existuje hodnota pod klicem Druhy : 0",lMojeTable.ContainsKey("Druhy")); public static void VypisTabulku(Hashtable Tabulka) //Vlastnost Current pouziteho enumeratoru vraci instance typu DictionaryEntry foreach(dictionaryentry lzaznam in Tabulka) Console.WriteLine("Klic : 0 - Hodnota : 1", lzaznam.key,lzaznam.value); Výstup: Klic : Treti - Hodnota : Treti prvek Klic : Druhy - Hodnota : Druhy prvek Klic : Prvni - Hodnota : Prvni prvek Pod klicem Prvni je v tabulce ulozena hodnota : První prvek V tabulce existuje hodnota pod klicem Druhy : True Z důvodu, že hešová tabulka je slovníkem (implementuje rozhraní IDictionary), při průchodu pomocí enumerátoru, tedy i v případě průchodu pomocí cyklu foreach, dostáváme instance třídy DictionaryEntry. V příkladu byl jako typ klíče použit typ String, jehož použití je jednoduché a navíc je na třídě System.String implementována dobrá hešovací funkce. Příště se něco dozvíme o použití vlastních poskytovatelů hešových kódů a definici vlastního řazení pro námi definované třídy. Poznáváme C# a Microsoft.NET 27. díl třídy kolekcí III. V tomto dílu seriálu, který je opět spojen se třídami kolekcí, se podíváme na možnost definice vlastních porovnávacích metod, které ve výsledku ovlivní i způsoby řazení v kolekcích. Definice vlastního porovnání instance Zajisté se někdy dostanete do situace, kdy bude potřeba porovnat dvě instance tříd. Můžeme to například provést implementací nějaké vlastní instanční metody, která nám to 106

108 zajistí. Lepší volba ovšem je nechat naší třídu, která má mít námi určený způsob porovnávání, implementovat rozhraní IComparable, které se nachází v námi již známém jmenném prostoru System.Collections. To nám s sebou přinese mnohé výhody o kterých se dozvíme dále. Rozhraní IComparable nám jednoduše předepisuje naimplementovat metodu CompareTo, která přijímá parametr typu object a vrací celé číslo (int). Pokud je výsledek této metody menší než nula, znamená to, že je naše instance menší než porovnávaný objekt, který byl předán jako parametr. V opačném případě je naše instance větší. Metoda by měla vracet nulu pouze v případě, že jsou si instance rovny. Následující zdrojový kód obsahuje definici třídy Osoba, která implementuje rozhraní IComparable a tím pádem i zmiňovanou metodu. /// Trida predstavujici osobu, s definici /// vlastniho porovnavani instanci /// </summary> public class Osoba : IComparable private string jmeno; private string prijmeni; public Osoba(string Jmeno, string Prijmeni) this.jmeno = Jmeno; this.prijmeni = Prijmeni; #region IComparable Members public int CompareTo(object obj) if (!(obj is Osoba)) throw new ArgumentException("Predany objekt neni typu osoba"); Osoba losoba = (Osoba) obj; return String.Compare(this.prijmeni,lOsoba.prijmeni); #endregion public override string ToString() return jmeno + " " + prijmeni; Jak můžete vidět, tak tato implementace metody, porovnává na základě příjmení daných instancí osob. Pokud se jí pokusíme ve formě parametru předat instanci, která není typu osoba, k porovnání nedojde a bude vyhozena výjimka. K porovnání příjmení jsem použil statickou metodu Compare třídy System.String. Onu, o pár řádek výše, zmiňovanou výhodu použití implementace rozhraní IComparable k definici porovnávání, představuje to, že naše implementace metody CompareTo, bude 107

109 použita při řazení v nějaké kolekci. Třída ArrayList obsahuje instanční metodu Sort, která zařídí seřazení obsažených prvků. A jelikož naše třída Osoba má definováno porovnávání na základě příjmení budou prvky ArrayListu po zavolání metody Sort seřazeny podle příjmení. /// Priklad, ukazujici nasledek implementace rozhrani /// IComparable na tride Osoba. /// </summary> public class IComparablePriklad public static void Priklad() Osoba lmichal = new Osoba("Michal","Hynek"); Osoba lmartin = new Osoba("Martin","Pesek"); Osoba ltomas = new Osoba("Tomas","Berger"); ArrayList losoby = new ArrayList(); losoby.add(lmichal); losoby.add(lmartin); losoby.add(ltomas); losoby.sort(); VypisKolekci.VypisHodnoty(lOsoby); Výstup bude následující: Tomas Berger, Michal Hynek, Martin Pesek, Definice více možných způsobů porovnání Možná Vás napadlo, že zmíněný způsob implementace porovnávání instancí našich tříd je sice fajn, ale až do té doby, kdy budeme chtít použít více než jeden způsob porovnávání. Předchozí způsob je vhodný pouze pro definici výchozího způsobu porovnávání, protože rozhraní lze implementovat pouze jednou. Jak ale tedy zařídit, aby bylo možné konkrétní způsob řazení zvolit? Odpověď na tuto otázku za nás vyřešili návrháři knihoven.net frameworku a to v podobě použití rozhraní IComparer. Toto rozhraní totiž předepisuje metodu Compare, přijímající dva parametry typu object, které mají být mezi sebou porovnány. Tato metoda by měla vracet celočíselnou hodnotu, která je menší než nula pokud je první předaný objekt menší než druhý a analogicky větší než nula v případě, že je první objekt větší. Nula by měla být vráceno, když jsou si objekty rovny. Takže za záměrem umožnit porovnávání a tedy i následné řazení v kolekcích jak podle jména tak podle příjmení osob přidáme do třídy Osoba definice dvou vnitřních tříd implementující rozhraní IComparer. public class Osoba : IComparable private string jmeno; private string prijmeni; public Osoba(string Jmeno, string Prijmeni) this.jmeno = Jmeno; this.prijmeni = Prijmeni; 108

110 public static System.Collections.IComparer RazeniPodleJmena get return new PorovnaniPodleJmena(); public static System.Collections.IComparer RazeniPodlePrijmeni get return new PorovnaniPodlePrijmeni(); #region Definice tridy PorovnaniPodleJmena private class PorovnaniPodleJmena : System.Collections.IComparer #region IComparer Members public int Compare(object obj1, object obj2) Osoba losoba1 = (Osoba) obj1; Osoba losoba2 = (Osoba) obj2; return String.Compare(lOsoba1.jmeno,lOsoba2.jmeno); #endregion #endregion #region Definice tridy PorovnaniPodlePrijmeni private class PorovnaniPodlePrijmeni : System.Collections.IComparer #region IComparer Members public int Compare(object obj1, object obj2) Osoba losoba1 = (Osoba) obj1; Osoba losoba2 = (Osoba) obj2; return String.Compare(lOsoba1.prijmeni,lOsoba2.prijmeni); #endregion #endregion #region IComparable Members public int CompareTo(object obj) if (!(obj is Osoba)) throw new ArgumentException("Predany objekt neni typu osoba"); Osoba losoba = (Osoba) obj; return String.Compare(this.prijmeni,lOsoba.prijmeni); 109

111 #endregion public override string ToString() return jmeno + " " + prijmeni; Do naší třídy tedy přibyly dvě vnitřní třídy. První definuje porovnání podle jména a druhá podle příjmení. To jakým způsobem budou objekty porovnávány respektive řazeny určíme například u ArrayListu tak, že využijeme přetížení metody Sort. Některé její verze totiž očekávají instanci třídy implementující rozhraní IComparer. Mimo dvou vnitřních tříd přibyly do třídy Osoba i dvě nové statické vlastnosti (RazeniPodleJmena, RazeniPodlePrijmeni), které vracejí instance oněch vnitřních tříd a to hlavně proto, aby bylo určování způsobu porovnávání o něco příjemnější (jinak bychom vždy museli psát v kódu new pro vytváření instancí vnitřních tříd). /// Priklad pouziti razeni v ArrayListu podle /// parametru typu IComparer /// </summary> public class IComparerPriklad public static void Priklad() Osoba lmichal = new Osoba("Michal","Hynek"); Osoba lmartin = new Osoba("Martin","Pesek"); Osoba ltomas = new Osoba("Tomas","Berger"); ArrayList losoby = new ArrayList(); losoby.add(lmichal); losoby.add(lmartin); losoby.add(ltomas); //serazeni osob podle jmena losoby.sort(osoba.razenipodlejmena); VypisKolekci.VypisHodnoty(lOsoby); //serazeni osob podle prijmeni losoby.sort(osoba.razenipodleprijmeni); VypisKolekci.VypisHodnoty(lOsoby); Výstup po spuštění tohoto příkladu by měl vypadat takto: Martin Pesek, Michal Hynek, Tomas Berger, Tomas Berger, Michal Hynek, Martin Pesek, Poznáváme C# a Microsoft.NET 28. díl HashProvidery a Klonování Dnešní díl bude ještě z části souviset se třídami kolekcí. Dozvíme se totiž, co to jsou poskytovatelé hešových kódů (HashProvidery) a jak je možné tyto poskytovatele použít. V druhé části se budu zaobírat možností definovat způsob vytvoření kopie instance námi vytvářené třídy. 110

112 Komplexnější využití hešových tabulek a vlastní HashProvidery V jednom z předchozích dílů pojednávajících o třídách kolekcí v.net frameworku jsem popsal datovou strukturu HashTable neboli hešovou tabulku. Víme tedy, že hodnota asociované dvojice je vyhledána pomocí hešového kódu klíče, který by měl být pokud možno různý pro každou různou instanci třídy. Nemusí tomu tak ovšem vždy být a nějaké instance i přesto, že jsou logicky různé vrátí hešový kód stejný. Právě proto se při vyzvedávání hodnoty z tabulky ještě po zavolání metody GetHashCode na instanci představující klíč zavolá metoda Equals, která by měla v případě nálezu více odpovídajících prvků určit ten správný námi hledaný. My ovšem můžeme chtít, aby pro získání hešového kódu instance představující klíč v asociované dvojici byla použita jiná metoda než implicitně volaná GetHashCode. Řešení takovéto situace se naskýtá v podobě vytvoření třídy implementující rozhraní System.Collections.IHashProvider. Ve třídě naimplementujeme metodu GetHashCode, která přijímá jako parametr objekt, ke kterému má vytvořit hešový kód. Instanci takovéto třídy pak můžeme předat jedné z přetížených verzí konstruktoru třídy HashTable, čímž zajistíme, že bude vyhledáváno na základě námi určeného hešového kódu. Spolu s touto instancí můžeme konstruktoru předat i instanci třídy, která implementuje nám již známe rozhraní IComparer pokud nechceme, aby se pro porovnávání instancí při vyhledávání a přidávání nepoužívala implicitní metoda Equals. Pro příklad vzpomeňme na třídu Osoba z minulého dílu. Řekněme, že její instance chceme využít jako klíče v hešové tabulce a také chceme mít možnost se rozhodnout zda bude hešový kód z dané instance získáván na základě jména nebo příjmení osoby. Zdrojový kód by po obohacení třídy o dva HashProvidery mohl vypadat následovně: public class Osoba : IComparable private string jmeno; private string prijmeni; public Osoba(string Jmeno, string Prijmeni) this.jmeno = Jmeno; this.prijmeni = Prijmeni; public static System.Collections.IComparer PorovnavaniPodleJmena getreturn new PorovnaniPodleJmena(); public static System.Collections.IComparer PorovnavaniPodlePrijmeni getreturn new PorovnaniPodlePrijmeni(); public static System.Collections.IHashCodeProvider HashPodlePrijmeni getreturn new HashProviderPodlePrijmeni(); public static System.Collections.IHashCodeProvider HashPodleJmena getreturn new HashProviderPodleJmena(); private class PorovnaniPodleJmena : System.Collections.IComparer 111

113 public int Compare(object obj1, object obj2) Osoba losoba1 = (Osoba) obj1; Osoba losoba2 = (Osoba) obj2; return String.Compare(lOsoba1.jmeno,lOsoba2.jmeno); private class PorovnaniPodlePrijmeni : System.Collections.IComparer public int Compare(object obj1, object obj2) Osoba losoba1 = (Osoba) obj1; Osoba losoba2 = (Osoba) obj2; return String.Compare(lOsoba1.prijmeni,lOsoba2.prijmeni); public int CompareTo(object obj) if (!(obj is Osoba)) throw new ArgumentException("Predany objekt neni typu osoba"); Osoba losoba = (Osoba) obj; return String.Compare(this.prijmeni,lOsoba.prijmeni); /// Vraci hesovy kod pro hodnotu jmena instance tridy osoba /// </summary> private class HashProviderPodleJmena : System.Collections.IHashCodeProvider public int GetHashCode(object obj) if (!(obj is Osoba)) throw new ArgumentException("Predany objekt neni typu osoba"); Osoba losoba = (Osoba) obj; return losoba.jmeno.gethashcode(); /// Vraci hesovy kod pro hodnotu prjmeni instance tridy osoba /// </summary> private class HashProviderPodlePrijmeni : System.Collections.IHashCodeProvider public int GetHashCode(object obj) if (!(obj is Osoba)) throw new ArgumentException("Predany objekt neni typu osoba"); Osoba losoba = (Osoba) obj; return losoba.prijmeni.gethashcode(); public override string ToString() return jmeno + " " + prijmeni; 112

114 Do třídy přibyly dvě vnitřní třídy implementující rozhraní System.Collections.IHashProvider, které jsou pro pohodlnější využití zpřístupnění pomocí statických vlastností. Implementace metody GetHashCode je v případě první vnitřní třídy taková, že vrací hešový kód na základě jména osoby a v případě třídy druhé na základě jejího příjmení. Jak se použití konkrétního HashProvideru projeví ukazuje tento příklad: public class IHashProviderPriklad public static void Priklad() //urcime ze hash bude ziskavan podle jmena Hashtable Adresy = new Hashtable(Osoba.HashPodleJmena,Osoba.PorovnavaniPodleJmena); Osoba lmichal = new Osoba("Michal","Hynek"); Osoba lmartin = new Osoba("Martin","Pesek"); Osoba ltomas = new Osoba("Tomas","Berger"); Adresy.Add(lMichal,"Michalova adresa"); Adresy.Add(lMartin,"Martinova adresa"); Adresy.Add(lTomas,"Tomasova adresa"); Osoba lnejakymichal = new Osoba("Michal","Novak"); //jelikoz je vyhledavano podle jmena bude vrace Michalova adresa Console.WriteLine(Adresy[lNejakyMichal]); public static void Priklad2() //urcime ze hash bude ziskavan podle prijmeni Hashtable Adresy = new Hashtable(Osoba.HashPodlePrijmeni,Osoba.PorovnavaniPodlePrijmeni); Osoba lmichal = new Osoba("Michal","Hynek"); Osoba lmartin = new Osoba("Martin","Pesek"); Osoba ltomas = new Osoba("Tomas","Berger"); Adresy.Add(lMichal,"Michalova adresa"); Adresy.Add(lMartin,"Martinova adresa"); Adresy.Add(lTomas,"Tomasova adresa"); Osoba lnejakypesek = new Osoba("Petr","Pesek"); //jelikoz je vyhledavano podle prijmeni bude vrace Martinova adresa Console.WriteLine(Adresy[lNejakyPesek]); Kopírování instancí a Rozhraní ICloneable Občas můžeme potřebovat vytvořit novou instanci, která je kopií nějaké instance stávající. V některých případech může stačit využití protected metody MemberwiseClone třídy System.Object. Ovšem při jejím používání musíme být obezřetní a následující ukázka nastiňuje proč. public class ZapouzdrenaHodnota private int hodnota; public ZapouzdrenaHodnota(int Hodnota) this.hodnota = Hodnota; public int Hodnota getreturn hodnota; sethodnota = value; 113

115 public class MojeTrida private ZapouzdrenaHodnota cislo; public MojeTrida(ZapouzdrenaHodnota Cislo) this.cislo = Cislo; public MojeTrida Klon() return (MojeTrida)MemberwiseClone(); public ZapouzdrenaHodnota Cislo getreturn cislo; setcislo = value; Důvodem k obezřetnosti je skutečnost, že metoda MemberwiseClone, vytváří takzvanou mělkou kopii objektu. To znamená, že pokud jsou atributy kopírované třídy referenčního typu, jsou zkopírovány pouze hodnoty referencí, ale referencované instance zkopírovány nejsou. Takže pokud spustíme následující příklad: public class MojeTridaPriklad public static void Priklad() MojeTrida lmujobjekt = new MojeTrida(new ZapouzdrenaHodnota(5)); MojeTrida lmujobjekt2 = lmujobjekt.klon(); //pokud zmenime vnitrni hodnotu prvniho objektu, zmena se projevi //i u druheho objektu lmujobjekt.cislo.hodnota = 11; Console.WriteLine(lMujObjekt2.Cislo.Hodnota); bude vypsáno číslo 11. Takže je vhodné v takovýchto případech, kdy jsou atributy klonované třídy referenčního typu, naimplementovat takzvanou hlubokou, nebo také úplnou kopii. A právě k tomuto účelu je určeno rozhraní ICloneable, které předepisuje jednu jedinou metodu Clone. Takže implementace třídy z předchozího příkladu, by se mohla změnit do této podoby: public class MojeKlonovatelnaTrida : ICloneable private ZapouzdrenaHodnota cislo; public MojeKlonovatelnaTrida(ZapouzdrenaHodnota Cislo) this.cislo = Cislo; public ZapouzdrenaHodnota Cislo getreturn cislo; setcislo = value; #region ICloneable Members 114

116 public object Clone() return new MojeKlonovatelnaTrida(new ZapouzdrenaHodnota(cislo.Hodnota)); #endregion Nyní je již při klonování vytvořena i nová instance třídy ZapouzdrenaHodnota. public class MojeKlonovatelnaTridaPriklad public static void Priklad() MojeKlonovatelnaTrida lmujobjekt = new MojeKlonovatelnaTrida(new ZapouzdrenaHodnota(5)); MojeKlonovatelnaTrida lmujobjekt2 = (MojeKlonovatelnaTrida) lmujobjekt.clone(); lmujobjekt.cislo.hodnota = 11; Console.WriteLine(lMujObjekt2.Cislo.Hodnota); Takže po spuštění tohoto příkladu uvidíme na výstupu očekávané číslo 5. Poznáváme C# a Microsoft.NET 29. díl řetězce Po několika dílech, které pojednávali o třídách kolekcí bych dnešní díl rád věnoval bližšímu pohledu na řetězce, se kterými jsme se během našeho poznávání již několikrát setkali. Bližší pohled na třídu System.String Klasické řetězce jsou v prostředí MS. NET framework realizovány instancemi třídy System.String. Obsah jednotlivé instance této třídy je představován kolekcí, která obsahuje sekvenci objektů System.Char, tedy objektů jednotlivých znaků. /// Ukazuje, ze je instance tridy string slozena z jednotlivých /// objektu System.Char /// </summary> public static void VypisForEach() string s = "ahoj, jak se mate?"; foreach(system.char znak in s) Console.WriteLine(znak); Důležité je mít na paměti, že instance třídy System.String jsou takzvaně immutabilní, neboli neměnné. To, že jsou instance neměnné znamená, že po jejím vytvoření nemůže být její hodnota změněna a všechny operace, které jsou nad ní provedeny původní instanci nemění a vrací novou modifikovanou instanci. Takže důsledek této neměnnosti je takový, že pokud například pomocí operátoru += k existujícímu objektu řetězce nějaký jiný řetězec přidáváte, tak se hodnota původní instance nezmění, ale je vytvořena nová instance s novou hodnotu, na kterou je nasměrován odkaz referenční proměnné. 115

117 string retezec = "Ahoj"; //bude vytvorena nova instance tridy String //s hodnotou "Ahoj svete" a ref. promenna //retezec bude odkazovat na ni retezec += " svete"; Třída System.String obsahuje definice pro následující porovnávací a vyhledávací metody: Metoda Popis Compare Porovná dva řetězce CompareOrdinal Porovná dva řetězce s využitím ordinálního porovnání CompareTo Porovná aktuální instanci třídy System.String s instancí jinou EndsWith Určí, zda aktuální instance končí zadaným řetězcem. StartsWith Určí, zda aktuální instance začíná zadaným řetězcem. IndexOf Určí pozici prvního výskytu určitého řetezce nebo znaku v aktuální instanci LastIndexOf Určí pozici posledního výskytu určitého řetezce nebo znaku v aktuální instanci Porovnání instancí řetězců může být lingvistické (jazykové) nebo ordinální (číselné). V případě ordinálního porovnávání jsou porovnávány číselné hodnoty objektů Char (takzvaný Unicode codepoint) v instanci. Následující příklad ukazuje rozdíl mezi těmito způsoby porovnávání: public static void PorovnavaniPriklad() string lmala = "abc"; string lvelka = "ABC"; string lvysledek1 = (String.Compare(lMala,lVelka) < 0)? "je mensi nez" : "je vetsi nez"; //hodnota Unicode codepointu je pro a vetsi nez pro A string lvysledek2 = (String.CompareOrdinal(lMala,lVelka) < 0)? "je mensi nez" : "je vetsi nez"; Console.WriteLine("Lingvisticke porovnani: ",lmala,lvysledek1,lvelka); Console.WriteLine("Ordinalni porovnani: ",lmala,lvysledek2,lvelka); Výstup bude následovný: Lingvisticke porovnani: abc je mensi nez ABC Ordinalni porovnani: abc je vetsi nez ABC Kromě porovnávacích a vyhledávacích metod ještě třída System.String nabízí následující modifikační metody: Metoda Popis 116

118 Concat CopyTo Insert Join PadLeft Spojí dvě nebo více instancí dohromady. V případě že jsou parametry představovány objekty, je na nich zavolána metoda ToString. Zkopíruje určitý počet znaků z řetězce na zadané pozice pole znaků. Vytvoří novou instanci řetězce, do které je na určenou pozici vložen zadaný řetězec Umožňuje vytvořit novou instanci řetězce spojením určených elementů pole řetězců, přičemž mezi jednotlivé elementy je ve výsledném řetězci vložen zadaný oddělovač. Zapříčiní vytvoření nové instance řetězce o specifikované délce, kde je původní řetězec zarovnán doleva. Také umožňuje zvolit znak, který bude použit pro vyplnění volného místa v novém řetězci. PadRight Provádí to samé jako metoda PadLeft s tím rozdílem, že původní řetězec je zarovnán doprava. Remove Split Odstraní určitý počet znaků z instance řetězce o zadané pozice. Vytvoří pole řetězců, které obsahuje podřetězce vniklé rozdělením na místech zadaného znaku nebo znaků, představujích oddělovače. SubString Vytvoří novou instanci řetězce, jejíž hodnota je představována podřetězcem hodnoty instance na které byla zavolána. ToLower ToUpper Trim Vytvoří řetězec představující kopii instance, na které byla zavolána, kde jsou všechny znaky řetězce malá písmena. Vytvoří řetězec představující kopii instance, na které byla zavolána, kde jsou všechny znaky řetězce velká písmena. Odstraní z řetězce prázdné znaky TrimStart Odstraní specifikované znaky ze začátku řetězce TrimEnd Odstraní specifikované znaky z konce řetězce Následující zdrojový kód představuje příklad, který ukazuje použití některých metod třídy System.String: public static void Priklady() string lmujretezec = "Ahoj, jak se vsichni mate?"; Console.WriteLine("Puvodni retezec : 0",lMujRetezec); Console.WriteLine("Vysledek pouziti metody Concat: 0",String.Concat(lMujRetezec,"..doufam, ze fajn :-)")); Console.WriteLine("Vysledek pouziti metody Insert: 0", lmujretezec.insert(12," tady")); Console.WriteLine("Vysledek pouziti metody Join: 0", String.Join("- ",new String[2]lMujRetezec,"Pridano")); Console.WriteLine("Vysledek pouziti metody Remove: 0", lmujretezec.remove(12,8)); Console.WriteLine("Vysledek pouziti metody Substring: 0", lmujretezec.substring(0,4)); Console.WriteLine("Vysledek pouziti metody ToLower: 0", lmujretezec.tolower()); Console.WriteLine("Vysledek pouziti metody ToUpper: 0", lmujretezec.toupper()); Console.WriteLine("Vysledek pouziti metody PadLeft : 0",lMujRetezec.PadLeft(30,`_`)); Console.WriteLine("Vysledek pouziti metody PadRight : 0",lMujRetezec.PadRight(30,`_`)); Console.WriteLine("Vysledek pouziti metody Split:"); foreach(string lprvek in lmujretezec.split(` `)) Console.WriteLine(lPrvek); 117

119 Po vykonání této metody uvidíme následující výstup: Puvodni retezec : Ahoj, jak se vsichni mate? Vysledek pouziti metody Concat: Ahoj, jak se vsichni mate?..doufam, ze fajn :-) Vysledek pouziti metody Insert: Ahoj, jak se tady vsichni mate? Vysledek pouziti metody Join: Ahoj, jak se vsichni mate?-pridano Vysledek pouziti metody Remove: Ahoj, jak se mate? Vysledek pouziti metody Substring: Ahoj Vysledek pouziti metody ToLower: ahoj, jak se vsichni mate? Vysledek pouziti metody ToUpper: AHOJ, JAK SE VSICHNI MATE? Vysledek pouziti metody PadLeft : Ahoj, jak se vsichni mate? Vysledek pouziti metody PadRight : Ahoj, jak se vsichni mate? Vysledek pouziti metody Split: Ahoj, jak se vsichni mate? Poznáváme C# a Microsoft.NET 30. díl StringBuilder a Regulární výrazy Po minulém hlubším pohledu na třídu System.String představující řetězce v rámci.net, bych dnes rád čtenáře seznámil s užitečnou třídou využívanou pro řetězcové hodnoty, které budou měněny. Mimo to poodhalím použití regulárních výrazů v C#. Třída StringBuilder V minulém díle jsme se dozvěděli, že instance třídy System.String jsou takzvaně neměnné a také jakým způsobem jsou vytvářeny nové hodnoty těchto instancí. Zajisté vás napadla otázka, jak si tedy poradit v situaci, kdy potřebujeme měnit hodnotu instance obsahující nějaký text? V takovýchto situacích je vhodné použít třídu StringBuilder, která je obsažena ve jmenném prostoru System.Text základní knihovny tříd.net frameworku. Tato třída je k takovýmto operacím přímo předurčena, protože obsahuje instanční metody, které nějakým způsobem mění hodnotu konkrétní instance, bez toho, aby byla vytvořena instance nová. Mimo to, je přímo doporučeno tuto třídu používat u textových hodnot, které se budou častěji měnit, protože úpravy textových hodnot reprezentovaných instancemi třídy StringBuilder jsou mnohem méně náročnější na režii, než je tomu u hodnot reprezentovaných instancemi třídy System.String. Instance této třídy je vždy vytvořena s určitou kapacitou, která říká, na jaký počet znaků je v paměti vyhrazeno místo. Číslo představující tuto kapacitu je možné zadat jako parametr některé z přetížených verzí konstruktoru, nebo ho explicitně nastavit pomocí instanční vlastnosti Capacity. Mezi základní metody třídy StringBuilder patří: Metoda Append Insert Remove Popis Přidá textovou reprezentaci objektu na konec konkrétní instance Vloží textovou reprezentaci objektu na specifikovanou pozici v instanci Od určité pozice vyjme určený 118

120 počet znaků z instance Replace Nahradí specikovaný znak nebo řetězec v instanci jiným specikovaným znakem nebo řetězcem. EnsureCapacity Zjistí jestli je kapacita instance alespoň taková jaká je požadovaná hodnota, předaná ve formě parametru. Pokud tomu tak není je paměť pro instanci přealokována Následující příklad demonstruje použití této třídy: /// Ukazka pouziti tridy StringBuilder /// </summary> public class StringBuilderPriklady private static void ZpracujText(StringBuilder Text) int ldelka = Text.Length; Text.Append("-delka puvodniho textu byla : "); Text.Append(lDelka); public static void Priklad() //vytvoreni instance StringBuilderu, ktery ma kapacitu 50 znaku //a obsahuje text `Nejaky textik` StringBuilder ltext = new StringBuilder("Nejaky textik",50); Console.WriteLine("Puvodni text : 0",lText); ZpracujText(lText); Console.WriteLine("Text po zavolani metody ZpracujText : 0 ",ltext); Console.WriteLine("Text po pouziti metody Remove tridy StringBuilder : 0",lText.Remove(0,7).ToString()); Po spuštění tohoto příkladu byste měli vidět takovýto výstup: Puvodni text : Nejaky textik Text po zavolani metody ZpracujText : Nejaky textik-delka puvodniho textu byla : 13 Text po pouziti metody Remove tridy StringBuilder : textik-delka puvodniho textu byla : 13 Úvod do regulárních výrazů v C# V.NET frameworku a tedy i v jazyce C# je možné využívat takzvaných regulárních výrazů. Regulární výrazy jsou výborní pomocníci při různých pracích s textem. Umožňují nám text různě přetvářet, vyhledávat v něm a zobrazovat pouze informace, které jsou pro nás zajímavé. Regulární výrazy jsou vlastně samy o sobě speciálním jazykem, kterým vyjadřujeme naše požadavky na konkrétní zpracování daného textu a jejich použití v jednotlivých programovacích jazycích se nijak zásadně neliší. V prostředí.net frameworku jsou třídy pro práci s regulárními výrazy obsaženy ve jmenném prostoru System.Text.RegularExpressions. 119

121 Regulární výraz je v.net frameworku reprezentován instancí třídy RegEx. Nejzákladnějším použitím regulárního výrazu je vyhledání výskytu nějakého znaku či řetězce. U takovýchto použití můžeme na regulární výraz nahlížet jako na vzor, kterému by měl text vyhovět. V případě hledání výskytu určitého znaku nebo znaků bude vzor regulárního výrazu tvořen pouze hledanými znaky. Jak bychom tento jednoduchý regulární výraz použili v jazyce C# ukazuje následující zdrojový kód: /// Uvodni priklad do problematiky reg. vyrazu /// </summary> public class RegexPriklady private static int ZjistiIndex(string RetezecNaProhledani,string HledanyRetezec) //vytvoreni reg. vyrazu Regex lregex = new Regex(HledanyRetezec); //zkusime ziskat vyhovujici vysledek z urciteho retezce Match lvyhovujici = lregex.match(retezecnaprohledani); //pokud vyhledavani uspelo vratime index //vyskytu v prohledavanem retezci if (lvyhovujici.success) return lvyhovujici.index; return -1; public static void PrikladIndex() int lindex = ZjistiIndex("uiabcadfa","abc"); if (!lindex.equals(-1)) Console.WriteLine("Retezec abc se vyskytuje na pozici 0",lIndex); else Console.WriteLine("Retezec nebyl nalezen"); V příkladu jsem vytvořil instanci třídy RegEx, představující regulární výraz (v našem konkrétním přikladu se jednalo o regulární výraz abc ) a potom na instanci zavolal metodu Match, která slouží pro vyhledání výskytu řetězce zadaného konstruktoru třídy RegEx ve specifikovaném řetězci, který je metodě předán jako parametr. Tato metoda vrací instanci třídy Match, která reprezentuje výsledek hledání pomocí regulárního výrazu. Ke zjištění toho jestli bylo vyhledávání pomocí regulárního výrazu úspěšné použijeme instanční vlastnost Success třídy Match. Pokud tato vlastnost nabývá hodnoty true, hledání výskytu bylo úspěšné a index výskytu hledaného řetězce zjistíme pomocí vlastnosti Index. Příští díl bude věnován bližšímu pohledu na regulární výrazy. Poznáváme C# a Microsoft. NET 31. díl regulární výrazy Minule jsme se seznámili s pojmem regulární výraz. V tomto díle se na možné použití regulárních výrazů a jejich aplikaci v rámci.net podíváme trochu blíže. V minulém úvodu do světa regulárních výrazů jsme se zatím naučili zjistit, jestli řetězec obsahuje nějaký podřetězec a zjistit jeho pozici v prohledávaném řetězci. Kdyby byly 120

122 možnosti regulárních výrazů takto omezené, nebyl by smysl k jejich užití. Regulární výrazy nám, ale nabízejí spoustu užitečných funkcí. Libovolné znaky v regulárních výrazech V regulárním výrazu máme možnost určit, že na nějakých místech v řetězci mohou být libovolné znaky. K vyjádření libovolného znaku slouží zástupný symbol. (tečka). Tento symbol nám dovoluje vyjádřit, že na daném místě se může vyskytovat jeden libovolný znak. Pokud je naše přání takové, že chceme povolit neomezený počet určitých znaků použijeme symbolu * (hvězda). Následující příklad ukazuje možné použití těchto symbolů v regulárním výrazu, jehož pomocí je zjišťováno, zda řetězec představuje ovou adresu. /// Zjisti, zda-li predany retezec predstavuje ovou adresu /// pouzitim regularniho vyrazu /// </summary> /// <param name="retezec">vstupni retezec</param> public static bool Je (string Retezec) Regex lregex = new Regex(@".*@.*\..*"); return lregex.ismatch(retezec); public static void Priklad () string[] lhodnoty = "petr.pus@unicorn.cz","jouda#joudove.cz","spatna@adresa"; foreach(string lhodnota in lhodnoty) if (Je (lHodnota)) Console.WriteLine("Retezec 0 predstavuje ovou adresu.",lhodnota); else Console.WriteLine("Retezec 0 nepredstavuje ovou adresu.",lhodnota); Regulární výraz.*@.*\..* určuje, že řetězec může začínat libovolným počtem libovolných znaků (.* - tečka je libovolný znak a hvězda říká, že jich může být libovolný počet), po té musí následovat znak zavináče, po něm opět libovolný počet znaků, potom tečka a zase libovolné znaky. Tento příklad mimo jiné demonstruje způsob použití znaku tečka jako součásti regulárního výrazu. Pokud chceme určit, že se má v řetězci vyskytovat tečka jako znak, nemůžeme jednoduše napsat tečku do výrazu protože by byla chápána jako možnost libovolného znaku. Proto je pro vyjádření tečky použito zpětného lomítka (\.). Z důvodu výskytu zpětného lomítka musíme zabránit tomu, aby tato část řetězce nebyla brána jako tzv. escape-sekvence a to provedeme použitím vlastnosti jazyka C#, kterou je použití znaku zavináč před řetězcem. V takto označeném řetězci pak nejsou escape-sekvence zpracovávány. Intervaly znaků Další možností je specifikovat množinu určitých znaků v regulárním výrazu. K tomuto účelu slouží dvojice hranatých závorek mezi kterými je uveden námi požadovaný výčet znaků nebo je také možné specifikovat interval, kde uvedeme počáteční a koncový znak 121

123 onoho intervalu a oddělíme je pomlčkou. To znamená, že regulárnímu výrazu ve tvaru [aeiou] vyhoví pouze znaky a,e,i,o,u a pokud využijeme druhé možnosti, tj. určení intervalu tak výrazu [a-e] vyhoví pouze znaky a až e naší abecedy. Následující příklad zjišťuje, jestli řetězec představuje číslo a to právě pomocí definovaného intervalu znaků. /// Zjisti, zda-li predany retezec predstavuje cislo /// pouzitim regularniho vyrazu /// </summary> /// <param name="retezec">vstupni retezec</param> public static bool JeCislo(string Retezec) //pokud je v retezci jediny znak ruzny od cisla vyhovi regularnimu vyrazu Regex lreg = new Regex("[^0-9]"); //pokud se v retezci nevyskytl zadny znak ruzny od cisla je to v poradku return!lreg.ismatch(retezec); public static void PrikladCislo() string[] lhodnoty = " ","12ab12"; foreach(string lhodnota in lhodnoty) if (JeCislo(lHodnota)) Console.WriteLine("Retezec 0 predstavuje cislo.",lhodnota); else Console.WriteLine("Retezec 0 nepredstavuje cislo.",lhodnota); Výstup po spuštění tohoto příkladu by měl vypadat takto: Retezec predstavuje cislo. Retezec 12ab12 nepredstavuje cislo. V regulárním výrazu, který je uveden v příkladu se objevuje znak ^ (stříška), který v jazyku regulárních výrazů znamená negaci. Takže použití regulárního výrazu [^0-9] ve výsledku zapříčiní, že pokud se v řetězci nachází alespoň jeden znak různý od čísel nula až devět, bude tento řetězec regulárnímu výrazu vyhovovat. Skupiny v regulárních výrazech V regulárních výrazech se nám nabízí využití skupin ve vyhledávaném výrazu. Tyto skupiny využijeme v případě, že potřebujeme získat pouze část nalezeného řetězce. Skupiny jsou v regulárních výrazech tvořeny pomocí závorek. Následující příklad ukazuje použití skupin v regulárním výrazu pro získání a zvýšení hodnoty v korunách, která je představována řetězcem. /// Zjisti zda predany retezec predstavuje hodnotu v korunach a umoznuje /// k teto hodnote pridan urcity pocet korun. /// </summary> public static void PrikladKoruny(string Retezec,int PridanaHodnota) Regex lregex = new Regex(@"\b(\d+)\.(\d\d)Kc\b"); Match lmatch = lregex.match(retezec); if (lmatch.success) GroupCollection lgroupcol = lmatch.groups; int lnovahodnota = Int32.Parse(lGroupCol[1].Value) + PridanaHodnota; 122

124 Console.WriteLine("Hodnota po pricteni 0 korun je 1,2",PridanaHodnota,lNovaHodnota,lGroupCol[2]); else Console.WriteLine("Retezec nevyhovuje"); Regulární výraz \b(\d+)\.(\d\d)kc\b, který je použit v tomto příkladu definuje dvě skupiny. První skupina je tvořena libovolným počtem číslic (\d+ - symbol \d znamená totéž jako interval [0-9] a ono plus říká, že jich může být libovolný počet) představující koruny a druhá dvěma libovolnými číslicemi představující haléře. Hodnota korun a haléřů musí být oddělena tečkou. Dalším dosud nevysvětleným symbolem, který je v příkladu použit je symbol \b a ten značí pomyslné hranice řetězce. Protože pokud bych tento znak ve výrazu nepoužil, tak by před nebo za hodnotou mohli být jakékoli jiné znaky a řetězec by stejně výrazu vyhověl, poněvadž k vyhovění regulárního výrazu stačí, aby řetězec obsahoval podřetězec splňující vzor. Tímto symbolem tedy v jazyce regulárních výrazů řekneme, že před ani za řetězcem splňující námi dané podmínky se již nesmí nic jiného nacházet. Třídu Match ze jmenného prostoru System.Text.RegularExpressions známe již z minulého dílu. Novinkou při použití skupin v regulárních výrazech je ovšem třída GroupCollection, která představujíce kolekci všech skupin z vyhovujícího řetězce. V našem ukázkovém příkladu to budou skupiny tři (první skupina je celý vyhovující výraz, druhá jsou koruny a třetí haléře). Každá skupina je reprezentována instancí třídy Group a v příkladu je získána použitím indexeru definovaným na třídě GroupCollection. Pro kýžené přidání hodnoty je tedy vzata hodnota korun, převedena na typ Int32 a k ní je po té přičteno. Poznáváme C# a Microsoft.NET 32. díl I/O a streamy Cílem tohoto dílu je pro mě seznámit čtenáře se základy realizace vstupně/výstupních operací v.net frameworku. Dozvíme jaký obecný koncept pro tyto operace je v MS.NET použit a naučíme se různými způsoby pracovat se soubory. I/O operace v rámci.net V prostředí MS. NET frameworku jsou všechny vstupně/výstupní operace realizovány pomocí takzvaných datových proudů, neboli streamů. Stream je abstrakce určité sekvence bytů z nějakého zařízení, souboru, paměti nebo soketu TCP/IP. V rámci.net je takovýto datový proud představován abstraktní třídou Stream. Tato třída je bázovou třídou pro všechny datové proudy pracující s konkrétními objekty (souboru, I/O, zařízení, paměť). Třídy představující konkrétní datové proudy implementují její abstraktní členy potřebné pro uskutečnění požadovaného přenosu dat. To pro nás programátory znamená velkou výhodu v podobě zobecnění přístupu k různým objektům, na kterých chceme provádět I/O operace, protože použití tříd je díky společnému rozhraní pořád stejné a tím pádem se vůbec nemusíme zabývat implementačními detaily související s přístupem ke konkrétnímu druhu objektu. Všechny třídy související s problematikou vstupně/výstupních operací nalezneme ve jmenném prostoru System.IO základní knihovny tříd.net frameworku. Na jednotlivých implementacích datových proudů se nacházejí implementace metod Read a Write, které použijeme ke čtení bytů respektive k jejich zápisu. Obou metodám je ve formě parametru předán odkaz na pole bytů, představující buffer (vyrovnávací paměť). V případě metody Write je obsah tohoto bufferu zapsán do datového proudu a v případě metody Read je tomu naopak a data z proudu jsou do bufferu ukládána. 123

125 Některé datové proudy podporují náhodný přístup. Podpora náhodného přístupu u proudu znamená, že v dané sekvenci bytů se můžeme nastavit na libovolnou pozici a ne jen číst sekvenci od začátku. To jestli konkrétní proud, který chceme použit pro přístup k objektu, náhodný přístup podporuje, zjistíme přečtením instanční vlastnost CanSeek. Pokud proud tuto vlastnost podporuje, můžeme požadovanou pozici nastavit pomocí vlastnosti Position. Operace se soubory Jak jsme se dozvěděli výše, tak pro přístup ke konkrétnímu objektu na kterém chceme provádět I/O operace použijeme k tomu určenou implementaci abstraktní třídy Stream. V případě přístupu k souborům použijeme třídu FileStream. Následující příkladu ukazuje jak je pomocí této třídy možné zapsat byty do určitého souboru. /// Ukazka zapisu do souboru pomoci metody Write tridy Stream /// </summary> public static void ZapisBytuDoSouboru() FileStream lstream = null; try //vytvoreni streamu lstream = new FileStream(@"C:\pokus.txt",FileMode.Create); //vytvoreni bufferu bytu, ze ktereho bude zapsano byte[] lbuffer = new byte[]1,2,3,4; //pokyn k zapisu lstream.write(lbuffer,0,lbuffer.length); //vyprazdneni bufferu a provedeni vsech neprovedenych //zapisu do zarizeni (souboru) lstream.flush(); finally //uzavreni streamu lstream.close(); V příkladu jsem vytvořil proud k souboru C:\pokus.txt, a uvedl jsem pomocí výčtového typu FileMode, že chci tento soubor vytvořit a pokud existuje, tak jej chci přepsat. Následně jsem vytvořil pole dat, které bude zapsáno (čísla 1,2,3,4). Po té jsem dal pokyn k zápisu pomocí metody Write, které jsem pomocí parametrů sdělil, že chci zapsat všechna data z bufferu a že je chci zapsat od jeho začátku. Zavolání metody Flush na instanci streamu zapříčiní vyprázdnění všech bufferů (pokud nějaké stream používá) a zapsaní všech dat do souboru. Nakonec je stream pomocí metody Close uzavřen. Jak by se zapsaná data do tohoto souboru opět v programu načetla, demonstruje následující příklad. /// Ukazka nacteni souboru pomoci metody Read tridy Stream /// </summary> public static void NacteniBytuZeSouboru() FileStream lstream = null; try 124

126 //vytvoreni streamu lstream = new FileStream(@"C:\pokus.txt",FileMode.Open); //vytvoreni bufferu do ktereho budou zapsana nactena data //ze zarizeni byte[] lbuffer = new byte[4]; //nacteni dat ze souboru do bufferu lstream.read(lbuffer,0,lbuffer.length); //vypsani nactenych dat for(int i = 0; i < lbuffer.length; i++) Console.WriteLine(lBuffer[i]); finally //uzavreni streamu lstream.close(); V příkladu je opět vytvořen stream a pomocí něj jsou data ze souboru načtena do pole bytů, které je následně vypsáno pomocí cyklu for. Třídy BinaryWriter a BinaryReader Jistě vás napadlo, že pokud je jediný možný způsob jak číst a zapisovat data pomocí bytů, tak práce s IO v.net není zrovna nejpříjemnější. Samozřejmě tomu tak není a návrháři.net frameworku vymyslely třídy BinaryReader a BinaryWriter. Tyto třídy jsou velmi užitečné, protože jsou schopny číst respektive zapisovat základní datové typu z/do sekvence bytů tedy do proudu. Takže tyto třídy samy o sobě vytvořit proud vedoucí ke konkrétnímu objektu, ale umí již existující proud využít a zpříjemnit nám s ním práci. Jak použít třídu BinaryWriter pro zápis dat do souboru ukazuje tento příklad. /// Ukazka pouziti tridy BinaryWriter pro /// zapis dat do souboru /// </summary> public static void ZapisPomociBinaryWriteru() FileStream lstream = null; try //vytvoreni streamu lstream = new FileStream(@"C:\pokus.txt",FileMode.Create); //vytvoreni BinaryWriteru, ktery bude vyuzivat //nas proud k souboru BinaryWriter lwriter = new BinaryWriter(lStream); //zapsani retezce lwriter.write("retezec"); //zapsani cisla int lwriter.write(69); //zapsani cisla double lwriter.write(69.69); lwriter.flush(); lwriter.close(); finally lstream.close(); 125

127 V příkladu jsem vytvořil proud k souboru, který jsem následně předal konstruktoru třídy BinaryWriter a tím jsem zajistil, že data budou zapsána do mého proudu. Potom jsem pomocí různých přetížení metody Write pomocí BinaryWriteru zapsal nějaká data do proudu a tedy i do souboru. Jak takto zapsaná data ze souboru získáme, pomocí třídy BinaryReader je ukázáno v tomto příkladu. /// Ukazka nacteni dat ze souboru pomoci tridy BinaryReader /// </summary> public static void CteniPomociBinaryReaderu() FileStream lstream = null; try lstream = new FileStream(@"C:\pokus.txt",FileMode.Open); BinaryReader lreader = new BinaryReader(lStream); Console.WriteLine(lReader.ReadString()); Console.WriteLine(lReader.ReadInt32()); Console.WriteLine(lReader.ReadDouble()); finally lstream.close(); V příštím dílu se dozvíme, co ještě skrývá jmenný prostor System.IO. Poznáváme C# a Microsoft.NET 33. díl I/O podruhé Po seznámení se s principem streamů, které proběhlo v minulém díle, se dnes seznámíme s několika dalšími třídami ze jmenného prostoru System.IO. Díky těmto třídám jsme schopni například lépe pracovat s textovými soubory nebo zjišťovat informace o souborech. Třídy TextReader a TextWriter TextReader a TextWriter jsou abstraktní třídy, určené pro pohodlné čtení respektive zápis sekvence znaků. Každá z této dvojice abstraktních tříd má ve jmenném prostoru System.IO základní knihovny tříd.net frameworku dva potomky. Pro třídu TextReader to jsou StringReader a StreamReader a pro třídu TextWriter to jsou analogicky třídy StringWriter a StreamWriter. StringReader a StringWriter jsou implementace TextReaderu, které čtou/zapisují z/do prostého řetězce. Na rozdíl od této dvojice StreamReader a StreamWriter jsou určeny pro čtení respektive zápis z/do datového proudu bytů. Na třídě TextReader jsou implementovány užitečné metody ReadLine a ReadToEnd. První zmiňovaná načte jeden řádek textu ze určitého zdroje a posune pozici čtení na další řádek a metoda ReadToEnd slouží k načtení všech znaků ze zdroje a vrátí je jako souvislý řetězec. Třída TextWriter obsahuje definici metody WriteLine, která, jak již název napovídá, umí zapsat jeden řádek textu do zdroje. Následující příklad ukazuje použití StreamReaderu a StreamWriter pro práci s textovým souborem. /// Ukazka pouziti tridy StreamWriter pro zapis textu do souboru 126

128 /// </summary> public static void ZapisPomociStreamWriteru() Stream lstream = null; try lstream = new FileStream(@"C:\pokus.txt",FileMode.Create); TextWriter lwriter = new StreamWriter(lStream); lwriter.writeline("prvni radek"); lwriter.writeline("druhy radek"); lwriter.close(); finally lstream.close(); /// Ukazka pouziti tridy StreamReader pro cteni textu do souboru /// </summary> public static void CteniPomociStreamReaderu() Stream lstream = null; try lstream = new FileStream(@"C:\pokus.txt",FileMode.Open); TextReader lreader = new StreamReader(lStream); string lradek; while((lradek = lreader.readline())!= null) Console.WriteLine(lRadek); finally lstream.close(); Práce s pamětí pomocí proudů Stejně jako k souboru nebo síťovému připojení je možné přistupovat pomocí datových proudů i k paměti. Implementace abstraktní třídy Stream je pro tento typ úložiště představována třídou MemoryStream ze základní knihovny tříd.net frameworku. Tuto třídu pravděpodobně nejčastěji použijete v případech, kdy potřebujete pracovat pouze s dočasnými daty, která není třeba uchovávat v nějakém persistentním úložišti. Následující jednoduchý příklad obsahuje ukázku použití datového proudu do paměti. /// Ukazka pouziti tridymemorystream pro praci s pameti pomoci proudu /// </summary> public static void PraceSMemoryStreamem() MemoryStream lstream = null; try lstream = new MemoryStream(); BinaryWriter lwriter = new BinaryWriter(lStream); lwriter.write("data zapsana proudem do pameti"); //nastaveni pozice ve streamu na zacatek lstream.position = 0; 127

129 //precteni zapsanych dat BinaryReader lreader = new BinaryReader(lStream); Console.WriteLine(lReader.ReadString()); finally lstream.close(); Třída File Základní knihovna tříd.net frameworku obsahuje třídu File, která obsahuje pouze statické metody, určené pro práci se soubory jako například vytváření, kopírování, mazání, přesouvání, zjištění zda soubory existují nebo jejich otevírání. Mimo tyto základní operace je možné její pomocí zjišťovat nebo i nastavovat například čas vytvoření, poslední modifikace, posledního přístupu. Také je nám umožněno číst i nastavovat atributy konkrétního souboru. Jak může vypadat použití této třídy, demonstruje tento příklad. /// Priklad ukazujici nektere moznosti tridy System.IO.File /// </summary> public static void FileExam() string lpath //pokud soubor na dane ceste neexistuje vytvorime jej if (!File.Exists(lPath)) Stream lstream = File.Create(lPath); lstream.close(); StreamWriter lwriter = File.AppendText(lPath); lwriter.writeline("radek textu"); lwriter.close(); Console.WriteLine("Cas vytvoreni : 0", File.GetCreationTime(lPath)); //nastaveni atributu archivace File.SetAttributes(lPath,FileAttributes.Archive); Console.WriteLine("Attributy : ",File.GetAttributes(lPath)); Trochu si příklad rozebereme. Pomocí metody Exists zjistíme, jestli soubor na dané cestě, která je předána formálním parametrem, již existuje. V případě, že tomu tak není, využijeme metody Create, která soubor vytvoří a jako výsledek vrací odkaz na instancí třídy FileStream, pomocí něhož je možné s nově vytvořeným souborem pracovat. My tento Stream v příkladu pouze zavřeme, aby bylo možné s ním dále pracovat jinými postupy. Pokud bychom tak neučinili, tak by další příkazy skončili vyhozením výjimky. Následuje volání metody AppendText, která vrátí odkaz na instanci StreamWriteru asociovaného s určeným souborem, jehož pomocí zapíšeme do souboru nějaký text. Další akcí je vypsání času vytvoření souboru, čehož je dosaženo použitím metody GetCreationTime. Souboru potom pomocí metody SetAttribute nastavíme atribut archivace. Jako reprezentace atributů souboru je v základní knihovně tříd.net frameworku použit výčet (enum) FileAttributes jehož použití můžete v příkladu vidět. A na konec jsou voláním metody GetAttributes zjištěny všechny nastavené atributy souboru a vypsány. 128

130 Poznáváme C# a Microsoft.NET 34. díl informace o adresářích a sledování souborového systému Tento díl si bere za své seznámit vás s možnostmi zjišťování informací o adresářích a ve své druhé části také se zajímavým způsobem sledování změn souborového systému. Třída Directory První část tohoto dílu věnuji navázání na problematiku, kterou jsem zakončil díl minulý a tou byla třída File pro práci se soubory. Stejně jako je třída File určena pro manipulaci se soubory, tak třída Directory slouží pro takovéto operace s adresáři. Tyto operace jsou stejně jako u třídy File implementovány ve formě statických metod. Použití některých z těchto metod by mohlo vypadat nějak takto: /// Ukazka pouziti vybranych metod tridy Directory /// </summary> public class DirectoryExam public static void Example() string ldirpath //pokud dany adresar existuje, tak jej smazeme if (Directory.Exists(lDirPath)) Console.WriteLine("Mazu stary adresar.."); Directory.Delete(lDirPath); Console.WriteLine("Vytvarim novy adresar.."); //vytvoreni noveho adresare Directory.CreateDirectory(lDirPath); //nastaveni casu vytvoreni adresare - 5 let po soucasnosti DateTime lcreationtime = DateTime.Now.AddYears(5); Console.WriteLine("Nastavuji cas vytvoreni adresare 0 na 1",lDirPath,lCreationTime); Directory.SetCreationTime(lDirPath, lcreationtime); Třídy FileInfo a DirectoryInfo Třídy File a Directory nepředstavují jediný způsob, jak zjišťovat informace o souborech respektive adresářích nebo s nimi manipulovat. Druhou možností jsou třídy FileInfo a DirectoryInfo, které se taktéž nacházejí ve jmenném prostoru System.IO. Na rozdíl od dříve zmiňovaných tříd je potřeba pro provádění operací pomocí této dvojice tříd vytvořit jejich instance namísto volání statických metod. To s sebou přináší výhodu v případech, kdy se určitou instanci chystáme použít vícekrát a to z důvodu, že při používání statických metod tříd File a Directory jsou vždy ověřována přístupová práva k objektu. Naproti tomu jsou v případě tříd FileInfo a DirectoryInfo přístupová práva kontrolována pouze u některých metod a při vytváření instance. Jinak jsou poskytované operace této dvojice tříd velmi podobné s operacemi nabízenými třídami File a Directory. Na ukázku je zde tento příklad, který obsahuje použití třídy DirectoryInfo. /// Ukazka pouziti tridy DirectoryInfo 129

131 /// </summary> public class DirectoryInfoExam public static void Example() string ldirpath DirectoryInfo ltestinfo = new DirectoryInfo(lDirPath); if (ltestinfo.exists) Console.WriteLine("Jmeno : 0",lTestInfo.Name); Console.WriteLine("Korenovy adresar : 0", ltestinfo.root); string lsubdirname DirectoryInfo[] ldirs = ltestinfo.getdirectories(); //pokud existuji nejake podadresare tak je vypiseme if (ldirs.length > 0) Console.WriteLine("Obsazene podadresare : "); foreach(directoryinfo lcurrentinfo in ldirs) Console.WriteLine(lCurrentInfo.Name); Console.WriteLine("Vytvarim podadresar 0..",lSubDirName); ltestinfo.createsubdirectory(lsubdirname); else Console.WriteLine("Adresar 0 neexistuje",ldirpath); Sledování změn souborového systému V základní knihovně tříd prostředí.net framework existuje zajímavá třída, která nám umožňuje sledovat změny v souborovém systému. Jedná se o třídu FileSystemWatcher a je obsažena ve jmenném prostoru System.IO. Této třídě pomocí její instanční vlastnosti Path nastavíme adresář jehož obsah chceme sledovat. Po tomto přiřazení si můžeme pomocí instancí delegátů FileSystemEventHandler a RenamedEventHandler předplatit události které tato třída zveřejňuje. Mezi tyto události patří Changed, Created, Deleted a Renamed. Událost Changed je vyvolána v případě změny obsaženého souboru či adresáře jako je změna velikosti, atributů nebo nastavení zabezpečení, událost Created zase v případě vytvoření nového souboru či adresáře ve sledovaném adresáři, událost Delete je vyvolána když je ve sledovaném adresáři nějaký soubor nebo adresář smazán a událost Renamed když je soubor nebo adresář přejmenován. Důležitou instanční vlastností je také vlastnost EnableRaisingEvents, pomocí které nastavujeme jestli má instance třídy FileSystemWatcher již reagovat na změny v souborovém systému vyvoláváním výše zmíněných událostí. Další booleanovská vlastnost, která nese název IncludeSubdirectories indikuje jestli má být reagováno i na změny v podadresářích sledovaného adresáře. Následující příklad sleduje změny v určitém adresáři pomocí zmiňované třídy FileSystemWatcher. /// Priklad na sledovani zmen v adresari pomoci tridy FileSystemWatcher /// </summary> public class FileSystemWatcherExam public static void WatchFile(string path) FileSystemWatcher lwatcher = new FileSystemWatcher(); 130

132 //nastavime adresar, ktery ma byt sledovan lwatcher.path = path; //vytvorime instance delegatu lwatcher.changed += new FileSystemEventHandler(FSChanged); lwatcher.deleted += new FileSystemEventHandler(FSDeleted); lwatcher.renamed += new RenamedEventHandler(FSRenamed); lwatcher.created += new FileSystemEventHandler(FSCreated); //nastavime, ze maji byt sledovany i zmeny ve vnorenych adresarich lwatcher.includesubdirectories = true; //spustime sledovani lwatcher.enableraisingevents = true; Console.WriteLine("Sleduji adresar 0..",path); Console.ReadLine(); private static void FSChanged(object sender, FileSystemEventArgs e) Console.WriteLine("Objekt 0 v adresari byl zmenen - 1",e.Name,e.ChangeType); private static void FSDeleted(object sender, FileSystemEventArgs e) Console.WriteLine("V adresari byl smazan objekt 0",e.Name); private static void FSRenamed(object sender, RenamedEventArgs e) Console.WriteLine("V adresari byl objekt 0 prejmenovan na 1",e.OldFullPath,e.Name); private static void FSCreated(object sender, FileSystemEventArgs e) Console.WriteLine("V adresari byl vytvoren objekt 0",e.Name); Zajímavou vlastností této třídy, kterou bych rád zmínil je vlastnost NotifyFilter, které pomocí výčtu NotifyFilters můžeme specifikovat skupinu změn souborového systému pří kterých má být vyvolána událost Changed. Takže pokud bychom do příkladu přidali následující řádek: lwatcher.notifyfilter = NotifyFilters.Attributes NotifyFilters.Security; Bude událost Changed vyvolána pouze v případech, že je změněn atribut nějakého obsaženého objektu nebo je změněno jeho nastavení zabezpečení. Poznáváme C# a Microsoft.NET 35. díl izolovaná úložiště Dnešní díl bych vás rád seznámil s problematikou takzvaných izolovaných úložišt (Isolated storages) a také se způsoby jakými lze s tímto druhem úložiště pracovat. Kromě tohoto tématu se také zmíním o jiné implementaci tříd TextWriter a TextReader než jsou StreamWriter a StreamReader. 131

133 Třídy StringReader a StringWriter V jednom z minulých dílu tohoto seriálu věnovaných otázce vstupně výstupních operací jsme se seznámili s abstraktními třídami TextReader a TextWriter a také s jejich hojně využívanými implementacemi pro čtení a zápis z/do souborů kterými jsou StreamReader a StreamWriter. Kromě těchto implementací tříd TextReader a TextWriter se v základní knihovně tříd.net frameworku nachází ještě druhá dvojice implementací, kterou je implementace pro ty nejjednodušší druhy úložišť, kterými jsou instance třídy String nebo StringBuilder. Tyto třídy nesou jméno StringReader a StringWriter. Pro tu první, tedy pro třídy StringReader, je objekt, ze kterého jsou čtena data pomoci rozhraní, které je definováno třídou TextReader, představován instancí třídy String. /// Ukazka pouziti tridy StringReader /// </summary> public class StringReaderExam public static void RunExam() string lstr = "Prvni radek textu"; //pridame znak noveho radku lstr += Environment.NewLine; lstr += "Druhy radek textu"; lstr += Environment.NewLine; lstr += "Treti radek textu"; //vytvorime instanci tridy StringReader a nechame na ni //odkazovat ref. promennou typu TextReader TextReader lreader = new StringReader(lStr); string lline = null; //vypiseme text po radkach while( (lline = lreader.readline())!= null) Console.WriteLine(lLine); Jak můžeme vidět, tak je práce s objekty typu StringReader, pro mnoho úloh stejná jako při použití třídy StreamReader a to díky použití rozhraní definované třídou TextReader. Poznámka: Jelikož jsem chtěl ukázat použití metody Readline, musel jsem vytvořit víceřádkový řetězec. Jedním ze způsobů jak dostat do řetězce znak nového řádku je použití statické vlastnosti NewLine třídy Environment. Tak jako je třída StringReader implementací TextReaderu pro nejjednodušší datové úložiště v podobě řetězce, tak je třída StringWriter implementací TextWriteru a nyní je datové úložiště představováno instancí třídy StringBuilder. /// Ukazka pouziti tridy StringWriter /// </summary> public class StringWriterExam public static void RunExam() StringBuilder lbuilder = new StringBuilder(); //vytvorime instanci tridy StringWriter asociovanou s konkretni //instanci tridy StringBuilder 132

134 TextWriter lwriter = new StringWriter(lBuilder); //zapiseme data lwriter.write(5.4); lwriter.writeline(); lwriter.write("text"); lwriter.close(); //vypiseme data Console.WriteLine(lBuilder.ToString()); Izolovaná úložiště V některých případech je vhodné pracovat s daty, o kterých máme jistotu, že s nimi žádná jiná aplikace či uživatel manipulovat. Toho je možné dosáhnout použitím nějakých vlastních technik, které ale v určitých případech už mohou být příliš složité a zaberou při vývoji aplikace mnoho času. Právě pro ušetření času s vymýšlením vlastních technik pro dosažení výše zmíněného cíle nám Microsoft. NET framework nabízí k použití takzvaná izolovaná úložiště. Tato úložiště jsou vyčleněna pro uživatele a pro assembly, takže je zaručena dostatečná úroveň izolace od ostatních aplikací se kterými pracují jiní uživatelé. Předpokládejme příklad, kdy určitý uživatel (např. Jarda) používá.net aplikaci (např. apl.exe), ukládající svá data ve formě souborů a adresářů, která jsou uložena právě v izolovaném úložišti. Běhové prostředí.net zaručuje, že jakákoliv jiná.net aplikace, i když je spuštěna pod tímto uživatelem s těmito daty nebude nijak manipulovat. Navíc.NET runtime zaručuje, že pokud aplikaci apl.exe spustí nějaký jiný uživatel, stejně nebude moci pracovat se soubory, které aplikace vytvořila, když byla spuštěna uživatelem Jarda. A kde že to izolované úložiště ve vašem systému vlastně je? Odpověď na tuto otázku závisí na tom jaký operační systém používáte. Pokud používáte Microsoft Windows ve verzi 2000 nebo XP naleznete jej na cestě <systémový disk>\documents and Settings\<uživatel>\Local Settings\Application Data v případě nepřenášeného profilu nebo na cestě <systémový disk>\documents and Settings\<uživatel> \Application Data pokud pužíváte profíl cestovní. Pokud vás zajímaji jiné verze systému Windows odkážu vás na SDK pro.net framework. Pro práci s izolovanými úložišti je v základní knihovně.net frameworku určen jmenný prostor System.IO.IsolatedStorage. Pro abstrakci oblasti izolovaného úložiště slouží třída IsolatedStorageFile. Pomocí instance této třídy jsme schopni zjistit obsažené soubory a adresáře v izolovaném úložišti, nebo adresáře vytvářet čí odstraňovat. Následující příklad ukazuje jak je možné v izolovaném úložišti vytvořit soubory a adresáře. public static void WritingExam() //ziskani izolovaneho uloziste IsolatedStorageFile lisolatedfile = IsolatedStorageFile.GetUserStoreForAssembly(); //vytvoreni adresare lisolatedfile.createdirectory("isolovanyadresar"); //vytvoreni noveho souboru v izolovanem ulozisti a zapis dat IsolatedStorageFileStream lisolatedstream = new IsolatedStorageFileStream("test.txt",FileMode.Create,lIsolatedFile); lisolatedstream.write(new byte[3]1,2,3,0,3); lisolatedstream.close(); 133

135 //vytvoreni souboru do adresare IsolatedStorageFileStream lisolatedfilestream2 = new IsolatedStorageFileStream("IsolovanyAdresar",FileMode.Create); lisolatedfilestream2.close(); Na začátku si pomocí statické metody GetUserStoreForAssembly získáme odkaz na isolované úložiště vyhrazené pro kombinaci aktuální uživatel a assembly. Pomocí metody CreateDirectory vytvoříme v oblasti izolovaného úložiště adresář. Po té vytvoříme instanci třídy IsolatedStorageFileStream, což je potomek třídy FileStream určený pro práci se soubory v izolovaném úložišti. Stejným způsobem jako s instancí třídy FileStream vytovříme soubor a zapíšeme do něj nějaká data. A jak se soubory či adresáře s konkrétního izolovaného úložiště odstraní? Tak to ukazuje tento příklad: public static void DeletingExam() string lfilename = "temp.tmp"; string ldirname = "tempdir"; IsolatedStorageFile lisolatedfile = IsolatedStorageFile.GetUserStoreForAssembly(); Console.WriteLine("Vytvarim adresar 0", ldirname); lisolatedfile.createdirectory(ldirname); Console.WriteLine("Vytvarim soubor 0",lFileName); //vytvorime stream asociovany s konkretnim souborem uloziste a jeho pomoci vytvorime soubor IsolatedStorageFileStream lisolatedstream = new IsolatedStorageFileStream(lFileName,FileMode.Create,lIsolatedFile); lisolatedstream.close(); Console.WriteLine("Mazu soubor 0",lFileName); lisolatedfile.deletefile(lfilename); Console.WriteLine("Mazu adresar 0",lDirName); Získat všechny oblasti izolovaných úložišť je možné použitím enumerátoru, který získáme zavoláním metody GetEnumerator na třídě IsolatedStorageFile. Toho mimo jiné využívá i následující příklad. public static void EnumeratingExam() //ziskame vsechny soubory predstavujici isolovane uloziste IEnumerator lenum = IsolatedStorageFile.GetEnumerator(IsolatedStorageScope.User); while(lenum.movenext()) IsolatedStorageFile lfile = (IsolatedStorageFile) lenum.current; //ziskani informaci o assembly Url lurl = (Url)lFile.AssemblyIdentity; Console.WriteLine("Assembly : 0", lurl.value); //vypsani velikost souboru izolovaneho uloziste Console.WriteLine("Velikost : 0: ", lfile.currentsize); //ziskani souboru obsazenych v izol. ulozisti string[] lfiles = lfile.getfilenames("*"); Console.WriteLine("Obsazene soubory :"); for(int i = 0; i < lfiles.length; i++) Console.WriteLine(lFiles[i]); 134

136 Jak můžete v příkladu vidět, tak cestu k assembly ke které je izolované úložiště asociováno, je možné získat přečtením vlastnosti AssemblyIdentity na instanci třídy IsolatedStorageFile. Seznam adresářů a souborů v konkrétním izolovaném úložišti je možné získat pomocí metody GetFileNames, jíž ve formě parametru předáme řetězec představující filtr. Poznáváme C# a Microsoft.NET 36. díl úvod do reflexe V tomto dílu se začneme věnovat dle mého názoru velmi zajímavou problematikou, kterou je takzvaná reflexe. Pomocí reflexe je totiž možné za běhu programu dynamicky zjišťovat informace o existujících typech. Aplikace v.net frameworku Předtím než se podíváme přímo na problematiku reflexe v.net bude dobré, když se alespoň stručně zmíním o tom, z jakých částí jsou aplikace vlastně tvořeny. Jak již bylo v seriálu zmíněno základním zaveditelným prvkem aplikace pro.net framework je Assembly (Sestava). Assembly mimo to také tvoří základní jednotku pro správu verzí, jednotné rozlišování typů a specifikaci bezpečnostních oprávnění. Každá aplikace pro.net framework je tvořena přinejmenším jednou assembly a ta je zase tvořena čtyřmi částmi, kterými jsou: manifest, který obsahuje metadata o assembly metadata o typech obsažených v assembly kód v jazyce MSIL, který je spuštěn prostředím.net runtime zdroje (resources) Metadata o obsažených typech a MSIL kód spolu tvoří takzvaný modul, což je přenositelný spustitelný (PE portable executable) soubor. Nejjednodušší assembly jsou složeny z manifestu a jediného modulu s typy aplikace. I když to není časté, tak je možné vytvořit i sestavu s více moduly. Jednotlivé moduly s typy jsou pak představovány soubory s příponou.netmodule. Takovéto assembly jsou většinou tvořeny z optimalizačních důvodů, protože prostředí.net runtime nahrává moduly pouze v případě potřeby typu v nich obsažených. Možná se někteří z vás pozastavili na pojmem metadata. Nejedná se o nic jiného než o data, která nesou popisné informace o assembly či typu. Například manifest obsahuje mimo jiné tato metadata: jednoduchý název assembly číslo verze assembly veřejný klíč tvůrce a hešový kód assembly (volitelně) seznam souborů, které tvoří danou assembly a jejich hešové kódy seznam typů, které tvoří assembly a informaci ke kterému modulu v assembly je konkrétní typ připojen Reflexe v.net Jedním z důsledků použití mechanismu metadat pro všechny typy v prostředí.net frameworku je možnost tyto typy v našem programu prozkoumávat a tato věc je nazývána reflexe. Jmenný prostor, který obsahuje nemalý počet tříd, jež námi mohou být 135

137 použity pro manipulaci s danými elementy konkrétní aplikace nese název System.Reflection. Mechanismus reflexe nám tedy umožňuje procházení a manipulaci s objektovým modelem představující konkrétní aplikaci. Metadata, která jsou reflexí využívána jsou obvykle vytvářena kompilátorem při překladu aplikace. Kromě tohoto obvyklého způsoby tvorby metadat k elementům aplikace, je možné metadata vytvořit pomocí tříd, které se nacházejí ve jmenném prostoru System.Reflection.Emit. Každá aplikace pro.net framework běží v nějaké aplikační doméně, představující izolované běhové prostředí. Aplikační doménu je možné brát jako obdobu procesu ze světa programování ve Win32. Aplikační doména sama o sobě není popsána metadaty. Aplikační doména je představována třídou AppDomain, která nám pomocí své statické vlastnosti CurrentDomain předloží přístup k aktuální aplikační doméně aplikace a pokud na ní zavoláme metodu GetAssemblies získáme v podobě pole všechny instance třídy Assembly, která reflektuje objekt assembly v aplikační doméně. A stejně jako třída Assembly reflektuje assembly v aplikační doméně, tak ve jmenném prostoru System.Reflection existují i další třídy, které reflektují ostatní elementy aplikace.net. Pro lepší pochopení vztahů mezi těmito reflektujícími třídami jsem zhotovil jednoduchý UML diagram, který tyto třídy a jejich vztahy zobrazuje. Jak můžeme vidět, tak třemi základní typy v reflexi jsou třídy Module, MemberInfo a Assembly. Co reflektují třídy Module a Assembly je jasné již z jejich názvu.třída MemberInfo je třídou abstraktní a představuje předka pro třídy, které poskytují informace o členech třídy. Jedním z potomků této třídy je další abstraktní třída 136

138 MethodBase, která je předkem pro třídy ConstructorInfo a MethodInfo, které, jak název napovídá, poskytují informace o konstruktorech respektive metodách třídy. Třída Type ze jmenného prostoru System, která je taktéž jedním z potomků abstraktní třídy MemberInfo a je asi nejzákladnějším typem reflexe. Tato třída představuje informaci o daném typu, jinými slovy představuje jeho metadata. Na úvod do mechanismu reflexe jsem vytvořil jednoduchý příklad, ve kterém je pouze získána instance třídy Type a pomocí ní jsou zjištěny všechny členy. Abych na ukázku nepoužíval pouze vestavěné typy.net frameworku, tak jsem kvůli příkladu vytvořil ještě jednoduchou třídu Osoba. /// Trida predstavujici osobu, ktera bude pouzita v /// uvodnim prikladu na pouziti reflexe /// </summary> public class Osoba private string jmeno; private string prijmeni; private DateTime datumnarozeni; public string Jmeno getreturn jmeno; setjmeno = value; public string Prijmeni getreturn prijmeni; setprijmeni = value; public DateTime DatumNarozeni getreturn datumnarozeni; setdatumnarozeni = value; public Osoba() public int VypocistVek() TimeSpan lspan = DateTime.Now - datumnarozeni; return lspan.days / 365; /// Uvodni priklad pouziti reflexe /// </summary> public class PrikladReflexe public static void VypisCleny(Type Typ) Console.WriteLine("Vypisuji vlastnosti typu 0",Typ.Name); //ziskani vsech clenu MemberInfo[] lseznamclenu = Typ.GetMembers(); foreach(memberinfo linfo in lseznamclenu) 137

139 Console.WriteLine("Nazev clenu = 0 - druh clenu = 1", linfo.name,linfo.membertype); public static void Priklad() Osoba losoba = new Osoba(); //ziskani instance tridy Type pomoci metody GetType Type ltyposoba = losoba.gettype(); //ziskani instance tridy Type pomoci staticke Metody //GetType tridy type Type ltypint32 = Type.GetType("System.Int32"); //ziskani instance tridy Type pouzitim operatoru typeof Type ltypstring = typeof(string); VypisCleny(lTypOsoba); VypisCleny(lTypInt32); VypisCleny(lTypString); Jak můžeme v příkladu vidět, instanci třídy System.Type je možné získat třemi způsoby. Tím prvním je zavoláním metody GetType na konkrétní instanci třídy jejíž popis chceme získat. Druhým způsobem je použití statické metody GetType třídy System.Type, jíž jako parametr zadáme řetězec představující plně kvalifikovaný název třídy (to znamená název třídy včetně jmenného prostoru do něhož spadá). Třetím a podle mých informací i drobek rychlejším řešením získání instance třídy System.Type je použítí operátoru typeof jazyka C#. Příští díl se již podrobněji podíváme na jednotlivé reflektující třídy. Poznáváme C# a Microsoft.NET 37. díl použití reflexe Po minulém úvodu do světa reflexe se v tomto díle budeme věnovat vybraným reflektujícím typům. Dozvíme se jak je například možné pomocí reflexe za běhu dynamicky vyhledávat a vykonávat metody nebo jakým způsobem za běhu vytvoříme instanci třídy na základě jejích metadat. Třída System.Type Instance této představují abstrakci deklarace určitého typu ať už se jedná o třídu nebo strukturu, výčet (enum) či rozhraní. Tato třída je základní cestou k metadatům typu v mechanismu reflexe. Instanci této třídy lze získat několika způsoby, základní tři byly zmíněny v minulém díle (Type.GetType ve svých dvou verzích a operátor typeof). Pomocí instančních vlastností a metod této třídy můžeme zjišťovat rozličné informace o daném typu, včetně seznamu jeho členů, které mohou být jak víme datové členy, vlastnosti, metody, konstruktory a události. Na ukázku použití je zde následující příklad, který zjistí některé informace o typu Osoba, který byl použit již v minulém díle (jediná změna je, že byla do třídy přidána definice metody ToString, která při svém zavolání vrátí jméno a příjmení dané osoby) Zdrojový kód třídy Osoba nebudu v článku uvádět, můžete jej v případě potřeby nalézt v přiložených zdrojových kódech. /// Priklad na zjisteni informaci z metadat pomoci tridy Type 138

140 /// </summary> public class TypePriklad /// Vypise par informaci z metadat /// </summary> public static void VypisInfo(Type Typ) Console.WriteLine("Kvalifikovany nazev typu : 0 ",Typ.FullName); Console.WriteLine("Kvalifikovany nazev assembly do niz spada : 0",Typ.AssemblyQualifiedName); Console.WriteLine("Nazev predka : 0",Typ.BaseType.FullName); Console.WriteLine("Je hodnotovy typ : 0",Typ.IsValueType); Console.WriteLine("Trida je zapecetena : 0",Typ.IsSealed); Console.WriteLine("Trida je verejna : 0",Typ.IsPublic); public static void Priklad() //ziskame assembly s nazvem PrikladyZive37 Assembly lziveassembly = Assembly.Load("PrikladyZive37"); //ziskame vsechny typy, ktere se v ni nachazeji Type losobatype = lziveassembly.gettype("prikladyzive37.osoba"); VypisInfo(lOsobaType); Na přikladu můžete vidět další způsob získání instance třídy Type a to použitím metody GetType na instanci třídy Assembly, kterou jsme získali zavoláním metody Load, jíž jsme předali název assembly, kterou chceme reflektovat. Následně je získaný typ předán metodě VypisInfo, který vypíše pár informací o něm. Dynamické vytvoření instance třídy Občas můžeme chtít vytvořit instanci třídy k níž máme k dispozici informace právě v podobě instance třídy Type. Jedna z cest jak tohoto dosáhnout je využít třídu Activator ze jmenného prostoru System. Jeho metoda CreateInstance v jedné ze svých mnoha přetížených verzí očekává ve formě parametru instanci třídy Type. V tomto příkladu je třída Activator použita k vytvoření instance třídy za běhu programu. /// Ukazka pouziti tridy Activator pro dynamicke vytvoreni instance tridy /// </summary> public class ActivatorPriklad public static void CreateInstancePriklad() //ziskame pole instanci tridy Assembly, ktere nalezi do aplikacni domeny Assembly[] lassemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach( Assembly lassembly in lassemblies) //ziskame vsechny typy z assembly Type[] ltypes = lassembly.gettypes(); Console.WriteLine("Prohledavam assembly 0", lassembly.fullname); foreach(type ltype in ltypes) //pokud se jedna o instanci tridy Type predstavujici typ tridy //osoba, tak vytvorime jeji instanci 139

141 if ( ltype.name.equals("osoba") ) Console.WriteLine("Nalezena metadata pro tridu Osoba"); Osoba linstance = (Osoba) Activator.CreateInstance(lType); //nyni muzeme provadet libovolne operace s istanci tridy osoba linstance.jmeno = "Jan"; linstance.prijmeni = "Novak"; Console.WriteLine(lInstance.ToString()); Kromě použití třídy Activator jsem chtěl v tomto příkladu uvést i to jak je možné získat seznam assemblies (metoda GetAssemblies na instanci AppDomain) z aplikační domény a také jak získat informace o všech typech v konkrétní assembly (metoda GetTypes na instanci Assembly). V příkladu je procházeno polem instancí třídy Type z každé assembly nahrané v aplikační doméně a pokud je jednoduchý název typu shodný s názvem třídy Osoba, tak je dynamicky vytvořena její instance. V tomto konkrétním příkladu bude seznam získaných assembly obsahovat pouze dvě instance třídy Assembly. První z nich bude představovat informace o assembly mscorlib.dll, což je assembly.net frameworku samotného a druhá bude představovat assembly naši tj. PrikladyZive37.dll. Poznámka: Možná teď někteří z vás kroutí hlavou a diví se, že jsem pro získání instance třídy Type popisující typ Osoba nepoužil jednoduše Type.GetType, ale jak jsem psal, tak v tomto příkladu jsem chtěl také demonstrovat možné získání seznamů aplikačních elementů. Reflexe datových členů Reflektujícím typem datových členů třídy nebo struktury je třída FieldInfo. Jejím využitím je například možné zjistit modifikátor přístupu daného datového členu a snadné je i zjištění jeho datového typu. To nám ukazuje první příklad použití tohoto typu. public static void VypisInfo() Type losobatype = typeof(osoba); //ziskani vsech instancnich dat. clenu FieldInfo[] lfields = losobatype.getfields(bindingflags.public BindingFlags.NonPublic BindingFlags.Instance); foreach(fieldinfo lfield in lfields) Console.WriteLine("Nazev clenu : 0",lField.Name); Console.WriteLine("Typ datoveho clenu : 0", lfield.fieldtype); Console.WriteLine("Clen je privatni : 0",lField.IsPrivate); Console.WriteLine("Clen je verejny : 0", lfield.ispublic); Console.WriteLine("Clen je staticky : 0", lfield.isstatic); Při získávání informací o datových členech typu si musíme dát pozor na použití příznaků BindingFlags, které se používají k určení výběru členů a to nejen v metodě GetFields, ale obecně u všech metod, které z metadat získávají instance potomků třídy MemberInfo. U reflexe datových členů je to však o to důležitější neboť pokud tyto příznaky neuvedeme korektně, tak námi získané pole bude prázdné. 140

142 Druhý příklad použití třídy FieldInfo je zde pro ukázku toho, jak je možné pomocí reflexe nastavit hodnotu datového členu. public static void NastaveniHodnotyPriklad() //vytvoreni instance Osoba losoba = new Osoba(); losoba.jmeno = "Jan"; Console.WriteLine("Jmeno osoby : 0", losoba.jmeno); Type losobatype = typeof(osoba); //ziskani datoveho clenu jmeno FieldInfo ljmenofield = losobatype.getfield("jmeno",bindingflags.instance BindingFlags.NonPublic BindingFlags.Public); //nastaveni datoveho clenu jmena instance losoba ljmenofield.setvalue(losoba,"karel"); Console.WriteLine("Jmeno osoby : 0", losoba.jmeno); Pro přiřazení hodnoty datového členu konkrétní instance je potřeba použít metody SetValue, jíž předáme instanci, na které chceme hodnotu členu pro něž máme získanou instanci třídy FieldInfo nastavit a samozřejmě kýženou hodnotu. Zajímavé na použití reflektivního přiřazování hodnot je fakt, že hodnotu lze nastavit nezávisle na modifikátoru přístupu datového členu, takže i když jsou všechny datové členy definované na třídě Osoba soukromé, je možné jim bez problému nastavit hodnotu. Reflexe je holt mocná zbraň. Obdobně jako se pracuje s instancemi třídy FieldInfo se pracuje i s instancemi třídy PropertyInfo, které slouží k reflexi přístupových vlastností. Reflexe metod K reflexi metod definovaných na jednotlivých typech je pro nás v rámci.net připravena třída MethodInfo. Stejně jako u ostatních reflektujících typů je možné o dané metodě zjistit mnoho zajímavých informací, jako je její návratový typ, zda-li je virtuální atd. public static void VypisInfo() Type losobatype = typeof(osoba); MethodInfo[] lmethods = losobatype.getmethods(bindingflags.nonpublic BindingFlags.Static BindingFlags.Instance BindingFlags.Public); foreach(methodinfo lmethod in lmethods) Console.WriteLine("Nalezena informace o metode 0", lmethod.name); Console.WriteLine("Metoda je virtualni : 0", lmethod.isvirtual); Console.WriteLine("Metoda je staticka : 0", lmethod.isstatic); Console.WriteLine("Navratovy typ metody : 0", lmethod.returntype); Console.WriteLine("Metoda je verejna : 0",lMethod.IsPublic); Jak můžeme vidět, tak pro získání metod stačí na instanci třídy Type zavolat metodu GetMethods, popřípadě specifikovat hledané metody pomocí výčtového příznaku BindingFlags. Přetížená verze této metody bez parametru vyhledá pouze veřejné instanční metody. Samozřejmě je kromě pouhého zjišťování informací o metodě i danou metodu zavolat. Za tímto účelem je na třídě MethodBase (společný předek tříd MethodInfo a ConstructorInfo) definována metoda Invoke, jejíž použití ukazuje následující příklad. 141

143 public static void VyvolaniPriklad() Type losobatype = typeof(osoba); //ziskani metody ToString MethodInfo ltostringmethod = losobatype.getmethod("tostring"); //vytvoreni instance pomoci reflexe Osoba linstance = (Osoba) Activator.CreateInstance(lOsobaType); linstance.jmeno = "Jan"; linstance.prijmeni = "Novak"; Console.WriteLine("Vyvolavam metodu ToString.."); //vyvolani metody na instanci object lresult = ltostringmethod.invoke(linstance,null); //vypsani vysledku volani metody Console.WriteLine(lResult); Instancí třídy MethodInfo reflektující metodu ToString jsme získali použitím metody GetMethod, které jsme předali název požadované metody. Prvním parametrem metody Invoke je objekt instance na kterém má být konkrétní metoda zavolána a druhým je pole objektů typu System.Object představující formální parametry metody (v tomto případě jsme metodě žádné parametry nepředávali). Jak by volání metody s parametry pomocí reflexe vypadalo? To se snaží ukázat příklad, který následuje. public static void VyvolaniParametryPriklad() Type lstringtype = typeof(string); //vytvoreni parametru pro konstruktor char[] lparam = new char[4]`t`,`e`,`x`,`t`; //vytvoreni instance pomoci reflexe string linstance = (string) Activator.CreateInstance(lStringType,new object[1]lparam); //vytoreni sady parametru pro metodu, aby nedoslo k nejednoznacnosti Type[] lmethodparams = new Type[1]; lmethodparams [0] = typeof(string); //ziskani metody IndexOf ve verzi prijimajici jeden parametr typu string MethodInfo lindexofmethod = lstringtype.getmethod("indexof",lmethodparams); object lresult = lindexofmethod.invoke(linstance,new object[1]"x"); Console.WriteLine(lResult); Jelikož je metoda IndexOf, která je definována na třídě String přetížená musíme přesně zvolit tu verzi, která má být reflektována, jinak by při jejím získávání pomocí metody GetMethod došlo k nejednoznačnosti a byla by vyvolána výjimka AmbiguousMatchException. Jak se takovéto situaci vyhnout? Odpověď na tuto otázku přichází v podobě použití přetížené verze metody GetMethod a to verze, která kromě názvu metody přijímá ještě druhý parametr a tím je pole objektů typu Type. V podobě tohoto pole totiž řekneme jaké parametry má hledaná metoda přijímat. V našem případě je toto pole pouze jednoprvkové a obsahuje pouze instanci třídy Type pro třídu String. Tím říkáme, že chceme získat verzi metody očekávající právě jeden parametr a ten má být typu String. Také konstruktor třídy String je přetížený a to se projevilo při dynamickém vytváření instnance pomocí třídy Activator, kde musíme zadat ve formě pole objekty představující parametry konstruktoru (zde konkrétně voláme verzi konstruktoru s parametrem typu pole znaků). 142

144 Poznáváme C# a Microsoft.NET 38. díl atributy a jejich reflexe Tento díl bude věnován seznámení s možností definice deklarativních informací k jednotlivým elementům aplikace, jež se v prostředí.net frameworku provádí užitím atributů. Také se naučíme vytvářet naše vlastní atributy a pomocí reflexe je i získávat. Proč atributy? V každém programovacím jazyce, pomocí specifických konstrukcí, které daný jazyk nabízí, deklarujeme u jednotlivých elementů programového kódu řadu informací. Například pokud vytváříme třídu, můžeme uvést, která všechna rozhraní jsou touto třídou implementována nebo u jednotlivých členů našeho typu specifikujeme modifikátory přístupu pro určení jejich viditelnosti. Omezením tohoto přístupu je fakt, že takto zadávané informace k elementům kódu jsou omezeny na použití předem určených konstrukcí konkrétního jazyka. V prostředí.net frameworku je možné pro zadání dodatečných informací k elementu programu použít takzvané atributy. Atributy jsou tedy jazykovými konstrukcemi, které mohou doplnit elementy programového kódu (assembly, moduly, typy, členy, návratové hodnoty a parametry) o specifické doplňující informace. Jak atributy použít? Stejně jako vše ostatní, tak i atributy jsou v prostředí.net frameworku objekty. Každý použitý atribut je instancí třídy, která buď přímo či nepřímo dědí od třídy System.Attribute. V jazyce C# jsou atributy specifikovány uvedením jména atributu do hranatých závorek nad konkrétním elementem programového kódu. V uvedeném příkladu je použití atributů demonstrováno na označení třídy za zastaralou pomocí atributu Obsolete. /// Ukazka pouziti atributu /// </summary> [Obsolete("Tato trida by jiz nemela byt dale vyuzivana",false)] public class StaraTrida //nejaka implementace tridy Podle konvence by měl název každé třídy představující třídu atributu končit slovem Attribute, aby třída byla odlišena od ostatních tříd. Ale při použití jednotlivých atributů nejsme nuceni specifikovat celý název atributu. Nemusíme tedy všude uvádět onu příponu Attribute, což jste vlastně mohli vidět na předchozím příkladu s použitím třídy atributu, jejíž celý název je ve skutečnosti ObsoleteAttribute. To znamená, že použití atributu v předchozím příkladu bylo ve výsledku shodné s tímto použitím: [System.ObsoleteAttribute("Tato trida by jiz nemela byt dale vyuzivana",false)] public class StaraTrida //nejaka implementace tridy 143

145 Samozřejmě lze k elementu přiřadit i více než jeden atribut a to tak,že jednotlivé atributy od sebe oddělíme čárkami. [Obsolete(),Serializable()] public class TridaViceAtributu //nejaka implementace tridy Poziční a pojmenované parametry atributů Atributy mohou přebírat parametry, které rozšiřují množství doplňujících informací při jejich použití. Při používání atributů se používají dva typy předávaných parametrů. Prvním z nich jsou parametry poziční, které odpovídají parametrům předávaným veřejným konstruktorům typu daného atributu. Naproti tomu, použití parametrů pojmenovaných představuje cestu, nastavení veřejných datových členů nebo vlastností typu daného atributu. Pojmenované parametry se narozdíl od pozičních paremetrů, musejí specifikovat zadaním jména datového členu nebo vlastnosti, kterou nastavují. V následujícím příkladu je použit atribut DllImport sloužící k použítí metod v DLL knihovnách napsaných v neřízeném jazyce. Pozičním parametrem atributu předáváme název knihovny ( user32.dll ) a pomocí pojmenovaného parametru CharSet sdělujeme jaká znaková sada má být použita pro řetězce. public class NejakaTrida [System.Runtime.InteropServices.DllImport("user32.dll",CharSet=System.Runti me.interopservices.charset.ansi)] public static extern void NejakaLinkovanaMetoda(); Cílené použití atributů V některých situacích může při použití atributu docházet k nejednoznačnosti. Například použijeme-li atribut pro metodu, může to znamenat, že atribut náleží k metodě nebo k její návratové hodnotě. class NejednoznacnePouziti [NejakyAttribut()] public int NejakaMetoda() return 0; I přes tuto nejednoznačnost půjde takovýto kód zkompilovat, protože C# má pro každý možný typ nejednoznačné deklarace atributu určený výchozí element pro který má atribut použít. V tomto konkrétním příkladu by to byla právě metoda. Pokud chcete vědět všechny výchozí elementy pří nejednoznačných deklaracích, odkážu vás na přehlednou tabulku uvedenou v.net framework SDK. Pokud nechceme, aby při takovéto deklaraci použití atributu byl atribut použit pro výchozí element, můžeme ono výchozí použití překrýt a to právě použitím cíleného použití atributu. Obecně se cílené použití atributu zapisuje takto : 144

146 [cil : seznam_atributů] Cíl může být jeden z těchto : assembly, field, event, method, module, param, property, return, type. class CilenePouziti [return: NejakyAttribut()] public int NejakaMetoda() return 0; Poznámka: Využití atributů aplikovaných pro návratové hodnoty metod nalezneme především při interakci s neřízeným kódem. Vytváření vlastních atributů Vytvářet vlastní atributy je velice snadné. Jediné co nám k vytvoření atributu stačí je vytvoření třídy, která přímo nebo nepřímo dědí ze střídy System.Attribute. Takže pokud se rozhodneme, že vytvoříme atribut nesoucí doplňující informace o autorovi nějakého elementu programového kódu, mohla by implementace vypadat nějak takto: /// Trida predstavujici uzivatelsky atribut slouzici k definici /// autora programoveho elementu /// </summary> //specifikujeme na kterych elementech muze byt atribut pouzit [AttributeUsage(AttributeTargets.Class AttributeTargets.Struct AttributeTar gets.method)] public class AuthorAttribute : Attribute private string jmeno; private string prijmeni; private double verze; public AuthorAttribute(string jmeno, string prijmeni) this.jmeno = jmeno; this.prijmeni = prijmeni; this.verze = 1.00; public string Jmeno getreturn jmeno; public string Prijmeni getreturn prijmeni; //tato vlastnost bude moci byt prirazena pomoci //pojmenovaneho parametru 145

147 public double Verze getreturn verze; setverze = value; Při použití atributu bude zadáváno jméno a příjmení autora a to pomocí pozičních parametrů, protože veřejný konstruktor typu tyto parametry očekává. Volitelně bude možné použitím pojmenovaného parametru specifikovat verzi elementu, protože vlastnost Verze je deklarována jako veřejná nastavitelná vlastnost. Třídě atributu jsme definovali atribut AttributeUsage, jehož použitím specifikujeme na které elementy programového kódu bude možné atribut použít. V našem případě půjde atribut použít na třídy, struktury a metody, což je uvedeno pomocí výčtu AttributeTargets. Získání uživatelských atributů při použití reflexe Nyní, když už umíme atributy vytvářet ještě potřebujeme zjistit zda je pro programový element konkrétní atribut definován. Teď když jsme již vybaveni znalostmi mechanismu reflexe si musíme naše znalosti o ní mírně rozvinout a to právě proto, že pomocí reflexe jsme schopni přítomnost atributu na elementu zjistit. V následujícím příkladu je u pár ukázkových tříd použit námi vytvořený atribut AuthorAttribute a použitím reflexe jsou pro jednotlivé třídy zjištěny jejich autoři, případně jsou zjištěny i autoři jednotlivých metod. /// Priklad na pouziti atributu AuthorAttribute a pouziti reflexe atributu /// </summary> public class AtributyPriklad static void VypisAutory(Type Typ) Console.WriteLine("Zpracovavam typ 0",Typ.Name); //zjistime, zda je pro dany typ definovan atribut AuthorAttribute if ( Attribute.IsDefined(Typ,typeof(AuthorAttribute)) ) //ziskame atribut AuthorAttribute lauthorattr = (AuthorAttribute) Attribute.GetCustomAttribute(Typ,typeof(AuthorAttribute)); Console.WriteLine("Autor typu : 0, Verze : 1",lAuthorAttr.Prijmeni + lauthorattr.jmeno, lauthorattr.verze); //ziskame seznam verejnych instnancnich metod MethodInfo[] lmethods = Typ.GetMethods(BindingFlags.Public BindingFlags.Instance); foreach(methodinfo lmethod in lmethods) //pokud je pro metodu definovan atribut typu AuthorAttribute vypiseme info if ( Attribute.IsDefined(lMethod, typeof(authorattribute)) ) AuthorAttribute lmethodauthor = (AuthorAttribute) Attribute.GetCustomAttribute(lMethod,typeof(AuthorAttribute)); Console.WriteLine("Autor metody 0 : 1, Verze : 2", lmethod.name, lmethodauthor.prijmeni + lmethodauthor.jmeno, lmethodauthor.verze); 146

148 public static void SpustPriklad() VypisAutory(typeof(PrvniTrida)); VypisAutory(typeof(DruhaTrida)); VypisAutory(typeof(TretiTrida)); [Author("Jiri","Joudek")] class PrvniTrida //nejaka implementace [Author("Michal","Racek",Verze=1.2)] class DruhaTrida [Author("Michal","Racek",Verze=1.1)] public void NejakaMetoda() //implementace metody //dalsi implementace tridy [Author("Jiri","Joudek",Verze=1.3)] class TretiTrida //nejaka implementace Přítomnost deklarace atributu pro konkrétní element zjistíme pomocí statické metody IsDefined třídy Attribute, které v jedné z přetížených verzí předáme instanci potomka třídy MemberInfo představující element u něhož přítomnost atributu zjišťujeme a druhým parametrem je typ atributu který hledáme. Pro získání atributu použijeme další statickou metodu třídy Attribute, kterou je metoda GetCustomAttribute, jíž v našem příkladu předáme stejné parametry jako v případě metody IsDefined. Jelikož metoda GetCustomAttribute vrací odkaz na objekt typu Attribute, tak pokud chceme používat členy definované na třídě AuthorAttribute musíme provést explicitní přetypování. Poznáváme C# a Microsoft.NET 39. díl další použití reflexe Dnešní díl, který je posledním dílem, ve kterém se budu zabývat reflexí, bude věnován použití reflexe pro vytváření instancí za běhu jiným způsobem než použitím třídy Activator. Také si ukážeme funkci polymorfizmu v reflexi a nakonec poodhalím některé třídy ze jmenného prostoru System.Relfection.Emit pro vytváření nových typů za běhu. Použití třídy ConstructorInfo Dosud jsme v našich příkladech, kde jsme vytvářeli instance jednotlivých typů pomocí reflexe používali třídu Activator a její metodu CreateInstance. Kromě tohoto způsobu, můžeme využít i třídu ConstructorInfo, která slouží k reflexi konstruktorů daného typu. Jelikož je tato třída potomkem abstraktní třídy MethodBase obsahuje, stejně jako nám již známá třída MethodInfo, metodu Invoke, která v případě třídy ConstructorInfo vyvolá 147

149 určitý konstruktor (pokud je takový nalezen). Následující příklad ukazuje použití této třídy na ukázkové třídě Zamestnanec. /// Ukazkova trida, ktera bude pozdeji instancovana pomoci reflexe /// </summary> public class Zamestnanec private string jmeno; private string prijmeni; private int hodinovamzda; public string Jmeno getreturn jmeno; public string Prijmeni getreturn prijmeni; public int HodinovaMzda getreturn hodinovamzda; sethodinovamzda = value; public Zamestnanec(string jmeno, string prijmeni, int hodinovamzda) this.jmeno = jmeno; this.prijmeni = prijmeni; this.hodinovamzda = hodinovamzda; public void VypisMzdu(int pocethodin) Console.WriteLine(pocetHodin * hodinovamzda); /// Ukazka vytvoreni instance pomoci reflexe pouzitim tridy ConstructorInfo /// </summary> public class ConstructorInfoPriklad public static void VytvoritZamestnance() Type lzamtype = typeof(zamestnanec); //vytvoreni seznamu typu parametru pro nalezeni konkretniho konstruktoru Type[] lparamtypes = new Type[3]; lparamtypes[0] = typeof(string); lparamtypes[1] = typeof(string); lparamtypes[2] = typeof(int); //hledame instancni, verejne konstruktory BindingFlags lflags = BindingFlags.Instance BindingFlags.Public; ConstructorInfo lconstinfo = lzamtype.getconstructor(lflags,null,lparamtypes,null); //vyvolani kostruktoru spolu s predanim parametru Zamestnanec linstance = (Zamestnanec) lconstinfo.invoke(new object[]"jan","novak",100); linstance.vypismzdu(160); 148

150 Vyvolání konstruktoru použitím metody Invoke je v zásadě stejné jako v případě použití u třídy MethodInfo, takže musíme pomocí pole instancí třídy Type specifikovat jakou verzi konstruktoru hledáme a pak pomocí pole objektů předat metodě Invoke hodnoty těchto parametrů. Reflexe a polymorfizmus Polymorfizmus je jayzce C# využíván nejen v případě standardních volání, ale i při používání reflexe. V následujícím příkladu je toto ukázáno na vytvoření abstraktní třídy Osoba, která obsahuje definici pro jednu abstraktní metodu. Třída má dva potomky, které její abstraktní metodu implementují. Zdrojový kód ukázkových tříd vypadá takto: /// Bazova trida pro osoby /// </summary> public abstract class Osoba public abstract void RekniPozdrav(); public class CeskaOsoba : Osoba public override void RekniPozdrav() Console.WriteLine("Ahoj lidi"); public class AnglickaOsoba : Osoba public override void RekniPozdrav() Console.WriteLine("Hello people"); Vlastní příklad získá za běhu assembly, která obsahuje definici výše uvedených typů, projde všechny typy v ní obsažené a zkoumá, zda-li je konkrétní typ potomkem třídy Osoba. V případě, že je tomu tak, je vytvořena instance typu a zavolána na něm metoda RekniPozdrav. Zdrojový kód je takovýto : /// Ukazka pouziti polymorfismu (pozdni vazby) v reflexi /// </summary> public class PolymofismusPriklad public static void Pozdravy() //ziskani nasi assembly Assembly lassembly = Assembly.Load("PrikladyZive39"); foreach(type ltype in lassembly.gettypes()) 149

151 //pokud je aktualni typ potomkem tridy Osoba vyvolame //na jeho instanci metodu RekniPozdrav if (ltype.issubclassof(typeof(osoba)) ) Osoba linstance = (Osoba) Activator.CreateInstance(lType); MethodInfo lmethod = ltype.getmethod("reknipozdrav"); lmethod.invoke(linstance,null); Výstup, jež po spuštění uvidíte a který jistě očekáváte je samozřejmě tento: Ahoj lidi Hello people Dynamické vytváření nových elementů aplikace pomocí reflexe Na závěr našeho několikadílného povídání o reflexi, bych zde rád čtenářům poskytl náhled na o něco složitější funkci reflexe, kterou je dynamické vytváření jednotlivých elementů aplikace (assembly, moduly, typy, členy typů) za jejího běhu. Jmenný prostor, který obsahuje třídy umožňující vytvářet nová metadata a kód MSIL nese název System.Reflection.Emit. Nejprve uvedu příklad, který v aktualní aplikační doméně vytvoří novou assembly, modul, v modulu dynamický typ a v něm metodu, potom příklad podrobněji popíšu. /// Ukazka vytvoreni novych elementu aplikace za behu /// </summary> public class EmitPriklad public static void VytvoreniTypu() //Ziskani aplikacni domeny AppDomain lcurrentdomain = AppDomain.CurrentDomain; //vytvoreni jmena pro assembly AssemblyName lassemblyname = new AssemblyName(); lassemblyname.name = "DynamickaAssembly"; //vytvoreni dynamicke assembly AssemblyBuilder lassemblybuilder = lcurrentdomain.definedynamicassembly(lassemblyname,assemblybuilderaccess.ru n); //vytvoreni modulu ModuleBuilder lmodulebuilder = lassemblybuilder.definedynamicmodule("dynamickymodul"); //vytvoreni typu TypeBuilder ltypebuilder = lmodulebuilder.definetype("dynamickytyp",typeattributes.public); //vytvoreni metody MethodBuilder lmethodbuilder = ltypebuilder.definemethod("dynamickametoda",methodattributes.public MethodAttributes.Virtual,typeof(void),null); //vytvoreni IL kody predstavujici telo metody ILGenerator lgenerator = lmethodbuilder.getilgenerator(); lgenerator.emitwriteline("vypsani pomoci dynamicke metody"); lgenerator.emit(opcodes.ret); //finalni vytvoreni typu 150

152 ltypebuilder.createtype(); //ziskani instance System.Type pro nas typ Type ldynamictype = lmodulebuilder.gettype("dynamickytyp"); object linstance = Activator.CreateInstance(lDynamicType); //vyvolani metody ldynamictype.getmethod("dynamickametoda").invoke(linstance,null); Předtím než se pustíme do vytváření nové dynamické assembly v aplikační doméně, musíme nejdříve pomocí instance třídy AssemblyName vytvořit její název. Pokud nám stačí assembly přiřadit pouze jednoduchý název, využijeme k tomu implicitní konstruktor a po té nastavíme instanční vlastnosti Name. Pokud jsme tak učinili, můžeme již použitím metody DefineDynamicAssembly na instanci třídy AppDomain na základě jména vytvořit novou assembly. Kromě jména assembly metodě ještě formou parametru předáme přístupový mód možné použití dynamické assembly a to pomocí výčtu AssemblyBuilderAccess. V našem příkladu je použita hodnota Run, která vytvářenou assembly umožňuje pouze spouštět, nikoliv ji však uložit na disk. Metoda DefineDynamicAssembly vrací odkaz na objekt typu AssemblyBuilder, který představuje dynamickou assembly. Na tomto objektu je následně zavoláním metody DefineDynamicModule vytvořen nový modul se specifickým jménem. Zavoláním této metody dostaneme odkaz na instanci třídy ModuleBuilder, pomocí níž analogickým způsobem vytvoříme novou definici typu (metoda DefineType) a použitím výčtu TypeAttributes řekneme, že se jedná o typ veřejně přístupný. Na instanci třídy TypeBuilder, pomocí metody DefineMethod definuje v typu novou metodu a výčtem MethodAttributes specifikujeme, že metoda je veřejná a virtuální. Po té získáme generátor mezikódu IL (ILGenerator) pro definici těla metody. Pomocí metody EmitWriteLine zajistíme vygenerování IL kódu, který odpovídá příkazu Console.WriteLine v C#. Pro ukončení metody musíme vygenerovat odpovídající IL kód. To zajistíme metodou Emit třídy ILGenerator, jíž předáme instanci třídy OpCodes, která ve formě svých statických datových členů poskytuje prostředek pro reprezentaci IL kódu pro jednotlivé akce. V našem příkladu je použit statický datový člen OpCodes.Ret, který reprezentuje IL kód právě pro konec těla metody. Pro ukončení vytvoření nového typu použijeme metodu CreateType třídy TypeBuilder. Nakonec je získána instance třídy System.Type, vytvořena instance a zavolána definovaná metoda. Poznáváme C# a Microsoft.NET 40. díl serializace Dnešní díl se začneme věnovat serializaci. Vysvětlíme si co tento pojem znamená, jak vytvářet serializovatelné typy a jakými způsoby tyto typy serializovat. Co je to serializace? Mnoho instancí, které při svém programování používáme mají celkem krátký život. Jsou nějakou cestou (konstruktorem, skrze reflexi, tovární metodou ) vytvořeny, po té jsou nějakým způsobem použity a pak když nejsou potřeba (neexistuje na ně jakákoliv reference) jsou při úklidu (Garbage collection) Garbage collectorem odstraněny z paměti. Ovšem, ne všechny instance jsou používány takto krátce a některé instance nesoucí potřebná data potřebujeme zachovat na delší dobu. To dokážeme zařídit tak, že 151

153 onu instanci uložíme do nějakého perzistentního úložiště (relační databáze, soubor..). Takovémuto uložení instance do perzistentního úložiště se říká Serializace. V podstatě jde o to, že instance je převedena na Stream, tím pádem může být, jak již bylo řečeno, uložena, ale také díky tomu, že je Streamem, i poslána např. pomocí HTTP protokolu na zcela jiný počítač. Serializovatelné typy, musí být i ty, které jsou používány při takzvaném remotingu, což je možnost používat objekty mezi různými aplikačními doménami. Opačnému procesu, tedy procesu pří kterém je z nějakého proudu vytvořena instance, se naopak říká Deserializace. Jak učinit typy serializovatelnými? K tomu, abychom naše typy učinili serializovatelnými (schopnými se serializovat) vedou dvě cesty. První cestou je použití atributu System.Serializable, což je cesta jednodušší a v obvyklých aplikacích častěji používaná. Druhým způsobem je nechat námi vytvářený typ implementovat rozhraní System.ISerializable. My se dnes budeme zabývat použitím atributu Serializable. V případě použití tohoto atributu na náš typ se o průběh serializace instance tohoto typu kompletně postará běhové prostředí.net runtime. Takže pokud bychom chtěli mít třídu představující osobu a chtěli bychom, aby bylo možné instance této třídy serializovat, provedeme to jednoduše takto : /// Ukazkova trida predstavujici osobu, jejiz instance mohou byt serializovany /// </summary> [Serializable] public class Osoba private DateTime datumnarozeni; private string jmeno; private string prijmeni; public Osoba(string Jmeno, string Prijmeni, DateTime DatumNarozeni) this.jmeno = Jmeno; this.prijmeni = Prijmeni; this.datumnarozeni = DatumNarozeni; //zbytek tridy V případě této výchozí serializace ( = použití atributu Serializable) jsou serializovány všechny datové členy daného typu a to včetně těch soukromých. Je důležité vědět, že všechny typy, kterých jsou datové členy daného typu musí být taktéž serializovatelné. Pokud by tomu tak nebylo a pokusili bychom se o serializaci, došlo by k vyhození výjimky System.Runtime.Serialization.SerializationException. /// Tato trida by nemohla byt serialozovana protoze obahuje datovy clen /// ktery nelze serializovat /// </summary> [Serializable] public class NemoznaSerializace NeserializovatelnyTyp clen; 152

154 public struct NeserializovatelnyTyp //implementace typu I když je třída označena atributem Serializable, její datový člen není a proto ji nelze serializovat. V případě naší ukázkové třídy Osoba, není se serializací problém, protože všechny typy datových členů (String a DateTime), jejichž definici obsahuje, jsou označeny jako serializovatelné. Jak instanci serializovat? Nyní když víme jakým způsobem učinit typ serializovatelným a také jaké je potřeba dodržet pravidla, potřebujeme ještě vědět jak vlastní serializaci provedeme. Ve jmenném prostoru System.Runtime.Serialization se nachází rozhraní IFormatter, které předepisuje funkcionalitu pro formátování serializovaných objektů. Přesněji, obsahuje předpis pro metody Serialize a Deserialize, které zařizují, jak název napovídá, vlastní serializaci respektive deserializaci. V základní knihovně tříd.net frameworku jsou pro naše použití k dispozici dvě základní implementace tohoto rozhraní a to třídy BinaryFormatter a SoapFormatter. BinaryFormatter je implementace, která serializuje a desearilizuje objekty v binárním formátu a nalezneme ji ve jmenném prostoru System.Runtime.Serialization.Formatters.Binary. SoapFormatter zase serializuje a deserializuje objekty do formátu SOAP, což je v podstatě implementace XML a nachází se ve jmenném prostoru System.Runtime.Serialization.Formatters.Soap. Následující příklad demostruje použití serializace instance třídy Osoba do binárního formátu, tedy pomocí třídy BinaryFormatter. public static void Serializace() DateTime ldate = new DateTime(1984,2,9); Osoba losoba = new Osoba("Jan","Novak",lDate); Stream lstream = new FileStream("C:/test.bin",FileMode.Create); IFormatter lformatter = new BinaryFormatter(); try lformatter.serialize(lstream,losoba); 153

Zápis programu v jazyce C#

Zápis programu v jazyce C# Zápis programu v jazyce C# Základní syntaktická pravidla C# = case sensitive jazyk rozlišuje velikost písmen Tzv. bílé znaky (Enter, mezera, tab ) ve ZK překladač ignoruje každý příkaz končí ; oddělovač

Více

Základy jazyka C# Obsah přednášky. Architektura.NET Historie Vlastnosti jazyka C# Datové typy Příkazy Prostory jmen Třídy, rozhraní

Základy jazyka C# Obsah přednášky. Architektura.NET Historie Vlastnosti jazyka C# Datové typy Příkazy Prostory jmen Třídy, rozhraní Základy jazyka C# doc. Ing. Miroslav Beneš, Ph.D. katedra informatiky FEI VŠB-TUO A-1007 / 597 324 213 http://www.cs.vsb.cz/benes Miroslav.Benes@vsb.cz Obsah přednášky Architektura.NET Historie Vlastnosti

Více

8 Třídy, objekty, metody, předávání argumentů metod

8 Třídy, objekty, metody, předávání argumentů metod 8 Třídy, objekty, metody, předávání argumentů metod Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost třídám a objektům, instančním

Více

1. Dědičnost a polymorfismus

1. Dědičnost a polymorfismus 1. Dědičnost a polymorfismus Cíl látky Cílem této kapitoly je představit klíčové pojmy dědičnosti a polymorfismu. Předtím však je nutné se seznámit se základními pojmy zobecnění neboli generalizace. Komentář

Více

Generické programování

Generické programování Generické programování Od C# verze 2.0 = vytváření kódu s obecným datovým typem Příklad generická metoda, zamění dva parametry: static void Swap(ref T p1, ref T p2) T temp; temp = p1; p1 = p2; p2 =

Více

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e); TŘÍDY POKRAČOVÁNÍ Události pokračování public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e); class Bod private int x; private int y; public event ZmenaSouradnicEventHandler ZmenaSouradnic;

Více

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15 Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15 KAPITOLA 1 Úvod do programo vání v jazyce C++ 17 Základní pojmy 17 Proměnné a konstanty 18 Typy příkazů 18 IDE integrované vývojové

Více

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech 7 Formátovaný výstup, třídy, objekty, pole, chyby v programech Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost formátovanému výstupu,

Více

Výčtový typ strana 67

Výčtový typ strana 67 Výčtový typ strana 67 8. Výčtový typ V této kapitole si ukážeme, jak implementovat v Javě statické seznamy konstant (hodnot). Příkladem mohou být dny v týdnu, měsíce v roce, planety obíhající kolem slunce

Více

24-2-2 PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: 23.7.2013 KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

24-2-2 PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: 23.7.2013 KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1 24-2-2 PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE AUTOR DOKUMENTU: MGR. MARTINA SUKOVÁ DATUM VYTVOŘENÍ: 23.7.2013 KLÍČOVÁ AKTIVITA: 02 UČIVO: STUDIJNÍ OBOR: PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) INFORMAČNÍ TECHNOLOGIE

Více

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39 Programování v C# Hodnotové datové typy, řídící struktury Petr Vaněček 1 / 39 Obsah přednášky Referenční datové typy datové položky metody přístupové metody accessory, indexery Rozhraní Pole 2 / 39 Třídy

Více

11.5.2012. Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

11.5.2012. Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9 Obsah přednášky 9 Základy programování (IZAPR, IZKPR) Přednáška 9 Základy dědičnosti, přístupová práva Ing. Michael Bažant, Ph.D. Katedra softwarových technologií Kancelář č. 03 022, Náměstí Čs. legií

Více

Úvod Seznámení s předmětem Co je.net Vlastnosti.NET Konec. Programování v C# Úvodní slovo 1 / 25

Úvod Seznámení s předmětem Co je.net Vlastnosti.NET Konec. Programování v C# Úvodní slovo 1 / 25 Programování v C# Úvodní slovo 1 / 25 Obsah přednášky Seznámení s předmětem Co je.net Vlastnosti.NET 2 / 25 Kdo je kdo Petr Vaněček vanecek@pf.jcu.cz J 502 Václav Novák vacnovak@pf.jcu.cz?? Při komunikaci

Více

PŘETĚŽOVÁNÍ OPERÁTORŮ

PŘETĚŽOVÁNÍ OPERÁTORŮ PŘETĚŽOVÁNÍ OPERÁTORŮ Jazyk C# podobně jako jazyk C++ umožňuje přetěžovat operátory, tj. rozšířit definice některých standardních operátorů na uživatelem definované typy (třídy a struktury). Stejně jako

Více

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace Předmět: Vývoj aplikací Téma: Visual Studio Vyučující: Ing. Milan Káža Třída: EK3 Hodina: 19,2 Číslo: V/5 Programování

Více

PREPROCESOR POKRAČOVÁNÍ

PREPROCESOR POKRAČOVÁNÍ PREPROCESOR POKRAČOVÁNÍ Chybová hlášení V C# podobně jako v C++ existuje direktiva #error, která způsobí vypsání chybového hlášení překladačem a zastavení překladu. jazyk C# navíc nabízí direktivu #warning,

Více

Seznámení s prostředím dot.net Framework

Seznámení s prostředím dot.net Framework Základy programování v jazyce C# Seznámení s prostředím dot.net Framework PL-Prostředí dot.net - NET Framework Je základním stavebním prvkem, na kterém lze vytvářet software. Jeho součásti a jádro je založené

Více

Předmluva k aktuálnímu vydání Úvod k prvnímu vydání z roku Typografické a syntaktické konvence... 20

Předmluva k aktuálnímu vydání Úvod k prvnímu vydání z roku Typografické a syntaktické konvence... 20 Obsah 5 Obsah Předmluva k aktuálnímu vydání 15 1 Úvod k prvnímu vydání z roku 2000 16 Typografické a syntaktické konvence................ 20 2 Základní pojmy 21 2.1 Trocha historie nikoho nezabije................

Více

14.4.2010. Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

14.4.2010. Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod. Základy programování (IZAPR) Přednáška 7 Ing. Michael Bažant, Ph.D. Katedra softwarových technologií Kancelář č. 229, Náměstí Čs. legií Michael.Bazant@upce.cz Obsah přednášky 7 Parametry metod, předávání

Více

Seminář Java II p.1/43

Seminář Java II p.1/43 Seminář Java II Seminář Java II p.1/43 Rekapitulace Java je case sensitive Zdrojový kód (soubor.java) obsahuje jednu veřejnou třídu Třídy jsou organizovány do balíků Hierarchie balíků odpovídá hierarchii

Více

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007 Úvod do programovacích jazyků (Java) Michal Krátký 1 Katedra informatiky VŠB Technická univerzita Ostrava Úvod do programovacích jazyků (Java), 2006/2007 c 2006 Michal Krátký Úvod do programovacích jazyků

Více

Úvod do programovacích jazyků (Java)

Úvod do programovacích jazyků (Java) Úvod do programovacích jazyků (Java) Michal Krátký Katedra informatiky VŠB Technická univerzita Ostrava Úvod do programovacích jazyků (Java), 2007/2008 c 2006 2008 Michal Krátký Úvod do programovacích

Více

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007 Úvod do programovacích jazyků (Java) Michal Krátký 1 Katedra informatiky VŠB Technická univerzita Ostrava Úvod do programovacích jazyků (Java), 2006/2007 c 2006 Michal Krátký Úvod do programovacích jazyků

Více

7. Datové typy v Javě

7. Datové typy v Javě 7. Datové typy v Javě Primitivní vs. objektové typy Kategorie primitivních typů: integrální, boolean, čísla s pohyblivou řádovou čárkou Pole: deklarace, vytvoření, naplnění, přístup k prvkům, rozsah indexů

Více

3. Je defenzivní programování technikou skrývání implementace? Vyberte jednu z nabízených možností: Pravda Nepravda

3. Je defenzivní programování technikou skrývání implementace? Vyberte jednu z nabízených možností: Pravda Nepravda 1. Lze vždy z tzv. instanční třídy vytvořit objekt? 2. Co je nejčastější příčinou vzniku chyb? A. Specifikace B. Testování C. Návrh D. Analýza E. Kódování 3. Je defenzivní programování technikou skrývání

Více

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/04.0006

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/04.0006 Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/04.0006 Lekce 1 Jazyk Java Tento projekt je spolufinancován Evropským sociálním fondem

Více

1. Programování proti rozhraní

1. Programování proti rozhraní 1. Programování proti rozhraní Cíl látky Cílem tohoto bloku je seznámení se s jednou z nejdůležitější programátorskou technikou v objektově orientovaném programování. Tou technikou je využívaní rozhraní

Více

Programování v C++ 2, 4. cvičení

Programování v C++ 2, 4. cvičení Programování v C++ 2, 4. cvičení statické atributy a metody, konstruktory 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019 Přehled Přístupová práva

Více

Dědičnost (inheritance)

Dědičnost (inheritance) Dědičnost (inheritance) Úvod Umožňuje objektům převzít (zdědit) členy jiných objektů a pouze je rozšířit o Auto: lze odvodit Vztah je osobní auto, cisterna jsou auta Základní pojmy Bázová třída (rodič)

Více

Úvod do programovacích jazyků (Java)

Úvod do programovacích jazyků (Java) Úvod do programovacích jazyků (Java) Michal Krátký Katedra informatiky VŠB Technická univerzita Ostrava Úvod do programovacích jazyků (Java), 2007/2008 c 2006 2008 Michal Krátký Úvod do programovacích

Více

IRAE 07/08 Přednáška č. 1

IRAE 07/08 Přednáška č. 1 Úvod do předmětu OOP Objekt Proč OOP? Literatura, osnova předmětu viz. cvičení Základní prvek OOP sw inženýrství = model reálných objektů (věcí) člověk, auto, okno (ve windows), slovník, = model abstraktní

Více

Programování v C++ 3, 3. cvičení

Programování v C++ 3, 3. cvičení Programování v C++ 3, 3. cvičení úvod do objektově orientovaného programování 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019 Přehled Dokončení spojového

Více

ADT/ADS = abstraktní datové typy / struktury

ADT/ADS = abstraktní datové typy / struktury DT = datové typy obor hodnot, které může proměnná nabývat, s operacemi na tomto oboru určen: obor hodnot + výpočetní operace např. INT = { 2 147 483 648 až +2 147 483 647} + {+,,*,/,} ADT/ADS = abstraktní

Více

Programování v C++ 1, 6. cvičení

Programování v C++ 1, 6. cvičení Programování v C++ 1, 6. cvičení dědičnost, polymorfismus 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019 Přehled 1 2 3 Shrnutí minule procvičené

Více

Proměnná. Datový typ. IAJCE Cvičení č. 3. Pojmenované místo v paměti sloužící pro uložení hodnoty.

Proměnná. Datový typ. IAJCE Cvičení č. 3. Pojmenované místo v paměti sloužící pro uložení hodnoty. Proměnná Pojmenované místo v paměti sloužící pro uložení hodnoty. K pojmenování můžeme použít kombinace alfanumerických znaků, včetně diakritiky a podtržítka Rozlišují se velká malá písmena Název proměnné

Více

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ 1) PROGRAM, ZDROJOVÝ KÓD, PŘEKLAD PROGRAMU 3 2) HISTORIE TVORBY PROGRAMŮ 3 3) SYNTAXE A SÉMANTIKA 3 4) SPECIFIKACE

Více

NPRG031 Programování II --- 2/2 Z, Zk

NPRG031 Programování II --- 2/2 Z, Zk NPRG031 Programování II --- 2/2 Z, Zk paralelka Y St 14:00-15:30 v S3 Pavel Töpfer Kabinet software a výuky informatiky MFF UK MFF Malostranské nám., 4. patro, pracovna 404 pavel.topfer@mff.cuni.cz http://ksvi.mff.cuni.cz/~topfer

Více

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue.

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue. 23. Třídy, generické třídy, instance, skládání, statické metody a proměnné. Zapouzdření, konstruktory, konzistence objektu, zpřístupnění vnitřní implementace, modifikátory public a private. Polymorfismus,

Více

Jazyk C# (seminář 5)

Jazyk C# (seminář 5) Jazyk C# (seminář 5) Pavel Procházka KMI 23. října 2014 Přetěžování metod motivace Představme si, že máme metodu, která uvnitř dělá prakticky to samé, ale liší se pouze parametry V C# můžeme více metod

Více

IRAE 07/08 Přednáška č. 2. atr1 atr2. atr1 atr2 -33

IRAE 07/08 Přednáška č. 2. atr1 atr2. atr1 atr2 -33 Objekt jako proměnná Objekty a metody Objekt = proměnná referenčního typu vznik pomocí new, chování viz pole jako referenční proměnná minulý semestr Stack Heap objekt ref this 10 20 atr1 atr2 jinyobjekt

Více

1 - Úvod do platformy.net. IW5 - Programování v.net a C#

1 - Úvod do platformy.net. IW5 - Programování v.net a C# 1 - Úvod do platformy.net IW5 - Programování v.net a C# Strana 1 Obsah přednášky Objektově orientované paradigma.net Framework Základní rysy jazyka C# Strana 2 Objektová orientace C# implementuje základní

Více

Ukazka knihy z internetoveho knihkupectvi www.kosmas.cz

Ukazka knihy z internetoveho knihkupectvi www.kosmas.cz Ukazka knihy z internetoveho knihkupectvi www.kosmas.cz Upozornění pro čtenáře a uživatele této knihy Všechna práva vyhrazena. Žádná část této tištěné či elektronické knihy nesmí být reprodukována a šířena

Více

typová konverze typová inference

typová konverze typová inference Seminář Java Programování v Javě II Radek Kočí Fakulta informačních technologií VUT Únor 2008 Radek Kočí Seminář Java Programování v Javě (2) 1/ 36 Téma přednášky Rozhraní: použití, dědičnost Hierarchie

Více

Mnohotvarost (polymorfizmus)

Mnohotvarost (polymorfizmus) Mnohotvarost (polymorfizmus) TYPY MNOHOTVAROSTI... 2 PŘETĚŽOVÁNÍ METOD, PŘETĚŽOVÁNÍ OPERACÍ... 3 PŘETÍŽENÍ OPERÁTORŮ... 4 ČASTO PŘETĚŽOVANÉ OPERÁTORY... 4 PŘEPISOVÁNÍ... 7 VIRTUÁLNÍ METODY... 10 SEZNAM

Více

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd 7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd Algoritmizace (Y36ALG), Šumperk - 7. přednáška 1 Třída jako zdroj funkcionality Třída v jazyku Java je programová jednotka tvořená

Více

Obsah. Úvod 11 Základy programování 11 Objektový přístup 11 Procvičování 11 Zvláštní odstavce 12 Zpětná vazba od čtenářů 12 Errata 13

Obsah. Úvod 11 Základy programování 11 Objektový přístup 11 Procvičování 11 Zvláštní odstavce 12 Zpětná vazba od čtenářů 12 Errata 13 Úvod 11 Základy programování 11 Objektový přístup 11 Procvičování 11 Zvláštní odstavce 12 Zpětná vazba od čtenářů 12 Errata 13 KAPITOLA 1 Na úvod o Javě 15 Počítačový program 15 Vysokoúrovňový programovací

Více

Programování v Javě I. Únor 2009

Programování v Javě I. Únor 2009 Seminář Java Programování v Javě I Radek Kočí Fakulta informačních technologií VUT Únor 2009 Radek Kočí Seminář Java Programování v Javě (1) 1/ 44 Téma přednášky Datové typy Deklarace třídy Modifikátory

Více

10 Balíčky, grafické znázornění tříd, základy zapozdření

10 Balíčky, grafické znázornění tříd, základy zapozdření 10 Balíčky, grafické znázornění tříd, základy zapozdření Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost příkazům balíčkům, grafickému

Více

Programování v Javě I. Leden 2008

Programování v Javě I. Leden 2008 Seminář Java Programování v Javě I Radek Kočí Fakulta informačních technologií VUT Leden 2008 Radek Kočí Seminář Java Programování v Javě (1) 1/ 45 Téma přednášky Datové typy Deklarace třídy Modifikátory

Více

Objektově orientované programování

Objektově orientované programování 10. října 2011 Pragmatické informace Volitelný předmět, zápočet: zápočtový program(s dokumentací), aktivní účast na cvičení(body v CodExu), praktický test, zkouška: zkoušková písemka na objektový návrh

Více

První kapitola úvod do problematiky

První kapitola úvod do problematiky První kapitola úvod do problematiky Co je to Flex Adobe Flex je ActionSript (AS) framework pro tvorbu Rich Internet Aplications (RIA), tedy knihovna AS tříd pro Flash. Flex používáme k vytvoření SWF souboru

Více

Jazyk C# (seminář 3)

Jazyk C# (seminář 3) Jazyk C# (seminář 3) Pavel Procházka KMI October 8, 2014 Motivace Největší využití v programování okenních aplikací a GUI knihoven. Data reprezentujeme pomocí objektů (tříd), máme tedy ucelený pohled na

Více

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members)

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members) Osnova přednášky Programové prostředky řízení Úvod do C# II. Členy (Members) Jmenné prostory (Namespaces) Třídy (Classes) Struktury (Structs) Pavel Balda Západočeská univerzita v Plzni, FAV, KKY 2 Členy

Více

PB161 Programování v jazyce C++ Přednáška 7

PB161 Programování v jazyce C++ Přednáška 7 PB161 Programování v jazyce C++ Přednáška 7 Statické položky tříd Základy OOP Nikola Beneš 6. listopadu 2018 PB161 přednáška 7: static, základy OOP 6. listopadu 2018 1 / 21 Klíčové slovo static Znáte z

Více

Funkční objekty v C++.

Funkční objekty v C++. Funkční objekty v C++. Funkční objekt je instance třídy, která má jako svou veřejnou metodu operátor (), tedy operátor pro volání funkce. V dnešním článku si ukážeme jak zobecnit funkci, jak používat funkční

Více

PB161 Programování v jazyce C++ Přednáška 7

PB161 Programování v jazyce C++ Přednáška 7 PB161 Programování v jazyce C++ Přednáška 7 Statické položky tříd Základy OOP Nikola Beneš 6. listopadu 2018 PB161 přednáška 7: static, základy OOP 6. listopadu 2018 1 / 21 Klíčové slovo static Znáte z

Více

Programování v C++ 1, 5. cvičení

Programování v C++ 1, 5. cvičení Programování v C++ 1, 5. cvičení konstruktory, nevirtuální dědění 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019 Přehled 1 2 3 Shrnutí minule procvičené

Více

Jazyk C# (seminář 6)

Jazyk C# (seminář 6) Jazyk C# (seminář 6) Pavel Procházka KMI 29. října 2014 Delegát motivace Delegáty a události Jak docílit v C# funkcionální práce s metodami v C je to pomocí pointerů na funkce. Proč to v C# nejde pomocí

Více

Logické operace. Datový typ bool. Relační operátory. Logické operátory. IAJCE Přednáška č. 3. může nabýt hodnot: o true o false

Logické operace. Datový typ bool. Relační operátory. Logické operátory. IAJCE Přednáška č. 3. může nabýt hodnot: o true o false Logické operace Datový typ bool může nabýt hodnot: o true o false Relační operátory pravda, 1, nepravda, 0, hodnoty všech primitivních datových typů (int, double ) jsou uspořádané lze je porovnávat binární

Více

Objektové programování

Objektové programování Objektové programování - přináší nové možnosti a styl programování - vytváří nový datový typ, který umí vše co standardní datové typy + to co ho naučíme - překladač se k tomuto typu chová stejně jako k

Více

Paměť počítače. alg2 1

Paměť počítače. alg2 1 Paměť počítače Výpočetní proces je posloupnost akcí nad daty uloženými v paměti počítače Data jsou v paměti reprezentována posloupnostmi bitů (bit = 0 nebo 1) Připomeňme: paměť je tvořena řadou 8-mi bitových

Více

Základy objektové orientace I. Únor 2010

Základy objektové orientace I. Únor 2010 Seminář Java Základy objektové orientace I Radek Kočí Fakulta informačních technologií VUT Únor 2010 Radek Kočí Seminář Java Základy OO (1) 1/ 20 Téma přednášky Charakteristika objektově orientovaných

Více

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám.

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám. 13 Rozhraní, výjimky Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám. Doba nutná k nastudování 2 2,5 hodiny

Více

URČITÝM ZPŮSOBEM PODOBNÉ

URČITÝM ZPŮSOBEM PODOBNÉ Objekty Svět se skládá z objektů! konkrétní x abstraktní hmatatelné x nehmatatelné (letadlo) x (chyba v programu) Objekty mohou obsahovat jiné objekty (tělo obsahuje buňky, letadlo součásti). Objekty URČITÝM

Více

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace Předmět: Vývoj aplikací Téma: Datové typy Vyučující: Ing. Milan Káža Třída: EK3 Hodina: 5 Číslo: V/5 Programování v jazyce

Více

VÝUKOVÝ MATERIÁL. Bratislavská 2166, 407 47 Varnsdorf, IČO: 18383874 www.vosassvdf.cz, tel. +420412372632 Číslo projektu

VÝUKOVÝ MATERIÁL. Bratislavská 2166, 407 47 Varnsdorf, IČO: 18383874 www.vosassvdf.cz, tel. +420412372632 Číslo projektu VÝUKOVÝ MATERIÁL Identifikační údaje školy Vyšší odborná škola a Střední škola, Varnsdorf, příspěvková organizace Bratislavská 2166, 407 47 Varnsdorf, IČO: 18383874 www.vosassvdf.cz, tel. +420412372632

Více

Programové konvence, dokumentace a ladění. Programování II 2. přednáška Alena Buchalcevová

Programové konvence, dokumentace a ladění. Programování II 2. přednáška Alena Buchalcevová Programové konvence, dokumentace a ladění 2. přednáška Alena Buchalcevová Proč dodržovat programové konvence? velkou část životního cyklu softwaru tvoří údržba údržbu provádí většinou někdo jiný než autor

Více

11 Diagram tříd, asociace, dědičnost, abstraktní třídy

11 Diagram tříd, asociace, dědičnost, abstraktní třídy 11 Diagram tříd, asociace, dědičnost, abstraktní třídy Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost diagramům tříd, asociaci,

Více

Platforma.NET 11.NET Framework 11 Visual Basic.NET Základní principy a syntaxe 13

Platforma.NET 11.NET Framework 11 Visual Basic.NET Základní principy a syntaxe 13 Obsah Úvod 11 Platforma.NET 11.NET Framework 11 Visual Basic.NET 12 1 Základní principy a syntaxe 13 Typový systém 13 Hodnotové typy 13 Struktury 15 Výčtové typy 15 Referenční typy 15 Konstanty 16 Deklarace

Více

NPRG031 Programování II 1 / :47:55

NPRG031 Programování II 1 / :47:55 NPRG031 Programování II 1 / 43 23. 2. 2016 11:47:55 Objekty Svět se skládá z objektů! Objekt = data + funkce (metody) konkrétní x abstraktní hmatatelné x nehmatatelné (letadlo) x (chyba v programu) Objekty

Více

Abstraktní třída a rozhraní

Abstraktní třída a rozhraní Abstraktní třída a rozhraní Někdy se může stát, zejména při psaní v hierarchické struktuře hodně nadřazených tříd, že tušíme, že bude ve zděděných třídách vhodné použít nějakou metodu. Tuto metodu ještě

Více

Dědění, polymorfismus

Dědění, polymorfismus Programování v jazyce C/C++ Ladislav Vagner úprava Pavel Strnad Dědění. Polymorfismus. Dnešní přednáška Statická a dynamická vazba. Vnitřní reprezentace. VMT tabulka virtuálních metod. Časté chyby. Minulá

Více

Algoritmizace a programování

Algoritmizace a programování Algoritmizace a programování Výrazy Operátory Výrazy Verze pro akademický rok 2012/2013 1 Operace, operátory Unární jeden operand, operátor se zapisuje ve většině případů před operand, v některých případech

Více

Lekce 6 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ JAZYK C

Lekce 6 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ JAZYK C Identifikační údaje školy Číslo projektu Název projektu Číslo a název šablony Autor Tematická oblast Číslo a název materiálu Anotace Vyšší odborná škola a Střední škola, Varnsdorf, příspěvková organizace

Více

C++ přetěžování funkcí a operátorů. Jan Hnilica Počítačové modelování 19

C++ přetěžování funkcí a operátorů. Jan Hnilica Počítačové modelování 19 C++ přetěžování funkcí a operátorů 1 Přetěžování funkcí jazyk C++ umožňuje napsat více funkcí se stejným názvem, těmto funkcím říkáme přetížené přetížené funkce se musí odlišovat typem nebo počtem parametrů,

Více

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března Polymorfismus Cíle lekce Cílem lekce je vysvětlit význam pojmu polymorfismus jako základní vlastnosti objektově orientovaného programování. Lekce objasňuje vztah časné a pozdní vazby a jejich využití.

Více

Úvod do programování - Java. Cvičení č.4

Úvod do programování - Java. Cvičení č.4 Úvod do programování - Java Cvičení č.4 1 Sekvence (posloupnost) Sekvence je tvořena posloupností jednoho nebo více příkazů, které se provádějí v pevně daném pořadí. Příkaz se začne provádět až po ukončení

Více

SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ

SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ Vstupy a výstupy pokračování Kódování textů Texty (řetězce nebo znaky) v jazyce C# jsou v paměti uloženy v kódování označovaném běžně Unicode (kódová stránka 1200).

Více

5 Přehled operátorů, příkazy, přetypování

5 Přehled operátorů, příkazy, přetypování 5 Přehled operátorů, příkazy, přetypování Studijní cíl Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně budou uvedeny detaily týkající se operátorů. Doba nutná k nastudování

Více

Teoretické minimum z PJV

Teoretické minimum z PJV Teoretické minimum z PJV Pozn.: následující text popisuje vlastnosti jazyka Java zjednodušeně pouze pro potřeby výuky. Třída Zavádí se v programu deklarací třídy což je část programu od klíčových slov

Více

Úvod do programování. Lekce 1

Úvod do programování. Lekce 1 Úvod do programování Lekce 1 Základní pojmy vytvoření spustitelného kódu editor - psaní zdrojových souborů preprocesor - zpracování zdrojových souborů (vypuštění komentářů atd.) kompilátor (compiler) -

Více

NMIN201 Objektově orientované programování 2016/17 1 / :03:29

NMIN201 Objektově orientované programování 2016/17 1 / :03:29 NMIN201 Objektově orientované programování 2016/17 1 / 42 21. 11. 2016 11:03:29 Objekty Svět se skládá z objektů! konkrétní x abstraktní hmatatelné x nehmatatelné (letadlo) x (chyba v programu) Objekty

Více

PROGRAMOVÁNÍ PRO MS WINDOWS 1

PROGRAMOVÁNÍ PRO MS WINDOWS 1 Cílem předmětu je seznámit posluchače s moderními prostředky pro tvorbu aplikací pro Windows jmenovitě s programovou platformou.net a programovacím jazykem C#. V průběhu budou vysvětlena následující témata:

Více

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007 Úvod do programovacích jazyků (Java) Michal Krátký Katedra informatiky VŠB Technická univerzita Ostrava Úvod do programovacích jazyků (Java), 2006/2007 c 2006 Michal Krátký Úvod do programovacích jazyků

Více

Připravil: Ing. Vít Ondroušek, Ph.D. Technologie.Net Framework

Připravil: Ing. Vít Ondroušek, Ph.D. Technologie.Net Framework Připravil: Ing. Vít Ondroušek, Ph.D. Technologie.Net Framework úvod, historie, základy.net framework, programovací jazyky, vývojové prostředky Úvod strana 2 Cíl předmětu Seznámit se s vývojem aplikací

Více

NMIN201 Objektově orientované programování 1 / :36:09

NMIN201 Objektově orientované programování 1 / :36:09 NMIN201 Objektově orientované programování 1 / 26 8.10.2013 15:36:09 Objekty Svět se skládá z objektů! konkrétní x abstraktní hmatatelné x nehmatatelné (letadlo) x (chyba v programu) Objekty mohou obsahovat

Více

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

IB111 Programování a algoritmizace. Objektově orientované programování (OOP) IB111 Programování a algoritmizace Objektově orientované programování (OOP) OP a OOP Objekt Kombinuje data a funkce a poskytuje určité rozhraní. OP = objektové programování Vše musí být objekty Např. Smalltalk,

Více

III/2 Inovace a zkvalitnění výuky prostřednictvím ICT

III/2 Inovace a zkvalitnění výuky prostřednictvím ICT Číslo a název šablony Číslo didaktického materiálu Druh didaktického materiálu Autor Jazyk Téma sady didaktických materiálů Téma didaktického materiálu Vyučovací předmět Cílová skupina (ročník) Úroveň

Více

Programování v jazyce C a C++

Programování v jazyce C a C++ Programování v jazyce C a C++ Richter 1 Petyovský 2 1. března 2015 1 Ing. Richter Miloslav, Ph.D., UAMT FEKT VUT Brno 2 Ing. Petyovský Petr, UAMT FEKT VUT Brno C++ Stručná charakteristika Nesdíĺı normu

Více

Algoritmizace a programování

Algoritmizace a programování Algoritmizace a programování Řídicí struktury jazyka Java Struktura programu Příkazy jazyka Blok příkazů Logické příkazy Ternární logický operátor Verze pro akademický rok 2012/2013 1 Struktura programu

Více

2 Datové typy v jazyce C

2 Datové typy v jazyce C 1 Procedurální programování a strukturované programování Charakteristické pro procedurální programování je organizace programu, který řeší daný problém, do bloků (procedur, funkcí, subrutin). Původně jednolitý,

Více

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody Dynamická alokace paměti Jazyky C a C++ poskytují programu možnost vyžádat si část volné operační paměti pro

Více

Programovací jazyk Pascal

Programovací jazyk Pascal Programovací jazyk Pascal Syntaktická pravidla (syntaxe jazyka) přesná pravidla pro zápis příkazů Sémantická pravidla (sémantika jazyka) pravidla, která každému příkazu přiřadí přesný význam Všechny konstrukce

Více

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Maturitní otázky z předmětu PROGRAMOVÁNÍ Wichterlovo gymnázium, Ostrava-Poruba, příspěvková organizace Maturitní otázky z předmětu PROGRAMOVÁNÍ 1. Algoritmus a jeho vlastnosti algoritmus a jeho vlastnosti, formy zápisu algoritmu ověřování správnosti

Více

IRAE 07/08 Přednáška č. 7. Začátek (head)

IRAE 07/08 Přednáška č. 7. Začátek (head) Fronta (Queue) FIFO First In First Out (první dovnitř první ven) Vložení položky (Enqueue) Vyjmutí položky (Dequeue) Přidávání prvků Konec (Tail) Začátek (head) na jedné straně (konec) Odebírání prvků

Více

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky JAVA Třídy Definice třídy úplná definice [public] [abstract] [final] class Jmeno [extends Predek] [impelements SeznamInterfacu] {... // telo tridy public veřejná třída abstract nesmí být vytvářeny instance

Více

Jazyk C++ 1. Blok 3 Objektové typy jazyka C++ Třída. Studijní cíl. Doba nutná k nastudování. Průvodce studiem

Jazyk C++ 1. Blok 3 Objektové typy jazyka C++ Třída. Studijní cíl. Doba nutná k nastudování. Průvodce studiem Jazyk C++ 1 Blok 3 Objektové typy jazyka C++ Studijní cíl Ve třetím bloku bude představen a rozebrán nejdůležitější objektový typ jazyka C++ a to sice třída. Po absolvování bloku bude student schopen navrhovat

Více

přetížení operátorů (o)

přetížení operátorů (o) přetížení operátorů (o) - pro vlastní typy je možné přetížit i operátory (tj. definovat vlastní) - pro definici slouží klíčové slovo operator následované typem/znakem operátoru - deklarace pomocí funkčního

Více

Pokud neuvedeme override, vznikne v synu nová (nevirtuální) metoda (a pochopitelně se nezavolá, jak bychom

Pokud neuvedeme override, vznikne v synu nová (nevirtuální) metoda (a pochopitelně se nezavolá, jak bychom Poznámky k virtuálním metodám aneb co když zkoušíme překladač zlobit... Pokud neuvedeme override, vznikne v synu nová (nevirtuální) metoda (a pochopitelně se nezavolá, jak bychom chtěli). Pokud neuvedeme

Více