E N C Y K L O P E D I E
Z O N E R
P R E S S
C# 4.0 Ŏešení praktických programátorských úloh
Ben
Watson
C# 4.0 řešení praktických programátorských úloh
Ben Watson
C# 4.0 HOW-TO Ben Watson Authorized translation from the English language edition, entitled C# 4.0 HOW-TO, 1st Edition, 0672330636 by WATSON, BEN, published by Pearson Education, Inc, publishing as Sams Publishing, Copyright © 2010 by Ben Watson. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc. CZECH language edition published by ZONER SOFTWARE, a.s., Copyright © 2010. Autorizovaný překlad originálního anglického vydání knihy C# 4.0 HOW-TO, vydání první, ISBN 0672330636, autor WATSON, BEN, vydal Pearson Education, Inc, ve vydavatelství Sams Publishing, Copyright © 2010 Ben Watson. Všechna práva vyhrazena. Žádná část této publikace nesmí být reprodukována nebo předávána žádnou formou nebo způsobem, elektronicky ani mechanicky, včetně fotokopií, natáčení ani žádnými jinými systémy pro ukládání bez výslovného svolení Pearson Education, Inc. České vydání vydal ZONER SOFTWARE, a.s., Copyright © 2010.
C# 4.0 – řešení praktických programátorských úloh Autor: Ben Watson Copyright © ZONER software, a.s. Vydání první v roce 2010. Všechna práva vyhrazena. Zoner Press Katalogové číslo: ZR1015 ZONER software, a.s. Nové sady 18, 602 00 Brno Překlad: RNDr. Jan Pokorný Odborný redaktor: Miroslav Kučera Technický redaktor: Hana Fruhwirtová Šéfredaktor: Ing. Pavel Kristián Obálka a fotografie na obálce: Lenka Křížová DTP: Miroslav Kučera
Informace, které jsou v této knize zveřejněny, mohou být chráněny jako patent. Jména produktů byla uvedena bez záruky jejich volného použití. Při tvorbě textů a vyobrazení bylo sice postupováno s maximální péčí, ale přesto nelze zcela vyloučit možnost výskytu chyb. Vydavatelé a autoři nepřebírají právní odpovědnost ani žádnou jinou záruku za použití chybných údajů a z toho vyplývajících důsledků. Všechna práva vyhrazena. Žádná část této publikace nesmí být reprodukována ani distribuována žádným způsobem ani prostředkem, ani reprodukována v databázi či na jiném záznamovém prostředku či v jiném systému bez výslovného svolení vydavatele, s výjimkou zveřejnění krátkých částí textu pro potřeby recenzí. Veškeré dotazy týkající se distribuce směřujte na: Zoner Press ZONER software, a.s Nové sady 18, 602 00 Brno tel.: 532 190 883 e-mail: knihy@zoner.cz www.zonerpress.cz
ISBN 978-80-7413-094-6
3
Stručný obsah Část I – Základy C#
21
Kapitola 1
Základy typů
23
Kapitola 2
Vytváření univerzálních typů
43
Kapitola 3
Obecné zásady psaní kódu
61
Kapitola 4
Výjimky
79
Kapitola 5
Čísla
93
Kapitola 6
Výčty
115
Kapitola 7
Řetězce
125
Kapitola 8
Regulární výrazy
147
Kapitola 9
Generiky
155
Část II – Zpracování dat
169
Kapitola 10
Kolekce
171
Kapitola 11
Soubory a serializace
193
Kapitola 12
Sítě a web
217
Kapitola 13
Databáze
253
Kapitola 14
XML
277
Část III – Interakce s uživatelem
293
Kapitola 15
Delegáti, události a anonymní metody
295
Kapitola 16
Formuláře Windows
309
Kapitola 17
Grafika s formuláři Windows a GDI+
343
Kapitola 18
Windows Presentation Foundation (WPF)
379
Kapitola 19
ASP.NET
417
Kapitola 20
Silverlight
457
Část IV – Pokročilé funkce C#
473
Kapitola 21
LINQ
475
Kapitola 22
Správa paměti
487
Kapitola 23
Vlákna, asynchronní a paralelní programování
505
Kapitola 24
Reflexe a vytváření pluginů
533
Kapitola 25
Aplikační vzory a tipy
543
Kapitola 26
Interakce s operačním systémem a hardwarem
589
Kapitola 27
Ostatní záležitosti
609
Dodatek A
Nepostradatelné nástroje
633
4
Podrobný obsah Úvod
18
Část I – Základy C# Kapitola 1
Základy typů
21 23
Vytvoření třídy
24
Definice členských proměnných, vlastností a metod
25
Definice statických členů
27
Konstruktory
27
Inicializace vlastností při jejich konstrukci
29
Klíčová slova const a readonly
29
Opětovné využívání kódu ve více konstruktorech
30
Odvozování z třídy
31
Volání konstruktoru základní třídy
31
Překrytí metody nebo vlastnosti základní třídy
32
Vytvoření rozhraní
35
Implementace rozhraní
36
Vytvoření struktury
38
Vytvoření anonymního typu
39
Použití abstraktní základní třídy pro zamezení vytváření instancí
40
Rozhraní, nebo abstraktní základní třída?
41
Kapitola 2
Vytváření univerzálních typů
43
Formátování typu s ToString()
44
Jak určit, zda jsou typy shodné?
48
Jak na hašování typů s GetHashCode()?
50
Jak na seřazování typů?
50
Přidělování indexu typům
52
Oznamování klientům, že se něco změnilo
54
Přetěžování operátorů
55
Převod typů na jiné typy
56
5 Jak zabránit dědění
57
Povolení null u hodnotového typu
58
Kapitola 3
Obecné zásady psaní kódu
61
Deklarace proměnných
62
Odložení kontrol typů na dobu běhu (dynamické typy)
63
Dynamické typování pro zjednodušení interoperability s COM
65
Deklarace polí
66
Vytváření vícerozměrných polí
66
Alias jmenného prostoru
67
Podmínkový operátor (:?)
68
Podmínkový operátor (??) zahrnující null
69
Přidávání metod do existujících typů pomocí rozšiřujících metod
70
Volání metod s výchozími parametry
72
Volání metod s pojmenovanými parametry
73
Odložené vytvoření hodnoty až do okamžiku, kdy se na ni odkazuje
73
Vynucení kódových kontraktů
75
Kapitola 4
Výjimky
79
Generování výjimky
80
Zachycení výjimky
80
Zachycení více výjimek
81
Opětovné vygenerování výjimky
82
(Téměř) zaručené vykonání s finally
83
Získávání užitečných informací z výjimek
84
Vytvoření vlastní třídy výjimky
86
Zachycení nezpracovaných výjimek
88
Rady pro používání výjimek
92
Kapitola 5
Čísla
93
Volba mezi Float, Double a Decimal
94
Enormně velká celá čísla (BigInteger)
95
Komplexní čísla
96
Formátování čísel v řetězci
98
6 Převod řetězce na číslo
102
Převody mezi číselnými soustavami
103
Převod čísla na bajty (a naopak)
106
Jak určit, zdali je celé číslo sudé?
107
Jak určit, zdali je celé číslo mocnina dvou?
107
Jak určit, zdali je číslo prvočíslem?
108
Jak určit počet jednotkových bitů?
108
Převod stupňů na radiány
109
Zaokrouhlování čísel
110
Generování více náhodných čísel
112
Generování jedinečných identifikátorů (GUID)
113
Kapitola 6
Výčty
115
Deklarace výčtu
116
Deklarace indikátorů jako výčet
117
Určení, zdali je nastaven konkrétní indikátor
118
Převod výčtu na celé číslo (a naopak)
118
Určení, zda je výčet platný
119
Získání seznamu hodnot výčtu
119
Převod řetězce na výčet
119
Připojování metadat k výčtům pomocí rozšiřujících metod
120
Tipy pro práci s výčty
123
Kapitola 7
Řetězce
125
Převod řetězce na bajty (a naopak)
126
Vytvoření vlastního kódovacího schématu
127
Řádné porovnávání řetězců
131
Změna velikosti písmen
132
Detekce prázdných řetězců
133
Řetězení řetězců: měli byste používat StringBuilder?
133
Řetězení prvků kolekce do řetězce
135
Přidávání znaků pro nový řádek
136
Převod řetězce na pole řetězců
136
Převod binárních dat na řetězec (datový formát Base64)
138
7 Opačné pořadí slov
140
Přirozené seřazení řetězců obsahujících čísla
141
Kapitola 8
Regulární výrazy
147
Vyhledávání textu
148
Extrakce skupin textu
149
Nahrazování textu
149
Shoda a validace
150
Zvyšování výkonu regulárních výrazů
153
Kapitola 9
Generiky
155
Vytvoření generického seznamu
156
Vytvoření generické metody
157
Vytvoření generického rozhraní
158
Vytvoření generické třídy
159
Vytvoření generického delegáta
161
Používání více generických typů najednou
162
Omezování generického typu
162
Převod IEnumerable<Child> na IEnumerable<Parent> (kovariance)
165
Převod IComparer<Child> na IComparer<Parent>(kontravariance)
167
Vytváření entic (dvojice, trojice atd.)
168
Část II – Zpracování dat Kapitola 10
Kolekce
169 171
Výběr správné třídy pro kolekci
172
Inicializace kolekce
174
Iterace přes kolekci nezávisle na její implementaci
174
Vytvoření vlastní kolekce
175
Vytváření vlastních iterátorů pro kolekci
179
Opačné pořadí prvků pole
183
Opačné pořadí prvků propojeného seznamu
183
Získání jedinečných prvků z kolekce
184
8 Četnosti prvků v kolekci
185
Implementace prioritní fronty
186
Vytvoření prefixového stromu (Trie)
189
Kapitola 11
Soubory a serializace
193
Vytváření, čtení a zapisování souborů
194
Odstranění souboru
196
Kombinování proudů (komprimace souboru)
197
Získání velikosti souboru
199
Získání bezpečnostních informací o souboru
199
Kontrola existence souboru a adresáře
201
Seznam jednotek
201
Seznam adresářů a souborů
202
Výběr adresáře procházením
203
Vyhledání souboru nebo adresáře
204
Manipulace s cestami souborů
206
Vytváření jedinečných nebo dočasných názvů souborů
208
Sledování změn souborového systému
208
Získání cest ke složkám Dokumenty, Obrázky atd.
210
Serializace objektů
211
Serializace do proudu v paměti
214
Ukládání dat, když má aplikace omezená oprávnění
214
Kapitola 12
Sítě a web
217
Překlad názvu hostitele do IP adresy
218
Získání názvu hostitele a IP adresy aktuálního stroje
218
Ping na nějaký stroj
219
Získání informací o síťové kartě
220
Vytvoření klienta a serveru TCP/IP
221
Odeslání e-mailu přes SMTP
224
Stahování webového obsahu přes HTTP
225
Nahrání souboru z FTP serveru
229
Odstranění HTML značek z textu
230
Vložení webového prohlížeče do aplikace
231
9 Konzumace RSS kanálu
232
Dynamické generování kanálu RSS v IIS
236
Komunikace mezi procesy na tomtéž stroji (WCF)
238
Komunikace mezi dvěma stroji na téže síti (WCF)
245
Komunikace přes internet (WCF)
247
Odhalování služeb za běhu (WCF)
249
Kapitola 13
Databáze
253
Vytvoření nové databáze ve Visual Studiu
254
Připojení k databázi a získávání dat
256
Vkládání dat do databázové tabulky
261
Odstraňování dat z tabulky
262
Spuštění uložené procedury
263
Transakce
264
Vázání dat k ovládacímu prvku pomocí sady dat
266
Detekce, zda je databázové připojení dostupné
273
Automatické mapování dat na objekty s knihovnou Entity Framework
274
Kapitola 14
XML
277
Serializace objektu do XML a z XML
278
Zápis XML úplně od začátku
282
Čtení XML souboru
284
Validace XML dokumentu
286
Dotazy na XML s XPath
288
Transformace databázových dat do XML
289
Transformace XML do HTML
290
Část III – Interakce s uživatelem Kapitola 15
Delegáti, události a anonymní metody
293 295
Rozhodnutí až při běhu, která metoda se má zavolat
296
Jak se přihlásit k odběru události
298
Publikace události
299
10 Uživatelské rozhraní se musí aktualizovat na jeho vláknu
301
Přiřazení anonymní metody delegátovi
303
Použití anonymní metody jako zpracovatele událostí
304
Jak těžit z výhod kontravariance
306
Kapitola 16
Formuláře Windows
309
Modální a nemodální formuláře
310
Pruh nabídek
311
Dynamické vypínání prvků nabídek
314
Stavový řádek
314
Panel nástrojů
315
Vytvoření rozhraní pro rozdělení okna
316
Dědění formuláře
318
Uživatelský ovládací prvek
322
Časovač
327
Konfigurační hodnoty pro aplikaci a uživatele
328
Efektivní práce s ListView ve virtuálním režimu
331
Vodorovné posouvání nakláněním kolečka myši
333
Vyjímání a vkládání do schránky
337
Obnovení čekacího kurzoru do původní podoby
342
Kapitola 17
Grafika s formuláři Windows a GDI+
343
Definice barev
344
Systémový dialog pro výběr barev
344
Převod barev mezi RGB a HSV
345
Kreslení obrazců
349
Pera
352
Vlastní štětce
354
Transformace
356
Kreslení textu
358
Kreslení textu šikmo
358
Kreslení obrázků
359
Kreslení průhledných obrázků
360
Kreslení do bufferu mimo obrazovku
360
11 Přímý přístup k pixelům bitmapy pro lepší výkon
361
Kreslení s vyhlazováním hran
363
Kreslení bez problikávání
364
Změna velikosti obrázku
365
Vytvoření miniatury obrázku
365
Snímání obsahu obrazovky v prostředí s několika monitory
367
Vzdálenost kurzoru myši od daného bodu
369
Je bod uvnitř daného obdélníka?
370
Je bod uvnitř dané kružnice?
370
Je bod uvnitř dané elipsy?
371
Mají dva obdélníky neprázdný průnik?
372
Tisk a náhled před tiskem
372
Kapitola 18
Windows Presentation Foundation (WPF)
379
Zobrazení okna
380
Volba metody rozvržení
381
Pruh nabídek
381
Stavový řádek
383
Panel nástrojů
383
Standardní příkazy
384
Vlastní příkazy
385
Vypínání a zapínání příkazů
388
Rozbalování a sbalování skupiny ovládacích prvků
389
Reakce na události
390
Oddělení vzhledu od funkcionality
391
Změna stylů při běhu pomocí triggerů
392
Vázání vlastností ovládacího prvku k jinému objektu
393
Formátování hodnot během vázání dat
398
Převod hodnot na jiný typ během vázání dat
398
Vázání ke kolekci
399
Definice způsobu pro zobrazování vázaných dat
400
Definice vzhledu ovládacích prvků pomocí šablon
401
Animace vlastností prvku
402
Realizace trojrozměrné geometrie
403
12 Video na trojrozměrném povrchu
406
Interaktivní ovládací prvky na trojrozměrném povrchu
409
WPF v aplikacích formulářů Windows
413
Formuláře Windows v aplikaci WPF
414
Kapitola 19
ASP.NET
417
Prohlížení ladicích a sledovacích informací
418
Zjištění schopností webového prohlížeče
420
Přesměrování na jinou stránku
421
Formulářová autentizace pro přihlašování uživatelů
423
Jednotný vzhled se vzorovými stránkami
426
Přidání navigačního menu
427
Vázání dat ke GridView
428
Vytvoření uživatelského ovládacího prvku
430
Tvorba flexibilního uživatelského rozhraní s Web Parts
434
Vytvoření jednoduché ajaxové stránky
439
Ověřování platnosti dat
441
Udržování stavu aplikace
445
Udržování stavu uživatelského rozhraní
446
Udržování uživatelských dat v relaci
447
Uložení stavu relace
449
Použití cookies pro obnovení stavu relace
450
ASP.NET Model-View-Controller (MVC)
451
Kapitola 20
Silverlight
457
Vytvoření projektu Silverlightu
458
Přehrávání videa
459
Sestavení ukazatele průběhu pro stahování a přehrávání
463
Reakce na události časovače na vláknu uživatelského rozhraní
465
Obsah v trojrozměrné perspektivě
466
Běh aplikace bez webového prohlížeče
467
Zobrazení výstupu z webové kamery
469
Tisk dokumentu
471
13
Část IV – Pokročilé funkce C# Kapitola 21
LINQ
473 475
Dotaz na objekt kolekce
476
Seřazení výsledků
478
Filtrování kolekce
478
Získání kolekce částí objektů (projekce)
479
Slučování tabulek
479
Dotaz na XML
480
Vytvoření XML
481
Dotazování s Entity Framework
481
Dotaz na webovou službu (LINQ to Bing)
483
Urychlování dotazů s PLINQ (paralelní LINQ)
486
Kapitola 22
Správa paměti
487
Kolik paměti používá aplikace
488
Úklid neřízených prostředků pomocí finalizace
489
Úklid řízených prostředků pomocí vzoru propouštění
491
Vynucený svoz odpadků
496
Vytvoření cache, která umožňuje svoz odpadu
496
Ukazatele
500
Urychlení přístupu k poli
501
Zamezení přesunů paměti
502
Alokace neřízené paměti
503
Kapitola 23
Vlákna, asynchronní a paralelní programování
505
Snadná dělba práce mezi procesory
506
Používání datových struktur ve více vláknech
510
Asynchronní volání metody
510
Fond vláken
512
Vytvoření vlákna
512
Výměna dat s vláknem
513
Ochrana dat používaných ve více vláknech
515
14 Použití metody třídy Interlocked místo zámků
517
Ochrana dat ve více procesech
518
Omezení aplikace na jedinou instanci
520
Omezení počtu vláken, která mohou přistupovat k prostředku
520
Signalizace vláknům událostmi
523
Vícevláknový časovač
526
Zámek typu Reader-Writer
527
Použití asynchronního programovacího modelu
529
Kapitola 24
Reflexe a vytváření pluginů
533
Jmenovitý seznam typů v assembly
534
Přidání vlastního atributu
535
Dynamické vytvoření instance třídy
537
Vyvolání metody na dynamicky vytvořené instanci třídy
537
Implementace pluginové architektury
539
Kapitola 25
Aplikační vzory a tipy
543
Stopky pro potřeby profilace kódu
544
Vyznačování zastaralého kódu
545
Zkombinování událostí do jediné události
546
Implementace vzoru pozorovatel
551
Zprostředkovatel událostí
554
Uložení pozice aplikace na obrazovce
558
Implementace funkce Zpět pomocí objektů příkazů
559
Vzor Model-View-ViewModel ve WPF
567
Lokalizace
577
Lokalizace aplikace formulářů Windows
578
Lokalizace aplikace ASP.NET
579
Lokalizace aplikace WPF
581
Lokalizace aplikace Silverlightu
585
Nasazení aplikace do provozu s ClickOnce
587
Kapitola 26
Interakce s operačním systémem a hardwarem
Verze operačního systému, opravného balíčku a CLR
589 590
15 Informace o CPU a dalším hardwaru
590
Vyžádání oprávnění správce vyvoláním Řízení uživatelských účtů
593
Zapisování do protokolu událostí
595
Přístup k registru
597
Správa služeb Windows
599
Vytvoření služby Windows
599
Volání nativních funkcí Windows pomocí P/Invoke
602
Volání funkcí C v DLL z C#
603
Paměťově mapované soubory
604
Zajištění, aby aplikace pracovala v 32bitovém i 64bitovém prostředí
605
Reakce na změny v konfiguraci systému
606
Využití předností funkcí Windows 7
607
Informace o stavu napájení
608
Kapitola 27
Ostatní záležitosti
609
Okno, které není obdélníkové
610
Ikona pro oznamovací oblast
614
Spořič obrazovky ve WPF
617
Úvodní obrazovka
626
Přehrání zvukového souboru
631
Míchání karet
632
Dodatek A
Nepostradatelné nástroje
633
Reflector
634
NUnit
635
NDepend
638
FXCop
639
Virtual PC
640
Process Explorer a Process Monitor
641
RegexBuddy
642
LINQPad
643
Kde najdete další nástroje
644
Rejstřík
645
16
Věnování Rodičům, Michaelu a Dianně Watsonovým, kteří se mi v mých pubertálních letech přinejmenším dvakrát podvolili, když jsem jim podrobně zdůvodňoval, proč by mi měli koupit software kompilátoru C++, a kteří dělali všechno, co bylo v jejich silách, aby rozvíjeli to, na co jsem měl nadání a co mě zajímalo. Bez jejich vzoru, lásky a rad bych nikdy ničeho nedosáhl. Mé báječné ženě Leticii, která byla nanejvýš trpělivá a laskavá, když jsem se pustil do nového a strastiplného projektu během těch všech ostatních změn v našem životě.
Poděkování Jsem velmi vděčný lidem, kteří sešli ze své cesty proto, aby mi pomohli s touto knihou: Brooku Farlingovi (editor), že mě našel a dal mi tuto příležitost, za veškerou jeho koordinač-
ní práci, trpělivost a tah na branku, aby se podařilo knihu vydat. Marku Strawmayerovi (korektor), že prošel kód a text a zajistil kvalitu toho, co je v knize. Publikačnímu
Sheri Cain.
týmu u Sams, což byl Mark Renfrow, Lori Lyons, Bart Reed, Nonie Ratcliff,
17
O autorovi Ben Watson vyvíjí v .NET od chvíle, kdy bylo vydáno. Než se přidal k Microsoftu, pracoval jako vůdčí vývojář pro firmu GeoEye, kde v .NET vyvinul komunikační systémy pro námořnictvo. Naposled pracoval v týmu Query Pipeline pro Bing, kde pomáhal navrhovat a implementovat masivně škálovatelné distribuované systémy a další interní záležitosti tohoto vyhledávače.
Sdělte nám svůj názor Jako čtenáři této knihy se stáváte těmi nejdůležitějšími kritiky a komentátory. Vážíme si vašeho názoru a chtěli bychom vědět, co děláme správně, co bychom mohli dělat lépe, ve kterých oblastech bychom měli publikovat, a také vaše další podnětné myšlenky, o které jste ochotni se podělit. Jako odborný redaktor Zoner Press vítám vaše názory. Můžete mi psát – poslat e-mail nebo dopis – a sdělit mi, co se vám v této knize líbilo nebo nelíbilo, stejně tak co bychom měli udělat, aby naše další knihy byly lepší. Pokud mi napíšete, nezapomeňte, prosím, připojit název knihy, ISBN, jméno autora, vaše jméno, telefon, fax nebo e-mail. Pozorně zhodnotím vaše názory a poskytnu je všem lidem, kteří pracovali na této knize. Prosím, vězte, že nemohu pomoci s technickými problémy, které se týkají obsahu knihy, a že díky velkému množství e-mailů, jež dostávám, nemohu zaručit odpověď na každou zprávu. E-mail: miroslav.kucera@zoner.cz nebo knihy@zoner.cz. Adresa: Zoner Press ZONER software, a.s. Miroslav Kučera Nové sady 18, 602 00 Brno
Zdrojové soubory Zdrojové soubory k této knize lze stáhnout z následující adresy: http://zonerpress.cz/download/C_4_0_reseni_praktickych_uloh.zip
Velikost souboru ke stažení je cca 31 MB.
18
Úvod Tato kniha přistupuje ke svému tématu hodně odlišně než typické programovací příručky. Tím, že je strukturována do návodů ve formě praktických postupů ve stylu "jak na to", předkládá látku ve scénářích rozložených na kroky, které se snadno reprodukují. V jednotlivých návodech jsem se snažil omezit vysvětlující text na nezbytné minimum, abych se mohl plně soustředit na samotný kód. V kódech nechybí komentáře, které vysvětlují jeho netriviální úseky. Kniha není pouhou strohou učebnicí jazyka či knihovnou. Neprobírají se zde pouze samotné funkce jazyka, uvidíte propracované praktické ukázky aplikačních vzorů, užitečné algoritmy i řadu šikovných tipů, prostě všechno, co můžete využít v mnoha situacích. Vývojáři, jak začínající, tak i pokročilí, najdou v této knize stovky užitečných témat. Ať už je to sekce o méně známých operátorech C#, nebo jak seřazovat řetězce obsahující čísla, nebo jak implementovat funkci Zpět (Undo), kniha prostě obsahuje hotová řešení, která jsou užitečná v mnoha rozličných situacích, bez ohledu na to, jakou úrovní dovedností je vývojář vybaven. Zkrátka, je to ten druh knihy, který jsem chtěl mít na svém stole neustále po ruce, když jsem se poprvé učil programovat a zároveň jazyk C#, vždy, když jsem si něco potřeboval rychle ověřit nebo si připomenout, jak se dělá to či ono.
Jak z knihy vytěžit co nejvíc Tuto knihu jsem navrhl tak, aby se snadno četla po jednotlivých tématech. Mým cílem je, abyste plně porozuměli jazyku C# 4.0. Předmět výkladu je rozdělen do čtyř částí, které jsou složeny z kapitol, v nichž se snadno orientuje a snadno se s nimi pracuje. Část I, "Základy C#", se věnuje běžné funkcionalitě C#, kterou budete využívat pokaždé, bez ohledu na to, co budete programovat. Ačkoliv se někomu může zdát jako příliš elementární, obsahuje spoustu užitečných rad, takže budete schopni vytěžit maximum i ze základních témat. Kapitola 1 – Základy typů Kapitola 2 – Vytváření univerzálních typů Kapitola 3 – Obecné zásady psaní kódu Kapitola 4 – Výjimky Kapitola 5 – Čísla Kapitola 6 – Výčty Kapitola 7 – Řetězce Kapitola 8 – Regulární výrazy Kapitola 9 – Generiky
19 V části II, "Zpracování dat", detailně prodiskutujeme problematiku ukládání dat a manipulace s nimi (včetně práce s daty umístěnými někde na internetu). Kapitola 10 – Kolekce Kapitola 11 – Soubory a serializace Kapitola 12 – Sítě a web Kapitola 13 – Databáze Kapitola 14 – XML
V části III, "Interakce s uživatelem", se probírají nejoblíbenější paradigmata uživatelského rozhraní v .NET, ať už pracujete na své ploše, na webech, nebo děláte obojí. Kapitola 15 – Delegáti, události a anonymní metody Kapitola 16 – Formuláře Windows Kapitola 17 – Grafika s formuláři Windows a GDI+ Kapitola 18 – Windows Presentation Foundation (WPF) Kapitola 19 – ASP.NET Kapitola 20 – Silverlight
V části IV, "Pokročilé funkce C#", se probírají pokročilejší témata. Jedná se o látku, s jejíž pomocí dostanete své aplikace na kvalitativně vyšší úroveň, pokud jde o výkon, návrhové vzory, užitečné algoritmy a další aspekty. Kapitola 21 – LINQ Kapitola 22 – Správa paměti Kapitola 23 – Vlákna, asynchronní a paralelní programování Kapitola 24 – Reflexe a vytváření pluginů Kapitola 25 – Aplikační vzory a tipy Kapitola 26 – Interakce s operačním systémem a s hardwarem Kapitola 27 – Ostatní záležitosti Dodatek A – Nepostradatelné nástroje
Veškerý kód jsme naprogramovali ve Visual Studiu 2010, v mnoha případech ovšem vystačíte i s dřívějšími verzemi, zejména jedná-li se o kód, který nepožaduje .NET 4. Nemáte-li Visual Studio, můžete si ze stránek www.microsoft.com/express/default.aspx stáhnout verzi Express. S touto verzí budete schopni sestavit prakticky všechny ukázky kódu uvedené v této knize.
20
Jak dále rozvíjet své vědomosti Žádná samostatná kniha nemůže plně pokrýt C# a .NET Framework. Dokonce ani nemůže doufat, že se jí podaří vyčerpávajícím způsobem probrat nějaké dílčí téma z tohoto světa. A i kdyby taková kniha existovala, patrně byste ji vůbec neunesli a celý svůj život strávili její četbou. Jakmile však zvládnete základy, existuje spousta zdrojů, v nichž můžete najít odpovědi na své další otázky, a hlouběji se ponořit do .NET. MSDN dokumentace pro .NET, která je umístěna na http://msdn.microsoft.com/en-us/ aa139615.aspx, je naprosto prvotřídní. U většiny témat najdete nejenom ukázky kódu, ale i vysvětlení toho, jak se používají. Jako bonus navíc má kdokoliv možnost přidat na konec každého tématu užitečný obsah. Najde se tam spousta dobrých tipů od ostatních vývojářů .NET. Vývojářská fóra .NET, viz adresa http://social.msdn.microsoft.com/Forums/en-US/category/netdevelopment, jsou vynikajícím místem, kde můžete získat odpovědi na své otázky od expertů, z nichž mnozí se podíleli na vývoji a testování .NET. Zjistil jsem také, že dobrým místem pro otázky je web StackOverflow.com. Nejlepší rada, kterou vám ohledně rozvíjení vašich vědomostí mohu dát, je ta, abyste prostě psali software. Vydržte u toho, přemýšlejte o nových projektech, používejte nové technologie, snažte se překračovat své momentální schopnosti. Tato i jiné knihy jsou velmi užitečné, ale pochopitelně jen do jisté hranice. Pak už nezbude nic jiného, než abyste do toho skočili po hlavě a začali psát kód. Při této činnosti vám kniha vždy poslouží jako spolehlivá referenční příručka, nebudete-li náhodou vědět, jak se pustit do nějakého tématu. Mnoho štěstí při kódování!
21
ČÁST I Základy C# Obsah této části: Kapitola 1
Základy typů
Kapitola 2
Vytváření univerzálních typů
Kapitola 3
Obecné zásady psaní kódu
Kapitola 4
Výjimky
Kapitola 5
Čísla
Kapitola 6
Výčty
Kapitola 7
Řetězce
Kapitola 8
Regulární výrazy
Kapitola 9
Generiky
23
KAPITOLA 1 Základy typů Obsah této kapitoly: Vytvoření třídy Definice členských proměnných, vlastností a metod Definice statických členů Konstruktory Inicializace vlastností při jejich konstrukci Klíčová slova const a readonly Opětovné využívání kódu ve více konstruktorech Odvozování z třídy Volání konstruktoru základní třídy Překrytí metody nebo vlastnosti základní třídy Vytvoření rozhraní Implementace rozhraní Vytvoření struktury Vytvoření anonymního typu Použití abstraktní základní třídy pro zamezení vytváření instancí Rozhraní, nebo abstraktní základní třída?
24
Kapitola 1 – Základy typů
V této kapitole si vysvětlíme základy, jak vytvářet typy v C#. Jestliže už něco o C# víte, poslouží většina kapitoly jako rekapitulace. Poté, co probereme členy třídy, mezi něž patří členské proměnné, vlastnosti a metody, dozvíte se něco více o konstruktorech a vytváření (a implementaci) rozhraní. Také se dozvíte, kdy se mají používat struktury. Ačkoliv informace obsažené v této kapitole jsou základní, lze je označit za důležité. Podobně jako u mnoha jiných věcí i zde platí, že pokud nemáte v malíčku základy, nikdy nic neuděláte pořádně.
Vytvoření třídy Scénář/problém. Potřebujete vytvořit deklaraci nějaké třídy.
Řešení. Začněme něčím jednoduchým. Deklarujme třídu, která bude obsahovat souřadnice bodu v trojrozměrném prostoru. // Výchozí jmenné prostory, které se mají importovat, // Visual Studio je zařadí do každého souboru using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ClassSample { // Veřejná, takže bude viditelná vně assembly public class Vertex3d { } }
Třída, kterou jsme nadefinovali, je prozatím prázdná, ale v této kapitole ji postupně naplníme. Třída je definovaná jako veřejná (public), což znamená, že je viditelná pro jakýkoliv další typ, který se odkazuje na její assembly. Programovací jazyk C# definuje několik modifikátorů přístupu, které jsou podrobně uvedeny v tabulce 1.1. Tabulka 1.1. Modifikátory přístupu. Přístup
Aplikovatelné na
Popis
Veřejný (public)
Typy, členy
Přístupné všem, dokonce i vně assembly
Privátní (private)
Typy, členy
Přístupné kódu v tomtéž typu
Interní (internal)
Typy, členy
Přístupné kódu v tomtéž assembly
C# 4.0 – řešení praktických programátorských úloh
25
Přístup
Aplikovatelné na
Popis
Chráněný (protected)
Členy
Přístupné kódu v tomtéž typu nebo odvozeném typu
Chráněný interní (protected internal)
Členy
Přístupné kódu v tomtéž assembly nebo odvozené třídě v jiném assembly
Jestliže třída nespecifikuje svou přístupnost, výchozí je internal.
Definice členských proměnných, vlastností a metod Scénář/problém. Potřebujete do definice třídy přidat členské proměnné, vlastnosti a metody.
Řešení. Přidejte základní prvky třídy Vertex3d, aby mohla být k něčemu užitečná. public class Vertex3d { // Členské proměnné (fields) private double _x; private double _y; private double _z; // Vlastnosti public double X { get { return _x; } set { _x = value; } } public double Y { get { return _y; } set { _y = value; } } public double Z { get { return _z; } set { _z = value; } } // Metoda public void SetToOrigin() { X = Y = Z = 0.0;
Kapitola 1 – Základy typů
26 } }
Pár poznámek k předchozímu kódu: Všechny
členské proměnné jsou deklarovány jako privátní (private), což se obecně dopo-
ručuje. Ačkoli vlastnosti jsou deklarovány jako veřejné (public), mohou být také privátní (priva-
te), chráněné (protected) nebo chráněné interní (protected internal), jak si přejete. Vlastnosti mohou mít get, set, nebo obojí. Ve vlastnostech je value implicitní argument (neboli předpokládaný v kódu).
V následujícím příkladu by se do X předala hodnota 13.0 v argumentu value. Vertex3d v = new Vertex3d(); v.X = 13.0;
Automaticky implementované vlastnosti Často uvidíte kód tohoto druhu: class MyClass { private int _field = 0; public int Field { get { return _field; } { set { _field = value; } } }
Pro něj má C# zkrácený zápis: class MyClass { public int Field { get; set; } // Hodnota se nyní musí inicializovat v konstruktoru public MyClass() { this.Field = 0; } }
Poznámka. Nemůžete mít automaticky implementovanou vlastnost, která by měla pouze get (pokud byste neměli žádnou podpůrnou členskou proměnnou, jakým způsobem byste nastavili vlastnost?), nicméně může mít privátní set: public int Field { get; private set; }
C# 4.0 – řešení praktických programátorských úloh
27
Definice statických členů Scénář/problém. Potřebujete definovat data nebo metody, které se aplikují na typ, nikoliv na jednotlivé instance. Často se také využívají u metod, které operují na více instancích daného typu.
Řešení. Přidejte klíčové slovo static, viz následující metoda, která sčítá dva objekty typu Vertex3d. public class Vertex3d { ... public static Vertex3d Add(Vertex3d a, Vertex3d b) { Vertex3d result = new Vertex3d(); result.X = a.X + b.X; result.Y= a.Y + b.Y; result.Z = a.Z + b.Z; return result; } }
Statická metoda se volá takto: Vertex3d a = new Vertex3d(0,0,1); Vertex3d b = new Vertex3d(1,0,1); Vertex3d sum = Vertex3d.Add(a,b);
Konstruktory Scénář/problém. Potřebujete automaticky inicializovat nové objekty třídy.
Řešení. Definujte speciální metodu, které se říká konstruktor. Má stejný název jako třída a nemá žádný návratový typ. Konstruktor se spustí poté, co se vytvořil typ; nikdy se nevolá přímo. Podívejte se nyní na dva konstruktory pro třídu Vertex3d – jeden přebírá argumenty, druhý provádí jistou výchozí inicializaci. class Vertex3d { public Vertex3d()
Kapitola 1 – Základy typů
28 {
_x = _y = _z = 0.0; } public Vertex3d(double x, double y, double z) { this._x = x; this._y = y; this._z = z; } }
Poznámka. Konstruktory nemusejí být veřejné. Například můžete označit konstruktor jako chráněný (protected), čímž bude přístupný pouze z odvozených tříd. Dokonce můžete mít privátní konstruktor, aby se zabránilo vytváření instancí (u pomocných tříd), nebo aby byl přístupný pouze z jiných metod téže třídy (patrně půjde o statické tovární metody).
Statický konstruktor a inicializace Scénář/problém. Třída má statická data, která je potřeba inicializovat.
Řešení. Statické členské proměnné se dají inicializovat dvěma způsoby. Prvním způsobem je statický konstruktor, což je obdoba standardního konstruktoru, ovšem s tím rozdílem, že nemá modifikátory přístupu a ani argumenty: public class Vertex3d { private static int _numInstances; static Vertex3d() { _numInstances = 0; } ... }
Kvůli lepšímu výkonu se ovšem dává vždy, kdykoliv je to možné, přednost inicializaci statických členských proměnných, jak to vidíte zde: public class Vertex3d { private static int _numInstances = 0; }
217
KAPITOLA 12 Sítě a web Obsah této kapitoly: Překlad názvu hostitele do IP adresy Získání názvu hostitele a IP adresy aktuálního stroje Ping na nějaký stroj Získání informací o síťové kartě Vytvoření klienta a serveru TCP/IP Odeslání e-mailu přes SMTP Stahování webového obsahu přes HTTP Nahrání souboru z FTP serveru Odstranění HTML značek z textu Vložení webového prohlížeče do aplikace Konzumace RSS kanálu Dynamické generování kanálu RSS v IIS Komunikace mezi procesy na tomtéž stroji (WCF) Komunikace mezi dvěma stroji na téže síti (WCF) Komunikace přes internet (WCF) Odhalování služeb za běhu (WCF)
218
Kapitola 12 – Sítě a web
Jen výjimečně dnes najdeme aplikaci, která nemá žádnou funkcionalitu spojenou s internetem. Softwarový svět je stále více a více propojený a u většiny softwaru se očekává, že bude mít nějakou síťovou funkcionalitu, i kdyby to mělo být jen něco tak triviálního, jako je kontrola aktualizací. Ačkoliv k dobrým řešením síťových úloh vede trnitá cesta, tato kapitola vás posune o hezký kus dopředu. Předvedu vám několik úloh, z nichž několik lze označit za vysloveně základní.
Překlad názvu hostitele do IP adresy Scénář/problém. Potřebujete přeložit název hostitele, jako je microsoft.com, do jeho IP adres.
Řešení. Hodně funkcionality, se kterou se setkáte v této dvanácté kapitole, obsahuje jmenný prostor System.Net (včetně tak základních věcí, jako jsou třídy IPAddress a Dns). string host = "www.microsoft.com"; // Připomínám, že hostitel může mít více IP adres IPAddress[] addresses = Dns.GetHostAddresses(host); foreach (IPAddress addr in addresses) { Console.Write("\t{0}", addr); }
Podívejte se na výstup z ukázkového běhu pro několik běžných hostitelských názvů: www.microsoft.com 207.46.19.254 207.46.19.190 www.live.com 204.2.160.40 204.2.160.49 www.google.com 208.67.219.230 208.67.219.231 www.yahoo.com 209.131.36.158
Získání názvu hostitele a IP adresy aktuálního stroje Scénář/problém. Chcete získat hostitelský název a IP adresy aktuálního stroje.
Řešení. K řešení z předchozí sekce stačí přidat volání, kterým získáte název aktuálního hostitele.
C# 4.0 – řešení praktických programátorských úloh
219
string hostname = Dns.GetHostName(); Console.WriteLine("Název hostitele: {0}", hostname); IPAddress[] addresses = Dns.GetHostAddresses(hostname); foreach (IPAddress addr in addresses) { Console.WriteLine("IP adresa: {0} ({1})", addr.ToString(), addr.AddressFamily); }
A takhle vypadá výstup na mém stroji. Povšimněte si v seznamu IPv6 adres: Název hostitele: Ben-Desktop IP adresa: fe80::2c4c:372:e7ee:35b7%14 (InterNetworkV6) IP adresa: fe80::c1aa:9268:a7f0:a203%8 (InterNetworkV6) IP adresa: 192.168.1.2 (InterNetwork) IP adresa: 2001:0:4137:9e50:2c4c:372:e7ee:35b7 (InterNetworkV6)
Ping na nějaký stroj Scénář/problém. Potřebujete detekovat, zdali jsou síť nebo hostitel dostupní.
Řešení. .NET obsahuje rozhraní, které umožňuje udělat tzv. ping. Toto rozhraní podporuje synchronní a asynchronní operace: System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping(); System.Net.NetworkInformation.PingReply reply = ping.Send("yahoo.com"); Console.WriteLine("adresa: {0}", reply.Address); Console.WriteLine("volby: don’t fragment: {0}, TTL: {1}", reply.Options.DontFragment, reply.Options.Ttl); Console.WriteLine("cesta tam a zpět: {0}ms", reply.RoundtripTime); Console.WriteLine("stav: {0}", reply.Status);
Poznámka. Mnohé sítě jsou nakonfigurovány tak, že nepovolují ping vně svých lokálních sítí. Při používání této funkcionality tak musíte zachytávat PingException a prohlížet vnitřní výjimky, abyste se dozvěděli konkrétní příčinu nezdaru. Když se podíváte do dokumentace MSDN na metodu Send(), naleznete další možné druhy výjimek.
220
Kapitola 12 – Sítě a web
Výstup vypadá takto: adresa: 69.147.114.224 volby: don’t fragment: False, TTL: 49 cesta tam a zpět: 111ms stav: Success
Získání informací o síťové kartě Scénář/problém. Chcete získat nízkoúrovňové informace o všech síťových adaptérech v počítači.
Řešení. Tuto funkčnost nabízí třída System.Net.NetworkInformation.NetworkInterface. NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface nic in nics) { // V zásadě jde jen o to, dotázat se na houf vlastností Console.WriteLine("ID: {0}", nic.Id); Console.WriteLine("Název: {0}", nic.Name); Console.WriteLine("Fyzická adresa: {0}", nic.GetPhysicalAddress()); IPInterfaceProperties props = nic.GetIPProperties(); PrintIPCollection("DHCP Servery: ", props.DhcpServerAddresses); }
Kompletní kód najdete v projektu NicInfo ve zdrojových kódech pro tuto kapitolu. A takto vypadá výstup pro jeden adaptér: ID: {6B124BB0-CFBE-4DA8-831E-3FD323733CD4} Název: Local Area Connection Popis: Marvell Yukon 88E8053 PCI-E Gigabit Ethernet Controller Typ: Ethernet Stav: Up Rychlost: 100000000 Podporuje Multicast: True Jen přijímat: False Fyzická adresa: 000129A4C39B DHCP Servery: DNS Servery: 208.67.222.222 208.67.220.220
C# 4.0 – řešení praktických programátorských úloh
221
Vytvoření klienta a serveru TCP/IP Scénář/problém. Jste v situaci, kdy existující přenosový protokol nevyhovuje vašim potřebám. Pro vysvětlení – protokol HTTP může být příliš zjednodušující, SOAP příliš těžký a RPC příliš složitý. Také nemůžete použít ani WCF (Windows Communication Foundation). V takové situaci budete muset navrhnout svůj vlastní protokol aplikační úrovně, který bude postaven na TCP/IP.
Řešení. V .NET se klient i server TCP/IP vytvoří velmi lehce. Pusťme se nejprve do vytvoření serveru, viz výpis 12.1. Jeho logika je snadná na pochopení: Spustíme cyklus. V něm čekáme na nějaké připojení. Jakmile se připojení uskuteční, vytvoříme vlákno, abychom je mohli zpracovat. Získáme zprávu. Odešleme odpověď. Uzavřeme připojení a vrátíme se z vlákna.
Výpis 12.1. TCP server. using System; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading; namespace TcpServer { class Program { static void Main(string[] args) { IPAddress localhost = IPAddress.Parse("127.0.0.1"); TcpListener listener = new System.Net.Sockets.TcpListener(localhost, 1330); listener.Start(); while (true) { Console.WriteLine("Čekáme na připojení"); // AcceptTcpClient čeká na nějaké připojení z klienta TcpClient client = listener.AcceptTcpClient(); // Nastartujeme nové vlákno, abychom mohli zpracovat toto připojení, // takže můžeme jít zpět a čekat na dalšího klienta
Kapitola 12 – Sítě a web
222
Thread thread = new Thread( new ParameterizedThreadStart(HandleClientThread)); thread.Start(client); } } static void HandleClientThread(object obj) { TcpClient client = obj as TcpClient; bool done = false; while (!done) { string received = ReadMessage(client); Console.WriteLine("Přijato: {0}", received); done = received.Equals("bye"); if (done) SendResponse(client, "BYE"); else SendResponse(client, "OK"); } client.Close(); Console.WriteLine("Připojení uzavřeno"); } private static string ReadMessage(TcpClient client) { byte[] buffer = new byte[256]; int totalRead = 0; // Čte bajty, dokud proud neindikuje, že další nejsou do { int read = client.GetStream().Read(buffer, totalRead, buffer.Length - totalRead); totalRead += read; } while (client.GetStream().DataAvailable); return Encoding.Unicode.GetString(buffer, 0, totalRead); } private static void SendResponse(TcpClient client, string message) { // Zajistěte, aby se na druhém konci dekódovalo se stejným formátem! byte[] bytes = Encoding.Unicode.GetBytes(message); client.GetStream().Write(bytes, 0, bytes.Length); } } }
C# 4.0 – řešení praktických programátorských úloh
223
Jediný způsob, jak se dá zastavit běh tohoto serveru, je použít klávesovou zkratku Ctrl+C (což je v podstatě standard pro konzolové servery). Nyní se pustíme do klienta, viz výpis 12.2. Jeho logika je neméně průzračná: Připojíme se k serveru. Běháme v cyklu, dokud nedostaneme signál, že máme skončit. Odešleme zprávu. Přečteme odpověď. Je-li odpověď "BYE", signalizuje to konec.
Výpis 12.2. TCP klient. using System; using System.Text; using System.Net; using System.Net.Sockets; namespace TcpClientTest { class Program { static void Main(string[] args) { TcpClient client = new TcpClient("127.0.0.1", 1330); bool done = false; Console.WriteLine("Napište ‘bye’, chcete-li připojení ukončit"); while (!done) { Console.Write("Zadejte zprávu, která se má odeslat na server: "); string message = Console.ReadLine(); SendMessage(client, message); string response = ReadResponse(client); Console.WriteLine("Odpověď: " + response); done = response.Equals("BYE"); } } private static void SendMessage(TcpClient client,string message) { // Zajistěte, aby se na druhém konci kódovalo se stejným formátem! byte[] bytes = Encoding.Unicode.GetBytes(message); client.GetStream().Write(bytes, 0, bytes.Length); }
Ben Watson
C# 4.0 řešení praktických programátorských úloh ZONER software, a.s. významný producent softwaru v oblasti digitální fotogra¿e, poþítaþové gra¿ky a multimédií, poskytovatel internetových služeb, souvisejících s prezentací na internetu a e-komercí, a nakladatelství odborné literatury.
Sháníte rychlá, robustní a efektivní programovací řešení pro Microsoft C# 4.0? Pak je tato kniha přesně to, co hledáte. Najdete v ní přes 200 hotových řešení, nejlepší praktiky či vyzkoušené ukázky kódu pro všechno možné (od tříd po výjimky, od sítí po XML, od LINQ po Silverlight). Kniha zahrnuje hlavní rozšíření jazyka, která byla představena v C# 4.0 a .NET 4.0. Pokud trpíte chronickým nedostatkem času, na nic nečekejte a začněte rovnou zde. Získejte hned teď odpovědi, jimž můžete důvěřovat, a kód, který můžete hned používat. Témata popisovaná v knize: • • • • • • • • • • • • •
Základy typů Vytváření univerzálních typů Obecné zásady psaní kódu Výjimky Čísla Výčty Řetězce Regulární výrazy Generiky Kolekce Soubory a serializace Sítě a web Databáze
• • • • • • • • • • • • •
XML Delegáti, události a anonymní metody Formuláře Windows Grafika s formuláři Windows a GDI+ Windows Presentation Foundation (WPF) ASP.NET Silverlight LINQ Správa paměti Vlákna, asynchronní a paralelní programování Reflexe a vytváření pluginů Aplikační vzory a tipy Interakce s operačním systémem a hardwarem
O autorovi Ben Watson je softwarový inženýr z týmu Bing společnosti Microsoft. Pomáhá navrhovat a implementovat masivně škálovatelné distribuované systémy a další interní záležitosti pro vyhledávač Bing. Než se přidal k Microsoftu, pracoval jako vůdčí vývojář pro GeoEye, což je hlavní poskytovatel satelitního zobrazování. Bloguje na PhilosophicalGeek.com, kde zveřejnil spoustu populárních návodů pro psaní kódu v C# a C++. Zdrojové soubory ke knize zonerpress.cz/download/C_4_0_reseni_praktickych_uloh.zip
Z O N E R
www.zoner.cz
P R E S S
Pod tímto logem vycházejí publikace určené pro každého vážného zájemce o práci s počítačem. Od ryze praktických příruček a průvodců až po komplexní publikace o všem, co potřebuje grafik, webdesignér, vývojář či programátor při každodenní práci. Na slevy, které můžete získat, a vydavatelský plán, v němž vedle knih domácích odborníků najdete celou řadu titulů světově uznávaných autorů, se informujte na adrese vydavatelství.
© Foto: Lenka Křížová
E N C Y K L O P E D I E
DOPORUČENÁ CENA: 539 kč KATALOGOVÉ ČÍSLO: ZR1015
Zoner Press tel.: 532 190 883 e-mail: knihy@zoner.cz www.zonerpress.cz ZONER software, a.s., Nové sady 18, 602 00 Brno
ISBN 978-80-7413-094-6
9 7 8 8 0 7 4 1 3 0 9 4 6