Professional Documents
Culture Documents
Prace nad ksik Wstp do programowania w jzyku C# rozpoczem pod koniec roku 2005,
a zakoczyem pod w marcu 2006 roku. W tym samym roku ukazaa si ona nakadem
wydawnictwa Helion. W zamyle miaa ona stanowi wprowadzenie do jzyka C# i mam
nadzieje, e dziki niej - drogi
Czytelniku - zapoznasz si z podstawami jzyka C#.
Ksika zostaa oparta o najnowszy wwczas framework - 2.0, dlatego te informacje
w niej si znajdujce mog by nieaktualne. Mam nadzieje, e mimo wszystko publikacja ta
stanie si przydatna dla osb, ktre chc nauczy si programowania w jzyku C#.
Korzystajc z tego chciabym zaapelowa do Czytelnikw: jeeli znajdziecie w ksice jaki
bd. Czy to logiczny, czy literwk. Prosz - poprawcie go! Mechanizm witryny
4programmers.net umoliwia edycj kadego z tekstw. Nawet jeli to, co napisaem jest
ewidentnym kamstwem wynikajcym z mej niewiedzy. Nie boj si edytowa artykuw.
Wszystkie zmiany s zapisywane na serwerze.
Nie przecigajc. ycz miej lektury!
Przedmowa
Pamitam, jak wiele lat temu uczc si programowania, miaem trudnoci ze zrozumieniem
niektrych poj i zagadnie znajdujcych si w rnego rodzaju literaturze. Nie dlatego, e
autorzy byli sabymi programistami wrcz przeciwnie! Byli na tyle dowiadczeni, e
zapominali, jak to jest by pocztkujcym, i nie potrafili w umiejtny sposb przekaza
swojej wiedzy. Wiem rwnie, e wiele pocztkujcych osb ma problemy ze zrozumieniem
nieco trudniejszych aspektw programowania. Zamierzam wobec tego zaprezentowa swoj
wiedz w jak najprostszy sposb, tak jakbym to ja uczy si programowa.
Aby zacz pisa wasne programy, nie musisz posiada adnych nadzwyczajnych
umiejtnoci, potnego komputera czy matematycznego umysu. Chcc programowa w
jzyku C#, nie potrzebujesz take adnych drogich narzdzi kade oprogramowanie
potrzebne do tego, aby zacz programowa, moesz cign za darmo z internetu
.
Zakadam drogi Czytelniku, e jeste osob pocztkujc, dopiero raczkujc w tej tematyce.
By moe jeste osob, ktra miaa ju wczeniej styczno z programowaniem, lecz chce lub
musi nauczy si jzyka C#. Tak czy inaczej, w niniejszej ksice postaram si przedstawi
swoj wiedz tak, jakby nigdy wczeniej nie mia stycznoci z jzykiem C#. Sukcesywnie
bd prezentowa kolejne partie materiau oraz objania coraz trudniejsze pojcia. Mam
nadziej, e czytanie tej ksiki bdzie dla Ciebie przyjemnoci, a programowanie w jzyku
C# stanie si Twoj pasj i na dugo zapamitan przygod.
Rozdzia 1
Wprowadzenie
Wielu ludzi tak naprawd ma mylne wyobraenie o programowaniu. Brana ta przesiknita
jest wieloma mitami, jakby byo to zajcie niezwykle trudne, ktrym trudni si moe jedynie
geniusz matematyczny. Nic bardziej mylnego! Programowanie nie jest wcale trudne. W dobie
jzykw programowania tzw. wysokiego poziomu naley opanowa pewne podstawy, nauczy
si mylenia programistycznego, a poznawanie bardziej zaawansowanych technik jest
kwesti czasu i dobrych chci.
W tym rozdziale zaprezentuj kilka kluczowych poj, ktrymi posuguj si programici, a
ktrych Ty rwnie bdziesz uywa czsto i po prostu musisz je zna. Czy zastanawiae si
kiedy, jak to si dzieje, e aplikacje s uruchamiane, wykonuj jakie czynnoci? Jak to si
dzieje, e dziaaj? Jak powstaj programy? Jeeli nie znasz odpowiedzi na te pytania, ten
rozdzia jest dla Ciebie obowizkow lektur przed przystpieniem do faktycznej nauki
jzyka.
Edytory kodu
Edytorw kodu jest mnstwo. Kod moesz pisa nawet w najprostszej wersji Notatnika z
systemu Windows. Na rynku istnieje wiele aplikacji (zarwno darmowych, jak i
komercyjnych) wspomagajcych proces powstawania programw, udostpniajcych wygodne
funkcje przydatne przy edycji kodu.
Bardzo zaawansowany, umoliwiajcy pisanie i kompilowanie aplikacji w C# jest pakiet
Borland Developer Studio firmy Borland (www.borland.pl). Jest to produkt komercyjny
umoliwiajcy rwnie pisanie aplikacji w jzyku Delphi oraz C++.
Innym rodowiskiem popularnym wrd programistw C# jest narzdzie Visual Studio 2005.
Umoliwia programowanie i kompilacj aplikacji napisanych w jzykach C++, C# oraz J#.
To narzdzie rwnie jest komercyjne. Firma Microsoft udostpnia jednak wersje ubosze,
lecz darmowe, ktre mona cign za darmo ze strony firmy Microsoft
(http://msdn.microsoft.com/vstudio/express/default.aspx).
Na rysunku 1.1 zaprezentowano rodowisko Visual C# 2005 Express Edition, dziki ktremu
z powodzeniem mona pisa aplikacje w jzyku C#.
Kompilatory
Obowizkowym narzdziem programisty jest kompilator. To on umoliwia generowanie
aplikacji wykonywalnych na podstawie kodu jzyka. rodowisko Visual C# Express Edition
(ale rwnie Borland Developer Studio) ma wbudowan obsug kompilatora jzyka C#.
Napisaem wbudowan obsug, gdy kompilator C# jest dostarczany za darmo wraz ze
rodowiskiem .NET
To nieprawda. Sama idea programowania jest taka sama, w wielu jzykach stosuje si
podobne mechanizmy, ale stwierdzenie, e wszystkie s podobne, jest duym uoglnieniem.
Jeeli pytasz, czy atwiej nauczy si programowania, majc ju dowiadczenie w innym
jzyku, to tak jest to prawda. Szczeglnie jzyki Java, JavaScript, C, C++ oraz C# s do
siebie na tyle podobne, e nauka drugiego, kiedy znamy ju jeden, nie powinna stanowi
problemu.
Jzyki programowania
Jak ju powiedziaem, kod programu musi by zapisany w okrelonym jzyku
programowania (np. w tej ksice prezentuj programy w utworzone w C#). Istniej dziesitki
jzykw programowania bardziej lub mniej popularne oraz bardziej lub mniej
skomplikowane. Kady z tych jzykw wyrnia si inn skadni i innym sposobem
dziaania. W swojej karierze moesz si spotka z nastpujcymi jzykami:
Ada,
Asembler,
Algol,
Basic,
QBasic,
QuickBasic,
Turbo Basic,
Visual Basic,
C,
C#,
C++,
Clipper,
COBOL,
Fortran,
Haskel,
Java,
JavaScript,
LISP,
Logo,
PHP,
Pascal,
Perl,
Python,
Prolog,
Smalltalk,
Tcl.
Asembler
Asembler jest niskopoziomowym jzykiem programowania zaprojektowanym w latach 1947
1952 przez Grace Hopper. Asembler by w gruncie rzeczy rewolucyjny uwalnia
programist od koniecznoci zapisywania programw w formie binarnej. W tym jzyku jedno
polecenie praktycznie odpowiada jednemu rozkazowi procesora. Obecnie Asembler jest
uwaany za trudny jzyk programowania (w porwnaniu np. z Delphi czy C#), ale wci
bywa uywany. Podstawow zalet Asemblera jest to, e daje niesamowite moliwoci w
manipulowaniu komputerem. Wikszo jzykw wysokiego poziomu pozwala na wczanie
wstawek Asemblera do swojego kodu. W praktyce wyglda to tak, e podczas pisania kodu
np. w Pascalu moemy do tworzonego programu doczy fragment kodu Asemblera.
W ten sposb wikszo obecnych programw nie jest pisana w caoci w Asemblerze (ze
wzgldu na do skomplikowan skadni), ale zawiera wstawki tego jzyka. Przykadowo,
system operacyjny Linux by pisany w jzyku C, mimo to nie obeszo si bez zastosowania
fragmentw kodu Asemblera.
W powyszym opisie znajduj si dwa pojcia jzyk wysokiego oraz niskiego poziomu. W
praktyce czynnikiem rozrniajcym jzyk poziomu wysokiego od niskiego jest zoono.
Przykadowo, Pascal jest jzykiem wysokiego poziomu, bo zawiera gotowe funkcje i
procedury (np. do wywietlania tekstu na ekranie suy komenda writeln) i dziki temu jest
atwy do nauczenia si. Rwnie projektowanie aplikacji w jzykach wysokiego poziomu jest
o wiele szybsze ni w Asemblerze. W Asemblerze w celu wywietlenia tekstu na ekranie nie
obejdziemy si bez obsugi odpowiedniego przerwania.
Pojcie przerwania zwizane jest z systemem operacyjnym, a konkretnie z procesorem.
Przerwanie powoduje wstrzymanie aktualnie wykonywanego programu i wykonanie przez
procesor kodu procedury obsugi. Jest to pojcie zwizane z bardziej zaawansowanymi
aspektami programowania, nie musisz teraz si tym przejmowa.
Fortran
Nazwa Fortran jest utworzona od angielskich sw Formula Translator. Fortran jest uwaany
za pierwszy jzyk wysokiego poziomu. Zosta zaprojektowany w latach 50. XX wieku przez
Johna Backusa i zesp IBM. Fortran jest wykorzystywany przede wszystkim do pisania
programw sucych do analizy numerycznej i do wykonywania skomplikowanych oblicze
naukowych. Jzyk ten zyska niesamowit popularno i jest uywany do dzisiaj (jako nowy,
ulepszony jzyk programowania Fortran 90, Fortran 95 oraz Fortran 2000).
C++
W roku 1983 Bjarne Stroustrup zmodyfikowa jzyk C, dodajc do niego nowe funkcje.
Przede wszystkim rewolucyjn zmian w porwnaniu z C jest moliwo programowania
obiektowego. To zagadnienie zostanie dokadniej omwione w dalszej czci ksiki. C++ (na
pocztku mia nosi nazw C z klasami) jest rwnie niezwykle popularnym jzykiem
programowania, czsto stosowanym przez programistw na caym wiecie. Jzyki C oraz
C++ s do siebie bardzo podobne, wic jeli znamy jeden z nich, atwiej przyjdzie nam nauka
drugiego.
Perl
Perl (ang. Practical Extraction and Report Language) jest interpretowanym jzykiem
programowania autorstwa Larrego Walla, bardzo czsto wykorzystywanym jako jzyk
skryptowy na stronach WWW. W zamyle autora jzyk Perl mia pomc w rozwizywaniu
trudnych zada oraz przyspiesza prac nad programem.
Perl nie jest kompilowanym jzykiem programowania. Oznacza to, e nie nastpuje
kompilacja programu, czyli przeksztacanie kodu napisanego przez programist do postaci
pliku wykonywalnego. Zamiast tego w komputerze musi by zainstalowany tzw. interpreter,
ktry bdzie odczytywa instrukcje jzyka i odpowiednio na nie reagowa.
Naturalnym rodowiskiem Perla jest system UNIX, aczkolwiek jzyk ten jest take dostpny
w wersjach przeznaczonych na inne platformy. Bardzo czsto jest wykorzystywany podczas
tworzenia skryptw CGI przeznaczonych dla stron WWW. Obecnie Perl jest wypierany przez
modszy i prostszy jzyk PHP, cho wietnie si nadaje si do pisania skryptw
manipulujcych plikami tekstowymi.
Obecnie najnowsz, wci rozwijan wersj tego jzyka jest Perl 6.
CGI, czyli Common Gateway Interface, jest obecny praktycznie od pocztku istnienia
internetu
i stron WWW. CGI umoliwia dynamiczne generowanie stron WWW, czyli w praktyce pisanie
tak rnych skryptw jak ksigi goci, fora dyskusyjne itp.
PHP
PHP (angielski akronim rekurencyjny PHP Hypertext Preprocessor) jest jzykiem
programowania, ktry w ostatnich latach robi oszaamiajc karier. Autorem tego jzyka jest
Rasmus Lerdorf, ktry napisa pierwsz jego wersj w roku 1995. PHP podobnie jak Perl
jest interpretowanym jzykiem programowania, wykorzystywanym gwnie do
generowania dynamicznych stron WWW. Jego zalet jest niezwyka prostota i jednoczenie
due moliwoci. Oglnie rzecz biorc, PHP jest jednym z najprostszych jzykw
programowania.
PHP jest darmowy, oparty na licencji GPL (ang. GNU Public License) i rozwijany w dalszym
cigu przez setki programistw z caego wiata.
Turbo Pascal
Pocztki Pascala jako jzyka programowania sigaj pocztku lat 70. XX wieku. Opracowano
go specjalnie do nauki programowania. Jest to bardzo prosty jzyk, dajcy jednoczenie due
moliwoci. W roku 1983 firma Borland wypucia na rynek komercyjny produkt o nazwie
Turbo Pascal i ta data jest uwaana za przeomow. Turbo Pascal natychmiast zyska
popularno, ktr w dalszym cigu utrzymuje.
Take dzisiaj Turbo Pascal jest zalecany do nauki programowania dla pocztkujcych: uczy
mylenia programistycznego. Jest wykorzystywany rwnie na rnych szczeblach
nauczania: na studiach czy w szkoach rednich do rozwizywania problemw i tworzenia
algorytmw.
Java
Jzyk Java zosta opracowany przez firm Sun Microsystems. Kady z pewnoci nieraz
sysza o tym jzyku, gdy ostatnio sta si on bardzo popularny, gwnie dziki niezalenoci
od systemu operacyjnego. Z reguy programy napisane i skompilowane dla platformy
Windows nie bd dziaay w innych systemach, np. w Linuksie. W przypadku Javy jest
inaczej, poniewa kod rdowy nie jest kompilowany na kod maszynowy, tak jak to ma
miejsce w innych jzykach programowania, ale zostaje poddany procesowi translacji na kod
poredni. Ten z kolei zostaje poddany procesowi interpretacji przez tzw. maszyn wirtualn,
ktra musi by zainstalowana w systemie.
Mona powiedzie, e maszyna wirtualna jest programem, ktry wykonuje inne programy. W
przypadku Javy maszyna wirtualna interpretuje kod poredni.
Maszyna wirtualna Javy zostaa zaimplementowana dla wikszoci systemw operacyjnych i
dziki temu ten jzyk programowania jest tak uniwersalny.
O maszynach wirtualnych opowiem w kolejnych rozdziaach tej ksiki.
Jzyk maszynowy
Na ostatnich stronach pado wiele nowych poj, takich jak kompilator, kompilacja,
translacja czy jzyk interpretowany. Aby nieco usystematyzowa t wiedz, powiem kilka
sw o tym, jak waciwie dziaaj aplikacje i czym jest jzyk maszynowy.
Typowy program przeznaczony na platform Windows ma rozszerzenie .exe i zwyko si o
nim mwi plik wykonywalny. Jest to plik w formacie PE (ang. Portable Executable)
zawierajcy m.in. skompilowan wersj kodu rdowego, tzw. kod maszynowy. Kod
maszynowy jest postaci gotow do bezporedniego (lub poredniego) wykonania przez
procesor. Oprcz kodu maszynowego plik wykonywalny zawiera rwnie informacje
charakterystyczne dla systemu, std niemoliwe jest uruchomienie takiej aplikacji na innych
systemach operacyjnych.
Typowym daniem uruchomienia aplikacji jest podwjne kliknicie ikony reprezentujcej
dany program. Wwczas system operacyjny aduje tak aplikacj do pamici, skd jest
wykonywana przez procesor.
W kadym bd razie najwaniejsze jest, aby zapamita, i kod maszynowy to
skompilowana wersja kodu rdowego gotowa do wykonania. Istnieje rwnie pojcie
jzyka maszynowego. Jest to rwnie jzyk programowania polegajcy na zapisywaniu
instrukcji jako liczb, ktre s rozkazami pobieranymi przez procesor. Teoretycznie rzecz
biorc, istnieje moliwo operowania na skompilowanym kodzie maszynowym. W taki
sposb powstaway pierwsze aplikacje, gdy nie istniay kompilatory (pierwszy kompilator
musia zosta napisany w kodzie maszynowym). Dawniej wic programowanie byo profesj,
ktr zajmowali si najlepsi, gdy bya to czynno niesamowicie trudna. Taka technika
nie jest ju wykorzystywana (lub szczeglnie rzadko), nie bdziemy ju do niej wraca w
dalszej czci ksiki.
Dziaanie kompilatorw
Wiesz ju, czym s kompilatory. Wane dla zrozumienia dalszej czci ksiki jest poznanie
zasad ich dziaania.
Pierwsza wersja GCC, napisana przez Richarda Stallmana, ukazaa si w 1987 roku. Obecna
wersja, oznaczona jako 3.3.2, jest udostpniona do pobrania ze strony http://gcc.gnu.org/.
Mona by zapyta, w jakim celu pisz o GCC czy o jzyku C przecie nie jest to tematem
niniejszej ksiki. To prawda, wspominam o tym tylko dlatego, e dziaanie kompilatora
jzyka C# jest nieco inne, ale o tym opowiem w dalszej czci ksiki.
Prekompilacja
Ten proces polega na usuwaniu komentarzy z kodu rdowego. Teraz nastpuje rwnie
odczytanie makrodefinicji jzyka C, ktre zaczynaj si od sowa #define. Nie jest to jednak
tematem tej ksiki i nie bdziemy si tym szczegowo zajmowa.
Czytelnik, ktry mia wczeniej styczno z jzykiem C, zapewne wie, czym s komentarze czy
makrodefinicje. Tematem komentarzy w jzykach programowania zajmiemy si w dalszej
czci ksiki.
Optymalizacja kodu
Nastpnie kompilator analizuje kod Asemblera i poddaje go optymalizacji. Jest to opcjonalna
funkcja w kompilatorach GCC, lecz warto j wykorzystywa, gdy czsto jest przydatna.
Naley te wspomnie, e moliwo optymalizacji kodu jest do zaawansowan funkcj
kompilatora.
Optymalizacja jest procesem poprawiania kodu programu, tak aby jego wykonywanie byo
jak najbardziej efektywne, a sam program zajmowa moliwie jak najmniej pamici.
Optymalizacja powinna nalee do programisty jako wyrobiony nawyk pisania efektywnych
programw, lecz w obecnych czasach maszyna znacznie uatwia t cz pracy.
Asemblacja
Zoptymalizowany kod Asemblera moe zosta poddany asemblacji. Proces asemblacji jest
zwizany z przeksztacaniem kodu Asemblera na kod maszynowy.
Konsolidacja
Konsolidator, nazywany te czsto linkerem, tworzy aplikacj wykonywaln, ktr mona
uruchomi w sposb niezaleny od kompilatora i przykadowo przetestowa jej
dziaanie. Innymi sowy, linker tworzy plik .exe gotowy do uycia.
Jzyki interpretowane
Istniej jzyki, do ktrych nie ma kompilatorw. Takie aplikacje nie s kompilowane do pliku
.exe. Program jest cay czas przechowywany w postaci kodu rdowego. Na maszynie, na
ktrej taka aplikacja ma zosta uruchomiona, musi by zainstalowany tzw. interpreter.
Kadorazowo sprawdza on skadni programu i na bieco wykonuje instrukcje zapisane w
kodzie rdowym. Przykadem jzykw interpretowanych jest PHP i Perl.
Jzyk C#
Jzyk C# 1 (czytaj: si szarp) jest nowym jzykiem programowania zaprojektowanym przez
firm Microsoft i cile zwizanym z platform .NET. czy w sobie zalety jzykw C++
oraz Java, jest do nich bardzo podobny, co uatwia jego nauk osobom, ktre wczeniej miay
do czynienia ze wspomnianymi jzykami.
Gwnym architektem jzyka jest Anders Hejlsberg, duski programista. Wczeniej pracowa
on dla firmy Borland, gdzie uczestniczy w tworzeniu rodowiska Turbo Pascal oraz Delphi.
C# jest do specyficznym jzykiem, jak wspomniaem cile zwizanym z platform
.NET. Aplikacje pisane w tym jzyku wymagaj rodowiska uruchomieniowego CLR, ktre
zostanie omwione w dalszej czci ksiki.
Instalacja rodowiska
W trakcie pisania przykadw do tej ksiki bd korzysta z darmowego rodowiska Visual
C# 2005 Express Edition, ktre moesz pobra ze strony
http://msdn.microsoft.com/vstudio/express/visualcsharp/download/.
Po pobraniu i uruchomieniu pliku instalacyjnego (o rozmiarach kilku MB) nasz system
zostanie przygotowany do cignicia rodowiska .NET Framework 2.0 i Visual C# Express
Edition oraz systemu pomocy. W sumie bdzie to nieco ponad 300 MB. Po cigniciu
wszystkich potrzebnych plikw uruchomiony zostanie instalator, ktry powinien
bezproblemowo zainstalowa wszelkie aplikacje potrzebne do tego, aby zacz programowa.
Jak si uczy
Jak si uczy programowania? Kady ma inne sposoby, mniej lub bardziej skuteczne. Wzie
t ksik do rki zapewne dlatego, e interesujesz si przedstawion w niej tematyk i dziki
temu nauka bdzie przychodzia Ci atwiej. Gorzej, jeeli musisz nauczy si programowania
w C#, poniewa tak nakaza Ci szef w takich przypadkach nauka bdzie trudniejsza. To
naturalne oswojenie si z tym, co si lubi, co sprawia przyjemno, jest w gruncie rzeczy
atwe. Tak samo jest z C#. Jeeli interesujesz si programowaniem, to nauka tego jzyka
bdzie przyjemnym dowiadczeniem.
Pocztkowe trudnoci
Jeeli jeste naprawd pocztkujcy w tej dziedzinie, to przez jaki czas moe Ci by ciko.
Nie mwi tego, aby kogokolwiek zniechca, lecz raczej aby pocieszy tak jest w wielu
przypadkach, wic nie ma si czym przejmowa. Z czasem wiele kwestii stanie si bardziej
czytelnych i atwiejszych. Moe si bowiem zdarzy, i na pocztku tej ksiki bd
wspomina o sprawach, ktre zostan omwione dopiero w dalszych rozdziaach (aczkolwiek
bd si stara unika niezrozumiaych poj), tak wic nie naley si przejmowa, jeli co
wydaje si niejasne dalej znajd si odpowiedzi.
Pomoc systemowa
Zawsze mona skorzysta z systemu pomocy! Najwaniejszym klawiszem skrtu jest F1,
ktry powoduje uruchomienie systemu pomocy. Niestety, pomoc jest w caoci napisana w
jzyku angielskim, tak wic jest niezbdna jest przynajmniej podstawowa umiejtno
czytania w tym jzyku.
Praktyka
Praktyka czyni mistrza! Kady na pewno nieraz sysza to przysowie. Warto si do niego
stosowa. Po zapoznaniu si z jak now funkcj warto sprawdzi jej dziaanie w praktyce
Pierwsza aplikacja
Nasz pierwszy program nie bdzie realizowa adnych konkretnych zada. Bdzie to typowe
okno systemu Windows z przyciskiem. Po naciniciu przycisku wywietlone zostanie nowe
okno z napisem Hello World.
Uruchom rodowisko Visual C# 2005 Express Edition (jeeli chcesz, moesz skorzysta z
bardziej zaawansowanych wersji Visual Studio).
1.
2.
3.
4.
W tym momencie zosta utworzony nowy projekt typu Windows Forms. Okno o nazwie
Form1 to okno naszej aplikacji. W trakcie projektowania programu mamy moliwo
obserwacji, jak taka aplikacja bdzie wygldaa w trakcie dziaania.
Po lewej stronie ekranu znajduje si pozycja Toolbox. Gdy naprowadzimy na ni myszk,
rozwinie si okno Toolbox zawierajce spis tzw. komponentw (rysunek 1.2) lub kontrolek
(takie okrelenie rwnie jest czsto stosowane).
Kompilacja i uruchamianie
Na razie nasz program skada si jedynie z przycisku. Poza tym nic nie robi, bo nacinicie
przycisku nie powoduje adnej reakcji. Jednak ju teraz moesz uruchomi nasz projekt i
sprawdzi jego dziaanie.
Jest to bardzo proste. Wystarczy z menu Debug wybra Start Debugging lub nacisn
przycisk F5. rodowisko Visual C# Express Edition oprcz edytora kodu i edytora
formularzy zawiera rwnie debuger oraz kompilator. Nacinicie klawisza F5 spowoduje
skompilowanie programu, a nastpnie jego uruchomienie. Po chwili nasza aplikacja w postaci
okna z przyciskiem powinna zosta uruchomiona. Naturalnie jest to bardzo prosty program,
ktry nie realizuje adnego zadania. Zwr uwag, e zaprojektowalimy (stworzylimy)
program, nie piszc ani jednej linii kodu!
Komponenty
W zaprezentowanym przykadzie skorzystalimy z komponentu Button. W tym miejscu
nale Ci si wyjanienia, czym waciwie jest komponent. Typ aplikacji, ktry przed chwil
stworzylimy, nazywany jest aplikacj typu Windows Forms. Wicej na ten temat dowiesz si
z lektury rozdziau 2. oraz 10. Istotne jest to, e projektowanie aplikacji tego typu umoliwia
stosowanie podstawowych kontrolek systemu Windows, takich jak przyciski, listy rozwijane,
pola edycyjne, panele itp. Umoliwia to projektowanie interfejsu aplikacji. Mona wic
powiedzie, e komponenty s klockami umoliwiajcymi szybkie projektowanie aplikacji.
Waciwoci komponentw
Kada kontrolka (komponent) posiada waciwoci, ktre umoliwiaj ustalenie jej
specyficznych cech, takich jak pooenie, kolor, czcionka itp. Spis waciwoci komponentw
znajduje si w oknie Properties (rysunek 1.4), ktre standardowo umiejscowione jest w
prawym dolnym rogu rodowiska Visual C# Express Edition.
Piszemy kod
Mamy ju formularz, przycisk, lecz brakuje nam reakcji na nacinicie go. Musimy wobec
tego oprogramowa odpowiednie zdarzenie (reakcje) na wcinicie kontrolki. Kliknij
dwukrotnie komponent Button. Zostaniesz przeniesiony do edytora kodu, gdzie naley wpisa
kod, ktry zostanie wykonany, w momencie gdy uytkownik
nacinie przycisk. My chcemy, aby wywietlone zostao okienko, co realizuje poniszy kod:
MessageBox.Show("Hello World");
Zapisywanie projektu
Moje gratulacje! Wanie napisae swj pierwszy program w C#. Myl, e warto by byo
zapisa go na pamitk. Aby zapisa projekt, z menu File wybierz Save All. W tym momencie
musisz wybra katalog, w ktrym zapisane zostan pliki projektu (przycisk Browse).
Zapisanie projektu nastpi po naciniciu przycisku Save. W tym momencie w wybranym
katalogu zostanie utworzony szereg plikw i folderw. Ich zawarto w tym momencie jest
nieistotna. Najwaniejsze s pliki z rozszerzeniem *.cs, ktre zawieraj rzeczywisty kod
rdowy naszego programu.
Podsumowanie
Po przeczytaniu tego rozdziau powiniene mie podstawow wiedz na temat
programowania. Najwaniejsze jest, aby wiedzia ju, czym jest kompilator, kod rdowy
czy proces kompilacji. Powiniene mie zarys tego, jak dziaaj kompilatory. W tym rozdziale
zarysowaem jedynie tematyk programowania oraz poruszyem podstawowe pojcia, ktre
bd rozwija w dalszej czci ksiki.
[1] Powszechnie nazw jzyka zapisuje si jako C#, czyli uywa si znaku # (ang. hash), a nie
znaku krzyyka (ang. sharp) uywanego w notacji muzycznej. Spowodowane jest to brakiem
znaku sharp w wielu czcionkach.
Rozdzia 2
Podstawy platformy .NET
W poprzednim rozdziale opisaem, na czym polega programowanie. Nadszed czas, aby
zgbi t wiedz i dowiedzie si, czym jest programowanie dla platformy .NET. Nazwa
wydaje si do dziwna, niewtpliwie kojarzy si z internetem. I prawidowo! Ta technologia
ma sporo wsplnego z globaln sieci.
Internet odgrywa coraz wiksz rol w naszym yciu. Nie suy ju tylko do przegldania
statycznych stron WWW. Teraz internet to gwnie komunikatory oraz dynamiczne strony
WWW, ktre umoliwiaj wiksz interakcj z uytkownikiem. Przykadowo, po otwarciu
strony traktujcej o gospodarce uzyskujemy na bieco wgld w najnowsze notowania akcji,
kursy walut czy ciekawostki. Po rejestracji w takim serwisie mona otrzymywa codzienne
informacje kierowane na wskazany telefon komrkowy
czy skrzynk e-mail. Strony WWW dostarczaj teraz coraz bardziej interesujcej rozrywki
dziki animacjom czy grom wykonanym za pomoc technologii Flash. Wiele serwisw
umoliwia rejestracj, w ktrej mona ustala swoje preferencje, takie jak wygld strony czy
korzystanie z rozmaitych opcji.
Obecnie nie istnieje jednak technologia pozwalajca na swobodne komunikowanie si ze sob
serwisw internetowych. Np. przegldajc oferty
sklepu muzycznego, chcielibymy zamwi bilet na koncert ulubionego zespou. Takie
zamwienia obsuguje jednak inna witryna. Oczywicie sklep muzyczny moe oferowa
swoim klientom
kupno biletu na koncert poprzez podanie np. odnonika do strony, na ktrej mona zamwi
bilet, lub poredniczenie w sprzeday. Nie jest to jednak idealne i wygodne rozwizanie. Czy
nie lepiej byoby, gdyby witryny mogy si ze sob komunikowa? Wyobramy sobie taki
scenariusz: nasza strona sklep muzyczny wysya zapytanie do serwisu organizatora
koncertu i sprawdza dostpno biletw na wystp danego wykonawcy wraz z cenami oraz
terminami. W takim przypadku wszystko odbywaoby si automatycznie nie jest potrzebna
jakakolwiek ingerencja czowieka, gdy informacje s stale pobierane ze strony organizatora
koncertu.
Midzy innymi takie scenariusze zakada platforma .NET. Chodzi o integracj usug, sprztu i
innych urzdze, takich jak palmtopy i telefony komrkowe. Microsoft chce, aby aplikacje
mona byo uruchamia w telefonach, aby programy te mogy pobra aktualne dane z
internetu (na przykad aktualne terminy koncertw). O tym jednak opowiem nieco dalej
najpierw przecie naley wyjani, w jaki sposb odbywa si programowanie dla systemu
Windows.
Interfejs programistyczny
Kolejnym pojciem, ktre trzeba zna, jest API, czyli programistyczny interfejs aplikacji
Wizja .NET
W lipcu 2000 roku Microsoft zwoa konferencj programistw, na ktrej zademonstrowano
.NET. W zamierzeniu .NET jest platform nowej generacji, przeznaczon do tworzenia
aplikacji Windows. Bill Gates w swoim przemwieniu porwna powstanie platformy .NET
do przejcia z systemu DOS do Windows 3.1., a pniej z przejciem do Windows 95. W
rzeczywistoci .NET nie jest nowym systemem operacyjnym ani jzykiem programowania.
Jest podstaw, pewna ide, sposobem budowania aplikacji dla systemu Windows. Platforma
.NET to po prostu nowe podejcie do tworzenia oprogramowania.
Jednym z zaoe platformy .NET jest porzucenie dotychczasowego modelu Win32 i
zastpienie go cakowicie nowym, prostszym oraz bardziej zaawansowanym.
W poprzednim rozdziale opisaem proces instalowania platformy .NET. W rzeczywistoci
podczas tej operacji instalator kopiuje na dysk twardy komputera szereg bibliotek DLL,
aplikacji dla programistw oraz dokumentacj. Dziki temu mona korzysta z nowych
narzdzi oferowanych przez Microsoft. Windows 2003 standardowo jest wyposaony w
platform .NET, wic nie ma potrzeby ponownej instalacji.
Zgodnie z definicj firmy Microsoft:
.NET jest stworzonym przez firm Microsoft rozwizaniem dla usug sieciowych, stanowi
now generacj oprogramowania czcego si z naszym wiatem informacji, urzdzeniami i
uytkownikami w jednolity, spersonalizowany sposb.
Taka definicja zapewne wydaje si troch niejasna, dlatego w tym rozdziale postaram si
Kompilator csc.exe
Pamitasz, jak w poprzednim rozdziale wspominaem o kompilatorze jzyka C#
udostpnianym za darmo wraz ze rodowiskiem .NET Framework? Nie miaem jeszcze okazji
zaprezentowa sposobu dziaania tego programu. W poprzednim rozdziale wspominaem
rwnie, i do programowania nie jest potrzebny aden specjalistyczny edytor. Wystarczy
zwyky, najprostszy edytor tekstu, jak np. Notatnik z systemu Windows.
Uruchom Notatnik i wpisz nastpujc tre programu:
using System;
class Welcome
{
static void Main()
{
Console.WriteLine("Cze!");
Console.ReadLine();
}
}
Plik o tej treci zapisz w katalogu, w ktrym znajduje si kompilator csc.exe. W moim
wypadku jest to katalog C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727. Plik zapisz
pod nazw welcome.cs. Ten prosty program wywietla na ekranie konsoli tekst Cze!.
1. Z menu Start wybierz Programy/Akcesoria/Wiersz polece.
2. Wpisz komend cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727, ktra
spowoduje otwarcie katalogu z kompilatorem.
3. Nastpnie wpisz polecenie csc welcome.cs, ktre spowoduje skompilowanie
programu.
Jeeli wszystko poszo dobrze, czyli kod rdowy nie zawiera bdw, na ekranie konsoli
powinien pojawi si tekst:
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights
reserved.
Konkluzja
Podsumujmy pokrtce, czym jest .NET:
C# a .NET
Podsumujmy wic nasz wiedz dotyczc jzyka C#:
Chcc pisa aplikacj na platformie .NET, nie jeste skazany jednie na jzyk C#. Platforma
.NET wspiera rwnie inne jzyki, takie jak C++, Visual Basic.NET, Delphi i inne.
Rodzaje aplikacji
W poprzednim rozdziale stworzye swoj pierwsz aplikacj w C#. Jak pamitasz, program
skada si z jednego formularza (okna). W takim wypadku mwimy o wykorzystaniu
biblioteki Windows Forms. Owa biblioteka, bdca oczywicie czci platformy .NET,
umoliwia uycie tzw. komponentw, takich jak przyciski, listy rozwijane, pola edycyjne.
Przed chwil pokazaem, jak mona stworzy aplikacj konsolow.
Aplikacje konsolowe
Aplikacje konsolowe to programy uruchamiane w oknie konsoli. Tego rodzaju aplikacje nie
posiadaj interfejsu uytkownika i s przydatne, jeeli chodzi o prosty program, ktry bdzie
uywany przez wsk grup ludzi posiadajcych wiedz potrzebn do uruchomienia go i
sterowania nim z linii polece.
Przykad aplikacji konsolowej zosta przedstawiony w tym rozdziale wczeniej, gdy
zaprezentowaem moliwo skompilowania prostego programu napisanego w C#.
Windows Forms
W rozdziale 1. pokazaem, w jaki sposb utworzy prosty program wizualny na podstawie
formularza i z wykorzystaniem komponentu. Po uruchomieniu takiego programu uytkownik
widzi standardowe okno Windows wraz z etykiet porodku. Tego typu aplikacje bd dziaa
jedynie w przypadku, gdy na komputerze jest zainstalowane rodowisko .NET Framework.
Windows Forms (czsto nazywana w skrcie WinForms) jest bibliotek wizualn,
umoliwiajc zaprojektowanie graficznego interfejsu uytkownika (GUI, czyli Graphical
User Interface). WinForms jest czci rodowiska .NET Framework, tak wic takie
komponenty jak Button (uyty w przykadzie z rozdziau 1.) mog zosta uyte zarwno w
Delphi, jak i w C# czy innym jzyku obsugiwanym przez platform .NET (np. Visual
Basic.NET).
O bibliotece Windows Forms bdzie mowa w rozdziale 10.
Usugi sieciowe
Usugi sieciowe s aplikacjami udostpnianymi poprzez interfejs WWW. Nie chodzi tutaj
bynajmniej o strony WWW, gdy aplikacje sieciowe mog komunikowa si ze sob za
pomoc mechanizmu zdalnego wywoywania procedur (ang. Remote Procedure Calls
RPC), przewanie uywajc do tego protokou HTTP.
Dobrym przykadem jest wypoyczalnia DVD. Szef takiej firmy moe podpisa umow z
wacicielami sklepw z pytami DVD, ktrzy na swoich stronach bd oferowali pyty z
wypoyczalni. Wwczas serwisy wacicieli sklepw mog komunikowa si z usug
sieciow znajdujc si na serwerze przynalenym do wypoyczalni w celu pobrania
aktualnej oferty, sprawdzenia, czy dany tytu jest ju wypoyczony i kiedy ma zosta
zwrcony. Podobnie oprogramowanie zainstalowane w placwce/placwkach wypoyczalni
moe komunikowa si z serwerem, pobierajc aktualne dane o klientach czy wypoyczonych
tytuach. Wszystkie dane znajduj si na jednym serwerze i s udostpniane przez usug
sieciow. Wszelkie zmiany w tytuach bd produktach bd przeprowadzane jedynie na
centralnym serwerze nie zachodzi wwczas potrzeba aktualizacji oprogramowania w
placwkach.
Usugi sieciowe mona take sprzedawa. Jeeli kto napisze innowacyjn usug
wykorzystujc niepowtarzalny, nowatorski algorytm szyfrujcy, moe j udostpnia w
internecie. Klienci natomiast mog dostosowa odpowiednio swoje oprogramowanie, tak aby
umoliwi wykorzystanie tej nowej usugi. W takim przypadku wszelkie poprawki czy
naprawa bdw bd dotyczyy tylko tej jedynej usugi, a klienci nie musz nawet o tym
wiedzie. Warunkiem jest jedynie zapewnienie dostpnoci takiej aplikacji przez internet.
Inny przykad: autorzy popularnej wyszukiwarki Google udostpnili programistom jej
interfejs. Dziki usugom sieciowym mona wykorzysta mechanizmy Google do
przeszukiwania zasobw internetu. Nastpnie wyniki takiego wyszukiwania mona
zaprezentowa w swoim programie lub stronie internetowej.
Usugi sieciowe s uniwersalnym sposobem wymiany informacji pomidzy aplikacjami czy
stronami WWW. Teraz aplikacja nie musi skada si z tylko jednego moduu (pliku
wykonywalnego .exe), ale moe dzieli si na kilka mniejszych, ktre zostan rozmieszczone
na kilku serwerach. Centralna aplikacja, znajdujca si na komputerze klienta, moe
bezproblemowo czy si z serwerami i wymienia dane oraz realizowa zadania.
Niezaleno
Bardzo wanym aspektem usug sieciowych jest ich niezaleno od platformy jzykowej.
Wane jest, aby usuga dziaaa pod kontrol .NET Framework, jednak nieistotne jest to, w
jakim jzyku zostaa napisana. Moe to by wic C#, Visual Basic.NET czy Delphi.
Aplikacja, ktra korzysta z danej usugi, rwnie moe by napisana w jakimkolwiek jzyku
obsugiwanym przez .NET.
Uniwersalno
A zatem ju wiadomo, e usuga napisana w C# moe by wykorzystywana przez aplikacj
napisan w Delphi. Jak to si wic dzieje, e pomidzy usug a uywajc jej aplikacj
zachodzi swobodna komunikacja
? Chciabym zasygnalizowa kilka podstawowych poj zwizanych nie tylko z usugami,
lecz oglnie z platform .NET.
Komunikacja z usugami sieciowymi odbywa si poprzez sie WWW, a konkretniej przez
wykorzystywany tutaj protok HTTP. Aplikacja moe wysa do usugi sieciowej zapytanie
w formacie XML, a nastpnie otrzyma odpowied rwnie w tym formacie. Kluczow
rol w procesie komunikowania si z usugami sieciowymi odgrywa wanie jzyk XML
opracowany przez konsorcjum W3C.
Konsorcjum W3C (ang. World Wide Web Consorcium) jest organizacj wyznaczajc
standardy technologii zwizanych z internetem (specyfikacje, wytyczne aplikacji itp.).
Organizacja W3C jest odpowiedzialna m.in. za opracowanie standardu HTML, ktry
obowizuje do dzisiaj. Stanowi niewtpliwy autorytet w dziedzinie definiowania standardw
sieciowych. Wicej informacji na temat konsorcjum mona znale na stronie www.w3c.org.
Z usugami sieciowymi wie si kilka nastpnych poj:
Podsumowanie
Uf! W tym rozdziale zaprezentowaem kolejn dawk teoretycznej wiedzy z zakresu
[1] Nazwy kodowe nadawane s projektom we wczesnym stadium rozwoju, wtedy gdy nie
wiadomo jeszcze, jaka bdzie rzeczywista nazwa produktu.
[2] Odnios si w tym momencie do starego powiedzenia. Owszem, .NET ma dziaa na
kadej platformie, ale pod warunkiem e bdzie to produkt firmy Microsoft. Powstaje jednak
darmowy projekt (Open Source), ktrego zaoeniem jest stworzenie platformy zintegrowanej
z .NET dla systemu Linux. Projekt nosi nazw dotGNU, a jego strona internetowa to
www.dotgnu.org.
Rozdzia 3
Podstawy jzyka C#
Zawsze, na kadym kroku staram si podkrela, i tworzenie aplikacji nie opiera si jedynie
na ukadaniu klockw (komponentw) w oknie formularza. Oczywicie, nowoczesne jzyki
programowania (takie jak C#) oraz rodowiska tworzenia aplikacji (Visual C# Express
Edition) daj nam moliwo szybkiego oraz przyjemnego projektowania aplikacji, lecz nie
na tym polega programowanie! Naley mie wiedz na temat podstawowych elementw
jzyka programowania oraz podstawowych polece sucych do sterowania prac programu.
W tym rozdziale zajmiemy si wanie jzykiem C#. Celowo podkreliem sowo jzyk,
gdy omwi podstawowe terminy programistyczne oraz elementy kadego
wysokopoziomowego jzyka programowania. Odstawimy w tym miejscu na chwil
przyjemne i adne projektowanie wizualne (przy pomocy komponentw) na rzecz aplikacji
konsolowych. Wszystko dlatego, i moim zdaniem prociej jest nauczy si danego jzyka na
przykadach zawierajcych jak najmniejsz ilo kodu, tak jak to ma miejsce w przypadku
programw konsolowych.
Skadnia to specyficzne sowa kluczowe (elementy danego jzyka suce do sterowania prac
programu) oraz znaki, ktre musz zosta zapisane w okrelonym porzdku.
Podstawowa skadnia
Kod rdowy musi skada si z polece zakoczonych okrelonymi znakami. Nie mona
pozostawi w kodzie adnego baaganu nawet pominicie jednego znaku czy zwyka
literwka mog spowodowa brak moliwoci uruchomienia programu. Tak jak w jzyku
polskim, pominicie odpowiedniego znaku interpunkcyjnego jest bdem. Takie banalne z
pozoru bdy mog by prawdziw udrk dla pocztkujcego programisty i w konsekwencji
spowodowa spowolnienie prac nad programem. Z czasem wyrabia si pewien nawyk, dziki
ktremu popenia si coraz mniej bdw, a nawet jeli to s one dla bardziej
zaawansowanego programisty atwiejsze do wykrycia.
Na szczcie nowoczesne kompilatory potrafi bardzo precyzyjnie wskaza rdo bdu wraz
ze szczegowym komunikatem oraz numerem linii, w ktrej on wystpi.
Zacznijmy wic. Utwrz swj pierwszy projekt aplikacji konsolowej.
1. Z menu File wybierz New Project.
2. Zaznacz ikon Console Application.
3. Kliknij przycisk OK.
W tym momencie rodowisko utworzy nowy projekt aplikacji konsolowej (rysunek 3.1). W
edytorze kodu zostanie automatycznie wpisany startowy kod naszego programu. Taki kod
moesz w tym momencie skompilowa nie zawiera on adnych bdw i jest to
praktycznie punkt startowy do rozwijania aplikacji.
Najprostszy program
Napiszmy najprostszy program w jzyku C#. W swoim projekcie musimy pozostawi pewne
niezbdne elementy programu. Kod rdowy najprostszego programu moe wyglda
nastpujco:
class Foo
{
static void Main(string[] args)
{
}
}
Oczywicie taka aplikacja nic nie robi, zaraz po uruchomieniu (klawisz F5) zostanie
zamknita.
Wielko znakw
Kompilator jzyka C#, podobnie jak C++ czy Javy, rozrnia wielko znakw. Przykadowo
polecenie Foo nie jest rwne poleceniu foo z punktu widzenia kompilatora to dwa rne
polecenia. Jeeli wic wczeniej programowae w Delphi lub Turbo Pascalu (ktre nie
rozrniay wielkoci znakw), musisz przywykn do tego, e kompilator C# rozrnia
wielko znakw, i powica duo uwagi temu, co piszesz.
Dla przykadu doprowad wygld naszej prostej aplikacji do takiego ksztatu:
class Foo
{
static void main(string[] args)
{
}
}
Po uruchomieniu takiego programu (F5) na ekranie konsoli zostanie wywietlony tekst: Witaj
wiecie!. Jest to potwierdzeniem moich sw o tym, i to wanie w metodzie Main
rozpoczyna si waciwe dziaanie programu, czyli wykonanie instrukcji:
System.Console.WriteLine("Witaj wiecie!");
Taki kod powoduje wywietlenie na ekranie konsoli tekstu zapisanego pomidzy apostrofami.
Nie moesz zapomina o obowizkowych nawiasach, bez ktrych aplikacja nie zostanie
skompilowana.
Na szybkim komputerze program moe zosta skompilowany i uruchomiony z tak prdkoci,
i nie dostrzeemy w ogle jego dziaania, gdy od razu zostanie zamknity. Abymy to my
mogli decydowa, kiedy aplikacja zostanie zamknita, naley doda do programu
odpowiednie instrukcje. Jakie? Wskazwki znajdziesz w dalszej czci rozdziau.
expected.
Jest to bardzo wana zasada i musisz o niej pamita. Nie martw si po jakim czasie znak
rednika na kocu wyraenia bdziesz stawia automatycznie.
Nazw klasy od sowa kluczowego musi dzieli co najmniej jedna spacja. Klasy mog
zawiera m.in. metody, takie jak metoda Main. Zawarto klasy musi mieci si pomidzy
klamrami { oraz }:
class Bar { }
Tak naprawd kada aplikacja jzyka C# musi posiada przynajmniej jedn klas lub
struktur! O strukturach opowiem w dalszej czci ksiki; teraz jedynie zaznaczam ten fakt,
gdy moe on mie znaczenie dla osb, ktre programoway wczeniej np. w jzyku C++.
Wcicia, odstpy
Z punktu widzenia kompilatora nie jest istotne, jak pisany jest kod, czy zawiera odstpy oraz
wcicia. Rwnie dobrze moe by pisany w jednej linii:
class Foo { static void Main(string[] args) {
System.Console.WriteLine("Witaj wiecie!");
} }
Czytelno takiego kodu pozostawia jednak wiele do yczenia i dlatego taki sposb pisania
nie jest zalecany.
Sowa kluczowe
Kady jzyk programowania posiada specyficzne elementy, tzw. sowa kluczowe. Mog one
oznacza rozkaz czy instrukcj, ktre s w dany sposb interpretowane przez kompilator.
Standardowo w rodowisku Visual C# Express Edition sowa kluczowe wyrniane s
kolorem niebieskim. Do sw kluczowych C# mona zaliczy m.in. class, void, static,
string. Przykadowo, sowo class oznacza deklaracj klasy o danej nazwie. Deklaracja w
kontekcie jzyka programowania moe zwyczajnie oznacza utworzenie danego elementu
(np. klasy).
Symbole
Symbole to pojedyncze znaki, ktre wraz ze sowami kluczowymi tworz skadni.
Przykadowo, znak cudzysowu (") okrela pocztek oraz koniec acucha tekstu. Sprbuj
usun cudzysowy z instrukcji wypisujcej tekst na konsoli:
System.Console.WriteLine(Witaj wiecie!);
Podczas prby kompilacji kompilator zasygnalizuje bd, poniewa nieznane s dla niego
instrukcje Witaj oraz wiecie, ktre wraz z cudzysowem tworz acuch, a zwarto
acucha nie jest przez niego sprawdzana pod wzgldem wystpienia danych polece.
Komentarze
Najprostszym elementem kadego jzyka programowania s komentarze. W kodzie
rdowym moesz dodawa wzmianki informacje absolutnie niemajce wpywu na
dziaanie programu. W trakcie kompilacji s one usuwane przez kompilator i nie znajduj si
w pliku
wynikowym .exe. Oczywicie kompilator nie usuwa tych komentarzy fizycznie, tzn. ze rda
programu.
Komentarze maj ogromny wpyw na proces powstawania aplikacji, szczeglnie jeeli
pracujesz w grupie. W kodzie moesz zawrze informacje przeznaczone dla innych
czytajcych go programistw, o tym jak on dziaa i co robi.
Podstawowym typem w jzyku C# s komentarze jednej linii w stylu jzyka C++. Przykad:
class Foo
{
// to jest komentarz
// metoda Main tutaj zaczynamy dziaanie!
static void Main(string[] args)
{
System.Console.WriteLine("Witaj wiecie!");
}
}
Tekst znajdujcy si po znakach // nie bdzie brany pod uwag przez kompilator. W
nowoczesnych edytorach kodu (takich jak w rodowisku Visual C#) komentarze s specjalnie
wyrniane (w moim przypadku kolorem zielonym). Tego typu komentarze nazywane s
komentarzami jednej linii, z tego wzgldu i obowizuj jedynie w linii, ktra rozpoczyna si
od znakw //:
// tu jest komentarz
ale tutaj ju nie obowizuje
rozdziale.
Gwn bibliotek w .NET Framework jest mscorlib.dll. Zawiera ona przestrze nazw
System, ktra z kolei zawiera klas Console! Klasy z kolei zawieraj metody, m.in.
WriteLine, ktra suy do wypisywania tekstu na konsoli. Jak widzisz, system zalenoci jest
do skomplikowany, a wielo poj, jakie trzeba opanowa, moe przyprawi o bl gowy!
Dlatego na razie nie bd Ci zadrcza skomplikowanymi definicjami oraz opisami tych
poj bdziesz je poznawa stopniowo w trakcie czytania tej ksiki.
Na tym etapie wane jest aby wiedzia, e istniej metody, ktre na wzr funkcji
realizuj gotowe zadania takie jak np. wypisywanie tekstu na konsoli. Mona powiedzie, e
s to polecenia, ktre mona wykorzysta w aplikacji, chocia taka terminologia nie jest
raczej dopuszczalna. Nie zagbiajc si w szczegy, postaram si opisa kilka
podstawowych elementw jzyka programowania w C#, ktre do czsto bd
wykorzystywane w dalszej czci ksiki.
Funkcje
Funkcje jako takie nie istniej w C#! Zamiast tego mwimy o wspomnianych ju w tej
ksice metodach. Idea jest w zasadzie identyczna, ale aby unikn nieporozumie, bd si
stara nie uywa sowa funkcja. Z tym sowem spotkasz si zapewne nie raz w swojej
karierze, gdy mechanizm funkcji jest obecny w wielu jzykach programowania.
Funkcje to wydzielony blok kodu realizujcy jakie zadanie.
Chciabym w paru sowach przybliy ide tzw. programowania proceduralnego.
Idea programowania proceduralnego zacza si pojawia wraz z bardziej zaawansowanymi
aplikacjami. Tradycyjny modu projektowania nie sprawdza si dobrze, gdy programy
zaczy by bardziej skomplikowane wwczas ich konserwacja i naprawianie bdw byy
niezwykle trudne.
Kto mdry wymyli wtedy, e mona by byo dzieli program na mniejsze czci tzw.
procedury. Przykadowo, jeeli napisano kod, ktry wywietla pewien komunikat i koczy
dziaanie programu, a w fragment jest uywany wiele razy w tej aplikacji, to naleaoby go
dublowa wiele razy. Powoduje to nie tylko zwikszenie objtoci kodu, ale rwnie potguje
podatno na bdy. Bo co si stanie, jeeli wanie w tym maym, wielokrotnie powtrzonym
w aplikacji fragmencie, wystpi bd? Naleaoby wwczas przeszuka cay kod i w kadym
miejscu poprawia usterk.
Teraz, w nowoczesnych jzykach programowania mona umieci pewien fragment kodu w
procedurze i wywoa j za kadym razem, kiedy zajdzie potrzeba jego wykonania!
Generalnie w jzykach takich jak C++, PHP, Java nie istniej procedury, lecz funkcje. Sama
idea jest identyczna, ale z uwagi na to, i w C# procedury nie istniej, nie bd o nich wicej
wspomina.
Metody
Mam nadziej, e masz ju w gowie pewien zarys tego, czym jest metoda. Ju wkrtce
nauczysz si pisa wasne metody oraz klasy. Aktualnie jedyne, co musisz wiedzie, to to, e
metody mog posiada tzw. parametry. Spjrz na poprzedni przykad uycia metody
WriteLine(). Parametrem tej metody by tekst Hello World!. Innymi sowy
przekazujemy metodzie tekst do wywietlenia na konsoli. Moesz myle o parametrach jak o
danych wejciowych przekazujesz metodzie dane, na ktrych ona operuje.
Obowizkowym elementem kadej metody s nawiasy, w ktrych podaje si parametry.
Jednak nie wszystkie metody w rodowisku .NET Framework maj parametry w takim
wypadku pozostawiamy pusty nawias, np.:
System.Console.Read();
Klasy
O klasach mona powiedzie, i jest to zestaw metod. Przykadowo, klasa Console zawiera
zestaw metod sucych do operowania na konsoli. Myl o klasach jak o przyborniku,
paczuszce zawierajcej przydatne narzdzia.
rodowisko .NET Framework udostpnia szereg klas gotowych do uycia. Przykadowo,
chcemy napisa program, ktry obliczy logarytm z danej liczby. Zamiast samemu mczy si
z pisaniem odpowiedniego kodu, moemy wykorzysta klas Math, ktra jest czci
rodowiska .NET Framework, i udostpniane przez ni mechanizmy.
Przestrzenie nazw
rodowisko .NET Framework jest na pierwszy rzut oka do specyficzne dla osb, ktre
programoway wczeniej na platformie Win32 lub dopiero si ucz. Bo c oznacza zapis:
System.Console.WriteLine("Witaj wiecie!");
Przede wszystkim jest strasznie dugi! Mamy tutaj kilka instrukcji oddzielonych znakiem
kropki. Czy nie atwiej i krcej byoby pisa po prostu sam nazw metody?
WriteLine("Witaj wiecie!");
Operator kropki
Pojcie operator zostanie wprowadzone w dalszej czci rozdziau. Znak kropki (.) stanowi
separator pomidzy nazw przestrzeni nazw, klas a nazw metody. Przy pomocy tego
operatora otrzymujemy dostp do elementw danej klasy czy przestrzeni nazw.
Uywajc rodowiska Visual Studio.NET (jak rwnie Visual C# Express Edition), moesz
korzysta z narzdzi wspomagajcych tworzenie aplikacji. Wykonaj mae dowiadczenie. W
edytorze napisz sowo System., koczc je kropk. Po chwili powinna pojawi si rozwijana
lista zawierajca list klas oraz innych zagniedonych przestrzeni nazw, ktre moemy
wykorzysta (rysunek 3.2).
Rysunek 3.2. Lista klas oraz przestrzeni nazw w obrbie przestrzeni System
Zwr uwag, e sowo kluczowe using znajduje si jeszcze przed klas Program.
Zmienne
W kadym programie, ktry ma wicej ni kilkanacie linijek kodu, zachodzi konieczno
przechowywania tymczasowych danych w pamici komputera. Takie dane przechowywane s
jedynie w trakcie dziaania programu.
Zmienne definiujemy jako obszar w pamici komputera, ktry suy do przechowywania
danych tymczasowych (obecnych w pamici do czasu wyczenia programu), majcych posta
liczb, tekstu itp.
Zapisywanie danych w pamici komputera w obecnych jzykach programowania jest bardzo
proste. Naley okreli unikaln nazw, pod ktr bdziemy uzyskiwa dostp do danych.
Deklarowanie zmiennych
Operacja utworzenia zmiennej nazywana jest deklaracj zmiennej. Musimy okreli unikaln
nazw zmiennej, ktra nie moe si powtarza w obrbie danej klasy czy metody. Musimy
rwnie okreli typ danej zmiennej, czyli zidentyfikowa dane, jakie bdziemy
przechowywa w pamici (tekst, liczby itp.).
Przykadowe zadeklarowanie zmiennej przedstawiem poniej:
class Foo
{
static void Main(string[] args)
{
string Bar;
}
}
W powyszym programie utworzyem zmienn o nazwie Bar oraz typie string. Jak
widzisz, sposb deklaracji jest bardzo prosty, schemat mona przedstawi w ten sposb:
Nazwy zmiennych musimy oddzieli znakiem przecinka. Z punktu widzenia kompilatora nie
ma znaczenia to, w jaki sposb deklarujesz zmienne, wic rwnie dobrze moesz je
zadeklarowa w ten sposb:
string Login;
string FName;
string LName;
Przydzia danych
Zadeklarowalimy ju zmienn, lecz nie przypisalimy do niej adnych danych, wic jej
zawarto jest pusta. Przypisanie danych do zmiennej jest rwnie proste jak deklaracja. W
tym celu uywamy operatora przypisania (=):
class Foo
{
static void Main(string[] args)
{
string Bar = "Hello World";
}
}
Jak widzisz, tutaj napisaem zawarto zmiennej; na skutek tego na konsoli wywietlony
zostanie tekst Hello my darling!.
Typy danych
Do tej pory deklarowalimy zmienne typu string, co oznaczao, i su one do
przechowywania tekstu. Jzyk C# oferuje kilka innych typw danych, ktrymi moemy
posugiwa si we wasnych aplikacjach. Przykadowo, typ int suy do przechowywania
liczb cakowitych:
int X = 10;
Jest to bardzo popularny typ danych. Typy danych jzyka C# rni si od siebie tzw.
zakresem. Np. maksymalna warto, jaka moe zosta przypisana do zmiennej typu int, to
2,147,483,647, natomiast maksymalna moliwa warto typu byte to 255.
Typy danych rni si od siebie rwnie iloci pamici, jak pochaniaj. Np. typ int
zajmuje w pamici 4 bajty, a typ byte jedynie 1. W tabeli 3.1 zaprezentowane zostay
wbudowane typy danych jzyka C# wraz z maksymalnym zakresem.
Tabela 3.1. Wbudowane typy danych jzyka C#
Typ danych
Zakres
byte
od 0 do 255
sbyte
od 128 do 127
short
od 32,768 do 32,767
int
od 2,147,483,648 do 2,147,483,647
uint
od 0 do 4,294,967,295
long
od 9,223,372,036,854,775,808 do 9,223,372,036,854,775,807
ulong
od 0 do 18,446,744,073,709,551,615
float
od 3.402823e38 do 3.402823e38
double
od 1.79769313486232e308 do 1.79769313486232e308
decimal
od 79228162514264337593543950335 do 79228162514264337593543950335
char
Pojedynczy znak
string
acuch znakw typu char
bool
true lub false
Restrykcje w nazewnictwie
Nie jest do koca prawd, e nazwa zmiennej moe by zupenie dowolna. Niestety, istniej
pewne restrykcje, o ktrych trzeba wiedzie. Na przykad pierwszym znakiem nazwy
zmiennej nie moe by cyfra nazwa ta musi rozpoczyna si od litery.
Nazwa zmiennej moe jednak zawiera na pocztku _, ale ju inne znaki, takie jak ( ) * & ^
% # @ ! / = + - [ } ] } ' " ; , . czy ?, nie s dozwolone.
Platforma .NET oraz sam jzyk C# obsuguj standard kodowania znakw Unicode, dlatego
w nazwach zmiennych mona uywa polskich znakw lub jakichkolwiek innych
wchodzcych w skad Unicode:
byte _gegka = 234;
Stae
Stae od zmiennych odrnia to, e zawarto przydzielana jest jeszcze w trakcie pisania kodu
i nie ulega pniejszym zmianom. Zawartoci staych nie mona zmienia w trakcie dziaania
aplikacji. Raz przypisana warto pozostaje w pamici komputera a do zakoczenia
dziaania aplikacji:
const double Version = 1.0;
Version = 2.0; // w tej linii kompilator wskae bd
Operacje na konsoli
Wiesz ju, czym s aplikacje konsolowe. Nie posiadaj one adnych okien, kontrolek itp.,
interakcja z uytkownikiem jest wic saba. Program moe jedynie wypisywa tekst na
konsoli (WriteLine()) lub odczyta tekst wpisany przez uytkownika. Moge zauway, e
program, ktry napisalimy wczeniej, zamyka si zaraz po uruchomieniu i wywietleniu
tekstu. To dlatego, e nie nakazalimy mu czeka na pozwolenie uytkownika.
name to nazwa uprzednio zadeklarowanej zmiennej typu string. Taka konstrukcja nakazuje
przypisanie do zmiennej wartoci tekstu wpisanego i pobranego w oknie konsoli. Innymi
sowy, zmienna name zawiera imi pobrane przez uytkownika.
Kolejna linia kodu, ktr trzeba objani, to:
Console.WriteLine("Mio mi " + name + ". Jak si masz?");
Jak widzisz, acuch tekstu zosta tutaj rozdzielony za pomoc znakw + poczono
go w jedn cao. W trakcie wykonywania instrukcji w miejsce name zostanie podstawione
imi pobrane od uytkownika.
Poznae ju pojcie metoda klasy, potrafisz korzysta z metod, jak rwnie przypisywa oraz
odczytywa dane ze zmiennych. Waciwoci klasy umoliwiaj okrelenie pewnych
zachowa danej klasy.
Przykadowo, klasa Console posiada waciwo BackgroundColor, ktra umoliwia nadanie
koloru ta w oknie konsoli. Ta sama klasa posiada waciwo Title, do ktrej moemy
przypisa tekst, jaki bdzie umieszczony na belce tytuowej okna konsoli.
Myl o waciwociach jak o zwykych zmiennych, do ktrych mona przypisywa dane, a
take je odczytywa. Oto przykadowe uycie dwch wspomnianych waciwoci:
using System;
class Program
{
static void Main(string[] args)
{
// nadanie wartoci dla waciwoci
Console.Title = "Hello World";
// okrelenie koloru ta (ciemny ty)
Console.BackgroundColor = ConsoleColor.DarkYellow;
// odczyt wartoci waciwoci
Console.WriteLine("Tytu tego okna to: " +
Console.Title);
Console.ReadLine();
}
}
Po uruchomieniu takiego programu w oknie konsoli wypisany zostanie tekst: Tytu tego
okna to Hello World. Na samym pocztku przypisaem warto do waciwoci Title
(warto Hello World), a nastpnie okreliem to tekstu. Niezrozumiaa dla Ciebie moe by
operacja okrelenia ta tekstu:
Console.BackgroundColor = ConsoleColor.DarkYellow;
Operatory
W kadym jzyku programowania wysokiego poziomu istniej symbole suce do
sterowania programem. Takie znaki nazywane s przez programistw operatorami. W trakcie
czytania tej ksiki miae okazj zastosowa operator przypisania (=). W rzeczywistoci
istnieje jednak o wiele wicej operatorw, ktre naley omwi.
Symbole operatorw w jzyku C# s praktycznie identyczne z tymi z jzyka C++ oraz Java.
Jeeli wic programowae wczeniej w jednym z tych jzykw, nie bdziesz mia problemu z
zapamitaniem poszczeglnych symboli.
Operatory porwnania
Czynno porwnywania stosujemy w naszym codziennym yciu. Jestemy w stanie na
przykad okreli, ktra z dwch osb jest wysza. Na podstawie liczby koni mechanicznych
silnikw jestemy w stanie oceni, ktry z nich ma wiksz moc.
W matematyce rwnie obowizuj takie znaki porwnania jak > (znak wikszoci) i < (znak
mniejszoci). Identyczne symbole s wykorzystywane w jzykach programowania (patrz
tabela 3.3).
Tabela 3.3. Operatory porwnania
Operator
Jzyk C#
Nierwnoci
!=
Rwnoci
==
Wikszoci
>
Mniejszoci
<
Wiksze lub rwne >=
Mniejsze lub rwne <=
W C# moemy porwnywa wartoci typw np. wartoci zmiennych typu int. Przykady
porwnywania wartoci poka podczas omawiania tzw. instrukcji warunkowych w dalszej
czci tego rozdziau.
Operatory arytmetyczne
Nauka, jak jest matematyka, dawno temu wyksztacia umowne symbole opisujce pewne
dziaania, jak np. dodawanie czy odejmowanie. Oczywicie komputer jako maszyna
umoliwia wykonywanie tych czynnoci w bardzo prosty sposb przy wykorzystaniu symboli
Jak widzisz, operator dziaa rnie, zalenie od sytuacji, w jakiej zosta uyty.
W jzykach takich jak C#, C/C++, Java czy PHP istniej bardzo wygodne operatory
pozwalajce na szybk inkrementacj (zwikszenie wartoci danej zmiennej) oraz
dekrementacj (zmniejszenie wartoci danej zmiennej).
Umieszczajc przed zmienn lub po niej symbol ++, powodujemy zwikszenie jej wartoci o
1. Analogicznie operator -- zmniejsza zawarto zmiennej o 1. Spjrz na poniszy fragment
programu:
int i = 5;
Console.WriteLine(i++);
Console.WriteLine(++i);
Jak mylisz, co zostanie wywietlone w oknie konsoli po wykonaniu takiego kodu? Cyfra 5
oraz 7. Pooenie operatora (przed lub po nazwie zmiennej) ma ogromne znaczenie. W
pierwszym przypadku metoda WriteLine() wywietli warto zmiennej i (czyli 5), a
nastpnie zwikszy j o 1. W drugim przypadku zwikszenie wartoci zmiennej i nastpi
przed wysaniem jej do metody WriteLine().
Oczywicie aby zwikszy warto danej zmiennej, mona zastosowa operator arytmetyczny
wraz z przypisaniem:
int x = 5;
x = x + 1; // rezultat: 6
Operatory logiczne
Operatory logiczne czsto s nazywane operatorami boolowskimi (ang. Boolean operators).
Wynika to z tego, e realizuj one operacje waciwe dla algebry Boolea.
Faktycznym zastosowaniem tych operatorw jest testowanie kilku warunkw. Wemy jaki
przykad z ycia codziennego: Jeeli bd mia 18 lat i 20 tysicy z, kupi sobie samochd.
W tym zdaniu operatorem jest i. Do spenienia kryterium (kupna samochodu) jest zatem
niezbdne spenienie cznie dwch warunkw (posiadania 20 tysicy z oraz ukoczenia 18
lat). Jeeli ktry z tych warunkw nie bdzie prawdziwy kryterium, czyli kupno
samochodu, nie zostanie spenione.
Podobny przykad mona przenie na platform programow. Na przykad jeeli zmienna X
posiada warto 20, a zmienna Y warto 10, to zrb to i tamto. Takie przykady bdziemy
realizowa nieco dalej tymczasem przyjrzyjmy si dostpnym w C# operatorom logicznym
(tabela 3.5).
Tabela 3.5. Operatory logiczne
Operator Jzyk C#
Logiczne i &&
Logiczne lub ||
Zaprzeczenie !
Operatory bitowe
Operatory bitowe oferuj nieco bardziej zaawansowane dziaania na liczbach binarnych. Nie
bdziemy si tym zajmowa wane, aby wiedzie, e takie manipulacje s w C# moliwe,
lecz wymagana jest do tego wiksza wiedza na temat dziaania komputera oraz rnych
systemw liczbowych (dziesitny, dwjkowy itp.).
Operatory bitowe prezentuje tabela 3.6.
Tabela 3.6. Operatory bitowe
Jzyk C#
Operator
Koniunkcja
&
Zaprzeczenie
~
Alternatywa
|
Dysjunkcja
^
Przesunicie w lewo <<
Przesunicie w prawo >>
Operatory przypisania
Sprawa jest dosy prosta. Przy pomocy tego operatora do zmiennej lub waciwoci
przypisujemy okrelone dane:
foo = 4;
bar = foo;
x += 2;
Oznacza to: do wartoci zmiennej x dodaj cyfr 2. Rwnie dobrze mona to wykona w
nastpujcy sposb:
x = x + 2;
Inne operatory
Naturalnie pokrtce omwione przeze mnie grupy operatorw to nie wszystko, co oferuje
jzyk C#. Na tym etapie nauki nie ma jednak sensu prezentowanie bardziej zaawansowanych
operatorw, gdy zwizane s one z pojciami, ktrych jeszcze nie omwiem. Kolejne
operatory bd omawia w dalszej czci ksiki, wraz z prezentowaniem nowych porcji
zagadnie.
Instrukcje warunkowe
Przed chwil bya mowa o operatorach jzyka C#, jednak bez sensu byoby opisywanie ich
bez wzmianki o instrukcjach warunkowych. S to konstrukcje, ktre su do sprawdzania,
czy dany warunek zosta speniony. Jest to praktycznie podstawowy element jzyka
programowania dziki instrukcjom warunkowym moemy odpowiednio zareagowa na
istniejce sytuacje i sterowa prac programu.
Przykadowo: uytkownik musi wpisa swoje imi na samym pocztku dziaania programu.
Moe si jednak zdarzy, e uytkownik specjalnie lub omykowo wpisze liczb. Jeeli
programista nie uwzgldni tej moliwoci i nie wprowadzi odpowiednich zabezpiecze, moe
si to skoczy le dla programu lub (w przypadku wikszych aplikacji) spowodowa bdy
zwizane z bezpieczestwem systemu!
Ostatnimi czasy w przegldarce Internet
Explorer wykryto powane bdy zwizane z bezpieczestwem, ich powodem by pasek
adresw. Zagroenie powstawao w chwili wpisania w pasku adresw odpowiedniego cigu
znakw. Oczywiste jest, jak wane jest sprawdzanie danych dostarczanych przez
uytkownika. Podstawowa zasada brzmi: nie wolno ufa danym podawanym aplikacji przez
uytkownika zawsze naley je sprawdza przed dalszym dziaaniem programu.
Instrukcja if
Podstawow instrukcj warunkow w jzyku C# jest if. Sowo if oznacza w jzyku angielskim
jeeli. Ta instrukcja warunkowa umoliwia przetestowanie jakiego warunku (np.
porwnanie, czy warto zmiennej X jest wiksza ni zmiennej Y) i zaprogramowanie
odpowiedniej reakcji, jeeli zostanie on speniony.
Programujc, uywamy instrukcji warunkowych cay czas. Wyobra sobie, e chcesz napisa
program, ktry pyta uytkownika o imi. Jeeli imi koczy si liter a, moemy z duym
prawdopodobiestwem stwierdzi, e jest ono kobiece. W takim wypadku program wywietla
na ekranie konsoli tekst: Witaj koleanko!. W przeciwnym wypadku wywietlamy tekst:
Witaj kolego!. Do tego wanie su instrukcje warunkowe.
Oto przykadowy program wykorzystujcy instrukcj warunkow if:
using System;
class Program
{
static void Main(string[] args)
{
int x = 5;
if (x == 5)
{
Console.WriteLine("Zmienna x ma warto 5!");
}
Console.ReadLine();
}
}
Generalnie jednak zalecam stosowanie klamer niezalenie od tego, ile instrukcji mamy do
wykonania.
Klamry w jzyku C# peni rol swego rodzaju pojemnika; okrelaj np. pocztek oraz
zakoczenie metody czy klasy. Tak samo oznaczaj rozpoczcie oraz zakoczenie bloku
instrukcji warunkowej.
Instrukcje zagniedone
Instrukcje if mona dowoli zagnieda:
if (x == 5)
{
Console.WriteLine("Zmienna x ma warto 5!");
if (y >= 10)
{
Console.WriteLine("Drugi warunek rwnie speniony");
}
}
ale tylko wtedy, gdy uytkownik poda kobiece imi. Dodatkowo musimy sprawdzi aktualn
godzin, powiedzmy, witamy si zwrotem dobry wieczr, jeeli jest po godzinie 18:00.
Mamy wic dwa problemy. Po pierwsze musimy sprawdzi, jaka jest ostatnia litera imienia
podanego przez uytkownika. Po drugie musimy pobra aktualn godzin. By moe
wybiegam nieco w przyszo i nie powinienem na tym etapie prezentowa tak trudnych
przykadw, ale pragn urozmaici Ci nauk.
Pierwszy problem mona rozwiza nastpujco. Ot przy pomocy symbolu [ oraz ]
moemy odwoa si do dowolnego miejsca w acuchu tekstowym (np. zmiennej typu
string). Przykadowo:
string Foo = "Adam";
Console.WriteLine(Foo[1]);
Znaki w acuchu tekstowym numerowane s od zera. Tak wic imi Adam ma dugo 4
znakw, z tym e pierwszy z nich posiada indeks 0:
----------------Indeks
| Numer
A
| 0
d
| 1
a
| 2
m
| 3
Powyszy kod wywietli wic na konsoli liter d. W naszym programie musimy wic
sprawdzi, jaka jest ostatnia litera acucha (naszego imienia). No dobrze, ale skd program
ma wiedzie, jak dugie imi poda uytkownik? Nie wie! Dlatego musimy uy waciwoci
Length, ktra pobiera i zwraca dugo danego acucha (dugo liczona w iloci znakw w
tekcie). Oto rozwizanie problemu:
string Foo;
int FooLength;
Foo = "Adam";
FooLength = Foo.Length; // mamy dugo acucha
Console.WriteLine(Foo[FooLength -1]);
W takim programie zmienna FooLength bdzie zawiera dugo acucha Foo. Poniewa
chcemy pobra ostatni znak w tekcie, od tej dugoci musimy odj cyfr 1.
Zajmijmy si drugim problemem. Jak pobra aktualn godzin? W tym celu musimy
skorzysta z dobrodziejstw klas .NET Framework, a konkretnie z klasy DateTime. Posiada
ona waciwo Now, ktra zawiera informacje o aktualnej godzinie, dniu, tygodniu itd. Ta z
kolei posiada kolejn waciwo Hour, ktra zwraca aktualn godzin w formie liczby
Uyem tutaj operatora && (logiczne i) do porwnania dwch warunkw. Innymi sowy,
sprawdzamy, czy ostatni liter zmiennej imie jest a oraz czy jest po godzinie 18:00. Jeeli te
dwa warunki zostan spenione, dopiero wwczas bdzie mg zosta wykonany odpowiedni
kod instrukcji (w tym wypadku wywietlenie komunikatu w oknie konsoli).
Zwr uwag, e w ostatnim przykadzie zamiast cudzysowu uyem apostrofw, aby
sprawdzi, czy ostatnim znakiem tekstu jest litera a. Wicej informacji na temat acuchw
znajdziesz w rozdziale 9.
W taki sposb moemy uywa dowolnych operatorw logicznych. Przykadowo, moesz
zastpi && operatorem ||:
if (imie[imie.Length - 1] == 'a' || DateTime.Now.Hour >= 18)
{
// dalszy kod
}
Teraz do wykonania kodu z ciaa instrukcji wystarczy spenienie jednego z dwch warunkw.
Innymi sowy: albo imi bdzie si koczy liter a, albo jest po godzinie 18:00.
Zastosowanie nawiasw
Na lekcjach matematyki w szkole podstawowej uczye si zapewne, e nawiasy okrge maj
najmniejsze znaczenie i ustpuj wanoci nawiasom kwadratowym oraz klamrom.
Zapomnij o tej regule w trakcie programowania. W jzyku C# w instrukcjach warunkowych
posugujemy si jedynie nawiasami okrgymi. Nie oznacza to jednak, e nie moemy
sterowa priorytetem operacji. Spjrz na przykad:
int x, y, z;
x = 2;
y = 4;
z = 10;
Zastosowaem tutaj podwjny nawias. Nakazaem tym samym, aby najpierw sprawdzony
zosta warunek z wewntrznego nawiasu. OK, jest on speniany, poniewa zmienna x zawiera
cyfr 2 i to wystarczy. Nastpnie sprawdzany jest drugi warunek, ktry nie zostaje speniony,
poniewa zmienna z ma warto 10, a wymagane jest, aby bya to liczba wiksza od 20.
Poniewa operator && wymaga, aby obydwa warunki zostay spenione, kod z ciaa instrukcji
if nie zostanie wykonany.
Podobnie jest w przypadku oblicze matematycznych. Jeeli chcemy wymusi wano
danego obliczenia, stosujemy nawiasy:
x = (2 + 2) * 2;
Console.WriteLine(x);
wypadku.
Jest ona do czsto wykorzystywana przez programistw:
// warunek sprawdza, czy jest po godzinie 18:00
if (DateTime.Now.Hour >= 18)
{
Console.WriteLine("Dobry wieczr koleanko!");
}
// warunek sprawdza, czy jest przed godzin 18:00
else
{
Console.WriteLine("Dzie dobry koleanko!");
}
Jeeli warunek w instrukcji if nie zostanie speniony, wykonany zostanie kod z ciaa
instrukcji else. Na listingu 3.2 zaprezentowany zosta peny kod programu.
Listing 3.2. Kod programu stosujcego instrukcje warunkowe
using System;
class Program
{
static void Main(string[] args)
{
string imie;
Console.WriteLine("Cze! Jak masz na imi?");
imie = Console.ReadLine();
// sprawdzamy, czy imi jest kobiece
if (imie[imie.Length - 1] == 'a')
{
// warunek sprawdza, czy jest po godzinie 18:00
if (DateTime.Now.Hour >= 18)
{
Console.WriteLine("Dobry wieczr koleanko!");
}
// warunek sprawdza, czy jest przed godzin 18:00
else
{
Console.WriteLine("Dzie dobry koleanko!");
}
}
else
{
Console.WriteLine("Cze Kolego!");
}
Console.ReadLine();
}
}
Jak widzisz, mamy tutaj kilka instrukcji warunkowych if oraz else zagniedonych w sobie,
co jest oczywicie dozwolone. Jeeli uytkownik poda imi mskie, pierwszy warunek nie
zostanie speniony, czyli zostanie wykonany kod z ciaa instrukcji else (na konsoli ujrzymy
tekst Cze Kolego!).
Instrukcja else if
Istnieje moliwo poczenia instrukcji if i else. Przypumy, e chcemy wywietli
wiadomo w oknie konsoli w zalenoci od aktualnej godziny. Spjrz na poniszy fragment
kodu:
int X = DateTime.Now.Hour;
if (X == 12)
{
Console.WriteLine("Jest poudnie!");
}
else if (X > 12 && X < 21)
{
Console.WriteLine("No c... ju po 12:00");
}
else if (X >= 21)
{
Console.WriteLine("Oj... to to rodek nocy");
}
else
{
Console.WriteLine("Dzie dobry");
}
Jeeli pierwsza instrukcja nie zostanie speniona, program przejdzie do sprawdzania kolejnej.
Jeeli i ta nie zostanie speniona, sprawdzi kolejn i jeszcze nastpn. W ostatecznoci
program wykona kod z ciaa instrukcji else.
Instrukcja switch
Jest to kolejna instrukcja warunkowa jzyka C#. Umoliwia sprawdzanie wielu warunkw.
Spjrz przez chwil na poprzedni przykad. Poczona instrukcja if-else if sprawdzaa warto
zmiennej X i wykonywaa odpowiedni kod w zalenoci od jej wartoci. Uycie instrukcji
switch to dobry pomys na zastpienie wielu instrukcji if.
Skadnia takiej instrukcji jest do specyficzna. Spjrz na poniszy przykad:
i
nt X = 10;
switch (X)
{
case 1:
// kod nr 1
break;
case 5:
// kod nr 2
break;
}
Jeeli warto zmiennej Mandat wynosi 10, zostanie wykonany odpowiedni kod (w tym
wypadku wywietlenie komunikatu), po czym instrukcja switch zostanie zakoczona
(pozostae warunki po sowach kluczowych case nie bd sprawdzane). Usunicie sowa
break spowoduje bd przy kompilacji: Control cannot fall through from one case
label ('case 10:') to another.
Warto domylna
W ostatnim przykadzie moesz zauway, i zmiennej Mandat przypisaem warto 50. W
takim wypadku aden z warunkw nie zostanie speniony, poniewa w instrukcji sprawdzamy
jedynie, czy wartoci jest 10 lub 20. W jzyku C# moemy uy sowa kluczowego
default, aby odpowiednio zareagowa, gdy aden ze wczeniejszych warunkw nie zostanie
speniony:
int Mandat = 50;
switch (Mandat)
{
case 10:
Console.WriteLine("10 z mog zapaci");
break;
case 20:
Console.WriteLine("Oj, 20 z to troszk duo");
break;
default:
Console.WriteLine("Niezidentyfikowana suma");
break;
}
Instrukcja goto
Sowo kluczowe break nakazuje zakoczenie instrukcji switch. Istnieje moliwo
przeskoczenia do innej etykiety case lub default przy pomocy instrukcji goto. Oto
przykad:
int Mandat = 50;
switch (Mandat)
{
case 10:
Console.WriteLine("10 z mog zapaci");
case 20:
Console.WriteLine("Oj, 20 z to troszk duo");
break;
default:
Console.WriteLine("Niezidentyfikowana suma");
goto case 10;
}
Przeanalizujmy taki kod. Poniewa warto zmiennej Mandat to 50, wykonany zostanie blok
z etykiety default. Zawarty w niej kod nakazuje przeskok do etykiety case 10, a ten z kolei
do case 20. Wskutek tak sformuowanego kodu na ekranie konsoli zostanie wywietlony
tekst:
Niezidentyfikowana suma
10 z mog zapaci
Oj, 20 z to troszk za duo
Uwaga! W ten sposb mona zaptli program. Programici okrelaj tym mianem
sytuacj, w ktrej w programie nie znajdzie si instrukcja nakazujca zakoczenie danego
kodu, co skutkuje cigym jego wykonywaniem. Oto przykad prezentujcy tak sytuacj:
case 10:
Console.WriteLine("10 z mog zapaci");
goto case 20;
case 20:
Console.WriteLine("Oj, 20 z to troszk duo");
goto case 10;
default:
Console.WriteLine("Niezidentyfikowana suma");
goto case 10;
W kadej etykiecie znajduje si instrukcja goto, ktra nakazuje skok do innej etykiety case.
Ptle
W wiecie programistw pod sowem ptla kryje si pojcie oznaczajce wielokrotne
wykonywanie tych samych czynnoci. Jest to bardzo wany element kadego jzyka
programowania, dlatego konieczne jest zrozumienie istoty jego dziaania.
Wyobramy sobie sytuacj, w ktrej trzeba kilka razy wykona t sam czynno. Moe to
by na przykad wywietlenie kilku linii tekstu. Zamiast wielokrotnie uywa
Console.WriteLine(), mona skorzysta z ptli. Za chwil przekonamy si, e
zastosowanie ptli w programie wcale nie jest trudne.
Ptla while
Ptla while umoliwia wielokrotne wykonywanie tego samego kodu, dopki nie zostanie
speniony warunek jej zakoczenia. W tym momencie musimy ponownie posuy si
operatorami logicznymi oraz porwnania. Oglna budowa ptli while wyglda nastpujco:
while (warunek zakoczenia)
{
// kod do wykonania
}
Napiszmy jaki prosty program, ktry bdzie wywietla w ptli dowolny tekst, powiedzmy
dziesiciokrotnie. Spjrz na poniszy kod:
using System;
class Program
{
static void Main(string[] args)
{
int X = 1;
while (X <= 10)
{
Console.WriteLine("Odliczanie..." + X);
++X;
}
Console.Read();
}
}
Warunek w ptli while sprawdza, czy warto zmiennej X jest mniejsza lub rwna liczbie 10.
Jeeli tak, zostanie wykonany kod z ciaa ptli, a nastpnie jej kolejna iteracja.
Iteracj nazywamy kolejne wykonanie ptli.
Jeeli uytkownik poda prawidowe imi, zmienimy warto tej zmiennej na true
(prawidowe imi); warunek zakoczenia ptli while to podanie prawidowego imienia:
while (Done == false)
Taki warunek zakoczenia ptli oznacza ni mniej, ni wicej, tylko: kontynuuj dziaanie ptli,
dopki warto zmiennej Done wynosi false. Listing 3.3 zawiera peny kod rdowy
programu.
Listing 3.3. Zastosowanie ptli while
using System;
class Program
{
static void Main(string[] args)
{
bool Done = false;
string name;
while (Done == false)
{
Console.WriteLine("Podaj imi kobiece: ");
name = Console.ReadLine();
if (name[name.Length - 1] == 'a')
{
Done = true; // uytkownik poda prawidowe
imi
Console.WriteLine("Cze " + name);
}
else
{
Moesz uruchomi tak aplikacj, aby sprawdzi jej dziaanie. Pozostae elementy
zastosowane w tym kodzie powinny by Ci znane z lektury tego rozdziau.
Warunki logiczne w ptlach
~~~~~~~~~~~~~~~~
Programujc, musisz przestawi si na mylenie logiczne. Musisz analizowa program,
mylc, jak go zanalizuje komputer. Naley wiedzie, e kady warunek, czy jest to warunek
zakoczenia ptli, czy warunek w instrukcji warunkowej, moe by albo speniony, albo nie.
S tylko dwie moliwoci tak lub nie.
W programie na listingu 3.3 warunek zakoczenia ptli by taki:
while (Done == false)
Oznacza on dokadnie to samo, nakazuje kontynuowanie ptli, jeli warto Done jest rna
od true. Skrcony zapis takiego warunku z zastosowaniem operatorw logicznych:
while (!Done)
Przypominam, i operator ! oznacza logiczne nie. Z punktu widzenia aplikacji oznacza to:
kontynuuj dziaanie ptli, dopki warto zmiennej Done jest rna od true.
Ptla do-while
Ptla do-while jest bardzo podobna do while. Waciwie oprcz budowy rni je tylko jeden
szczeg. Mianowicie ptla do-while zostanie wykonana co najmniej raz, poniewa warunek
jej zakoczenia jest sprawdzany po wykonaniu kodu z jej ciaa. Porwnaj dwie ptle
przedstawione poniej:
int X = 20;
while (X < 20)
{
Console.WriteLine("Ptla while: to nie powinno by
wykonane");
X++;
}
do
{
Console.WriteLine("Ptla do-while, no c to si
wykona");
X++;
}
while (X < 20);
Przypisaem zmiennej X warto 20. W ptli while warunkiem kontynuowania jest to, aby
warto zmiennej X bya mniejsza od 20. Taka ptla nie zostanie wykonana, poniewa
warunek nie zostanie speniony.
Warunek zakoczenia do-while jest taki sam jak w przypadku pierwszej ptli. Mimo tego po
uruchomieniu programu kod z ptli zostanie wykonany raz.
Budowa do-while jest podobna do zwykej ptli while:
do
{
// kod ptli
}
while (warunek zakoczenia);
Ptla for
Dla pocztkujcych programistw ptla for moe wyda si najtrudniejsza do opanowania,
posiada bowiem najtrudniejsz konstrukcj. Ta ptla rni si od wspomnianych wczeniej
tym, i naley zadeklarowa ilo jej iteracji. Uywamy jej zawsze wtedy, gdy wiemy, ile
powtrze danej ptli chcemy wykona.
Oto przykad prostej ptli for:
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Odliczanie..." + i);
}
Kod z ciaa ptli zostanie wykonany dziesiciokrotnie. Budow ptli mona zaprezentowa
nastpujco:
for (warto_startowa; warto_kocowa; licznik_ptli)
{
// kod ptli
}
Podsumujmy:
Po kadej iteracji zwikszany zostaje licznik ptli (czyli warto zmiennej i).
Pocztkowa warto zmiennej i jest przypisywana w nagwku (w naszym przykadzie
pocztkow wartoci jest 1).
W nagwku okrelamy warunek zakoczenia dziaania ptli (ptla bdzie
kontynuowana, dopki warto zmiennej i jest mniejsza lub rwna 10).
Wartoci startow dla tej ptli jest liczba 20. Ptla bdzie wykonywana, dopki warto
zmiennej i bdzie wiksza od 0. Kada iteracja spowoduje zmniejszanie licznika o 1.
Parametry opcjonalne
Zwr uwag, e poszczeglne informacje w nagwku ptli s oddzielone od siebie znakiem
rednika.
Ptla for w jzyku C# jest na tyle elastyczna, e pozwala na pominicie dowolnych
elementw nagwka.
Oto przykad:
for (int i = 20; i > 0; )
{
Console.WriteLine("Odliczanie..." + i);
i -= 2;
}
Zwr uwag, e licznik ptli zmniejszamy w jej ciele. W nagwku ta informacja zostaa
pominita.
Istnieje moliwo pominicia jakiegokolwiek elementu z nagwka:
for (; ; )
W ten sposb tworzymy tzw. ptl forever, ktra bdzie wykonywana w nieskoczono.
Instrukcja break
Zastosowanie sowa kluczowego break poznae przy okazji omawiania instrukcji switch.
Ta instrukcja ma rwnie inne zastosowanie w poczeniu z ptlami umoliwia ich
natychmiastowe zakoczenie (wyjcie z nich).
Oto przykad zastosowania tego sowa kluczowego w poczeniu z ptl for, typu forever:
using System;
class Program
{
static void Main(string[] args)
{
int i = 0;
for (; ; )
{
++i;
Console.WriteLine("Odliczanie..." + i);
if (i == 20)
{
break;
}
}
Console.Read();
}
}
Warto zmiennej i jest zwikszana po kadej iteracji ptli. Mamy rwnie instrukcj
warunkow, ktra porwnuje warto zmiennej i. Jeeli osignie ona warto 20, nastpuje
zakoczenie ptli.
Instrukcja continue
Instrukcja break umoliwia natychmiastowe wyskoczenie z ptli, a continue
przeskoczenie do nastpnej iteracji. Spjrz na poniszy listing:
using System;
class Program
{
static void Main(string[] args)
{
int i = 0;
for (; ; )
{
++i;
Zastosowaem ptle typu forever, w ktrej licznik jest zwikszany w jej ciele. Zastosowaem
rwnie instrukcj warunkow, ktra sprawdza, czy mamy do czynienia z parzyst wartoci
zmiennej i. Jeeli tak program pomija wykonywanie dalszego kodu i przeskakuje do
kolejnej iteracji.
Dla przypomnienia: operator % zwraca reszt z dzielenia. Jeeli po podzieleniu danej liczby
przez dwa reszta z dzielenia wynosi 0, mamy do czynienia z liczb parzyst.
Operator warunkowy
Jeeli jestemy przy temacie instrukcji warunkowych, warto wspomnie o operatorze
warunkowym ?:, ktry przypomina instrukcj if-else. Czsto zastosowanie tego operatora
zwiksza czytelno kodu przy prostych instrukcjach.
Budowa tego operatora jest do specyficzna:
warunek ? (pierwsze wyraenie) : (drugie wyraenie);
Jeeli warunek zostanie speniony, rezultatem takiej operacji jest wyraenie po znaku ?. W
przeciwnym wypadku rezultatem jest wyraenie po znaku :.
Oto przykad zastosowania tego operatora:
string S;
S = 10 > 20 ? "To nie zostanie wywietlone nigdy" : "To
zostanie wywietlone";
Console.WriteLine(S);
Konwersja danych
Na pocztku tego rozdziau wspominaem o typach jzyka C#. Nie wspomniaem jednak o
tym, i niektre typy s ze sob niekompatybilne. Przykadowo, nie mona do zmiennej typu
int przypisa zawartoci zmiennej typu string. Tak samo nie mona do zmiennej typu int
przypisa wartoci zmiennej typu double.
Aby poradzi sobie z tego typu problemem, moemy skorzysta z klasy Convert, ktra
udostpnia odpowiednie metody suce do konwersji danych. Oto przykadowy fragment
programu:
string S;
int I;
S = "18";
I = Convert.ToInt32(S);
I *= 2;
S = Convert.ToString(I);
Console.WriteLine(S);
Rzutowanie
Rzutowanie to sposb na oszukanie kompilatora, ktry jednoczenie daje moliwo
konwersji na rne typy danych. Oczywicie najprociej wytumaczy to na przykadzie.
Spjrz na poniszy kod:
char C;
byte B;
C = 'A';
B = C;
Taki kod nie zostanie prawidowo skompilowany, gdy prbujemy do zmiennej typu byte
przypisa warto zmiennej char, a to s zupenie rne typy danych! Rzutowanie to
mechanizm udostpniany przez jzyk programowania, polegajcy na zmianie typu danej
zmiennej:
B = (byte)C;
Jak widzisz, skadnia jest do charakterystyczna. Przed nazw zmiennej w nawiasach naley
poda nazw typu, na ktry bdziemy rzutowali. W konsekwencji wywoania takiego kodu
zmienna B bdzie zawieraa warto 65, co odpowiada numerowi ASCII litery A.
Rzutowanie to rwnie dobry sposb na zaokrglanie, a raczej obcinanie czci liczb
zmiennoprzecinkowych. Spjrz na poniszy fragment:
double d;
int i;
d = 454.54;
i = (int)d;
Przykadowa aplikacja
Poznae ju podstawowe elementy i mechanizmy jzyka programowania. Wystarczy to do
napisania prostej gry. Zasady s proste. Komputer losuje liczb, a zadaniem uytkownika jest
odgadnicie tej prawidowej. Gra dwch uytkownikw, a po kadym strzale (turze)
program podpowiada, czy naley celowa wyej, czy niej.
Wykorzystamy ptl for oraz instrukcje warunkowe. Zanim jednak przejdziemy do
kodowania caego mechanizmu, naley pobra od uytkownikw ich imiona:
Console.Write("Podaj imi gracza nr 1:
player1 = Console.ReadLine();
");
");
");
");
{
Console.WriteLine("Strzelaj niej!");
}
else
{
Console.WriteLine("Gratulacje! Wygra gracz "
+ player);
Console.Beep();
Done = true;
}
}
Console.Read();
}
}
Dyrektywy preprocesora
Preprocesor to mechanizm jzyka C#, ktry przetwarza kod rdowy programu przed jego
kompilacj. Dyrektywy preprocesora to specjalne sowa kluczowe, ktre analizowane s
jak sobie powiedzielimy przed waciw kompilacj.
Dziki dyrektywom moemy wpyn na zawarto kodu, ktry bdzie kompilowany.
Podczas pisania programu cay czas testujemy jego dziaanie poprzez kompilowanie i
uruchamianie go. W kodzie rdowym moemy zawrze instrukcje, ktre pomocne nam
bd w trakcie dziaania programu, np. w celu zidentyfikowania ewentualnych bdw. Ten
dodatkowy kod moemy wyczy w momencie, gdy kompilujemy finaln wersj aplikacji
gotow do dystrybucji.
Dziaanie dyrektyw preprocesora jzyka C# jest podobne do tej z jzyka C++.
Dyrektywy kompilatora poprzedzane s symbolem # i nie s zakaczane znakiem rednika.
Np.:
#define DEBUG
#if
#else
#elif
#endif
#define
#undef
#warning
#error
#line
#region
#endregion
#pragma
#pragma warning
#pragma checksum
Deklarowanie symboli
Jak wspomniaem, dyrektywa #define suy do deklarowaniu symbolu, ktry jest
odpowiednikiem staych jzyka C#. Przy pomocy dyrektywy #if moesz sprawdza, czy
dany symbol jest zadeklarowany, czy te nie:
#if DEBUG
Console.WriteLine("Symbol DEBUG jest zadeklarowany");
#endif
Instrukcje warunkowe
Dyrektywy #if, #elif, #else oraz #endif su do budowania instrukcji warunkowych
preprocesora. Dziki nim moemy ustali, jaki kod zostanie skompilowany. Instrukcje
Bdy i ostrzeenia
Dyrektywy #error oraz #warning su do generowania bdw i ostrzee w trakcie
kompilacji programu. Rzadko s stosowane, najczciej w poczeniu z instrukcjami
warunkowymi:
#define DEBUG
using System;
namespace FooApp
{
class Program
{
Wraz z dyrektyw naley okreli, czy wyczamy pokazywanie jakiego bdu (disable), czy
aktywujemy
(restore). Naley rwnie poda numer bdu.
Moemy jednorazowo wyczy lub wczy pokazywanie wielu bdw. W takim przypadku
numery bdw naley wymieni po przecinku. Dyrektywa #pragma warning disable (bez
podania numeru bdw) dezaktywuje wywietlanie wszelkich ostrzee.
Podsumowanie
Pocztki s zawsze trudne czy to pocztki nauki jzyka obcego, czy nowa praca.
Spotykamy si z rzeczami nowymi, do ktrych musimy przywykn. Identycznie jest z nauk
jzyka programowania. W tym rozdziale zasypaem Ci spor dawk informacji na temat
jzyka C#, ktre mog by dla Ciebie nie do koca zrozumiae. Niestety na tym etapie
naleao wprowadzi kilka do trudnych poj, ktrymi posuguj si programici, a ktre
szczegowo zostan objanione w dalszej czci ksiki.
Na podstawie wiedzy zdobytej w tym rozdziale jeste ju w stanie pisa proste aplikacje.
Poznae bowiem podstawowe mechanizmy suce do sterowania prac programu, takie jak
instrukcje warunkowe oraz ptle. W dalszej czci ksiki stopniowo bd zapoznawa Ci z
trudniejszymi elementami jzyka C#.
[1] Funkcje WinAPI s umieszczone w bibliotekach DLL. Teoretycznie wic mogyby istnie
dwie funkcje o tej samej nazwie, tyle e w rnych bibliotekach DLL. Mogoby to jednak
utrudnia prac programicie zastanawiaby si on, z ktr z funkcji ma do czynienia i jak
j wykorzysta.
Rozdzia 4
Przegld .NET Framework
W rozdziale 2. omwiem podstawowe aspekty platformy .NET. Powiniene ju zna
podstawowe pojcia zwizane z t technologi, a take umie pisa proste aplikacje w C#.
W tym rozdziale skupi si przede wszystkim na rodowisku .NET Framework. Poniewa
rodowisko .NET Framework obejmuje wiele poj oraz technologii, naley je opisa na tyle
szczegowo, aby wiedzia, jak dziaaj aplikacje w rodowisku .NET.
rodowisko .NET Framework obejmuje swym zakresem wszystkie warstwy tworzenia
oprogramowania: od systemu operacyjnego po bibliotek klas (jak np. Windows Forms). Co
to oznacza? Ot rodowisko .NET Framework zainstalowane na komputerze czuwa nad
aplikacj, odpowiada za odpowiednie zarzdzanie pamici oraz obsug bdw. Oczywicie
Ty jako programista nie musisz si tym przejmowa. Wszystko odbywa si automatycznie.
rodowisko CLR
Wsplne rodowisko uruchomieniowe (CLR) stanowi podstawowy element platformy .NET
Framework. W momencie uruchomienia aplikacji .NET CLR odpowiada za jej sprawne
wykonanie (zaadowanie do pamici), przydzia pamici, obsug bdw itp. W
standardowym modelu programowania Win32 za tego typu czynnoci odpowiada
system operacyjny (Windows). Po zainstalowaniu na komputerze rodowiska .NET
Framework odpowiednie biblioteki systemu pozwalaj na rozpoznanie, czy dany program jest
aplikacj Win32, czy te .NET. Jeeli jest to aplikacja .NET, uruchomione zostaje rodowisko
CLR, pod ktrego kontrol dziaa program. To, co dzieje si w tle, nas nie interesuje, nie
trzeba si tym przejmowa.
W jaki jednak sposb system operacyjny rozpoznaje, ktry program jest aplikacj .NET?
Dzieje si tak dlatego, e aplikacja wykonywalna .NET (plik .exe) jest inaczej zbudowana ni
standardowe programy Win32. T kwesti postaram si wyjani w paru kolejnych
podrozdziaach.
Kod poredni IL
Kompilatory dziaajce pod kontrol systemu Windows kompiluj kody rdowe do postaci
32-bitowego kodu maszynowego. W efekcie otrzymujemy aplikacje .exe czy biblioteki .dll.
Taki sposb uniemoliwia przeniesienie aplikacji na urzdzenia czy systemy, ktre dziaaj
pod kontrol innych procesorw. Moe jednak take stwarza problemy z innymi wersjami
systemu operacyjnego (w tym wypadku Windows). Dua w tym rola API systemu, z ktrego
korzystaj aplikacje. Wiadomo, e np. w systemie Linux nie istniej biblioteki DLL,
odpowiednik WinAPI systemu Windows 1. Z tego wzgldu takie programy nie mog zosta
uruchomione na systemach innych ni Windows.
Rozwizaniem tego problemu jest kompilacja programu do kodu poredniego nazywanego
Common Language Infrastructure (z ang. architektura wsplnego jzyka, CLI).
CLI na platformie .NET jest czsto nazywany MSIL (ang. Microsoft Intermediate Language)
lub po prostu IL.
Kod poredni IL przypomina kod jzyka Asembler:
.method family hidebysig virtual instance void
Dispose(bool Disposing) cil managed
{
// Code size
30 (0x1e)
.maxstack 2
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0016
IL_0003: ldarg.0
IL_0004: ldfld
class
[System]System.ComponentModel.Container
WinForm.TWinForm1::Components
IL_0009: brfalse.s IL_0016
IL_000b: ldarg.0
IL_000c: ldfld
class
[System]System.ComponentModel.Container
WinForm.TWinForm1::Components
IL_0011: callvirt
instance void
[System]System.ComponentModel.Container::Dispose()
IL_0016: ldarg.0
IL_0017: ldarg.1
IL_0018: call
instance void
[System.Windows.Forms]System.Windows.Forms.Form::Dispose(bool)
IL_001d: ret
} // end of method TWinForm1::Dispose
W takiej postaci kod IL jest kompilowany do kodu maszynowego, ktry moe ju zosta
uruchomiony. Tak wanie dziaa jzyk Java, z ktrego pomys zaczerpn Microsoft,
projektujc platform .NET. Jeeli wic kompilujesz swoje aplikacje napisane w C#, w
rzeczywistoci s one kompilowane do kodu poredniego IL, a nie do kodu maszynowego.
Wszystko to jest moliwe dziki tzw. maszynom wirtualnym, czyli aplikacjom
przystosowanym do konkretnej wersji systemu/procesora. rodowisko CLR odpowiada za
kompilacj kodu poredniego na maszynowy w trakcie uruchamiania aplikacji. Dzieje si to
jednak na tyle szybko, e jest niezauwaalne dla uytkownika.
Moliwe jest rwnie jednorazowe skompilowanie danego programu od razu na kod
maszynowy. Dziki temu przy kadym uruchamianiu programu s oszczdzane zasoby
systemowe potrzebne do uruchomienia kompilatora JIT (ang. Just-In-Time).
Modu zarzdzany
Kady modu zarzdzany jest przenonym plikiem systemu Windows. Moduy zarzdzane s
generowane przez kompilatory zgodne z platform .NET, a w ich skad wchodz nastpujce
elementy:
Podzespoy
To jest bardzo wane pojcie, ktrym nieraz bd si posugiwa w tej ksice. W
najprostszym ujciu podzespoem (ang. assembly) nazywamy kad aplikacj dziaajc pod
kontrol .NET.
Kady modu zarzdzany wymaga podzespou. Jeden podzesp moe zawiera jeden lub
wicej moduw zarzdzanych. Podzesp moe zawiera rwnie inne pliki, takie jak
dokumenty, grafik itp.
Na tym etapie zagadnienie to moe wyda Ci si niezwykle skomplikowane. Podzespoy
mog zawiera jeden lub wicej moduw zarzdzanych, w ktrych z kolei znajduj si kod
zarzdzany oraz metadane.
Dziaanie CLR
Wspomniaem wczeniej, e CLR zajmuje si uruchamianiem aplikacji napisanej w .NET
oraz oglnie zarzdzaniem procesem jej dziaania. Przeledmy proces uruchamiania
aplikacji w .NET. Oto kilka etapw, ktre przechodzi aplikacja od momentu, gdy uytkownik
zarzdzi jej uruchomienie:
Class Loader
Dotychczas jedynym formatem rozpoznawanym przez systemy Windows jako aplikacja
wykonywalna by stary format PE. Stary format PE zawiera skompilowany kod maszynowy
aplikacji. Kiedy instalujemy bibliotek .NET w systemie, dodawane jest uaktualnienie
mwice o nowym formacie PE, dziki czemu system jest w stanie rozpozna rwnie nowy
format plikw wykonywalnych.
Skoro teraz aplikacja .NET jest rozpoznawana przez system, ten w momencie jej
uruchamiania oddaje sterowanie do CLR. CLR odczytuje zawarto pliku oraz list klas
uywanych w programie. Lista klas jest odczytywana z przernych miejsc gwnie jest to
manifest oraz metadane, a take plik .config, ktry moe by doczany do programu. Po
odczytaniu klas nastpuje obliczenie iloci pamici potrzebnej do ich zaadowania. Nastpnie
klasy s adowane do pamici
.
Weryfikacja
Po zaadowaniu klas do pamici zawarto programu, czyli metadane oraz kod IL, zostaje
poddana weryfikacji. Jest to wany etap, gdy w przypadku niepowodzenia kod IL nie
zostanie przekazany kompilatorowi JIT.
Kompilator JIT
Kompilator JIT odgrywa znaczc rol w procesie uruchamiania aplikacji. Kod po
weryfikacji jest sprzekazywany do kompilatora, ktry kompiluje go do kodu maszynowego, a
nastpnie gotowy program zostaje zaadowany do pamici. Znajduje si on w tej pamici do
czasu zakoczenia dziaania aplikacji.
System CTS
Wiadomo ju, czym s typy jzyka programowania. Podstawowe typy, takie jak int, string,
omwiem w poprzednim rozdziale.
Wsplny system typw (ang. Common Type System) jest bogatym zbiorem typw
opracowanych przez firm Microsoft na potrzeby .NET. Programujc w C#, korzystamy z
tych typw cay czas! W rzeczywistoci bowiem np. typ int odpowiada typowi
System.Int32 z platformy .NET! Spjrz na poniszy przykad:
int Foo;
System.Int32 Bar;
Foo = 23;
Bar = Foo;
Jak widzisz, zadeklarowaem w programie dwie zmienne typu cakowitego, ktre s sobie
rwnowane.
W rzeczywistoci Int32 czy String to klasy znajdujce si w przestrzeni nazw System.
Mimo e typy jzyka C# wskazuj na odpowiedniki rodowiska .NET Framework, atwiej jest
stosowa skrcone wersje, takie jak int czy string. Takie kroki zostay poczynione z myl o
programistach jzyka C/C++, w ktrym obecne s takie wanie typy.
Tabela 4.1 zawiera list wbudowanych typw jzyka C# wraz z ich odpowiednikami CTS.
Tabela 4.1. Typy CTS
Typ C# Typ .NET Framework
bool
byte
sbyte
char
decimal
double
float
int
uint
long
ulong
object
short
ushort
string
System.Boolean
System.Byte
System.SByte
System.Char
System.Decimal
System.Double
System.Single
System.Int32
System.UInt32
System.Int64
System.UInt64
System.Object
System.Int16
System.UInt16
System.String
Specyfikacja CLS
Do tej pory moliwoci w komunikowaniu si pomidzy aplikacjami byy nieco ograniczone.
Kod aplikacji w rodowisku Win32 moe by dzielony na biblioteki DLL. Po umieszczeniu
funkcji w bibliotece DLL istnieje moliwo ich eksportu, co z kolei pozwala na ich
wykorzystanie przez aplikacje EXE.
W rodowisku .NET aplikacje maj pen zdolno do komunikowania si. Jeden podzesp
moe wykorzystywa funkcje drugiego i na odwrt. Wszystko to dziki kompilacji do kodu
poredniego IL, do ktrego s kompilowane wszystkie aplikacje, bez wzgldu na to, czy s
pisane w C#, czy w Delphi. Tak wic jeeli napisano program do szyfrowania danych, mona
(jeeli tylko programista tego zechce) udostpni jego funkcjonalno
innym podzespoom. Praktyczne przykady tego zagadnienia zaprezentuj w rozdziale 11.
Nie tylko jzyk poredni ma tu znaczenie, ale rwnie specyfikacja CLS (ang. Common
Language Specification, czyli wsplna specyfikacja jzykw). Jest to zestaw regu
okrelajcych nazewnictwo oraz inne kluczowe elementy jzyka programowania. Jeli
projektanci jzyka programowania, ktry docelowo ma dziaa dla .NET, chc, aby by on
kompatybilny z CLS oraz mia zdolno do komunikowania si z pozostaymi podzespoami,
musz dostosowa swj produkt do okrelonych wymaga.
Dodatkowe informacje na temat specyfikacji CLS mona take znale na stronach firmy
Microsoft:
http://msdn.microsoft.com/net/ecma/.
Biblioteka klas
Czytelnik wci spotyka si ze sowami: klasa, obiekt, ktrych zreszt czsto uywam w tym
rozdziale. Wspominaem wczeniej, e na platformie Win32 aplikacje korzystay z WinAPI,
czyli z zestawu funkcji pomocnych przy programowaniu. Wkrtce po tym pojawiy si takie
biblioteki jak MFC (w rodowisku Visual Studio) czy VCL (w rodowisku Delphi), ktre
jeszcze bardziej uatwiay programowanie.
Biblioteka klas .NET Framework (ang. .NET Framework Class Library) stanowi zestaw setek
klas, bibliotek, interfejsw, typw, ktre maj zastpi WinAPI. W zaoeniu FCL ma
poczy funkcjonalno WinAPI oraz dodatkowych bibliotek, takich jak VCL czy MFC
(ang. Microsoft Fundation Classes).
W jzyku C# korzystamy jedynie z klas .NET Framework, nie uywamy w ogle funkcji
WinAPI. Poniewa s setki klas, polecam korzystanie z internetowej dokumentacji
rodowiska .NET Framework (dostpnej na stronie firmy Microsoft), ktra zawiera opis
wszystkich klas, metod itp.
Powiedzielimy, e do programowania na platform .NET mona uywa wielu rnych
jzykw programowania. Piszc swj program, np. w Delphi, mog uywa tych samych klas
co w jzyku C#:
Console.WriteLine("Hello World"); // C#
Ani rodowisko Visual C# Express Edition, ani kompilator nie maj informacji
, ktry z plikw rdowych projektu jest plikiem gwnym. Po prostu ktry z moduw
musi zawiera miejsce startowe w postaci metody Main(). Jeeli kompilator odnajdzie
wicej ni jedn metod Main(), nawet jeeli bd one w rnych klasach i przestrzeniach
nazw, zasygnalizuje bd.
Wieloznaczno
Podzia projektu na poszczeglne pliki rdowe nie ma w C# takiego znaczenia jak podzia
na przestrzenie nazw (ang. namespaces).
Zamy, e mam firm. Pisz swoje aplikacje, a nastpnie udostpniam je innym. Pamitasz,
jak mwiem, e skompilowane podzespoy mog by w .NET wykorzystywane przez inne
aplikacje? Napisaem wietn aplikacj suc do kompresji dwiku. Mog w niej
zadeklarowa klas Console czy nawet DateTime, ktre potencjalnie mog kolidowa z
klasami o tych samych nazwach z przestrzeni System. Jednak tak nie jest, gdy swoje klasy
umieszczam w przestrzeni nazw Boduch:
namespace Boduch
{
class Foo
{
}
class Bar
{
}
class Console
{
}
}
Chodzi o to, e dana klasa moe mie zupenie inne znaczenie w zalenoci od tego, w jakiej
przestrzeni nazw zostaa zadeklarowana. W jednej aplikacji mog mie wiele przestrzeni
nazw. Nie powinna wic wydarzy si sytuacja, w ktrej jakie nazwy si dubluj w obrbie
jednej przestrzeni.
Kada przestrze nazw moe zawiera kolejne, zagniedone przestrzenie.
Wicej informacji na temat przestrzeni nazw oraz zawartych w niej klas mona znale na
stronie http://msdn.microsoft.com.
Wczenie danej przestrzeni nazw nastpuje przy pomocy sowa kluczowego using, ale
powiniene ju o tym wiedzie z lektury poprzedniego rozdziau.
Podsumowanie
W tym rozdziale zasypaem Ci spor dawk teoretycznych informacji odnonie rodowiska
.NET Framework. Wydaje mi si, e programista piszcy swoje aplikacje w jzyku C#, a
wic cile zwizanym z platform .NET, powinien zna zasady dziaania na niej tych
programw. Podstawowym skadnikiem platformy i idei .NET jest wanie rodowisko .NET
Framework, ktre to definiuje wiele poj zwizanych z procesem wytwarzania
oprogramowania. Ja zaprezentowaem jedynie podstawowe zwizane z tym pojcia. Jeeli
jeste zainteresowany szczegow dokumentacj technologii CLR czy CLS, odsyam Ci do
stron internetowych firmy Microsoft.
[1] Od wielu lat nieustannie trwaj prace nad odpowiednikiem WinAPI na system Linux.
Projekt Wine, bo o nim tutaj mowa, jest ju na tak wysokim stadium zaawansowania, e
umoliwia uruchamianie niektrych aplikacji systemu Windows na Linuksie.
[2] Niestety nie znalazem dobrego polskiego odpowiednika tego sowa, ktre w peni
oddawaoby istot rzeczy.
Rozdzia 5
Programowanie obiektowe
Wiele do tej pory mwiem o klasach, metodach i waciwociach. Unikaem jednak
stosowania skomplikowanych poj, ktre nie zostay wczeniej objanione. W tym rozdziale
zamierzam objani, na czym polega technika programowania zwana programowaniem
obiektowym.
Idea programowania obiektowego staje si coraz powszechniejsza, gdy aplikacje staj si
coraz bardziej skomplikowane i zoone. Nie wystarcza ju tylko podzia aplikacji na moduy,
taki kod naley podzieli na poszczeglne klasy. Starsze jzyki programowania, takie jak np.
C, nie umoliwiaj programowania obiektowego taka moliwo pojawia si dopiero u
nastpcy wspomnianego jzyka C++. Omawiany tutaj jzyk, czyli C#, jest w peni
obiektowy, dlatego dla programisty wrcz niezbdne jest poznanie, czym jest obiektowo i
jak tworzy wasne klasy. Tym wanie zajmiemy si w tym rozdziale.
strukturalnego, poniewa jest to jzyk typowo obiektowy. Taka sytuacja jak przedstawiona
powyej nie ma prawa si wydarzy.
System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
Ten plik zawiera kod rdowy naszego formularza Windows Forms. Na samym pocztku do
programu wczanych jest kilka najpopularniejszych przestrzeni nazw. W dalszej czci kodu
zadeklarowana jest przestrze nazw dla naszej aplikacji, a w niej klasa Form1. Zawiera ona
metod o tej samej nazwie, ktra wywouje funkcj InitializeComponent(). Ta funkcja nie
jest czci rodowiska .NET Framework, lecz zostaa utworzona przez rodowisko
Visual C# Express Edition w sposb automatyczny.
Spjrz na rysunek 5.1. Okno Solution Explorer zawiera hierarchiczn struktur naszej
aplikacji.
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Nasza aplikacja rozpoczyna swoje dziaanie od tego pliku, a konkretnie od klasy Program i
metody Main() (jak zapewne pamitasz, metoda Main() stanowi gwny element programu i
to w niej musz znajdowa si polecenia, ktre zostan wykonane w pierwszej kolejnoci). W
ciele metody znajduj si odwoania do metod klasy Application. Pierwsza z metod
EnableVisualStyles() uaktywnia wizualny tryb aplikacji. Kolejna metoda
SetCompatibleTextRenderingDefault() zwizana jest z graficzn bibliotek GDI. Nie
powiniene si teraz tym przejmowa. Najwaniejsza w tym kodzie jest metoda Run(), ktra
wywietla gwny formularz Form1. Jeeli skomentujemy t lini, program zostanie
uruchomiony, lecz aden formularz nie zostanie pokazany. W skutek tego program
natychmiast zostanie zamknity.
Jak widzisz, w parametrze metody Run() znajduje si konstrukcja new Form1(). Operator new
nakazuje utworzenie nowej instancji klasy Form1. Zostanie to omwione w dalszej czci
rozdziau.
Modu Form1.Designer.cs
Jest jeszcze jeden wany modu, o ktrym naley wspomnie, a mianowicie
Form1.Designer.cs. Zawiera on kod rdowy odpowiedzialny za zachowanie formularza
oraz wszystkich umieszczonych na nim komponentw. Zawarto czystego projektu
Windows Forms moe wyglda tak:
namespace WindowsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components =
null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources
should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not
modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new
System.ComponentModel.Container();
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}
#endregion
}
}
Generowanie kodu
Wykonajmy may test obrazujcy, w jaki sposb projektowanie wizualne oddziauje na kod
rdowy naszego projektu. Kliknij zakadk Form1.cs [Design] w oknie edytora kodu.
Dziki temu z powrotem przeczysz si do trybu projektowania wizualnego. W oknie
ToolBox znajd komponent Label, a nastpnie umie go na formularzu.
Jeeli okno ToolBox jest niewidoczne, z menu View wybierz ToolBox.
Ponownie otwrz modu Form1.Designer.cs. Zwr uwag na to, e zawarto metody
InitializeComponent() zostaa zmieniona:
Sowo kluczowe private to tzw. modyfikator, ale pojcie to zostanie objanione w dalszej
czci ksiki. Taki kod oznacza deklaracj zmiennej o nazwie label1, typu
System.Windows.Forms.Label.
Jak powiedziaem wczeniej, zawarto pliku Form1.Designer.cs nie powinna Ci w ogle
interesowa. rodowisko Visual C# Express Edition automatycznie generuje kod na
podstawie czynnoci, jakich dokonujemy w trakcie projektowania. Np. przesunicie
komponentu Label zaowocuje uaktualnieniem kodu w pliku Form1.Designer.cs.
Ukrywanie kodu
Edytor Visual C# Express Edition posiada moliwo ukrywania fragmentu kodu. W takim
przypadku z lewej strony edytora zostan wywietlone mae ikony, ktrych kliknicie
spowoduje rozwinicie ukrytego fragmentu. Jest to do ciekawe rozwizanie umoliwia
zwikszenie przejrzystoci kodu i schowanie fragmentu, ktry w danym momencie nie
interesuje programisty.
Wszystko to odbywa si za spraw sowa (dyrektywy) #region:
#region Windows Form Designer generated code
Zwr teraz uwag, i po lewej stronie edytora kodu znajduje si ikona, dziki ktrej ten
fragment mona schowa (rysunek 5.2).
Programowanie zdarzeniowe
Powiedzielimy ju sobie o programowaniu proceduralnym, strukturalnym. Tematyk tego
rozdziau jest programowanie obiektowe. Naley jeszcze wspomnie o metodzie zwanej
programowaniem zdarzeniowym. Typowa aplikacja Windows Forms po uruchomieniu jest
adowana do pamici. Nastpnie oczekuje na reakcj uytkownika nakazujc wykonywanie
Generowanie zdarze
Reakcj na okrelone czynnoci, czyli zdarzenia, najprociej generowa przy uyciu
rodowiska Visual C# Express Edition. Okno Properties zawiera waciwoci danego
komponentu (pooenie, rozmiar itp.) oraz zdarzenia (rysunek 5.3).
Lista zdarze jest domylnie pogrupowana w kategorie. Jak widzisz, moemy zaprogramowa
reakcj na okrelone czynnoci, takie jak podwjne kliknicie (DoubleClick) czy uycie
klawiszw klawiatury (KeyPress).
1.
2.
3.
4.
5.
Przykadowy program
Do tej pory pokazywaem, w jaki sposb zmienia waciwoci komponentu jedynie z
poziomu okna Properties. Naleaoby rwnie wiedzie, e jest to moliwe take z poziomu
kodu rdowego za pomoc operatora odwoania (.).
Nasz przykadowy program bdzie zmienia pozycj przycisku na formularzu. Nowa pozycja
bdzie losowana i ustawiana za kadym razem, gdy uytkownik kliknie przycisk.
Wykorzystanie klasy losujcej Random prezentowaem ju w rozdziale 3. Problemem jest
natomiast ustawienie nowej pozycji naszego komponentu.
Pooenie kadego komponentu w Windows Forms okrela waciwo Location, ktra
wskazuje na struktur System.Drawing.Point. Struktura ta (bdziemy o tym mwi w
kolejnym rozdziale) ma dwa pola X oraz Y. Listing 5.1 zawiera kod rdowy moduu
Form1.cs.
Listing 5.1. Kod rdowy moduu Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// tworzenie nowej instancji klasy
Random RandomObj = new Random();
// pobranie rozmiarw formularza
Point StartPoint = new Point(this.Size);
// wylosowanie nowej pozycji
int X = RandomObj.Next(1, StartPoint.X - 75);
int Y = RandomObj.Next(1, StartPoint.Y - 23);
this.button1.Location = new Point(X, Y);
}
}
}
Zacznijmy od pocztku. Jeeli chcemy oprogramowa dane zdarzenie (np. zdarzenie Click),
musimy je wygenerowa, co spowoduje utworzenie tzw. procedury zdarzeniowej. W tym
wypadku bdzie to metoda button1_Click(). Wygenerowanie zdarzenia Click nastpuje po
podwjnym klikniciu danego obiektu (w naszym przypadku komponentu Button). Spjrz
ponownie na list zdarze. Zwr uwag, e do zdarzenia Click przypisana jest metoda
button1_click (rysunek 5.4).
spowoduje zmian wartoci waciwoci Text na Tytu okna. W ten sam sposb mona si
odwoa do innych komponentw umieszczonych na formularzu oraz ich waciwoci. Jak
widzisz, w dalszej czci kodu instrukcja this.button1.Location.X odwouje si do
pooenia komponentu w poziomie.
Inna rzecz, ktra wymaga objanienia, to metoda Format() z klasy String. Metoda suy do
formatowania acucha. Innymi sowy, fraza {0} zostanie zamieniona przez warto
parametru metody Format() (czyli this.button1.Location.X); {1} zostanie zamieniona
przez warto kolejnego parametru itd. Zostanie to szczegowo objanione w rozdziale 9.
Obsuga zdarze
Ta sama metoda (procedura zdarzeniowa) moe by obsugiwana przez kilka zdarze.
Przykadowo, znajd na licie zdarze pozycj MouseHover. Z listy rozwijanej wybierz
button1_click(). Od tego momentu zdarzenie MouseHover bdzie obsugiwane przez
metod button1_click(). Moesz teraz uruchomi nasz program i sprawdzi jego dziaanie.
Zdarzenie MouseHover wykrywa moment, w ktrym kursor myszy znajdzie si nad danym
komponentem. Innymi sowy, przy przesuniciu kursora nad przycisk zostanie wykonana
metoda button1_click(), co spowoduje zmian pozycji komponentu.
Czasami moe zaj potrzeba przypisania tej samej procedury zdarzeniowej do kilku zdarze
rnych komponentw. Przykadowo, jeeli umiecimy na formularzu kolejny przycisk,
moemy oprogramowa jego zdarzenie Click, uywajc procedury zdarzeniowej
button1_click().
Klasy
Klasy to podstawa projektowania aplikacji w jzyku C#. Powiedzielimy sobie ju w
rozdziale 3., i nawet najprostszy program C# musi posiada przynajmniej jedn klas.
Na klasach opiera si cae rodowisko .NET Framework, w tym biblioteka Windows Forms.
Musisz przyzwyczai si do myli, i wszystko w C# jest klas! Kady typ, komponent! Np.
typ System.Int32 rwnie jest klas, a kiedy tworzymy now klas, automatycznie staje si
ona typem! Wiem, e jest to do zawia terminologia, ale wszystko powinno si wyjani w
dalszej czci rozdziau.
Klasy posiadaj funkcje, ktre nazywane s metodami. To rwnie powiniene wiedzie z
lektury poprzednich rozdziaw. Do tej pory o klasach wspominaem do sporadycznie, na
tyle, na ile byo to konieczne. Teraz mam zamiar omwi wszystkie elementy zwizane z
tworzeniem nowych klas. Mam nadziej, e dziki temu prezentowane wczeniej elementy
kodu, ktre nie byy objaniane, bd w peni zrozumiae po lekturze tego rozdziau.
Skadnia klasy
W jzyku C# klasy deklaruje si (czyli tworzy) z uyciem sowa kluczowego class:
class Foo
{
}
Do czego su klasy
Jzyk C# jest w peni obiektowy. W starszych jzykach, takich jak chociaby C++ czy
Delphi, nie istnieje wymg korzystania z klas. Przykadowo, prosty program w jzyku Delphi
wyglda tak:
program Foo;
begin
Writeln('Witam serdecznie!');
Readln;
end.
Nie wiem, jak dla Ciebie, ale dla mnie taka konstrukcja jest prostsza. Jzyk Delphi nie
wymusza stosowania klas, aczkolwiek umoliwia ich uycie. Dla Czytelnika, ktry wczeniej
programowa, troch niezrozumiaa moe by idea wykorzystania klas. Dlaczego C#
wymusza na nas konieczno ich stosowania? Jakie waciwie korzyci pyn z uycia klas?
Przykadowo, aby zoy komputer, nie musz wiedzie, jak dokadnie dziaa procesor i z
jakich elementw jest zbudowany. Wystarczy, e wiem, e jest to centralna jednostka
komputera i e niej nie uruchomi caoci. Musz take wiedzie, gdzie woy procesor i jak
go przymocowa.
Kierowca samochodu nie musi wiedzie, co auto ma pod mask, jakie s parametry jego
silnika, jak dziaa skrzynia biegw i co powoduje, e cao si porusza. Wystarczy e wie, i
do uruchomienia samochodu potrzebne s kluczyki musi rwnie umie posugiwa si
kierownic, dwigni zmiany biegw i pedaami.
Jeeli wraz ze swoimi wsplnikami projektujecie jak wiksz aplikacj, kady moe zaj
si przydzielonym zadaniem przykadowo, kto zajmuje si utworzeniem klasy sucej do
wyszukiwania plikw na dysku, jeszcze kto tworzeniem innej klasy, a inna osoba jedynie
wszystko koordynuje i czy w cao. Nie musi ona wiedzie, w jaki sposb dziaa klasa
wyszukujca pliki, ale musi wiedzie, jak j poczy z reszt programu, tak aby wszystko
dziaao zgodnie z oczekiwaniami. Tego z kolei mona si dowiedzie z instrukcji (czyli z
dokumentacji dostarczonej przez autora klasy).
Nie musz wiedzie, jak dziaa klasa Console, jakie jest jej wntrze. Obchodzi mnie tylko,
jak z niej korzysta, jakie metody udostpnia i jak si nimi posugiwa. Najwaniejsze jest
wic wywietlenie tekstu oraz pobranie tekstu wpisanego przez uytkownika.
Instancja klasy
Moge zauway, i w prezentowanych wczeniej kodach uywaem sowa kluczowego new.
Ten operator
(tak, jest to operator) uywany jest do tworzenia tzw. instancji klasy. W tym momencie
zostaje zarezerwowana pami potrzebna do wykonania metod znajdujcych si w tej klasie.
Istotn spraw jest to, e moe istnie wiele instancji danej klasy. Jest to przewaga w
stosunku do idei programowania strukturalnego. Kada instancja rezerwuje osobny blok
pamici. Ewentualne zmienne (pola) znajdujce si w obrbie klasy korzystaj z osobnych
przestrzeni adresowych i mog mie rne wartoci.
W niektrych przypadkach nie trzeba tworzy nowej instancji klasy. Zwr bowiem uwag,
i z klasy Console korzystaem, nie tworzc wczeniej adnej instancji, nie uywaem w
ogle operatora new. Ten aspekt zostanie wyjaniony w dalszej czci ksiki.
Spjrz na ponisz instrukcj:
System.Int32 i = new System.Int32();
Chodzi mi o sam sposb tworzenia nowej instancji klasy, ktry jest do specyficzny. Naley
si jednak nad tym zastanowi, gdy w ksice prezentowaem do tej pory skrcony zapis
tworzenia nowej instancji.
Przed uyciem klasy czy wbudowanego typu danych (np. int) naley zadeklarowa
wskazujc na nie zmienn. To ju wiesz z lektury poprzednich rozdziaw. Nastpnie naley
utworzy now instancj klasy i przypisa rezultat tej operacji do naszej zmiennej:
Foo MyFoo;
MyFoo = new Foo();
Od tej pory przy pomocy MyFoo mamy dostp do elementw klasy Foo, czyli metod oraz pl
czy waciwoci.
Instancja klasy nazywana jest obiektem.
class Foo
{
int X, Y;
public void Bar()
{
this.X = 10;
this.Y = 20;
}
}
To sowo kluczowe moesz traktowa jako ukryty wskanik do obiektu, nie jest ono
wymagane podczas odwoania do elementw klasy:
public void Bar()
{
X = 10;
Y = 20;
}
Jak widzisz, w kodzie prbujemy przypisa polom klasy wartoci przekazane w parametrze
metody Bar(). Kompilator wywietli wwczas ostrzeenie: Assignment made to same
variable; did you mean to assign something else?. Komputer to tylko maszyna i nie domyla
si, e chcemy przypisa wartoci polom, w kocu maj one takie same nazwy jak parametry.
Wtedy z pomoc przychodzi sowo kluczowe this, ktre pozwala jawnie okreli, e chodzi
nam o pola klasy:
this.X = X;
this.Y = Y;
Czasami moe zaj potrzeba przekazania w parametrze metody wskanika do instancji klasy.
Wtedy mona uy sowa kluczowego this: Foo(this);.
Klasy zagniedone
rodowisko .NET Framework dopuszcza moliwo zagniedania typw, w tym oczywicie
klas. Co to oznacza? Moliwe jest zadeklarowanie klasy wewntrz innej klasy! Oto prosty
przykad:
class Foo
{
public class Bar
{
}
}
Oczywicie przy tworzeniu instancji dla zagniedanego obiektu naley poda jego
lokalizacj, uywajc operatora odwoania:
Foo.Bar FooBar = new Foo.Bar();
Pola
Pojcie zmiennych poznae ju dawno. Zmienne w jzyku C# mog by umieszczone
jedynie w klasach lub w ich metodach. Jeeli zmienna jest umieszczona w klasie (nie
wewntrz metody), mwimy wwczas o polu klasy.
Pola po prostu s zmiennymi lub staymi, deklarowanymi na uytek klasy lub udostpnionymi
na zewntrz od niej do uytku programisty. Dostp do zawartoci pl moliwy jest dziki
operatorowi odwoania (.):
class Foo
{
public String About = "Klasa v. 1.0";
}
class Program
{
static void Main(string[] args)
{
Foo MyFoo;
Oczywicie dostp do pl danej klasy posiadaj rwnie metody, ktre w tej klasie si
znajduj.
Metody
Funkcje jzyka C# umieszczone wewntrz klas nazywane s metodami. Poniewa jzyk C#
nie umoliwia deklarowania funkcji poza klas, wszystkie funkcje jednoczenie s metodami.
W klasie moe by wiele metod, do ktrych dostp mamy przy pomocy operatora odwoania,
podobnie jak dostp do pl. Jedyna rnica jest taka, e odwoujc si do metody, musimy
uy symboli nawiasw okrgych, nawet wwczas, gdy metoda nie posiada adnych
parametrw np.:
Console.Read();
Taki kod odpowiada za deklaracj metody Bar() nieposiadajcej adnych parametrw ani
wartoci zwrotnej. Podsumowujc:
znajdujcy si w niej kod. Nagwek metody to nazwa wraz parametrami oraz typem
zwrotnym:
void Bar()
Deklarujc metod, zawsze trzeba okreli jej typ zwrotny. Innymi sowy, kada metoda
(funkcja) musi zwraca jak warto. Przykadowo, moemy zadeklarowa metod Power(),
ktra podnosi dan liczb do potgi i zwraca przemnoon warto typu int. Jeeli nie
chcemy, aby metoda zwracaa jakiekolwiek wartoci, naley uy typu void, ktry oznacza
warto pust.
W jzyku Pascal/Delphi istniej procedury (ktre nie zwracaj adnej wartoci) oraz funkcje
(ktre musz zwrci jak warto). W wikszoci jzykw, np. C, C++, Java i C#, nie
istniej procedury, a jedynie funkcje, ktre zwracaj jak warto.
Typ void jest aliasem dla typu .NET Framework System.void.
Zwracana warto
Aby lepiej zaprezentowa moliwo zwracania wartoci przez metody klasy, napiszmy
prost metod i zaprezentujmy jej uycie.
W naszej przykadowej klasie Foo zadeklaruj metod Power():
public int Power(int X)
{
return X * X;
}
Jest to prosta metoda, ktra zwraca warto typu int. Metoda posiada jeden parametr X
(rwnie typu int). W ciele metody nastpuje przemnoenie wartoci parametru X i zwrcenie
rezultatu.
Sowo kluczowe public to tzw. modyfikator dostpu. Pojcie to zostanie objanione w dalszej
czci rozdziau.
Sowo kluczowe return suy przede wszystkim do zwracania wartoci. Gdy ono wystpi,
kod zawarty w dalszej czci metody nie bdzie ju wykonywany:
int Bar()
{
return 2; // zwr warto 2
Jeeli warto zwrotna metody jest typu void, mona pomin wywoanie sowa return lub
pozostawi samo sowo kluczowe:
public void Bar()
{
return;
Console.WriteLine("Ten kod nie bdzie wykonywany");
}
Parametry metod
Troch si pospieszyem i w poprzednim przykadzie skorzystaem z tzw. parametru. Piszc
wasn metod, w nawiasach okrgych moemy okreli, jakie dane wejciowe spodziewamy
si otrzyma.
Przykadowo, parametrem metody Power() jest X typu int. Jest to parametr wejciowy, na
jakim nasza metoda operuje.
Innymi sowy, chcc uy metody Power(), w nawiasach musimy wpisa jak liczb typu
int, gdy tego wymaga od nas ta metoda, a tym samym kompilator. Parametry metod
mog by dowolnego typu.
Dla zabawy moemy napisa prost aplikacj, w ktrej zadeklarujemy metod przeliczajc
powiedzmy kilometry na mile angielskie. Jedna mila angielska to 1609,344 metry, czyli
1,6 km. Nasza metoda musi posiada parametr, ktry oznacza bdzie liczb kilometrw do
obliczenia. Taki kod moe wyglda tak:
static double KmToMile(double Km)
{
return ((Km * 1000) / 1609.344);
}
Parametr naszej metody nosi nazw Km i jest typu double. W ciele metody dokonujemy
dziaa matematycznych, ktre pozwol uzyska liczb mil zwracanych przy pomocy sowa
kluczowego return.
Cay program prezentuje listing 5.2.
Listing 5.2. Kod rdowy programu
using System;
namespace Temperature
{
class Program
{
static double KmToMile(double Km)
{
return ((Km * 1000) / 1609.344);
}
static void Main(string[] args)
{
Console.Write("Podaj liczb kilometrw: ");
double Km = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Liczba mil:
Console.ReadLine();
" + KmToMile(Km));
}
}
}
Parametry danej metody niekoniecznie musz by tego samego typu jak w zaprezentowanym
przykadzie (parametry s typu int); rwnie dobrze mona uy innych typw dostpnych w
C#:
static int Multiple(int X, int Y, float Z)
Oczywicie przekazujc dane w parametrze metody, musisz zadba o to, aby byy one zgodne
z typem zadeklarowanym w jej nagwku.
Parametry domylne
Znana zapewne wielu programistom moliwo nadawania domylnych parametrw dla
metod jest w jzyku C# niedostpna. Zamiast tego naley uywa mechanizmu przeciania
metod. Jest to zapewne wana informacja, jeeli programowae wczeniej w innym jzyku.
Jeeli dana metoda wymaga podania parametru, nie mona tego pomin przy jej
wywoywaniu. W takim wypadku kompilator wywietli bd No overload for method 'Multiple'
takes '0' argument.
Mwic o wywoaniu metody, mam na myli jej uycie. Z takim pojciem bdziesz spotyka si
w dalszej czci ksiki.
Przecianie metod
Przecianie (czsto nazywane przeadowywaniem) metod jest technik bardzo uyteczn i
czsto spotykan w rodowisku .NET Framework.
W danej klasie nie mog istnie dwie metody o tej samej nazwie. Nie mog istnie, pod
warunkiem e ich nagwek jest taki sam. Nie ma przeszkd, aby zadeklarowa dwie metody
o tej samej nazwie, jeeli tylko posiadaj rn ilo parametrw (lub parametry s rnych
typw). Oto przykad:
static int Multiple(int X, int Y)
{
return X * Y;
}
static double Multiple(double X, double Y)
{
return X * Y;
}
Program bdzie w stanie stwierdzi, o ktr metod nam chodzi, na podstawie parametrw,
jakie jej przekaemy:
Multiple(23.34, 35.45); // dobrze
Multiple(112, 10); // dobrze
Przekazywanie parametrw
Proste, jak mogoby si wydawa, przekazywanie parametrw do metod ma o wiele wiksze
zastosowanie w jzyku C#. S bowiem rne sposoby przekazywania parametrw do metod,
o czym przekonasz si po lekturze dalszej czci tego rozdziau.
}
static void Main(string[] args)
{
int X, Y;
X = 2;
Y = 3;
int Z = Power(X, Y);
Console.Write(Z);
Console.Read();
}
}
}
S1 = "Hello ";
S2 = "World";
}
static void Main(string[] args)
{
string MyS1, MyS2;
// przekazujc parametry, nie zapomnij
// o uyciu out
Foo(out MyS1, out MyS2);
Console.WriteLine(MyS1 + MyS2);
Console.Read();
}
}
Wskutek jego uruchomienia na ekranie konsoli pojawi si napis: Hello World. Deklarujc
parametry referencyjne w nagwku metody, musimy je poprzedzi sowem kluczowym out.
Rwnie przekazujc parametry, musimy uy tego sowa.
Kiedy uywamy parametrw referencyjnych, nic nie stoi na przeszkodzie, aby w zalenoci
od potrzeb przekazywa rwnie parametry przez warto. Spjrz na poniszy kod:
static void Foo(string S, out string S1, out string S2)
{
S1 = "Hello ";
S2 = "World" + S;
}
static void Main(string[] args)
{
string MyS1, MyS2;
Foo(" my darling!", out MyS1, out MyS2);
Console.WriteLine(MyS1 + MyS2);
Console.Read();
}
Pierwszy parametr metody Foo() jest przekazywany przez warto, kolejne dwa przez
referencj.
Zwracanie danych przez referencj czsto si przydaje, gdy metoda musi zwrci wiele
informacji na temat swojego dziaania. Kada metoda moe zwrci warto owszem. Ale
co wwczas, gdy musisz zwrci do programu gwnego kilka informacji? Niezastpiona jest
wtedy metoda, ktrej parametry zwracane s przez referencj.
Jeeli w programie nie przypisalibymy wartoci dla zmiennej MyS, podczas kompilacji
wystpiby bd: Use of unassigned local variable 'MyS'.
W programie C# kada zmienna przed uyciem musi zosta zainicjowana (czyli najprociej
mwic musz zosta przypisane do niej jakie dane). Poniszy fragment prezentuje kod,
ktrego prba kompilacji zakoczy si bdem (Use of unassigned local variable):
int I;
Console.WriteLine(I);
Dziedziczenie
Caa biblioteka Windows Forms oparta jest na dziedziczeniu. Ba caa biblioteka klas
rodowiska .NET Framework oparta jest na dziedziczeniu, ktre mona okreli jako
Klasa domylna
Kiedy deklarujemy now klas, nie ma wymogu podawania jej klasy bazowej! Jeeli nie
okrelimy klasy bazowej, to nasz typ automatycznie bdzie dziedziczy po klasie
System.Object, przejmujc od niej wszystkie metody.
Hermetyzacja
Pojcie hermetyzacji jest zwizane z ukrywaniem pewnych danych. Klasy udostpniaj na
zewntrz pewien interfejs opisujcy ich dziaanie i tylko z tego interfejsu moe korzysta
uytkownik. Klasy mog bowiem zawiera dziesitki, a nawet setki metod (funkcji), ktre
wykonuj rne czynnoci. My jako projektanci klasy powinnimy zapewni dostp jedynie
do niektrych metod, tak aby potencjalny uytkownik nie mg wykorzystywa wszystkich,
gdy moe to spowodowa nieprzewidywalne dziaanie programu, zawieszanie itp.
Wewntrz silnika samochodu te dochodzi do pewnych procesw, ale kierowca
nie musi o nich wiedzie. Informacji tych nie potrzebuje take inny element silnika, ktry si
z nim czy komunikowanie si pomidzy elementami przebiega ustalonym strumieniem i
to wystarczy.
C# pozwala na ukrywanie kodu w klasie, w tym celu stosuje si pewne klauzule nazwane w
C# modyfikatorami dostpu (ang. access modifiers).
Modyfikatory dostpu
Modyfikatory dostpu to sowa kluczowe okrelajce dostp do elementw klasy.
Elementami klasy s jej metody oraz pola, a take waciwoci (o tym za chwil).
Jzyk C# udostpnia cztery sowa kluczowe okrelajce sposb dostpu do danego czonka
klasy: private (prywatne), protected (chronione), public (publiczne) oraz internal
(wewntrzne). W zalenoci od sekcji, w ktrej metody zostan umieszczone, bd one
inaczej interpretowane przez kompilator. Modyfikator dostpu musi poprzedza deklaracj
danego elementu klasy:
class Bar
{
private string S;
public
int X, Y;
protected double D;
}
Sekcja private
Wszystkie pola czy metody umieszczone w klasie automatycznie s traktowane jako
prywatne. Oznacza to, e dostp do nich jest niemoliwy spoza klasy, w ktrej s
umieszczone! Oto przykad:
class MyClass
{
int X, Y;
}
class Program
{
static void Main(string[] args)
{
MyClass.X = 10; // bd!
MyClass Class = new MyClass();
Class.Y = 10; // bd!
}
}
Sekcja public
Modyfikator public oznacza, i elementy klasy opatrzone t klauzul bd traktowane jako
publiczne. Do takich elementw mona odwoywa si zarwno spoza klasy, jak i z jej
wntrza. Poniszy kod jest wic jak najbardziej prawidowy:
class MyClass
{
public int X, Y;
public void Foo()
{
X = 10;
Y = 20;
}
}
class Program
{
static void Main(string[] args)
{
MyClass Class = new MyClass();
Class.Foo();
Class.X = 20;
Class.Y = 50;
}
}
Modyfikatory dostpu maj takie same dziaanie w przypadku pl oraz metod. Chciabym
podkreli, i metody rwnie mog by prywatne lub publiczne, tak samo jak pola klasy.
Sekcja protected
Elementy klasy zadeklarowane jako protected s traktowane jako chronione. Ma to zwizek
z procesem dziedziczenia klas. Chronione elementy s niedostpne poza klas, w ktrej s
zadeklarowane. S one widoczne jedynie dla klas potomnych. Spjrz na poniszy przykad:
class A
{
protected int X;
public void Foo()
{
Console.WriteLine("Jestem metod z klasy A!");
}
}
class B : A
{
private void Bar()
{
X = 10;
}
}
class Program
{
static void Main(string[] args)
{
B MyB = new B();
MyB.Foo();
A MyA = new A();
MyB.X = 10; // Bd!
MyA.X = 10; // Bd!
}
}
Spjrz na metod Main() klasy Program. Podczas prby kompilacji takiego kodu odwoanie
si do elementu X, zarwno z obiektu MyA, jak i MyB, zakoczy si niepowodzeniem.
Natomiast w klasie B moemy wykorzysta pole X, mimo i zostao ono zadeklarowane w
klasie A.
Sekcja internal
Jest to zdecydowanie najrzadziej wykorzystywany modyfikator dostpu. Okrela on, e z
danego elementu bdzie mona skorzysta jedynie w obrbie podzespou, w ktrym jest
uyty. Szczegowe omwienie podzespow .NET znajdziesz w rozdziale 11.
Konstruktor
Sama klasa nie zajmuje miejsca w pamici komputera. W momencie utworzenia nowej
instancji program rezerwuje w pamici miejsce potrzebne do wykonania danej klasy.
Konstruktor to specjalna metoda, ktra jest wywoywana automatycznie w momencie
utworzenia instancji klasy. W rzeczywistoci instrukcja new Foo() oprcz utworzenia
instancji prbuje wywoa konstruktora tej klasy. Konstruktor jest jednak elementem
opcjonalnym i jego pominicie nie jest bdem.
Konstruktor jest metod o takiej samej nazwie co klasa, w ktrej jest zadeklarowany. Spjrz
na poniszy fragment kodu:
using System;
namespace MyConsole
{
class Foo
{
public Foo()
{
Console.WriteLine("Jestem konstruktorem klasy
Foo");
}
}
class Program
{
static void Main(string[] args)
{
Foo MyFoo;
MyFoo = new Foo();
Console.Read();
}
}
}
Zwr uwag, e w klasie Foo zadeklarowaem metod Foo(), ktra od tego momentu jest
konstruktorem klasy. Instrukcja:
MyFoo = new Foo();
tworzy now instancj klasy i wywouje jej konstruktor. Moesz to sprawdzi, uruchamiajc
powyszy program.
Konstruktor musi by opatrzony klauzul public. Nie moe on rwnie zwraca adnej
wartoci. Deklarujc konstruktor, nie okrelamy jego typu zwrotnego, nawet jeli uywamy
wartoci pustej (void).
Istnieje moliwo przeciania konstruktorw, tak jak mona przecia zwyke metody
klasy. Prosz pamita o tym, e jeeli chcemy w klasie umieci wiele konstruktorw, kady
z nich musi posiada rne parametry.
Konstruktor musi posiada tak sam nazw jak klasa. Nie ma moliwoci utworzenia
konstruktor klasy, ktry posiadaby inn nazw.
{
X = 30; // Bd!
}
}
Pola tylko do odczytu deklaruje si z uyciem sowa kluczowego readonly. Warto takiego
pola mona nada bezporednio w kodzie (w trakcie deklaracji), tak jak w przypadku staych,
lub w ciele konstruktora, tak jak zostao to zaprezentowane na przykadzie. Prba zmiany
wartoci pola w innym miejscu w kodzie koczy si bdem: A readonly field cannot be
assigned to (except in a constructor or a variable initializer).
Pola tylko do odczytu mog by dobrym rozwizaniem porednim pomidzy polami staymi
(const) a zwykymi.
Destruktor
Destruktor jest rwnie specjaln metod, wywoywan po zakoczeniu korzystania z danej
klasy. Przy zamykaniu aplikacji specjalny mechanizm rodowiska .NET Framework
(nazwany garbage collection) zwalnia pami zarezerwowan przez poszczeglne instancje
klas naszego programu. W momencie usuwania danej klasy z pamici wywoywany jest jej
destruktor. Jeeli programista chce jako zareagowa na moment usuwania klasy z pamici,
powinien umieci w niej destruktor.
Destruktor w klasie moe by tylko jeden i musi nosi tak sam nazw co klasa, w ktrej si
znajduje. Destruktor naley zadeklarowa, poprzedzajc jego nazw symbolem ~.
~Foo()
{
Console.WriteLine("Do widzenia...");
}
Waciwoci
Jzyk C# dopuszcza tworzenie publicznych pl. Dobr praktyk jest deklarowanie pl jako
elementw prywatnych. Klasa powinna wykorzystywa pola jako zmienne, jedynie na wasny
uytek. Do komunikowania si ze wiatem zewntrznym powinno si uywa waciwoci.
Waciwoci, podobnie jak pola (te pojcia s czsto ze sob mylone), su do gromadzenia
danych (czyli do odczytywania oraz przypisywania informacji). Oprcz tego w przypadku
Waciwoci jzyka C# mog by jedynie do odczytu lub jedynie do zapisu (albo do odczytu i
zapisu jednoczenie). Innymi sowy, moemy zabroni przypisywania danych do
waciwoci. W powyszym przykadzie waciwo Month jest tylko do odczytu, prba
przypisania danych zakoczy si komunikatem bdu: Property or indexer
'FooConsole.Foo.Month' cannot be assigned to -- it is read only.
Sowo kluczowe get okrela akcj, jaka bdzie wykonywana w momencie odczytu danych.
Natomiast inne sowo kluczowe set umoliwia zaprogramowanie czynnoci, jakie bd
wykonywane w momencie zapisu danych.
Przykadowo: piszemy klas, ktra posiada waciwo Month (miesic). Chcemy, aby
uytkownik mg do niej przypisa liczb z zakresu od 1 do 12 (w kocu mamy 12 miesicy
w roku). Dziki waciwociom moemy dokona sprawdzenia poprawnoci danych:
class Foo
{
private int month = 12;
public int Month
{
get
{
return month;
}
set
{
if (value > 1 && value <= 12)
{
month = value;
}
}
}
}
value jest sowem kluczowym jzyka C#. Przechowuje warto przypisan do waciwoci.
Moesz sprawdzi dziaanie takiego programu. Prba przypisania liczby wikszej od 12 (lub
mniejszej od 1) zakoczy si niepowodzeniem:
Foo MyFoo = new Foo();
MyFoo.Month = 23; // warto nie zostanie przypisana!
Kolejny przykad:
class Foo
{
private int hour;
private int second;
public int Hour
{
get
{
return hour;
}
set
{
if (value >= 0 && value <= 24)
{
hour = value;
second = value * 3600;
}
}
}
public string Info()
{
return "Godzina " + hour + ", " + second + " sekunda
tej doby";
}
}
Przy ustawianiu wartoci dla waciwoci Hour obliczana jest ilo sekund. Te wartoci s
nastpnie przechowywane w polach hour oraz second (uwaga na wielko znakw!).
Publiczna metoda Info() zwraca acuch zawierajcy informacje
odnonie do liczby sekund oraz godziny.
Podsumowujc:
Modyfikatory dostpu
Waciwoci rwnie mog posiada modyfikatory dostpu. To jest jasne. Ciekawostk jest
to, e modyfikatory dostpu mog by przypisywane blokom set oraz get:
public int Hour
{
get
{
return hour;
}
protected set
{
if (value >= 0 && value <= 24)
{
hour = value;
second = value * 3600;
}
}
}
Tak zadeklarowanej waciwoci nie mona nada wartoci, gdy blok set opatrzony jest
klauzul protected. Mona to dopiero zrobi w klasach potomnych:
class Bar : Foo
{
public Bar()
{
// przypisanie nowej wartoci
Hour = 23;
}
}
Elementy statyczne
Chciabym w tym momencie wspomnie o istotnym elemencie programowania obiektowego;
bardzo istotnym, jeeli chodzi o rodowisko .NET Framework, w ktrym wystpuje bardzo
czsto. Mowa tutaj o elementach statycznych.
Biblioteka klas .NET Framework to setki (jeli nie tysice) klas. Zwr uwag, e moemy
uywa niektrych klas, nie tworzc nowej instancji. Dobrym przykadem jest klasa Console,
ktrej prawdopodobnie uywae najczciej, czytajc t ksik. Nie przypominasz sobie,
aby kiedykolwiek tworzy instancj tej klasy, prawda?
Biblioteka klas zawiera mnstwo typw, ktre s statyczne, opatrzone klauzul static.
Klasy statyczne (klasa te jest typem!) mog dziaa bez koniecznoci tworzenia nowego
obiektu. Ba, niekiedy nie jest wwczas moliwe utworzenie nowej instancji takiej klasy przy
uyciu sowa kluczowego new. Klasy statyczne s adowane do pamici przez CLR w
momencie, gdy program lub przestrze nazw, ktra je zawiera, jest adowana.
Metody statyczne
Sowo kluczowe static jest waciwie modyfikatorem dostpu. Statyczne mog by nie
tylko klasy, ale rwnie metody czy waciwoci. Spjrz na poniszy przykad:
class Foo
{
public Foo()
{
Console.WriteLine("Tworzenie nowego obiektu!");
}
static public void Bar()
{
Console.WriteLine("Hello from static!");
}
}
W klasie Foo utworzyem konstruktor oraz metod Bar(), ktra jest statyczn metod
publiczn. Tak metod mog wywoa, nie tworzc nowej instancji klasy:
Foo.Bar();
Z metody statycznej Bar() nie moemy odwoa si do pola Field2, poniewa nie jest ono
statyczne.
Natomiast odwrotna sytuacja jest jak najbardziej dopuszczalna. Tzn. zwyke metody klas
mog odwoywa si do metod statycznych.
Klauzula static musi znajdowa si przed sowem okrelajcym typ zwrotny metody. Z kolei
pooenie modyfikatora dostpu nie ma znaczenia. Moemy wic napisa static public lub
public static.
Metody statyczne mog by prywatne lub publiczne, aczkolwiek deklarowanie prywatnej
metody statycznej mija si z celem.
Klasy statyczne
Klasa moe posiada statyczne metody, pola oraz waciwoci. Sowem kluczowym static
moemy opatrzy nawet ca klas. Wwczas wymuszamy, aby wszystkie elementy tej klasy
byy statyczne. Spjrz na poniszy kod:
static class Foo
{
public void Bar() // <-- bd
{
}
}
Mimo i klasa zostaa opatrzona klauzul static, metoda Bar() wci pozostaje zwyk
metod. Prba kompilacji takiego kodu zakoczy si bdem: 'Bar': cannot declare instance
members in a static class.
Statyczne klasy mog posiada konstruktory, lecz one take musz by statyczne i prywatne
(tj. pozbawione modyfikatora public):
static class Foo
{
static Foo()
{
Statyczny konstruktor nie moe posiada modyfikatorw dostpu oraz parametrw. Nie
mona go jawnie wywoa jest on wywoywany wwczas, gdy nastpi pierwsze odwoanie
do klasy:
Foo.Bar();
Polimorfizm
Pojcie polimorfizmu w jzyku C# jest zwizane z dziedziczeniem. Jest to do
skomplikowane pojcie (szczeglnie dla pocztkujcych programistw) pozwalajce na
tworzenie zaawansowanych klas bezporednio ze sob poczonych. Ja postaram si
wytumaczy to jak najprociej, prezentujc fragmenty kodu.
Polimorfizm jest najwikszym osigniciem techniki programowania obiektowego. Sowo to
pochodzi od greckiego polmorphos oznaczajcego wielopostaciowy, co odzwierciedla
znaczenie tej techniki. W programowaniu oznacza to moliwo operowania na obiektach
nalecych do rnych klas. Oczywicie ta definicja teraz wydaje Ci si bardzo mglista, wic
zacznijmy od pocztku
oraz chronione. Rozwamy teraz sytuacj, w ktrej w klasie potomnej chcemy zadeklarowa
metod istniejc w klasie bazowej. Myl, e poniszy przykad zobrazuje moje zamierzenia:
class Animal
{
public void Run()
{
Console.WriteLine("Metoda Run() z klasy Animal");
}
}
class Mammal : Animal
{
public void Run()
{
Console.WriteLine("Metoda Run() z klasy Mammal");
}
}
Klasa Mammal dziedziczy po Animal. Obie posiadaj metod Run(). Jeli utworzymy now
instancj klasy Mammal i wywoamy metod Run(), wykonany zostanie kod z klasy Mammal:
Mammal MyAnimal = new Mammal();
MyAnimal.Run();
Czyli wszystko dziaa tak, jak powinno. W takim programie kompilator C# wywietli
ostrzeenie: 'FooConsole.Mammal.Run()' hides inherited member
'FooConsole.Animal.Run()'. Use the new keyword if hiding was intended. Komunikat
informuje nas, e w klasie Mammal zadeklarowalimy metod Run(), ktra przykrywa element
o tej samie nazwie, uprzednio zadeklarowany w klasie Animal.
Chocia z punktu widzenia kompilatora nie jest to bd (taki kod zostanie skompilowany),
dobr praktyk jest jawne okrelenie, e wiemy, co robimy, i chcemy przykry element
uprzednio zadeklarowany w klasie bazowej. Suy do tego sowo kluczowe new, ktre
poznae ju wczeniej. W tym kontekcie to sowo suy jako modyfikator dostpu:
new public void Run()
{
Console.WriteLine("Metoda Run() z klasy Mammal");
}
Takie dziaanie nie dotyczy jedynie metod klas, ale wszystkich elementw, wczajc
waciwoci i pola.
Elementy statyczne rwnie mog by przykrywane. Spjrz na poniszy przykad:
class Animal
{
public static int Age = 12;
public static void Run()
{
Console.WriteLine("Metoda Run() z klasy Animal");
}
}
class Mammal : Animal
{
new public static int Age = 100;
new public static void Run()
{
Console.WriteLine("Metoda Run() z klasy Mammal");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Animal.Age);
Console.WriteLine(Mammal.Age);
Console.ReadLine();
}
}
W klasie Animal zadeklarowaem statyczne pole Age, ktremu nadaem warto 12. W klasie
potomnej to pole jest przykrywane i nadawana jest mu nowa warto. W skutek dziaania
takiego kodu na ekranie konsoli wywietlone zostanie:
12
100
Dla sprawdzenia zasad dziaania dziedziczenia moesz skomentowa lini odpowiedzialn za
przykrywanie elementu Age:
// new public static int Age = 100;
W tej metodzie najpierw wykonujemy kod metody Run() z klasy bazowej, a dopiero pniej
dalsze instrukcje z ciaa metody. Nie ma tutaj wikszej filozofii. Dostp do elementw klasy
bazowej uzyskujemy przy pomocy operatora odwoania (.).
Sowo kluczowe base ma bardziej zaawansowane zastosowanie w poczeniu z
konstruktorami klas:
class Animal
{
public string Name;
public Animal()
{
this.Name = "Pet";
}
public Animal(string Name)
{
this.Name = Name;
}
}
class Mammal : Animal
{
public Mammal()
: base()
{
// dodatkowy kod
}
public Mammal(string Name)
: base("Tina")
{
// dodatkowy kod
}
public void Run()
{
Console.WriteLine("Imi zwierztka to: " + this.Name);
}
}
Zacznijmy od pocztku. W klasie Animal utworzyem dwa konstruktory. Jeden z nich posiada
parametr Name. Taka konstrukcja jest Ci znana z lektury poprzednich fragmentw rozdziau.
W klasie potomnej rwnie zadeklarowaem dwa konstruktory. Pierwszy nie posiada
parametrw i wywouje konstruktor klasy bazowej (ktry rwnie nie posiada parametrw).
Drugi konstruktor klasy Mammal wywouje konstruktor klasy bazowej z parametrem Tina. Jak
mylisz, jaki bdzie rezultat dziaania poniszego kodu?
Mammal MyAnimal = new Mammal("Jack");
MyAnimal.Run();
Na konsoli zostanie wywietlony tekst: Imi zwierztka to: Tina. Dlaczego nie Jack?
Poniewa w konstruktorze klasy Mammal podajemy parametr. Teraz zwr uwag na kod tego
konstruktora. Wywouje on konstruktor bazowy z parametrem Tina. Poprawienie tego kodu
wymaga maej poprawki:
public Mammal(string Name)
: base(Name)
{
// dodatkowy kod
}
Metody wirtualne
Wyobra sobie, e w programie tworzysz klasy odpowiadajce gatunkom zwierzt. Mamy
wic klas bazow Animal. Mamy rwnie klasy pochodne Mammal (ssaki) oraz Fish
(ryby). W klasie macierzystej mamy metod Breath() (oddychaj), ktra oczywicie jest
rwnie dostpna w klasach potomnych, tyle e moe by inaczej interpretowana w klasie
Mammal oraz Fish. Ssaki oddychaj przecie pucami, a ryby skrzelami. Moemy wic w
klasie Fish utworzy now metod Breath(), ktra przykrywa oryginaln, zadeklarowan w
klasie bazowej. Moemy rwnie zadeklarowa tzw. metod wirtualn, ktra w klasach
potomnych moe by przedefiniowana.
Te dwa pojcia s cile zwizane z polimorfizmem.
Metoda wirtualna to taka, ktra jest przygotowana do zastpienia w klasie potomnej.
Metod wirtualn tworzymy, dodajc do jej deklaracji sowo kluczowe virtual:
class Animal
{
public int Age;
public virtual void Breath()
{
Console.WriteLine("Zwierzak oddycha...");
}
}
Przedefiniowanie metod
Przedefiniowanie (ang. override) to proces polegajcy na tworzeniu nowej wersji metody w
klasie potomnej. Polega on na utworzeniu metody, ktra opatrzona bdzie sowem
kluczowym override:
class Mammal : Animal
{
public override void Breath()
{
Console.WriteLine("Ssak oddycha pucami...");
}
}
class Fish : Animal
{
public override void Breath()
{
Console.WriteLine("Ryba oddycha skrzelami...");
}
}
// wywoanie metody
MyAnimal.Breath();
Console.ReadLine();
}
Elementy abstrakcyjne
W poprzednim przykadzie zadeklarowaem klas bazow Animal, ktra zawieraa metod
Breath(). Zamy, e w programie nie ma potrzeby uywania klasy Animal jedynie z
klas potomnych. Po co wwczas implementowa metody w klasie Animal? Mona opatrzy
deklaracj metody sowem kluczowym abstract. Klasa abstrakcyjna nie ma implementacji
(tj. definicji metod), ma jedynie nagwki (deklaracje):
abstract class Animal
{
public int Age;
Taka konstrukcja wymusza, aby klasy potomne posiaday metod Breath(). Klasa Animal
nie moe by w takim wypadku uywana, suy jedynie jako fundament do budowania
kolejnych klas.
Elementy zaplombowane
Elementy zaplombowane nie mog suy jako klasy bazowe. To jest podstawowe i jedyne
zastosowanie tego typu klas. Plombowanie klasy polega na opatrzeniu jej deklaracji sowem
kluczowym sealed:
sealed class Foo {}
Domylnie wszystkie klasy pisane przez nas powinny mie moliwo rozszerzenia jej
funkcjonalnoci
. Uywaj sowa kluczowego sealed tylko wwczas, gdy masz powany powd. Np. klasa jest
statyczna albo dziedziczy elementy zawierajce informacje wane dla bezpieczestwa. Wtedy
pamitaj, aby w zaplombowanej klasie nie umieszcza metod wirtualnych ani nie korzysta z
modyfikatora protected, gdy wtedy nie ma to sensu.
skomplikowanych zada, jak np. obsugiwanie plikw XML, pocze internetowych itp.
Pomys udostpniania programistom gotowych bibliotek klas nie jest nowy. Przed tym, jak na
rynku pojawia si platforma .NET, programici mogli korzysta z interfejsu WinAPI systemu
Windows. WinAPI nie jest jednak bibliotek obiektow raczej trudnym do obsugi i
przestarzaym interfejsem. Bardzo popularna bya udostpniona przez firm Microsoft
biblioteka MFC umoliwiajca tworzenie wizualnych aplikacji w rodowisku Visual C++.
Firma Borland lider na rynku aplikacji programistycznych w swoich rodowiskach C++
Builder oraz Delphi umieszczaa bibliotek VCL (ang. Visual Class Library), rwnie
umoliwiajc proste tworzenie aplikacji wizualnych. adna z nich nie bya jednak tak
rozbudowana i uyteczna jak FCL.
Przestrzenie nazw
Wiesz ju, e przestrzenie nazw s podstawowym sposobem organizacji i grupowania klas w
.NET Framework. Biblioteka FCL udostpnia kilka podstawowych przestrzeni nazw
grupujcych klasy:
Klasa System.Object
Bazow klas dla kadego z typw jest System.Object. Nawet jeeli nie okrelimy klasy
bazowej naszej klasy, to kompilator automatycznie przyjmie, e jest ni System.Object.
Owa klasa dostarcza podstawowych mechanizmw korzystania z klas podstawowe metody
zostay opisane w tabeli 5.1.
Tabela 5.1. Krtki opis metod uywanych w klasie System.Object
Metoda
Opis
Porwnuje, czy dwie instancje typu object s takie same (maj tak sam
Equals
zawarto).
ReferenceEquals Porwnuje, czy dwie instancje obiektu s tego samego typu.
GetHashCode
Zwraca unikalny numer instancji obiektu.
GetType
Zwraca informacje na temat obiektu: metody, waciwoci itp.
ToString
Znakowa reprezentacja obiektu zwraca jego typ.
}
}
class Program
{
static void Main(string[] args)
{
Bar MyBar = new Bar();
Console.Read();
}
}
}
Opakowywanie typw
Opakowywanie typw jest specyficzn cech jzykw dziaajcych w rodowisku .NET
Framework. Opakowywanie daje moliwo traktowania typw prostych jak obiektw.
Tutaj nale Ci si wyjanienia. W C# moemy wyrni dwa rodzaje typw proste oraz
referencyjne. Wbudowane typy s prostymi typami przechowywanymi na stosie. Mona do
nich zaliczy liczby cakowite, liczby zmiennoprzecinkowe podwjnej precyzji oraz wartoci
logiczne.
Stos jest tym obszarem pamici komputera, ktry jest alokowany (przydzielany) w momencie
uruchamiania jakiego programu. System operacyjny w tym momencie musi okreli, ile
pamici bdzie potrzebowa do prawidowego dziaania programu.
Typy referencyjne s umieszczane na tzw. stercie (ang. heap). Zmienna typu referencyjnego
przechowuje jedynie adres wskazujcy na rzeczywisty egzemplarz danego typu. Kiedy
tworzymy now instancj danego typu (przy pomocy operatora new), nastpuje alokacja (czyli
rezerwacja) pamici potrzebnej na przechowywanie danego obiektu.
Sterta jest obszarem pamici operacyjnej dostpnej w trakcie dziaania programu. Kiedy
tworzymy nowy obiekt, pami dla niego jest alokowana wanie na stercie.
Konwersja ze zmiennej typu prostego na posta obiektu i konwersja obiektu na zmienn typu
prostego w terminologii przyjtej przez programistw aplikacji .NET jest okrelana
odpowiednio jako opakowywanie (ang. boxing) oraz odpakowywanie (ang. unboxing).
Poniszy przykad prezentuje domniemane opakowywanie:
int Foo = 100;
object O = Foo;
Console.WriteLine(O);
Jak widzisz, polega to jedynie na przypisaniu wartoci zmiennej Foo do zmiennej O typu
object (typ object jest odpowiednikiem typu System.Object biblioteki FCL). Nie ma
potrzeby okrelania, e opakowujemy warto typu int, poniewa kompilator jest w stanie
si tego domyli. Moliwe jest jednak sprecyzowane opakowywanie polegajce na
podaniu typu danych:
int Foo = 100;
object O = (object)Foo;
Interfejsy
Interfejsy swoim dziaaniem przypominaj klasy abstrakcyjne. Mog zawiera jedynie
deklaracje metod oraz waciwoci. Interfejsy deklaruje si z uyciem sowa kluczowego
interface:
interface IFoo
{
int Power(int X, int Y);
}
Jak widzisz, w interfejsie o nazwie IFoo znajduje si metoda Power(). W takim wypadku
mwimy, e interfejs definiuje dane metody. Zwr uwag, e w interfejsie znajduje si
nagwek metody pozbawiony ciaa (czyli kodu).
Interfejsy s kolejnym elementem uywanym w metodologii zwanej programowaniem
obiektowym, czsto wykorzystywanym w bibliotece FCL. Klasy jzyka C# mog dziedziczy
jedynie elementy pojedynczej klasy. Jak sama nazwa wskazuje, interfejs definiuje wycznie
mechanizm poredniczcy w komunikacji klienta z obiektem. Za obsug interfejsu i
odpowiedni implementacj kadej z jego funkcji odpowiada klasa.
Dana klasa moe jednake dziedziczy wiele interfejsw. Poniewa interfejs nie moe
posiada kodu metod, mwimy wwczas, i klasa implementuje elementy interfejsu. Pewnie
zastanawiasz si jakie zalety niesie za sob wykorzystanie interfejsw. Su one przede
wszystkim do wymuszania deklaracji danych elementw w klasie. Spjrz na poniszy kod:
interface IFoo
{
int Power(int X, int Y);
}
class Foo : IFoo
{
}
Mamy tutaj interfejs IFoo oraz klas Foo, ktra go implementuje. Prba kompilacji takiego
programu zakoczy si komunikatem: 'FooConsole.Foo' does not implement interface
member 'FooConsole.IFoo.Power(int, int)'. Kompilator prbuje nam powiedzie, e w klasie
Foo nie znajduje implementacji elementu Power.
Implementowana metoda musi posiada taki sam nagwek jak ta zdefiniowana w interfejsie.
Nie moe to by metoda statyczna:
class Foo : IFoo
{
public int Power(int X, int Y)
{
return X * Y;
}
}
interface IBar
{
double Power(double X, double Y);
}
class Foo : IFoo, IBar
{
public int Power(int X, int Y)
{
return X * Y;
}
public double Power(double X, double Y)
{
return X * Y;
}
}
Istnieje moliwo, aby obydwa interfejsy definioway metod o tej samej nazwie. Wwczas
inna jest implementacja takich metod:
interface IFoo
{
void Foo();
}
interface IBar
{
void Foo();
}
class Foo : IFoo, IBar
{
void IFoo.Foo()
{
Console.WriteLine("Metoda implementujca IFoo.Foo");
}
void IBar.Foo()
{
Console.WriteLine("Metoda implementujca IBar.Foo");
}
}
Typy wyliczeniowe
Umoliwiaj one tworzenie nowych typw danych zawierajcych zbir elementw tego
samego typu. Jeeli programowae wczeniej w jzyku Delphi, by moe znane s Ci zbiory
(ang. set). Typy wyliczeniowe (nazywane czsto po prostu wyliczeniami) s podobnym
mechanizmem jak zbiory z jzyka Delphi.
Typy wyliczeniowe deklarujemy z uyciem sowa kluczowego enum:
enum Days { Pn, Wt, r, Czw, Pt, So, Nd };
Nd
};
Wartoci elementw
Typy wyliczeniowe s prostym mechanizmem zapewniajcym grupowanie danych. Ilo
elementw wyliczenia jest staa, nie podlega zmianom. Jedyna rzecz, ktr moemy ustali w
trakcie projektowania aplikacji, to ich warto. Ot domylnie pierwszy element wyliczenia
ma warto rwn 0, drugi 1 itd.
Moemy to prosto sprawdzi, dokonujc rzutowania:
Console.WriteLine((int)Days.Pn);
Teraz element Pn ma warto 10, Wt 11 itd. Element Czw posiada warto rwn 50, Pt 51.
Pamitaj, e na elementach wyliczenia nie moesz dokonywa adnych dziaa
arytmetycznych (dodawanie, odejmowanie). Najpierw konieczne jest rzutowanie na typ
cakowity.
Jeeli zakres dostpny dla typu int nie wystarcza, aby nada warto dla elementu wyliczenia,
mona jawnie okreli typ:
enum Foo : long { Min = -1200034676867853, Max = 564555666 };
Pamitaj o tym, aby dokona waciwego rzutowania, jeeli chcesz odczyta warto
elementw wyliczenia:
Console.WriteLine("Min: " + (long)Foo.Min);
Console.WriteLine("Max: " + (long)Foo.Max);
Struktury
Rwnie struktury odgrywaj wan rol w rodowisku .NET Framework. Zawsze lubiem
myle o strukturach jako uproszczonych klasach, gdy s one pozbawione niektrych
zaawansowanych cech, takich jak dziedziczenie czy polimorfizm.
Struktury s zorganizowanym zbiorem danych, niekoniecznie tego samego typu. Podobnie jak
klasy mog posiada pola, metody a nawet konstruktor.
Struktury deklarujemy z uyciem sowa kluczowego struct:
public struct Contact
{
string Name;
long Phone;
byte Age;
}
Uycie struktur nie wymaga tworzenia obiektu, a jedynie zmiennej wskazujcej na struktur:
Contact c1, c2;
c1.Name = "Jan Kowalski";
c1.Age = 23;
c1.Phone = "601-000-111";
c2.Name = "Piotr Nowak";
c2.Age = 43;
c2.Phone = "501-111-222";
Najwaniejsze w tym banalnym programie jest to, e struktur moemy przekaza jako
parametr metody, tak jak to uczyniem, deklarujc metod ShowInfo(). Wyobra sobie
sytuacj, w ktrej metoda miaaby tyle parametrw, ile elementw ma struktura Contact:
ShowInfo(string Name, string Phone, byte Age);
Struktura Contact posiada jedynie 3 parametry, ale nie trudno sobie wyobrazi sytuacj, w
ktrej ta liczba jest wiksza. Taki kod nie wygldaby zbyt efektownie, dlatego wiele
parametrw mona zastpi jednym, ktry wskazuje na struktur.
Konstruktory struktur
Sposb deklarowania konstruktorw w strukturach jest do ciekawy, dlatego warto o nim
wspomnie. Pierwsza uwaga: w strukturach nie mona deklarowa konstruktorw, ktre nie
posiadaj adnych parametrw!
Zadeklarowany konstruktor musi suy do przypisywania danych polom struktury:
public struct Contact
{
public string Name;
public string Phone;
public byte Age;
public Contact(string FName, string FPhone, byte FAge)
{
Name = FName;
Phone = FPhone;
Age = FAge;
}
}
Z ciekawoci moesz usun kod z ciaa konstruktora. Prba kompilacji nie powiedzie si,
kompilator zasygnalizuje bd: Field 'FooApp.Contact.Age' must be fully assigned before
control leaves the constructor.
Konstruktor struktury jest wywoywany przy pomocy operatora new, podobnie jak w
przypadku klas. Listing 5.6 zawiera zmodyfikowany kod z listingu 5.5 i prezentuje sposb
przypisania danych do struktury za pomoc konstruktora.
Listing 5.6. Przykad wykorzystania struktur
using System;
namespace FooApp
{
public struct Contact
{
public string Name;
public string Phone;
public byte Age;
public Contact(string FName, string FPhone, byte FAge)
{
Name = FName;
Phone = FPhone;
Age = FAge;
}
}
class Program
{
static void ShowInfo(Contact c)
{
Console.WriteLine("Witaj " + c.Name + ", wiem e
masz " +
c.Age + " lat, a Twj numer telefonu to: " +
c.Phone);
}
static void Main(string[] args)
{
Contact c1, c2, c3;
c1.Age = 23;
c1.Phone = "601-000-111";
c2.Name = "Piotr Nowak";
c2.Age = 43;
c2.Phone = "501-111-222";
c3 = new Contact("Janusz Piotrkowski", "654-435345", 12);
ShowInfo(c3);
Console.Read();
}
}
}
Operatory is i as
Istniej dwa operatory, is i as, ktre s stosowane w poczeniu z klasami. Programici
rzadko z nich korzystaj, jednak warto powici im nieco uwagi.
Zacznijmy od prostego przykadu. Wyobra sobie, e mamy kilka komponentw tego samego
typu np. TextBox. Chcemy oprogramowa zdarzenie KeyPress kadego z nich.
Oczywicie, aby uatwi sobie prac, generujemy zdarzenie jednego komponentu, a
pozostaym przypisujemy t sam procedur zdarzeniow.
Zwr uwag, e wygenerowana procedura zdarzeniowa posiada dwa parametry:
private void textBox1_KeyPress(object sender,
KeyPressEventArgs e)
{
}
Wanym parametrem jest sender, ktry wskazuje na typ object. Parametr ten umoliwia
programicie kontrol oraz daje informacj, z jakiego komponentu pochodzi zdarzenie. Teraz
przy pomocy operatora is moemy sprawdzi typ komponentu, z ktrego pochodzi zdarzenie:
if (sender is TextBox) { }
Przeadowanie operatorw
Mechanizm przeadowania operatorw (lub inaczej przeciania operatorw) by znany
ju wczeniej, m.in. programistom C++, teraz zosta wprowadzony rwnie na platformie
.NET.
Oglnie mona powiedzie, e dziki przeadowaniu operatorw mona dokonywa rnych
dziaa na obiektach klas (mnoenie, dodawanie, dzielenie itp.) tak samo jak na zmiennych,
tym samym upraszczajc nieco zapis kodu. Wyglda to tak: stosujc operator (np. +) w
obiektach klas, w rzeczywistoci wywoujemy odpowiedni metod klasy. Od projektanta
zaley, jaki kod bdzie miaa owa funkcja. Moe to wyglda np. tak:
Foo MyFoo = new Foo();
Foo MyBar = new Foo();
MyFoo = 10;
if (MyFoo != MyBar)
{
}
Na pocztku ten mechanizm moe wydawa si troch dziwny, lecz osoby majce wczeniej
styczno np. z C++ nie powinny mie problemu z jego zrozumieniem.
Klasa jest banalna i waciwie nie spenia adnego okrelonego dziaania. Liczba podana w
parametrze klasy zostaje przypisana do pola X. Nie to jest jednak wane. Najwaniejsza dla
nas jest statyczna metoda na samym kocu tego kodu. Nie posiada nazwy, a jej skadnia jest
do specyficzna:
operator ()
{
}
Oto przykad wykorzystania klasy:
Foo MyFoo = new Foo(5);
// inkrementacja
MyFoo++;
Console.WriteLine(MyFoo.GetValue); // rezultat: 6
W parametrze tej metody przekazywana jest instancja klasy Foo. Do wartoci pola X tej
instancji dodajemy cyfr 2. Nastpnie zwracamy instancj. Poniewa tematyka przeadowania
operatorw moe nie by zbyt klarowna na samym pocztku, rozbudujmy nasz program i
przeadujmy kolejny operator +. Oto rozwizanie:
public static Foo operator +(Foo Obj1, Foo Obj2)
{
return new Foo(Obj1.X + Obj2.X);
}
Jak widzisz, tutaj wymagane s dwa parametry, gdy dodajemy do sobie wartoci dwch
obiektw:
MyFoo = Foo1 + Foo2;
Zakadajc, e wszystkie obiekty s typu Foo, musimy obsuy proces przypisywania danych
do obiektu:
public static implicit operator Foo(int Obj1)
{
return new Foo(Obj1);
}
Dzielenie klas
Gdy na pocztku rozdziau prezentowaem kod rdowy formularza, ktry generowany jest
automatycznie przez rodowisko Visual C# Express Edition, moge zauway, e stosowane
jest tam sowo kluczowe partial. W przypadku duych projektw moesz podzieli kod
klasy na kilka rnych moduw. Wwczas konieczne jest uycie sowa kluczowego
partial:
Taki kod w trakcie kompilacji jest czony w jedn klas. Takie rozwizanie jest stosowne w
projektach typu Windows Forms. Np. kod, ktry odpowiada za tworzenie formularza oraz
komponentw, umieszczony jest w osobnym module *.designer.cs, co znacznie uatwia prac
nad projektem.
Podsumowanie
To by bardzo wany okres nauki jzyka C#. Zagadnienia zwizane z podstawami
programowania obiektowego s bardzo wane, a to dlatego, e jzyk C# jest w peni
obiektowy. Jednoczenie zdaj sobie spraw, e tematyka programowania obiektowego jest
trudna do zrozumienia dla pocztkujcych programistw i musi min troch czasu, aby
zostaa w peni zrozumiana.
Rozdzia 6
Delegaty i zdarzenia
W poprzednim rozdziale powiedzielimy sobie sporo o programowaniu obiektowym. Jest to
temat bardzo rozlegy, szczeglnie w kontekcie programowania w jzyku C#, ktry jest
cakowicie obiektowy. Nim przejdziemy do dalszego omawiania jzyka C#, chciabym
wspomnie o dwch mechanizmach, ktrych wyjanienie musi znale si w tej ksice,
mimo i s one nieco bardziej zaawansowane. Mowa bdzie o delegatach oraz zdarzeniach.
Na tym etapie nauki zapewne nie bdziesz mia okazji lub potrzeby uywania mechanizmw,
o ktrych traktuje ten rozdzia. Jest to jednak rzecz warta uwagi i dlatego postanowiem
przeznaczy kilka najbliszych stron na jej omwienie. By moe gdy osigniesz wyszy
Delegaty
Delegaty s czsto porwnywane do wskanikw funkcji z jzyka C++. Faktem jest, e C#
czerpie wiele z jzyka C++ i Javy, jednak wiele rzeczy jest moim zdaniem ulepszonych i
prostszych w uyciu, tak jak delegaty.
Pamitasz, jak opisywaem zjawisko polimorfizmu? Pisaem wwczas o pnym powizaniu,
o tym, i decyzja, jaki obiekt ma zosta utworzony, moe zapa w trakcie dziaania
programu. Teraz wyobra sobie sytuacj, w ktrej nie jeste pewien, jaka metoda ma zosta
wykonana, decyzja ta zostanie podjta w trakcie dziaania aplikacji, np. w zalenoci od
decyzji uytkownika.
Np. uyty algorytm zostanie wybrany przez program w trakcie dziaania na podstawie
zanalizowanych danych. Owszem moesz wykorzysta instrukcj if lub switch, ale przy
bardziej rozbudowanych aplikacjach praktyczniejsze bdzie uycie delegatw.
Mona powiedzie, e delegaty przechowuj referencj (adres w pamici) do danych metod.
Korzystajc z delegatw, moesz wykona kod znajdujcy si w metodach.
Tworzenie delegatw
Delegaty to w rzeczywistoci nowe typy danych. Mog by zadeklarowane w klasie jako typ
zagniedony lub poza ni. Oto przykad:
public delegate int Foo(int X);
Tworzenie nowych delegatw jest proste, wyglda jak deklarowanie nowej metody. Jedyn
rnic jest swko kluczowe delegate.
Delegaty s nowymi typami danych. Aby je wykorzysta, tworzymy wic nowe zmienne
wskazujce na te typy, do ktrych moemy przypisa metody odpowiadajce sygnaturze
delegatu.
Delegaty nie posiadaj implementacji, jedynie sygnatur (nagwek) zakoczon znakiem
rednika.
Sygnatur okrelaj parametry delegatu oraz typ zwrotny.
To, e w poprzednim zdaniu napisaem o przypisywaniu metody do zmiennej, nie byo
Jak widzisz, powysze metody posiadaj jeden parametr typu int oraz zwracaj warto
rwnie w postaci typu int, czyli identycznie jak nasz delegat Foo.
Delegaty mog zosta zainicjowane, zanim zostan uyte, tak jak to ma miejsce w przypadku
zwykych klas. Oznacza to, e naley utworzy obiekt delegatu i jako parametr poda w nim
nazw metody, na ktr ma wskazywa:
Foo MyFoo = new Foo(Power);
Od teraz uycie MyFoo() jest rwnoznaczne z wywoaniem metody Power() (listing 6.1).
Istnieje rwnie moliwo utworzenia zmiennej wskazujcej na delegat i przypisanie do niej
wartoci:
Foo MyFoo = Power;
}
static void Main(string[] args)
{
Foo MyFoo = new Foo(Power);
Console.WriteLine("10x10 = " + MyFoo(10));
MyFoo = Div;
Console.WriteLine("10/2
Console.Read();
= " + MyFoo(10));
}
}
}
Obiektowi MyFoo przypisaem metod Div(). Oznacza to, i od tej chwili uycie MyFoo()
rwna si bedzie uyciu metody Div().
Uycie delegatw
Jak wspomniaem, przy pomocy delegatw moemy decydowa w trakcie dziaania
programu, jaka metoda zostanie wykonana. Rysunek 6.1 prezentuje prosty program
wykorzystujcy mechanizm delegatw.
}
}
public delegate int Math(int X, int Y);
}
Owszem, kto moe powiedzie, e to samo zadanie mona zrealizowa, uywajc instrukcji
warunkowej:
Math MyMath;
int Result;
if (radioMultiple.Checked)
{
Result = Multiple(Int32.Parse(textX.Text),
Int32.Parse(textY.Text));
}
else
{
Result = Div(Int32.Parse(textX.Text),
Int32.Parse(textY.Text));
}
lblResult.Text = "Suma: " + Convert.ToString(Result);
Zgadza si, taki kod rwnie jest poprawny, ale moim zdaniem przejrzystszy jest jednak ten z
uyciem delegatw.
OK, zadanie z listingu 6.2 rzeczywicie mona zrealizowa przy pomocy instrukcji
warunkowej, wywoujc po prostu odpowiedni metod. Delegaty maj jednak wiksze
zastosowanie, o czym przekonasz si ju za chwil.
Funkcje zwrotne
Kolejnym zastosowaniem delegatw s funkcje zwrotne. Parametr danej metody moe
wskazywa na delegat. Spjrz na poniszy przykad:
static void Info(int X, int Y, Foo MyFoo)
{
MyFoo("Rezultat: " + (X + Y));
}
Info(2, 3, MyFoo);
Console.Read();
}
}
}
Delegaty zoone
Bardzo fajn moliwoci jest przypisywanie do delegatu wicej ni jednej metody. Wwczas
przy wywoaniu delegatu wywoane zostan wszystkie przypisane do niego metody.
Przypisywanie dodatkowych metod do delegatu jest bardzo proste. Mona do tego
wykorzysta operatory
+= oraz -=. Zmodyfikujmy nieco program z listingu 6.3:
Foo MyFoo;
MyFoo = OutputToConsole;
MyFoo += OutputToFile;
MyFoo("Hello World");
MyFoo -= OutputToFile;
MyFoo("Hello my darling!");
Metody anonimowe
Deklaracja delegatu to nie wszystko. Musimy rwnie przypisa odpowiedni metod, ktra
ma by wywoywana po uyciu delegatu. Uycie metod anonimowych pozwala na
przyspieszenie procesu projektowania poprzez pominicie nazwy metody.
Posumy si poprzednim przykadem. Spjrz na poniszy kod:
Do zmiennej MyFoo (typu Foo) przypisujemy kod, ktry bdzie wykonywany w momencie
napotkania instrukcji MyFoo. Tworzenie anonimowych metod przypomina tworzenie
zwykych. Rnic jest to, e tutaj pomijana jest nazwa metody, a do jej tworzenia uywane
jest sowo kluczowe delegate.
Zwr uwag na konieczno uycia znaku rednika po klamrze zamykajcej kod metody
anonimowej.
Zdarzenia
O zdarzeniach wspominaem ju w poprzednim rozdziale. Myl, e po dotychczasowej
lekturze masz pojcie o znaczeniu i zastosowaniu zdarze. Powiniene rwnie umie
wykorzysta zdarzenia, uywajc rodowiska
Visual C# Express Edition. Chciabym na chwil zatrzyma si przy zdarzeniach i opisa, jak
wyglda ich dziaanie i wykorzystanie wewntrz kodu.
Jak wiesz, zdarzenia uywane s do oprogramowania czynnoci zachodzcych w trakcie
dziaania aplikacji. Takim zdarzeniem moe by ruch mysz, kliknicie w obrbie
komponentu, przesunicie kontrolki i wiele innych.
Utwrz nowy projekt aplikacji Windows Forms. Po zaadowaniu niezbdnych elementw
przez rodowisko Visual C# Express Edition na formularzu umie przykadowy komponent
np. Button. Kliknij na niego podwjnie, aby wygenerowa kod:
private void button1_Click(object sender, EventArgs e)
{
}
Nie zastanawia Ci, jak to si dzieje, e po naciniciu przycisku wykonywany jest kod
znajdujcy si w tej metodzie? Zdarzenia w duej mierze opieraj si na delegatach. W pliku
*.designer.cs moesz odnale instrukcje, ktre odpowiadaj za utworzenie przycisku Button
na formularzu oraz za przypisanie okrelonej metody do danego zdarzenia:
this.button1.Click += new
System.EventHandler(this.button1_Click);
Dziaanie takiego programu prezentuje rysunek 6.2, a cay kod rdowy zawarty jest na
listingu 6.4.
Podsumowanie
Delegaty i zdarzenia nale do bardziej skomplikowanych mechanizmw jzyka C#, na
pocztku moesz nie dostrzega zalet ich wykorzystania. Z czasem jednak, kiedy bdziesz
projektowa bardziej zaawansowane aplikacje, delegaty mog okaza si bardzo wydajnym
mechanizmem zwikszajcym czytelno i wydajno kodu. Ze zdarzeniami natomiast
bdziesz mia kontakt cay czas, programujc z wykorzystaniem wizualnej biblioteki
Windows Forms.
Rozdzia 7
Tablice i kolekcje
Omwilimy ju spor cz tego, co oferuje jzyk C#. Powiedzielimy sobie o
najwaniejszym programowaniu obiektowym, ktre moe przysporzy najwicej kopotw
pocztkujcemu programicie. Nie zaprezentowaem do tej pory bardzo wanego elementu
Czym s tablice
Wyobra sobie, e w swojej aplikacji musisz przechowa wiele zmiennych tego samego typu.
Dla przykadu, niech bd to dni tygodnia typu string. Proste? Owszem, wystarczy
zadeklarowa siedem zmiennych typu string:
string pon, wt, r, czw, pt, so, nd;
Deklarowanie tablic
Tablice deklaruje si podobnie jak zwyke zmienne. Jedyn rnic jest zastosowanie
nawiasw kwadratowych:
typ[] Nazwa;
W miejsce typ naley poda typ danych elementw tablicowych (np. int, string), a w
miejsce nazwa nazw zmiennej tablicowej. Przykadowo:
int[] Foo;
W tym miejscu zadeklarowalimy tablic Foo, ktra moe przechowywa elementy typu int.
Przed uyciem takiej tablicy naley zadeklarowa, z ilu elementw ma si ona skada. W
tym celu korzystamy ze znanego nam operatora new:
Foo = new int[5];
=
=
=
=
=
100;
1000;
10000;
100000;
1000000;
Console.WriteLine(Foo[4]);
Moliwy jest rwnie skrtowy zapis deklaracji tablic, podobny do tego znanego z tworzenia
obiektw:
int[] Foo = new int[5];
Indeks
Tablica skada si z elementw. Kademu z nich przypisany jest tzw. indeks, dziki ktremu
odwoujemy si do konkretnego elementu tablicy. w indeks ma posta liczby i wpisujemy
go w nawiasach kwadratowych, tak jak to zaprezentowano w poprzednim przykadzie. Spjrz
na kolejny przykad:
char[] Foo = new char[5];
Foo[0]
Foo[1]
Foo[2]
Foo[3]
Foo[4]
=
=
=
=
=
'H';
'e';
'l';
'l';
'o';
Uwaga! Naley uwaa, aby nie odwoa si do elementu, ktry nie istnieje! Jeeli
zadeklarowalimy tablic 5-elementow i odwoujemy si do szstego elementu (poprzez
indeks nr 5), kompilator C# nie zareaguje! Bd zostanie wywietlony dopiero po
uruchomieniu programu.
Inicjalizacja danych
Po utworzeniu tablicy kademu elementowi przypisywana jest domylna warto. Np. w
przypadku typu int jest to cyfra 0. Programista po zadeklarowaniu takiej tablicy ma
moliwo przypisania wartoci dla konkretnego elementu.
Istnieje moliwo przypisania wartoci dla konkretnego elementu ju przy deklarowaniu
tablicy. Naley wwczas wypisa wartoci w klamrach:
char[] Foo = new char[5] {'H', 'e', 'l', 'l', 'o'};
Console.WriteLine(Foo[4]);
Jzyk C# dopuszcza uproszczony zapis takiego kodu wystarczy pomin ilo elementw
tablicy:
char[] Foo = {'H', 'e', 'l', 'l', 'o'};
Tablice wielowymiarowe
0]
0]
0]
0]
0]
0]
0]
=
=
=
=
=
=
=
"Pn";
"Wt";
"r";
"Czw";
"Pt";
"So";
"Nd";
Foo[0,
Foo[1,
Foo[2,
Foo[3,
Foo[4,
Foo[5,
Foo[6,
1]
1]
1]
1]
1]
1]
1]
=
=
=
=
=
=
=
"Mon";
"Tue";
"Wed";
"Thu";
"Fri";
"Sat";
"Sun";
Jzyk C# nie ogranicza nas w iloci wymiarw. Moemy wic wprowadzi do naszej tablicy
kolejny wymiar. Poniszy fragment prezentuje deklaracj tablicy 2x4x2:
string[, ,] Foo = new string[2, 4, 2];
lub:
int[,] Foo = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
Ptla foreach
Podczas omawiania zagadnienia ptli nie wspomniaem o jednej wanej ptli sucej do
operowania na tablicach. Poniewa tematyka tablic w rozdziale 3. nie bya omawiania, pragn
wspomnie o tej ptli wanie tutaj.
Ptla ta, znana programistom PHP, Perl czy te Delphi, dla .NET jako parametru wymaga
tablicy. Spjrz na poniszy fragment kodu:
string[] Foo = new string[7];
Foo[0]
Foo[1]
Foo[2]
Foo[3]
Foo[4]
Foo[5]
Foo[6]
=
=
=
=
=
=
=
"Pn";
"Wt";
"r";
"Czw";
"Pt";
"So";
"Nd";
Przy prbie kompilacji wywietlony zostanie bd: A local variable named 'Bar'
Identyczny rezultat jak ten pokazany przed chwil mona osign, stosujc ptl for:
for (int i = 0; i < Foo.Length; i++)
{
Console.WriteLine(Foo[i]);
}
0]
0]
0]
0]
0]
0]
0]
=
=
=
=
=
=
=
"Pn";
"Wt";
"r";
"Czw";
"Pt";
"So";
"Nd";
Foo[0,
Foo[1,
Foo[2,
Foo[3,
Foo[4,
Foo[5,
Foo[6,
1]
1]
1]
1]
1]
1]
1]
=
=
=
=
=
=
=
"Mon";
"Tue";
"Wed";
"Thu";
"Fri";
"Sat";
"Sun";
Tablice tablic
Mechanizm tablic jest w jzyku C# bardzo rozbudowany. Umoliwia nawet tworzenie tablic,
ktre zawieraj kolejne tablice. Poniszy kod prezentuje deklaracj takiej tablicy:
int[][] Foo = new int[2][];
Ten zapis oznacza, i tablica Foo zawiera bdzie kolejne dwie tablice o nieokrelonej
jeszcze liczbie elementw. Te dwie kolejne tablice rwnie musz zosta utworzone:
Foo[0] = new int[50];
Foo[1] = new int[1000];
int[][]
2}, // zwr uwag na brak rednika!
2, 3}
na obecno rednika!
Console.WriteLine(Foo[0][1]);
Moim zdaniem przejrzystszy jest skrtowy zapis powyszego kodu, rwnie akceptowany
przez kompilator C#:
int[][] Foo =
{
new int[] {1, 2},
new int[] {1, 2, 3}
};
Tablice struktur
O strukturach i wyliczeniach powiedzielimy sobie w rozdziale 5. W niektrych przypadkach
przydatna okazuje si moliwo deklarowania tablic struktur lub typw wyliczeniowych. Jest
to sprawa do prosta, jeeli znasz ju podstawy uycia tablic, bowiem zamiast typu
dotychczas uywanego (czyli int, string, char itp.) naley uy wczeniej zadeklarowanej
struktury:
public struct Bar
{
public string Name;
public byte Age;
}
class Program
{
static void Main(string[] args)
{
Bar[] BarArr = new Bar[2];
BarArr[0].Name = "Janusz Kowalski";
BarArr[0].Age = 52;
BarArr[1].Name = "Piotr Nowak";
BarArr[1].Age = 18;
}
}
Klasa System.Array
Chyba oswoie si ju z myl, e cae rodowisko .NET oparte jest na klasach, strukturach i
wyliczeniach? Nie inaczej jest w przypadku tablic. Kada tablica w jzyku C# dziedziczy po
klasie System.Array, ktra dostarcza podstawowych mechanizmw do manipulacji na
elementach tablicy. To dziki metodom tej klasy moemy pobra ilo elementw w tablicy,
posortowa j czy przeszuka. Kilka najbliszych stron zostanie przeznaczonych na opisanie
podstawowych elementw tej klasy.
Jeeli chodzi o waciwoci klasy, to najwaniejsz jest Length, ktra zwraca aktualn liczb
elementw tablicy. Ta sama klasa udostpnia rwnie waciwo LongLength, ktra zwraca
64-bitow warto okrelajc rozmiar wszystkich elementw w przypadku tablic
wielowymiarowych.
Warto rwnie wspomnie o waciwoci Rank, ktra zwraca liczb wymiarw danej tablicy:
int[,] Foo = new int[3, 2] { { 1, 2 }, { 1, 2 }, { 1, 2 } };
Console.WriteLine(Foo.Rank); // tablica dwuwymiarowa
(wywietli 2)
Metody klasy
W trakcie omawiania klasy System.Array naley wspomnie o paru metodach, ktre mog
Ci si przyda przy okazji operowania na tablicach.
BinarySearch()
Uywajc algorytmu przeszukiwania binarnego, przegldam elementy tablicy, aby znale
dan warto. Pierwszym parametrem tej metody musi by nazwa tablicy, na ktrej bdzie
ona operowa. Drugim parametrem szukany element. Oto przykad uycia tej metody:
string[] Foo = new string[] { "Pn", "Wt", "r", "Czw", "Pt" };
Console.WriteLine(Array.BinarySearch(Foo, "r"));
Metoda zwraca numer indeksu, pod jakim znajduje si szukany element, lub 1, jeeli nic nie
zostao znalezione. W zaprezentowanym przykadzie metoda zwrci warto 2.
Clear()
Metoda umoliwia wyczyszczenie tablicy. W rzeczywistoci ustawia kademu elementowi
warto 0 lub null w zalenoci od jego typu. Metoda przyjmuje trzy parametry. Pierwszym
jest nazwa tablicy, drugim numer indeksu, od ktrego ma rozpocz czyszczenie, a trzecim
zasig tego procesu. Dziki metodzie Clear() mona bowiem wyczyci okrelone
elementy z tablicy. Poniszy przykad prezentuje czyszczenie caej zawartoci:
Array.Clear(Foo, 0, Foo.Length);
Metoda Clear() nie zmienia rozmiaru czyszczonej tablicy. Jeeli czycimy tablic, ktra ma
powiedzmy 5 elementw, to po przeprowadzeniu tej operacji nadal bdzie ich miaa tyle
samo.
Do elementw wyczyszczonej tablicy ponownie moemy przypisywa jakie wartoci.
Sowo kluczowe null w jzyku C# oznacza warto pust.
Clone()
Metoda Clone() zwraca kopi tablicy, z ktrej zostaa wywoana np.:
Bar = Foo.Clone();
Od tej pory Bar bdzie posiadaa takie same elementy co Foo. Poniewa metoda Clone()
zwraca dane w postaci typu object, naley dokona rzutowania na waciwy typ. Tzn. jeeli
Copy()
By moe lepszym sposobem na utworzenie kopii tablicy bdzie zastosowanie metody
Copy(). Umoliwia ona dodatkowo okrelenie rozmiarw kopiowania, tj. ile elementw
zostanie skopiowanych. Oto przykad:
string[] Foo = new string[] { "Pn", "Wt", "r", "Czw", "Pt" };
string[] Bar = new string[Foo.Length];
// tworzenie kopii
Array.Copy(Foo, Bar, Foo.Length);
Find()
Metoda umoliwia przeszukanie caej tablicy w celu znalezienia danego elementu.
Kwalifikacja danego elementu jako znaleziony lub te nie odbywa si przy pomocy
zewntrznej metody. Oto przykadowy program:
{
Point[]
new
new
new
};
points = {
Point(10, 20),
Point(100, 200),
Point(400, 500)
FindAll()
Jak sama nazwa wskazuje, metoda FindAll() wyszukuje wszystkie elementy, ktre speniaj
dane kryteria poszukiwa. Oto jak powinien wyglda program z poprzedniego listingu, jeli
ma wyszukiwa wszystkie elementy:
static void
{
Point[]
new
new
new
};
Main(string[] args)
points = {
Point(10, 20),
Point(100, 200),
Point(400, 500)
}
Console.Read();
}
private static bool pointFind(Point point)
{
if (point.X % 2 == 0)
{
return true;
}
else
{
return false;
}
}
FindLast()
Metoda ta dziaa podobnie jak Find(). Jedyna rnica jest taka, e FindLast() szuka
ostatniego wystpienia danego elementu; nie koczy pracy, gdy znajdzie pierwszy element
pasujcy do kryteriw.
GetLength()
Zwraca ilo elementw w tablicy. Umoliwia dziaanie na tablicach wielowymiarowych. W
parametrze tej metody naley poda numer wymiaru, ktrego ilo elementw ma by
pobrana. Jeeli mamy do czynienia z tablic jednowymiarow, w parametrze wypisujemy
cyfr 0.
Klasa System.Array udostpnia rwnie metod GetLongLength(), ktra dziaa
analogicznie do GetLength(), z t rnic, i zwraca dane w postaci liczby typu long.
GetLowerBund(), GetUpperBund()
Metoda GetLowerBund() zwraca numer najmniejszego indeksu w tablicy. W przewaajcej
czci przypadkw bdzie to po prostu cyfra 0. Metoda GetUpperBund() zwraca natomiast
najwikszy indeks danej tablicy. Obie metody mog dziaa na tablicach wielowymiarowych;
wwczas naley w parametrze poda indeks wymiaru. Przykadowe uycie:
int[] Foo = { 1, 2, 3, 4, 5, 6 };
Console.WriteLine("Najmniejszy indeks: {0}, najwikszy: {1}",
Foo.GetLowerBound(0), Foo.GetUpperBound(0));
GetValue()
Prawdopodobnie nie bdziesz zmuszony do czstego korzystania z tej metody, zwraca ona
bowiem warto danego elementu tablicy. W parametrze tej metody musisz poda indeks
elementu, tak wic jej dziaanie jest rwnoznaczne z konstrukcj:
Tablica[1]; // zwraca element znajdujcy si pod indeksem 1
Initialize()
We wczeniejszych fragmentach tego rozdziau pisaem o inicjalizacji tablicy. Metoda
Initialize() moe to uatwi. Jej uycie powoduje przypisanie kademu elementowi pustej
wartoci (czyli moe to by cyfra 0 lub np. warto null). Jej uycie jest bardzo proste, nie
wymaga podawania adnych argumentw:
Foo.Initialize();
IndexOf()
Przydatna metoda. Zwraca numer indeksu na podstawie podanej wartoci elementu. Przykad
uycia:
W powyszym przykadzie na konsoli zostanie wywietlona cyfra 4, gdy pod tym numerem
kryje si element Pt.
Resize()
Metoda Resize() przydaje si wwczas, gdy musimy zmieni rozmiar danej tablicy. W
pierwszym jej parametrze musimy poda nazw tablicy poprzedzon sowem kluczowym ref
oznaczajcym referencj. Drugim parametrem musi by nowy rozmiar tablicy:
string[] Foo = { "Pn", "Wt", "r", "Czw", "Pt", "So", "Nd" };
Array.Resize(ref Foo, Foo.Length + 5);
SetValue()
Metoda SetValue() umoliwia nadanie wartoci dla danego elementu tablicy.
Prawdopodobnie nie bdziesz korzysta z niej zbyt czsto, gdy to samo dziaanie mona
zrealizowa przy pomocy operatora przypisania. Gdyby jednak mia wtpliwoci, co do jej
uycia, poniej prezentuj przykad:
Foo.SetValue("Weekend", 9);
Sowo params, ktre poprzedza waciw deklaracj parametru, mwi o tym, i liczba
elementw przekazywanych do metody bdzie zmienna. Oto przykad takiego programu:
using System;
namespace FooApp
{
class Program
{
static void Foo(params string[] args)
{
for (int i = 0; i < args.Length; i++)
{
Console.Write(args[i] + " ");
}
Console.WriteLine();
}
static void Main(string[] args)
{
Foo("Adam", "Paulina");
Foo("Adam", "Paulina", "Marta");
Console.Read();
}
}
}
Jak widzisz, moliwe jest przekazanie dowolnej liczby parametrw do metody Foo(). Kady
parametr bdzie kolejnym elementem tablicy i jest to przydatna cecha jzyka C#.
Moliwe jest przekazywanie parametrw rnego typu. Nagwek metody musi wyglda
wwczas tak:
static void Foo(params object[] args)
Zasady gry
Wydaje mi si, e wikszo Czytelnikw zna zasady gry kko i krzyyk, lecz na wszelki
wypadek je przypomn. W najpopularniejszym wydaniu gra odbywa si na planszy 3x3.
Gracze na przemian umieszczaj w polach swoje znaki, dc do zajcia trzech pl w jednej
linii. Gracz ma przyporzdkowany znak krzyyka (X) lub kka (O). Jedno pole moe by
zajte przez jednego gracza i nie zmienia si przez cay przebieg gry (rysunek 7.1).
Specyfikacja klasy
Zacznijmy od utworzenia nowego moduu w naszym projekcie. Na pasku narzdziowym
znajd przycisk Add New Item (Ctrl+Shift+A) i wybierz pozycj Code File.
Przede wszystkim naley zadeklarowa nowy typ wyliczeniowy identyfikujcy znak
umieszczony na planszy do gry:
public enum FieldType {ftCircle = 1, ftCross = 10};
Nie przypadkowo wartociom typu wyliczeniowego nadaem indeksy 1 oraz 10. Te liczby
zostan wykorzystane w algorytmie obliczania, ktry z graczy wygra (lub czy w ogle kto
wygra). Jeeli mamy ju typ wyliczeniowy, naley zadeklarowa tablic, ktra bdzie
kluczowa w caej grze. Bdzie bowiem przechowywa stan gry i zawarto planszy
jednoczenie:
private FieldType[,] FField = new FieldType[3, 3];
Teraz warto zastanowi si, jakie zapisy powinny w tej klasie znale si w sekcji publicznej,
aby gotowa gra speniaa oczekiwania uytkownikw. A jakie s oczekiwania?
Funkcjonalno
gry nie musi by dua, wystarczy funkcja
, ktra na podstawie wsprzdnych X i Y umieci w tablicy odpowiednie pole (krzyyk lub
kko). Trzeba oczywicie zapewni dostp
do tej tablicy, czyli klas t naley zadeklarowa w sekcji public. Dodatkowo przydayby si
waciwoci, dziki ktrym bdzie mona okreli imiona graczy. Co poza tym? Na pewno
bdziemy chcieli wiedzie, czy gra zostaa zakoczona i kto wygra. Potrzebne bd jeszcze
dwie metody Start() oraz NewGame(). Pierwsza rozpoczyna gr, druga resetuje
ustawienia (liczb zwycistw poszczeglnych graczy).
Ustawienia gracza
Gracze bd identyfikowani za pomoc nazw. Na samym pocztku gry uytkownicy bd
mogli wpisa swoje imiona. Proponuj wic pj dalej i utworzy now struktur Player,
ktra bdzie zawiera informacj
o graczu:
// struktura opisujca gracza
public struct Player
{
// nazwa gracza
public string Name;
// ilo zwycistw
public int Winnings;
// reprezentujcy go symbol
public FieldType Type;
}
Zarys klasy
Powiedzielimy ju o informacji o graczach oraz planszy do gry. Wane jest take, aby
zawrze w klasie waciwo zwracajc aktywnego uytkownika, tj. takiego, ktry w danej
chwili ma wykona ruch. Naley rwnie zadeklarowa waciwo Winner typu bool, ktra
bdzie przypisywa warto true, jeeli ktry z graczy wygra. Bdzie to informacja, aby
zakoczy gr. W swojej klasie skorzystaem z waciwoci, z ktrych przewaajca cze
jest tylko do odczytu.
Na podstawie tego, co powiedziaem, list pl oraz waciwoci mona zapisa tak, jak to
zostao przedstawione na listingu 7.2.
Listing 7.2. Zarys klasy GomokuEngine
using System;
// typ pola (kko circle lub krzyyk (cross))
public enum FieldType {ftCircle = 1, ftCross = 10};
// struktura opisujca gracza
public struct Player
{
// nazwa gracza
public string Name;
// ilo zwycistw
public int Winnings;
// reprezentujcy go symbol
public FieldType Type;
}
class GomokuEngine
{
// tablica 3x3 reprezentujca pole gry
private FieldType[,] FField = new FieldType[3, 3];
// zmienna oznaczajca zwycistwo ktrego gracza (true)
private bool FWinner;
// ID gracza, ktry teraz wykonuje ruch
private int FActive;
// tablica graczy (tylko dwch graczy)
set
{
SetPlayer1(value);
}
}
// waciwo zwraca informacje o graczu nr 2
public string Player2
{
get
{
return GetPlayer2();
}
set
{
SetPlayer2(value);
}
}
// waciwo tylko do odczytu, zwraca informacje o polu
bitwy ;-)
public FieldType[,] Field
{
get
{
return FField;
}
}
}
Naley wzi pod uwag fakt, i uytkownik podajc wsprzdne, bdzie podawa wartoci z
przedziau 1 3. Poniewa elementy w tablicy s indeksowane od 0, naley podane wartoci
pomniejszy o 1. Nastpnie przeprowadzamy proces tzw. walidacji danych, czyli sprawdzenia
ich poprawnoci. Uytkownik nie moe postawi znaku na ju zajtym polu. Nie moe
rwnie poda wsprzdnych wikszych ni 3.
Jeeli jednak wszystko pjdzie dobrze, w tablicy FField, w odpowiednim miejscu stawiamy
odpowiedni znak. w znak odczytujemy przy pomocy metody GetActive(). Ostatnim
krokiem jest sprawdzenie, czy nie naley zakoczy gry (metoda CheckWinner()). Jeeli nie
trzeba jej zakoczy, zamieniamy aktywnego gracza.
W powyszym kodzie brakuje sprawdzania, czy uytkownik nie poda wsprzdnych
mniejszych od cyfry 1. To jest zadanie dla Ciebie. Mam nadziej, e poradzisz sobie z tym
problemem.
Jeeli w metodzie Set() proces walidacji zostanie przeprowadzony bez problemw, zwraca
ona warto true. W przeciwnym wypadku false.
Sprawdzenie wygranej
Metoda CheckWinner() ma sprawdza, czy ktry z graczy wygra, tj. czy zapeni trzy pola
w jednej linii. Zastanwmy si, ile moe by kombinacji wygranych w grze kko i krzyyk.
Skoro pl jest dziewi, mona naliczy osiem wygranych kombinacji (trzy w poziomie, trzy
w pionie i dwie na ukos). Spjrzmy na rysunek 7.2.
}
// waciwo zwraca aktywnego gracza
public Player Active
{
get
{
return GetActive();
}
}
// zwraca informacje o graczu nr 1
public string Player1
{
get
{
return GetPlayer1();
}
set
{
SetPlayer1(value);
}
}
// waciwo zwraca informacje o graczu nr 2
public string Player2
{
get
{
return GetPlayer2();
}
set
{
SetPlayer2(value);
}
}
// waciwo tylko do odczytu, zwraca informacje o polu
bitwy ;-)
public FieldType[,] Field
{
get
{
return FField;
}
}
// metoda sprawdza, czy gracz nr 1 lub 2 wygra gr
private void Sum(int Value)
{
if (Value == 3 || Value == 30)
{
FPlayer[FActive].Winnings++;
FWinner = true;
}
}
// algorytm sprawdza, czy ktry z graczy wygra gr
private void CheckWinner()
{
for (int i = 0; i < 3; i++)
{
Sum((int)FField[i, 0] + (int)FField[i, 1] +
(int)FField[i, 2]);
Sum((int)FField[0, i] + (int)FField[1, i] +
(int)FField[2, i]);
}
Sum((int)FField[0, 0] + (int)FField[1, 1] +
(int)FField[2, 2]);
Sum((int)FField[0, 2] + (int)FField[1, 0] +
(int)FField[2, 0]);
}
/* rozpoczyna waciw gr */
public void Start()
{
// przyporzdkowanie symbolu danemu graczowi
FPlayer[0].Type = FieldType.ftCircle;
FPlayer[1].Type = FieldType.ftCross;
FWinner = false;
// czyszczenie tablicy
System.Array.Clear(FField, 0, FField.Length);
}
// nowa gra ilo zwycistw zostaje wyzerowana
public void NewGame()
{
FPlayer[0].Winnings = 0;
FPlayer[1].Winnings = 0;
}
// Metoda suy do ustawiania symbolu na danym polu
public bool Set(int X, int Y)
{
// poniewa indeks tablic rozpoczyna si od zera,
naley zmniejszy
// wartoci wsprzdnych, bo user podaje wsprzdne
numerowane od 1
--X;
--Y;
// sprawdzenie, czy pole nie jest zajte
if (FField[X, Y] > 0)
{
Interfejs aplikacji
Na samym pocztku wykorzystamy utworzon klas w aplikacji konsolowej. Aby uytkownik
mg zapeni okrelone pole, musi poda jego wsprzdn X i Y. Nastpnie aplikacja
wywietla aktualny stan gry (rysunek 7.3).
Menu do gry
Menu jest do proste. Zawiera kilka opcji, ktre mona wybra przy pomocy klawiatury.
Operacje rysowania menu zawarem w osobnej metodzie Menu():
static void Menu()
{
Console.Clear();
Console.WriteLine("------- Kko i krzyyk ------");
Console.WriteLine("------------------------------");
Console.WriteLine(" 1 -- Start gry
");
Console.WriteLine(" 2 -- Opcje
");
Console.WriteLine(" 3 -- O programie
");
Console.WriteLine(" Esc -- Zakoczenie
");
Console.WriteLine("------------------------------");
}
Wyjanienia wymaga waciwie tylko pierwsza linia z ciaa tej metody: Console.Clear().
Poniewa ta metoda bdzie wywoywana wiele razy w naszym programie, naley wyczyci
zawarto okna konsoli.
Sterowanie menu
Uytkownik bdzie mg sterowa menu za pomoc klawiszy klawiatury. Trzeba wic pobra
informacje na temat wcinitego klawisza. Umoliwia to metoda ReadKey() z klasy Console,
zwracajca informacje w postaci typu ConsoleKeyInfo:
ConsoleKeyInfo Key;
Key = Console.ReadKey(true);
Warunkiem zakoczenia programu jest nacinicie klawisza Esc, wic wywietlanie menu
naley kontynuowa w ptli. Oto cay kod metody Main():
static void Main(string[] args)
{
// utworzenie nowej instancji klasy
GomokuObj = new GomokuEngine();
ConsoleKeyInfo Key;
do
{
Menu();
// pobranie wcinitego klawisza
Key = Console.ReadKey(true);
// w zalenoci od wcinitego klawisza, wykonujemy
okrelon metod
switch (Key.KeyChar)
{
case '1':
Start();
break;
case '2':
Option();
break;
case '3':
About();
break;
case '4':
break;
}
}
while (Key.Key != ConsoleKey.Escape);
}
{
// instancja klasy
static GomokuEngine GomokuObj;
// metoda wywietla informacje o autorze
static void About()
{
Console.WriteLine(
"
Kko i Krzyyk v. 1.0
\n" +
"
Copyrigh (c) Adam Boduch 2006
\n" +
"
E-mail: adam@boduch.net
\n");
Console.ReadLine();
}
// metoda wywietla menu gry
static void Menu()
{
Console.Clear();
Console.WriteLine("------- Kko i krzyyk ------");
Console.WriteLine("------------------------------");
Console.WriteLine(" 1 -- Start gry
");
Console.WriteLine(" 2 -- Opcje
");
Console.WriteLine(" 3 -- O programie
");
Console.WriteLine(" Esc -- Zakoczenie
");
Console.WriteLine("------------------------------");
}
// gwna metoda rozpoczcie gry
static void Start()
{
// jeeli uytkownik nie poda imion, przekierowujemy
go do metody Option
if (GomokuObj.Player1 == null || GomokuObj.Player2 ==
null)
{
Option();
}
// inicjalizacja gry
GomokuObj.Start();
// licznik
tur
int Counter = 1;
Console.WriteLine();
int X, Y;
char C;
// gra bdzie kontynuowana, dopki kto nie wygra
// jednym z warunkw zakoczenia jest rwnie remis
while (GomokuObj.Winner == false)
{
Console.WriteLine("Tura nr. " +
Convert.ToString(Counter) + ", Gracz: " +
GomokuObj.Active.Name);
// pobranie wsprzdnych pola
Console.Write("Podaj wsprzdn Y: ");
X = Convert.ToInt32(Console.ReadLine());
Console.Write("Podaj wsprzdn X: ");
Y = Convert.ToInt32(Console.ReadLine());
// jeeli nie mona umieci znaku na polu, nie
wykonujemy dalszego kodu
// przechodzimy do kolejnej iteracji
if (!GomokuObj.Set(X, Y))
{
continue;
}
Counter++;
// jeeli jest to 9 ruch, widocznie jest remis
przerywamy ptl
if (Counter == 9)
{
break;
}
// ponisze ptle maj za zadanie rysowanie
planszy do gry
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
switch (GomokuObj.Field[i, j])
{
case FieldType.ftCross:
C = 'X';
break;
case FieldType.ftCircle:
C = 'O';
break;
default:
C = '_';
break;
}
Console.Write(" {0} |", C);
}
Console.WriteLine();
}
}
if (GomokuObj.Winner)
{
Console.WriteLine("Gratulacje, wygra gracz " +
GomokuObj.Active.Name);
}
else
{
Console.WriteLine("Remis!");
}
Console.WriteLine("Nacinij Enter, aby powrci do
menu");
Console.ReadLine();
}
// wywietlanie opcji do gry, czyli moliwoci wpisania
imion
static void Option()
{
Console.Write("Podaj imi pierwszego gracza: ");
GomokuObj.Player1 = Console.ReadLine();
Console.Write("Podaj imi drugiego gracza: ");
GomokuObj.Player2 = Console.ReadLine();
}
static void Main(string[] args)
{
// utworzenie nowej instancji klasy
GomokuObj = new GomokuEngine();
ConsoleKeyInfo Key;
do
{
Menu();
// pobranie wcinitego klawisza
Key = Console.ReadKey(true);
// w zalenoci od wcinitego klawisza,
wykonujemy okrelon metod
switch (Key.KeyChar)
{
case '1':
Start();
break;
case '2':
Option();
break;
case '3':
About();
break;
case '4':
break;
}
}
while (Key.Key != ConsoleKey.Escape);
}
}
wiczenie dodatkowe
W klasie GomokuEngine zaimplementowaem metod NewGame(), lecz nigdzie w programie
nie ma moliwoci rozpoczcia gry od nowa, czyli wyzerowania licznika wygranych. Ba,
licznik wygranych nie jest nawet nigdzie w programie prezentowany. To zadanie dla Ciebie!
Mechanizm indeksowania
Skoro powiedzielimy sobie o tablicach, grzechem byoby nie wspomnie o tzw. mechanizmie
indeksowania, funkcji jzyka C#, ktra pozwala odwoywa si do obiektu tak jak do
zwykych tablic (przy pomocy symboli [ ]). Jest to ciekawy element jzyka C#, nieco bardziej
zaawansowany, lecz myl, e warto o nim wspomnie.
O indeksatorach moesz myle jak o tablicach poczonych z moliwoci deklarowania
waciwoci klas. Wyobra sobie, e przy pomocy symboli [ oraz ] moesz odwoywa si do
pewnych elementw klasy:
TranslateList MyList = new TranslateList();
MyList[0] = "Foo";
}
}
}
Indeksy acuchowe
Tablice asocjacyjne, znane zapewne wielu programistom PHP, nie s niestety dostpne w
jzyku C#. Zamiast tego moemy jednak skorzysta z indeksatorw, ktre dopuszczaj uycie
acuchw w nazwach indeksu:
Console.WriteLine(MyList["Pn"]);
MyList["Pn"] = "Monday";
Console.WriteLine(MyList["Pn"]);
}
public string this[string index]
{
get
{
int IndexOf = System.Array.IndexOf(ShortDay,
index);
return LongDay[IndexOf];
}
set
{
int IndexOf = System.Array.IndexOf(ShortDay,
index);
LongDay[IndexOf] = value;
}
}
}
Takie rozwizanie jest jednak nieco problematyczne. Lepiej w takich przypadkach uywa
list i sownikw, o ktrych bdzie mowa w tym rozdziale.
Podsumowujc: mechanizm indeksowania mona traktowa jako rozbudowany system tablic,
poniewa daj one wiksz kontrol nad przypisywaniem i odczytywaniem danych.
Indeksatory nie mog by opatrzone modyfikatorem dostpu static.
Kolekcje
Pisaem ju o tablicach, zgbiem temat mechanizmu indeksowania, lecz omawiajc tablice
jzyka C#, nie sposb nie wspomnie jeszcze o mechanizmie kolekcji. Tablice jako takie
obecne s w wikszoci jzykw wysokiego poziomu. Niekiedy mechanizm korzystania z
tablic jest naprawd rozbudowany (jak np. w jzyku PHP), a niekiedy moe okaza si
niewystarczajcy (jak w jzyku C#). Tablice w jzyku C# maj wiele ogranicze, ktre
moemy atwo omin, stosujc mechanizm kolekcji. Mwic o ograniczeniach, mam na
myli brak moliwoci nadawania okrelonych indeksw dla elementw tablicy (jeeli nie
chcemy, aby elementy byy indeksowane od 0) czy trudnoci w usuwaniu lub dodawaniu
kolejnych elementw tablicy. Deklarujc tablic w C#, musimy poda z gry jej rozmiar lub
nada jej elementy.
Kolekcje stanowi bardziej zaawansowany mechanizm przechowywania zbioru rnych
obiektw. Odpowiednie klasy dostarczaj bardziej zaawansowane metody suce do operacji
na zbiorze elementw kolekcji. W przestrzeni nazw System.Collections znajduje si wiele
klas sucych do operowania na zbiorach danych. W dalszej czci tego rozdziau omwimy
kilka z nich.
Interfejsy System.Collections
W przestrzeni nazw System.Collections zadeklarowanych jest wiele klas sucych do
operowania na zbiorach danych, a ich zaleno jest do dua, co moe spowodowa pewn
dezorientacj. Powrmy jeszcze na chwil do klasy System.Array. Mimo i naley ona do
przestrzeni nazw System, implementuje metody interfejsw ICloneable, IList,
ICollection oraz IEnumerable! Moemy wic o niej powiedzie, i jest prost klas
wykorzystujc mechanizm kolekcji!
Mona wyrni 8 interfejsw zdefiniowanych wewntrz przestrzeni System.Collections:
IEnumerable, ICollection, IList, IDictionary, IEnumerator, IComparer,
IDictionaryEnumerator, IHashCodeProvider. W tabeli 7.2 znajduje si krtki opis kadej
z nich.
Tabela 7.2. Opis interfejsw z przestrzeni System.Collections
Interfejs
Opis
Udostpnia interfejs, ktry umoliwia przegldanie elementw
IEnumerable
kolekcji w jednym kierunku.
ICollection
Dziedziczy metody IEnumerable. Definiuje rozmiar kolekcji.
Definiuje kolekcj, ktrej elementy s dostpne za porednictwem
IList
indeksw.
Definiuje kolekcj, w ktrej elementy s dostpne za porednictwem
IDictionary
kluczy.
Definiuje metody umoliwiajce przegldanie kolejnych elementw
IEnumerator
kolekcji.
Definiuje waciwoci umoliwiajce pobieranie kluczy i wartoci
IDictionaryEnumerator
sownikw (obiektw klas Dictionary).
IComparer
Definiuje metod suc do porwnywania dwch obiektw.
IHashCodeProvider
Definiuje metod zwracajc unikalny kod danego obiektu.
aden z tych interfejsw nie moe oczywicie dziaa samodzielnie. One jedynie definiuj
metody, ktre s nastpnie implementowane w klasach reprezentujcych. Tym zajmiemy si
w dalszej czci rozdziau. Teraz omwi pokrtce kilka najwaniejszych interfejsw.
IEnumerable
Bazowy interfejs. Definiuje waciwie tylko jedn metod GetEnumerator(). Zwraca ona
egzemplarz klasy, ktry implementuje interfejs IEnumerable. I to waciwie wszystko, co
znajduje si w tym interfejsie i wymaga omwienia.
ICollection
Interfejs, ktry dziedziczy po IEnumerable. Definiuje przydatn waciwo, z ktrej
bdziesz korzysta nie raz. Jest to waciwo Count, ktra zwraca ilo elementw w
kolekcji. Definiuje rwnie metod CopyTo(), ktra umoliwia skopiowanie obiektu
zawartego w danej kolekcji do tablicy (System.Array). Metoda ta posiada dwa parametry.
Pierwszy z nich to nazwa tablicy, do ktrej zostan skopiowane dane. Drugi to indeks
(liczony od zera), od ktrego rozpocznie si kopiowanie.
IList
Interfejs IList przejmuje waciwoci oraz metody po interfejsach ICollection oraz
IEnumerable. Jest to bazowy interfejs dla tzw. list. Definiuje metody oraz waciwoci
umoliwiajce dodawanie i kasowanie elementw listy. Na razie przyjrzyj si metodom i
waciwociom zdefiniowanym w interfejsie IList (tabela 7.3.). Przykady wykorzystania
klas implementujcych ten interfejs zaprezentuj w dalszej czci ksiki.
Tabela 7.3. Metody oraz waciwoci zdefiniowane w interfejsie IList
Waciwo/Metoda
Opis
IsFixedSize
Waciwo okrela, czy lista ma stay rozmiar.
Waciwo okrela, czy dana lista zwraca swoje wartoci jedynie do
IsReadOnly
odczytu.
Waciwo umoliwia pobranie lub ustawienie elementw pod danym
Item
indeksem.
Add()
Metoda umoliwia dodanie elementw do listy.
Clear()
Usuwa elementy z danej listy.
Contains()
Sprawdza, czy lista zawiera dan warto.
IndexOf()
Zwraca indeks danej wartoci.
Insert()
Umoliwia wstawienie elementu pod wskazan pozycj.
Remove()
Usuwa pierwsze wystpienie danego elementu.
RemoveAt()
Usuwa element okrelony danym indeksem.
IDictionary
Interfejs definiujcy metody oraz waciwoci umoliwiajce korzystanie z tzw. sownikw.
Sowniki umoliwiaj przechowywanie danych, do ktrych dostp uzyskuje si, podajc
odpowiedni klucz nie indeks, jak to jest w tablicach oraz listach. Tym zagadnieniem
zajmiemy si nieco pniej.
Interfejs, podobnie jak IList, definiuje waciwoci IsFixedSize, IsReadOnly i Items oraz
metody Add(), Clear(), Contains(), Remove(). Dodatkowo posiada dwie waciwoci:
IEnumerator
Interfejs IEnumerator definiuje waciwo Current zwracajc biecy element kolekcji.
Dodatkowo definiuje metody MoveNext() oraz Reset(). Pierwsza z nich suy do
przechodzenia do kolejnego elementu kolekcji, a druga do przejcia do pierwszego
elementu.
Stosy
Po tym przydugim wstpie czas przej do rzeczy bardziej praktycznych, czyli obsugi
kolejek. Na pocztek chciabym wspomnie o klasie Stack (ang. stos), ktra suy do
przechowywania danych zgodnie z zasad FILO (ang. First-in Last-out), co oznacza:
pierwszy wchodzi, ostatni wychodzi.
Wyobra sobie stos talerzy jeden ley na drugim. Aby wycign ten na samym dole,
musisz najpierw cign te lece na nim, prawda? Identycznie dziaa klasa Stack. Moesz
swobodnie dodawa kolejne elementy do kolekcji, lecz nie ma sposobu na usunicie tego
dodanego na samym pocztku.
Spjrz na listing 7.5, ktry prezentuje przykad uycia klasy Stack oraz wywietlania
zawartoci danego stosu.
Listing 7.5. Przykad wykorzystania klasy Stack
using System;
using System.Collections;
namespace FooConsole
{
class Program
{
static void Main(string[] args)
{
Stack MyStack = new Stack();
for (int i = 0; i < 20; i++)
{
MyStack.Push("Pozycja nr " + i);
}
Console.WriteLine("Ostatni element: " +
MyStack.Peek());
Console.WriteLine("Usunity element: " +
MyStack.Pop());
Console.WriteLine();
IEnumerator MyEnum = MyStack.GetEnumerator();
while (MyEnum.MoveNext())
{
Console.WriteLine(MyEnum.Current.ToString());
}
Console.Read();
}
}
}
Przed uyciem klasy Stack naley wywoa konstruktor klasy. Mona w nim okreli
przewidywaln ilo elementw lub wywoa konstruktor bez parametrw, tak jak ja to
zrobiem. Wwczas zostanie nadana domylna ilo elementw, czyli 10. W razie potrzeby ta
warto zostanie automatycznie zwikszona, wic nie musisz si ba, e wystpi bd (takie
ryzyko istniao przy okazji korzystania z tablic).
Korzystajc z metody Push(), umieszczam na stosie kolejne elementy. Metoda Peek() suy
do pobrania ostatniego elementu stosu, natomiast Pop() zwraca ostatni element, po czym go
usuwa.
Zwr uwag na sposb wywietlania elementw z kolekcji. W tym celu zadeklarowaem
zmienn wskazujc na interfejs IEnumerator. Uyem rwnie metody GetEnumerator(),
ktra zwraca implementowany obiekt. Dziki temu, posugujc si odpowiednimi metodami,
jestem w stanie pobra kolejne elementy ze stosu i je wywietli.
Klasa Stack implementuje interfejsy ICollection, IEnumerable, ICloneable. Pamitaj
wic o tym, i zawiera metody oraz waciwoci zdefiniowane w tych interfejsach.
Ze wzgldu na to, e klasa Stack nie umoliwia usuwania dowolnego elementu stosu, pewnie
nie bdziesz z niej czsto korzysta. Waciwie zaley to tylko od Twoich potrzeb. Jeeli
istnieje konieczno umieszczenia elementw w formie stosu, wydaje mi si, e zastosowanie
klasy Stack bdzie dobrym rozwizaniem. Jeeli musisz mie moliwo usuwania
dowolnego elementu, zapewne bdziesz zmuszony skorzysta z list.
Kolejki
Klasa Queue dziaa podobnie jak Stack. Rny jest jednak sposb ich dziaania. Klasa Queue
dziaa zgodnie z zasad FIFO (ang. First-in First-out), co mona przetumaczy jako:
pierwszy wchodzi, pierwszy wychodzi. Aby lepiej zrozumie zasad dziaania tej klasy,
wyobra sobie zwyk kolejk sklepow. Ten, kto stoi w niej ostatni, obsugiwany jest jako
ostatni, prawda? Pierwsza osoba w kolejce wychodzi ze sklepu jako pierwsza. Klasa Queue
rwnie nie posiada metody umoliwiajcej usunicie dowolnego elementu. Zamiast tego
istnieje moliwo umieszczenia danego elementu jako ostatniego w kolejce, co realizuje
metoda Enqueue(). Jeeli mwimy o usuwaniu, to realizuje to metoda Dequeue(), ktra
usuwa pierwszy element w kolejce. Podsumowujc: jeeli chcemy usun ostatni element
kolejki, najpierw musimy usun wszystkie pozostae utworzone wczeniej. Na listingu 7.6
znajduje si kod rdowy zmodyfikowanego programu z listingu 7.5.
Listing 7.6. Program prezentujcy uycie klasy Queue
using System;
using System.Collections;
namespace FooConsole
{
class Program
{
static void Main(string[] args)
{
Queue MyQueue = new Queue();
for (int i = 0; i < 20; i++)
{
MyQueue.Enqueue("Pozycja nr " + i);
}
Console.WriteLine("Pierwszy element: " +
MyQueue.Peek());
Console.WriteLine("Usunity element: " +
MyQueue.Dequeue());
Console.WriteLine();
IEnumerator MyEnum = MyQueue.GetEnumerator();
while (MyEnum.MoveNext())
{
Console.WriteLine(MyEnum.Current.ToString());
}
Console.Read();
}
}
}
Klasa ArrayList
ArrayList stanowi prawdopodobnie najlepsz alternatyw pomidzy klas Stack oraz
Queue. Posiada ona rwnie metody obecne w klasie Array, wic operowanie ni jest
podobne do obsugi zwykych tablic. Implementuje interfejsy IList, ICollection,
IEnumerable, ICloneable, wic miej na uwadze to, i zawiera waciwoci i metody, o
ktrych wspominaem kilka stron wczeniej, przy okazji omawiania tych interfejsw.
Listy
Opisaem ju, czym charakteryzuj si kolejki oraz stosy. Chciabym teraz pj nieco dalej i
zaj si tematyk list oraz typami generycznymi. Zostawmy na razie moliwoci oferowane
przez przestrze nazw System.Collections i pjdmy dalej. Zajmijmy si moliwociami
oferowanymi przez .NET 2.0, z ktrego zapewne teraz korzystasz, a konkretnie klasami
znajdujcymi si w przestrzeni System.Collections.Generic.
Typy generyczne
Podczas omawiania klas nie wspomniaem o jednej waciwoci klas szeroko
wykorzystywanej przy kolekcjach. Jest to nowo w rodowisku .NET Framework 2.0
(poprzednia wersja 1.1 nie posiadaa moliwoci wykorzystania typw generycznych),
wzorowana na technologii templates z jzyka C++.
Spjrz na poniszy kod prezentujcy, w jaki sposb moemy doda elementy do kolekcji typu
ArrayList i wywietli je:
ArrayList Foo = new ArrayList();
Foo.Add("Adam");
Foo.Add("Marta");
Foo.Add(100);
Foo.Add(2.34);
for (int i = 0; i < Foo.Count; i++)
{
Console.WriteLine("Indeks: {0} warto: {1}", i, Foo[i]);
}
Przy pomocy Add() dodajemy do listy kolejne elementy, raz typu string, pniej liczb
staoprzecinkow i wreszcie liczb rzeczywist. Jest to absolutnie dopuszczalne, gdy
parametr metody Add() jest typu object, a jak wiadomo wszystkie typy .NET
Framework dziedzicz po tej klasie.
Przy kadym wywoaniu metody Add() program musi wykona pakowanie (ang. boxing)
typw, a przy wywietlaniu odpakowywanie. Przy duej iloci danych trwa to do dugo.
O technice
pakowania oraz odpakowywania pisaem w rozdziale 5.
Dlatego jeeli zamierzasz umieci w kolekcji dane tego samego typu, lepiej wykorzysta
klasy oferowane przez przestrze nazw System.Collection.Generic. Znajduj si tam
klasy, odpowiedniki ju wspomnianych Stack oraz Queue, ktre dziaaj szybciej na danych
tego samego typu. Nie ma w przestrzeni nazw System.Collection.Generic klasy
ArrayList. Zamiast tego moemy jednak wykorzysta klas List, ktra jest waciwie
generycznym odpowiednikiem ArrayList. Listing 7.7 prezentuje prosty przykad uycia
klasy List:
Listing 7.7. Przykad uycia klasy List
using System;
using System.Collections.Generic;
namespace FooConsole
{
class Program
{
static void Main(string[] args)
{
List<int> Foo = new List<int>();
Foo.Add(10);
Foo.Add(100);
Foo.Add(10000);
for (int i = 0; i < Foo.Count; i++)
{
Console.WriteLine("Indeks: {0} warto: {1}",
i, Foo[i]);
}
Console.Read();
}
}
}
Przy deklarowaniu i utworzeniu obiektu klasy List musiaem poda typ danych (int), na
jakim chcemy operowa. Oczywicie istnieje moliwo operowania na dowolnym typie
danych wwczas w miejsce T naley poda jego nazw.
Teraz przy tworzeniu egzemplarza klasy kompilator bdzie wymaga, aby poda rwnie typ
danych, na ktrych ma ona operowa np.:
Generic<string> MyGeneric = new Generic<string>();
Przyjo si, e przy deklarowaniu typw generycznych stosujemy fraz <T>. Kompilator nie
wymusza jednak takiego nazewnictwa, wic rwnie dobrze moemy napisa: class
Generic<Type> {}.
Po zadeklarowaniu takiej klasy w jej obrbie typ T bdzie oznacza typ danych podany
podczas jej tworzenia. Mona go dowolnie wykorzystywa. Np.:
class Generic<T>
{
public void Add(T X)
{
Console.WriteLine("{0}", X);
}
}
Metody generyczne
Istnieje rwnie moliwo deklarowania metod generycznych. Ich tworzenie wyglda bardzo
podobnie:
static void Foo<T>(T Bar)
{
Console.WriteLine("{0}", Bar);
}
Wywoujc tak metod, moesz, aczkolwiek nie musisz, podawa typu danych np.:
Foo("Adam"); // dobrze
Foo<int>(12); // dobrze
Korzystanie z list
Klasa List posiada spore moliwoci implementujc interfejsy IList, ICollection,
IEnumerable, IList, ICollection, IEnumerable, umoliwia dodawanie, usuwanie
dowolnych pozycji list. Nie bd prezentowa spisu wszystkich metod oraz waciwoci, gdy
to zostao waciwie powiedziane ju wczeniej, przy okazji omawiania tablic oraz
interfejsw. Zaprezentuj za to prost aplikacj przedstawiajc uycie list.
Aplikacja bdzie do prosta. Po uruchomieniu uytkownik bdzie mg doda do listy nazw
wojewdztwa wraz z jego stolic (rysunek 7.5).
{
// struktura przechowywana w licie
public struct Location
{
public string Province;
public string Capital;
}
public partial class MainForm : Form
{
private List<Location> MyList;
public MainForm()
{
InitializeComponent();
}
// metoda uaktualniajca stan komponentu ListBox
zgodnie ze stanem faktycznym
private void UpdateList()
{
lbLocation.Items.Clear();
for (int i = 0; i < MyList.Count; i++)
{
lbLocation.Items.Add(
String.Format("{0,-10} {1,10}",
MyList[i].Province, MyList[i].Capital
));
}
}
private void MainForm_Load(object sender, EventArgs e)
{
// utworzenie instancji klasy w momencie
zaadowania formularza
MyList = new List<Location>();
lbLocation.Items.Clear();
}
private void btnClear_Click(object sender, EventArgs
e)
{
MyList.Clear();
UpdateList();
}
private void btnDelete_Click(object sender, EventArgs
e)
{
// usunicie zaznaczonej pozycji
MyList.RemoveAt(lbLocation.SelectedIndex);
UpdateList();
}
private void btnAdd_Click(object sender, EventArgs e)
{
Location MyLocation = new Location();
MyLocation.Province = textProvince.Text;
MyLocation.Capital = textCapitol.Text;
textProvince.Text = textCapitol.Text = "";
MyList.Add(MyLocation);
UpdateList();
}
}
}
Sowniki
Programistom PHP zapewne znany jest mechanizm tablic, w ktrym zamiast indeksu
uywany jest tzw. klucz. Wiesz ju, e indeks jest unikaln wartoci identyfikujc dany
element w tablicy/licie. Omawiajc mechanizm indeksowania, pokazaem, w jaki sposb
zrobi indeks, ktry mgby przyjmowa wartoci acuchowe. W przypadku sownikw
zamiast indeksu jest klucz (warto typu string), ktry rwnie musi posiada unikaln
warto w obrbie caej kolekcji. Waciwie to, czy bdzie on typu string, czy
jakiegokolwiek innego, zaley ju tylko od nas. To samo dotyczy wartoci elementw
sownika. Najczciej sowniki su mimo wszystko do przechowywania wartoci
tekstowych, gdzie klucz i warto s typu string.
Klasa Dictionary zadeklarowana jest w przestrzeni nazw System.Collections.Generic w
sposb nastpujcy:
Jak wic widzisz, ilo interfejsw, ktre s w klasie implementowane, jest imponujca.
Deklarujc instancj klasy, naley poda zarwno typ klucza, jak i wartoci np.:
Dictionary<string, string> MyDictionary;
Korzystanie ze sownikw jest bardzo podobne do uywania zwykych list. Rnica jest taka,
i sowniki daj nam moliwo okrelenia typu klucza. Tak wic dla powyszej deklaracji,
dane do sownika moemy przypisywa w ten sposb:
MyDictionary["Klucz"] = "Warto";
Jeeli chcemy korzysta ze sownikw tak jak z list, naley utworzy nastpujc zmienn
wskazujc na klas:
Dictionary<int, string> MyDictionary;
Wwczas kluczem takiego sownika jest liczba staoprzecinkowa, a wartoci cig znakw
typu string.
Przykadowy program
Aby zaprezentowa moliwoci dziaania sownikw, napisaem prosty program
umoliwiajcy tumaczenie tekstu na podstawie swek wpisanych do sownika. Dziaanie jest
proste: przy pomocy metody Replace() program zamienia okrelony tekst znajdujcy si w
komponencie RichTextBox na wyrazy pobierane ze sownika.
Program napisaem na podstawie biblioteki wizualnej Windows Forms. Aplikacja zostaa
podzielona na dwie zakadki przy uyciu komponentu TabControl (rysunki 7.6 oraz 7.7).
Podsumowanie
By moe na tym etapie nauki jzyka nie dostrzegasz zalet stosowania tablic czy kolekcji.
By moe nie widzisz rwnie potrzeby stosowania tego typu rozwiza we wasnych
aplikacjach. Moesz mi wierzy lub nie, ale z czasem, gdy Twoje programy bd coraz
bardziej zaawansowane, zaistnieje konieczno wykorzystania tablic. Jako e tablice w C# nie
daj takich korzyci jak w innych jzykach (m.in. z powodu trudnoci w deklarowaniu
rozmiaru w trakcie dziaania programu), by moe bdziesz zmuszony do wykorzystania
kolekcji. Wwczas moesz sign po ten rozdzia i przeanalizowa prezentowane tutaj
przykady
Rozdzia 8
Obsuga wyjtkw
Trzeba sobie uwiadomi, e bdy w aplikacjach s niczym nieg na biegunie
elementem nieodcznym. Wielu programistw, czsto z popiechu, lecz take z braku
wystarczajcych umiejtnoci, marginalizuje problem bdw w aplikacjach. Wiele firm,
mimo ogromnego zaangaowania setek programistw oraz duych nakadw finansowych,
wci nie jest w stanie pozby si wszystkich bdw (np. Microsoft) i bez przerwy publikuje
nowe poprawki do swoich produktw. Tylko program zawierajcy trzy linie kodu rdowego
moe by pozbawiony jakichkolwiek bdw, lecz w przypadku skomplikowanych,
rozbudowanych aplikacji uniknicie niedoskonaoci jest niemoliwe. Dzieje si tak dlatego,
e programista jest tylko czowiekiem i po prostu si myli. Pozornie aplikacja moe
zachowywa si normalnie, a bd moe tkwi gdzie indziej. Nie mwi tutaj bowiem o
bdach wykrywanych w czasie kompilacji, najatwiejszych do usunicia, ktre sprowadza si
najczciej do drobnych poprawek, czsto bardzo miesznych na przykad dopisanie
rednika na kocu wyraenia. Najtrudniejsze do wykrycia s bdy zagniedone w kodzie,
kiedy program pozornie dziaa prawidowo, lecz nie do koca wykonuje operacje, ktrych
oczekuje uytkownik. Warto, aby w tym momencie dobrze zapamita stwierdzenie, i
program zawsze dziaa prawidowo, a jeli nie dziaa zgodnie z naszymi oczekiwaniami
jest to zwyczajnie wina projektanta.
Pomijam tutaj bdy kompilatora, bo te rwnie s tworzone przez ludzi i rwnie mog
zawiera bdy. Kompilator jest jednak produktem podwyszonego ryzyka: nie mona
pozwoli, aby zawiera choby najdrobniejsze bdy.
Bd w aplikacji czsto jest okrelany mianem bug (z ang. robak, pluskwa). Termin ten wzi
si z czasw, gdy komputery zajmoway due pomieszczenia i pobieray tyle energii co mae
osiedle, a ich obsug zajmowa si sztab ludzi. Robaki, ktre zalgy si gdzie w
zakamarkach ogromnej maszyny, czasami powodoway zwarcie instalacji elektrycznej. Od
tamtej pory bdy nazywa si bugami, a proces ich wykrywania debugowaniem (z ang.
debugging).
Czst przyczyn uwidocznienia bdu jest czynnik ludzki. Zamy na przykad, e piszesz
skomplikowan aplikacj biurow, z ktrej bdzie korzystao wielu ludzi. Nie kady z nich
jest informatykiem, nie kady posiada odpowiedni wiedz, aby wystarczajco dobrze
obsuy Twj program. Ty oczywicie powiniene dostarczy wraz z aplikacj podrcznik
uytkownika oraz projektowa interfejsy w sposb przejrzysty, lecz nie jeste w stanie
unikn sytuacji, w ktrej uytkownik obsuy j nieprawidowo na przykad w danym
polu tekstowym wpisze tekst zamiast liczby. Taki z pozoru bahy bd moe spowodowa
niespodziewane efekty i dziwne zachowania Twojego programu, dlatego nowoczesne jzyki
programowania dostarczaj odpowiednie mechanizmy, dziki ktrym jestemy w stanie
odpowiednio zareagowa na takie przypadki.
Czym s wyjtki
Taki kod nie zostanie skompilowany, poniewa kompilator wykryje prb dzielenia przez
zero. Mona go jednak atwo oszuka, podstawiajc wartoci pod zmienne:
int
X =
Y =
Z =
X, Y, Z;
10;
0;
X / Y;
Obsuga wyjtkw
W jzyku C#, jak i w wielu innych popularnych jzykach, kod naraony na wystpienie
nieprzewidzianych sytuacji musimy oznaczy sowem kluczowym try. Kod wystpujcy w
bloku try bdzie obserwowany i w razie wystpienia nieprzewidzianych sytuacji bdzie
mona odpowiednio zareagowa:
try
{
// tutaj kod
}
Sam blok try nie wystarczy, musimy gdzie zawrze kod, ktry bdzie wykonywany w razie
wystpienia bdu. W tym celu uywamy sowa kluczowego catch:
try
{
// kod
}
catch
{
// kod w razie wystpienia wyjtku
}
Sprbuj uruchomi taki kod. Wskutek jego dziaania na konsoli wywietlony zostanie tekst
Prosimy nie dzieli przez zero!.
Uruchamiajc projekt z poziomu rodowiska Visual C# Express Edition, moesz nie
zaobserwowa dziaania wyjtkw. Wszystko dlatego, e to rodowisko
odpowiada w takim przypadku za obsug bdw. Aby lepiej zobrazowa dziaanie wyjtkw,
uruchamiaj swoje programy bez uycia debuggera. W Visual C# Express Edition odpowiada
za to skrt klawiaturowy Ctrl+F5.
Blok finally
Blok catch jest opcjonalny. Rwnie dobrze w miejsce sowa kluczowego catch moemy
wpisa finally. Rnica jest spora: kod zawarty w bloku finally zostanie wykonany
zawsze, bez wzgldu na to, czy wyjtek wystpi, czy te nie. Jest to dobre miejsce na
wykonanie instrukcji, ktre musz by wykonane przed zamkniciem programu. Dobrym
przykadem s operacje na plikach
. Po otwarciu pliku dokonujemy odczytu danych i skomplikowanych operacji. Dodajmy do
tego, e plik jest otwarty na wyczno naszego programu. Chcemy, aby w razie wystpienia
Po uruchomieniu takiej aplikacji oba komunikaty zostan wywietlone tylko wtedy, jeeli
dzielenie spowoduje bd (w najlepszym
przypadku zostanie wywietlony jeden z nich, z bloku finally).
Zagniedanie wyjtkw
Bloki wyjtkw mona w miar potrzeb dowolnie zagnieda:
try
{
// kod
try
{
// kod
try
{
}
finally
{
// finally
}
}
catch
{
// jeszcze inny bd
}
}
catch
{
// bd
}
finally
{
// blok finally
}
Klasa System.Exception
Nieobsuone wyjtki, czyli takie, ktrych nie obsuguje nasza aplikacja, s obsugiwane
przez system. Owocuje to najczciej wystpieniem komunikatu o bdzie, czsto niejasnym,
informujcym np. o nieprawidowym odwoaniu do pamici. To ju jednak s ekstremalne
sytuacje.
W kadym razie jeli wystpi wyjtek, system dostarcza programicie informacji na jego
temat, ktre moe on obsuy wedle wasnego uznania. Informacje oczywicie dostarczone
s w formie obiektu klasy, ktrej klas bazow jest System.Exception. W rzeczywistoci
rodowisko .NET
Framework posiada cakiem pokan kolekcj wyjtkw dziedziczonych po tej wanie
klasie.
Poniszy kod prezentuje obsug wyjtku polegajcego na wywietlaniu dostarczonego
komunikatu o bdzie:
// kod
try
{
Z = X / Y;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Jak widzisz, parametr bloku catch jest opcjonalny, aczkolwiek dopuszczalny. Taki zapis
oznacza deklaracj zmiennej e typu Exception (czyli w rzeczywistoci System.Exception) i
przypisanie do niej informacji odnonie do bdu. Tabela 8.1 zawiera spis najwaniejszych
waciwoci klasy System.Exception.
Tabela 8.1. Najwaniejsze waciwoci klasy System.Exception
Waciwo
Opis
Data
Dodatkowe informacje na temat rda wystpienia wyjtku.
Umoliwia odczytanie lub ustawienie linka (np. do pomocy) zwizanego z
HelpLink
bdem.
Message
Komunikat bdu.
Umoliwia odczytanie lub przypisanie nazwy aplikacji lub obiektu, w ktrym
Source
wystpi bd.
TargetSite Umoliwia odczytanie metody, w ktrej wystpi bd.
Wywoywanie wyjtkw
throw jest najczciej uywane wewntrz bloku try, aczkolwiek dopuszczalne jest jego
uycie poza nim. W takiej sytuacji aplikacja moe uruchomi domyln obsug wyjtkw, co
najczciej koczy si komunikatem o bdzie.
try
{
FField[X, Y] = GetActive().Type;
}
catch
{
Console.WriteLine("Nieprawidowe pole! Naucz si gra!");
}
Ale czy koniecznie chcemy, aby komunikat bdu by wywietlany w oknie konsoli? Moe
lepszym rozwizaniem byoby, gdybymy pozwolili decydowa klasie wyjtku, co ma zrobi
z danym komunikatem?
BadPointException("Nieprawidowe pole! Naucz si gra!");
Dobr praktyk jest, aby klasy obsugi wyjtkw posiaday w nazwie swko Exception.
W konstruktorze klasy do odpowiednich waciwoci przypisywane s dane, ktre mog
pomc w ewentualnym odszukaniu i naprawie usterki. Zwr rwnie uwag, e konstruktor
klasy MediumException dziedziczy po takim samym konstruktorze z System.Array. W
bardziej rozbudowanych aplikacjach zalecane jest deklarowanie 3 konstruktorw dla klas
wyjtkw, kady z innymi parametrami:
public MediumException()
{
}
public MediumException(string Message)
: base(Message)
{
}
public MediumException(string Message, Exception inner)
: base(Message, inner)
{
}
Przykadowa aplikacja
Aby usystematyzowa wiedz na temat wyjtkw, proponuj napisanie prostej aplikacji z
wykorzystaniem biblioteki WinForms. W zalenoci od wybranej opcji bdzie ona generowa
dany wyjtek, a nastpnie odpowiednio go obsugiwa. Program w trakcie dziaania
zaprezentowany zosta na rysunku 8.1.
using System.Text;
using System.Windows.Forms;
namespace ExceptionApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void RunBtn_Click(object sender, EventArgs e)
{
try
{
if (lowExceptionRadio.Checked)
{
throw new LowException("Niegrony bd");
}
if (mediumExceptionRadio.Checked)
{
throw new MediumException("redni bd");
}
if (HighExceptionRadio.Checked)
{
throw new HighException();
}
}
catch (Exception ex)
{
RichBox.Clear();
RichBox.Text += String.Format(
"Komunikat: {0}\n" +
"Podzesp: {1}\n" +
"Metoda: {2}\n" +
"Podzesp: {3}", ex.Message,
ex.Source, ex.TargetSite, ex.HelpLink);
}
}
}
public class LowException : System.Exception
{
public LowException(string Message) : base(Message)
{
this.Source = "FooException";
this.HelpLink = "http://4programmers.net";
}
}
public class MediumException : System.Exception
{
public MediumException(string Message)
: base(Message)
{
this.Source = "FooException";
this.HelpLink = "http://4programmers.net/C_sharp";
}
}
public class HighException : System.Exception
Pamitaj, aby wszelkie klasy swojego programu umieszcza w kodzie niej ni klasa obsugi
formularza (w moim wypadku Form1). Inaczej rodowisko Visual C# Express Edition ma
problem z prawidowym dziaaniem w trybie projektowania.
Waciwie wszystko w tym kodzie powinno by dla Ciebie zrozumiae. W konstruktorze
przypisujemy wartoci waciwociom dziedziczonym po klasie System.Exception. Owe
waciwoci s pniej odczytywane w bloku catch.
Przepenienia zmiennych
Na pocztku tej ksiki wprowadziem pojcie typu danych. Tam rwnie wspomniaem o
tym, i kady typ danych posiada jaki maksymalny zakres, tj. maksymaln warto, jak
mona przypisa do zmiennej tego typu. Np. maksymalna warto, jak mona przypisa do
zmiennej typu byte, to 255. Prba przypisania wikszej wartoci zakoczy si bdem:
byte b = 256;
OK, kompilator wykrywa takie prby, lecz nie jest w stanie wykry prby przypisania
wartoci wikszej ni 255 w trakcie dziaania programu:
byte X = 250;
byte Y = 50;
byte Z = (byte) (X + Y);
Poniewa wartoci numerowane s od zera, zmienna Z bdzie miaa warto 44, a nie 45, jak
mogoby wynika z tego rachunku matematycznego.
Uycie sowa kluczowego checked spowoduje, i przy przepenieniu zmiennej zgaszany
bdzie wyjtek OverflowException:
using System;
namespace FooApp
{
class Program
{
static void Main(string[] args)
{
byte X, Y, Z;
X = 250;
Y = 50;
Z = 0;
try
{
Z = checked((byte)(X + Y));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Write(Z);
Console.Read();
}
}
W wyniku zaistnienia takiego kodu na ekranie konsoli wywietlona zostanie tre komunikatu
o bdzie. Sowa kluczowego checked mona uy jako operatora
(tak jak to przedstawiono w przykadzie powyej) lub w formie konstrukcji:
checked
{
// kod naraony na przepenienie zmiennej
}
Podsumowanie
Obsuga wyjtkw w C# nie jest ani trudna, ani te konieczna, aczkolwiek warto si nad tym
zastanowi podczas pisania aplikacji. Jeeli piszesz kod, ktry moe by naraony na ryzyko
wystpienia bdu, stosuj wywoanie try-catch-finally. Pozwoli Ci ono odpowiednio
zareagowa na ewentualne bdy oraz zwolni wszystkie zasoby zadeklarowane w trakcie
dziaania aplikacji.
Rozdzia 9
acuchy w C#
acuchy w informatyce oznaczaj cig znakw. Do tej pory pojcie cig kojarzyo Ci si
zapewne z typem string. Powiedzmy sobie szczerze, e jest to jeden z najczciej
wykorzystywanych typw danych w C#. W tym rozdziale mam zamiar wyoy tematyk
wykorzystania acuchw w C#, rwnie przy uyciu klasy System.StringBuilder.
Omwione zostan podstawowe metody suce do operowania na acuchach w rodowisku
.NET Framework.
Typ System.String
Podstawowym typem danych w rodowisku .NET Framework, ktry umoliwia operacje na
acuchach, jest System.String. W C# odpowiednikiem tego typu jest string i taki zapis
stosuje si najczciej ze wzgldu na skrcon form. Kady jzyk obsugujcy platform
.NET posiada wasny odpowiednik typu System.String, np. w Delphi jest to po prostu
String.
W zamierzchych czasach Turbo Pascala, ktry rwnie posiada obsug acuchw, dugo
jednego acucha moga wynosi maksymalnie 255 znakw. Nie jest to zbyt praktyczne, gdy
musimy przechowa w zmiennej naprawd du ilo tekstu. W nowoczesnych
jzykach programowania dugo tekstu, jaki moemy przypisa do acucha, jest niemal
nieograniczona, a praktycznie ogranicza j tylko ilo pamici, jak posiadamy w
komputerze. Ilo pamici, jaka jest rezerwowana na potrzeby danego acucha, zaley od
jego dugoci. Wszystko odbywa si automatycznie, nie musimy si martwi o przydzielanie
czy zwalnianie pamici.
acuch w .NET jest waciwie sekwencj (tablic) znakw typu String.Char poczon w
jedn cao. Do kadego elementu acucha moemy odwoywa si jak do elementu tablicy
przy pomocy symboli []:
System.String S = "Hello World";
Console.WriteLine(S[0]);
Unicode w acuchach
Platforma .NET w caoci wspiera system kodowania znakw Unicode. Standard ten w
zamierzeniu obejmuje wszystkie jzyki wiata i eliminuje problem tzw. krzaczkw. Oznacza
to, e acuchy, m.in. jzyka C#, s kodowane w systemie Unicode, co sprawia, e kady
znak zajmuje 2 bajty pamici. Co wicej kodowanie Unicode moemy rwnie stosowa
w kodzie rdowym, uywajc np. polskich znakw w nazwie zmiennych:
string gegka;
Jest to bardzo dobre rozwizanie majce uatwi pisanie aplikacji wielojzykowych. Dziki
temu unikamy rwnie problemu kodowania i ewentualnej konwersji systemu znakw.
Niezmienno acuchw
Kad warto raz przypisan acuchowi moesz zmieni to fakt. Co wic oznacza, e
acuchy s niezmienne? Oznacza to, e jeeli raz stworzylimy egzemplarz typu string, to
nie bdziemy mogli zmieni jego wartoci. Nie oznacza to jednak, e poniszy kod
spowoduje bd:
System.String S = "Hello World";
S = "Witaj wiecie";
Jest on jak najbardziej prawidowy, jednak jego realizacja wymaga zastosowania dwch
egzemplarzy (obiektw) w pamici komputera. Co prawda kod wyglda tak, jakbymy t
warto modyfikowali, ale w rzeczywistoci w pamici tworzony jest nowy obiekt z now
wartoci, ktra jest zwracana. Stara warto znajduje si w pamici i czeka na usunicie
przez mechanizm platformy .NET garbage collection. Nie naley si wic tym zbytnio
przejmowa.
Aby si przekona, e modyfikowana warto w rzeczywistoci wcale nie ulega modyfikacji,
moesz wykona mae wiczenie. Oto prosty fragment kodu:
System.String S = "Hello World";
Console.WriteLine(S.ToUpper());
Console.WriteLine(S);
Mimo i ten kod moe wydawa si nieco dziwny, jest jak najbardziej prawidowy. W
pierwszej kolejnoci pod indeksem nr 4 wstawiany jest nowy tekst. To owocuje powstaniem
w pamici nowego, zamienionego obiektu. Nastpnie wszystkie znaki w takim acuchu s
zamieniane na wielkie, co znowu tworzy nowy obiekt. Na kocu nastpuje zamiana znakw i
utworzenie nowego obiektu, ktry zostaje przypisany do zmiennej S. Pozostae obiekty bd
dostpne w pamici do czasu ich usunicia przez odmiecacz pamici (ang. garbage
collection).
Aby bardziej zobrazowa ca sytuacj, proponuj wklei do projektu nastpujcy kod:
System.String S = "Adam";
Console.WriteLine(S.Insert(4, " Boduch"));
Console.WriteLine(S.ToUpper());
Console.WriteLine(S.Replace("CH", "SZEK"));
// warto oryginalna
Console.WriteLine(S);
Konstruktory klasy
Aby korzysta z waciwoci klasy string, naley jedynie zadeklarowa zmienn wskazujc
na ten typ danych. Nie ma potrzeby jawnego wywoywania konstruktora tej klasy. Jeeli
jednak zajdzie taka potrzeba, klasa System.String posiada kilka przecionych
konstruktorw, ktre mog przyj rne wartoci. Oto przykad wywoania konstruktora,
ktry jako argument przyjmuje tablic znakw char:
char[] charArr = new char[] { 'H', 'e', 'l', 'l', 'o' };
System.String S = new System.String(charArr);
Operacje na acuchach
Klasa System.String udostpnia kilka ciekawych metod sucych do operowania na
acuchach. Zapewne z wielu z nich bdziesz nie raz korzysta w trakcie programowania w
C#, wic postanowiem tutaj opisa kilka najciekawszych.
Porwnywanie acuchw
Zacznijmy od rzeczy najprostszej, czyli od porwnywania wartoci acuchw. Najprociej
uy przecionych operatorw != oraz ==, ktre zadeklarowane zostay w klasie
System.String. Czyli porwnywanie wartoci acuchw wyglda tak jak np.
porwnywanie wartoci typw liczbowych:
string s1, s2;
s1 = "Hello";
s2 = "hello";
if (s1 != s2)
{
Console.WriteLine("Wartoci s rne");
}
Taka instrukcja warunkowa zwrci warto true, poniewa operator != rozrnia wielko
liter, ktra jest rna w zmiennych s1 i s2. Jeeli chcemy, aby aplikacja ignorowaa wielko
znakw, naley skorzysta z metody Compare():
s1 = "Hello";
s2 = "hello";
if (String.Compare(s1, s2, true) == 0)
{
Console.WriteLine("Wartoci s rwne");
}
Modyfikacja acuchw
W tabeli 9.1 zawarem najwaniejsze metody klasy System.String suce do modyfikacji
tekstu. Tabela zawiera skrtowy opis ich przeznaczenia; w dalszej czci rozdziau zawarem
krtkie przykady prezentujce dziaanie kadej z nich.
Tabela 9.1. Gwne metody klasy System.String
Funkcja/Procedura
Opis
Concat()
czy ze sob dwie lub wicej instancji klasy string.
Contains()
Sprawdza, czy cz acucha nie znajduje si w podanym acuchu.
Length()
Zwraca ilo znakw znajdujcych si w cigu znakowym.
Copy()
Tworzy now instancj cigu znakowego (kopiuje jego zawarto).
Insert()
Wstawia tekst w okrelone miejsce cigu znakowego.
Join()
Funkcja czy kilka elementw tekstowych w jeden.
Remove()
Funkcja umoliwia usunicie kawaka cigu znakw.
Replace()
Zamienia cz cigu znakowego.
Split()
SubString()
ToLower()
ToUpper()
Trim()
TrimEnd()
TrimStart()
Concat()
Metoda Concat() suy do czenia ze sob dwch lub wikszej iloci acuchw. Jej uycie
jest proste, bowiem ogranicza si do podania na licie parametrw acuchw, ktre maj
zosta poczone:
s1 = "Hello";
s2 = " World";
Console.WriteLine(String.Concat(s1, s2));
Contains()
Metoda Contains() moe by wykorzystana do wyszukiwania danej frazy w acuchu.
Zwraca true, jeeli dana fraza zostaa odnaleziona, lub false jeeli nie albo acuch jest
pusty. Naley zwraca uwag na wielko znakw, gdy metoda Contains() je rozrnia.
Oto prosty przykad jej wykorzystania:
string s1 = "Ja i Magosia wietn par byli!";
if (s1.Contains("Magosia"))
{
Console.WriteLine("String zawiera sowo \"Magosia\"");
}
Jeeli chcemy w acuchu tekstowym zawrze znak cudzysowu ("), naley poprzedzi go
backslashem (\). Znak \ rwnie musi by poprzedzony backslashem, jeeli ma zosta
wywietlony:
s1 = "C:\\Windows\\System";
Length
Podobnie jak w przypadku tablic waciwo Length() podaje rozmiar, tak w kontekcie
uycia z acuchami zwraca ilo znajdujcych si w nich znakw:
s1 = "Ja i Magosia wietn par byli!";
Console.WriteLine(s1.Length);
Waciwo Length jest jedynie do odczytu, nie mona przypisywa do niej adnych wartoci.
Copy()
Metoda Copy() tworzy now instancj klasy z identyczn zawartoci:
s1 = "Ja i Magosia wietn par byli!";
s2 = String.Copy(s1);
Insert()
Metoda Insert() umoliwia wstawienie tekstu w dane miejsce acucha. Pierwszym
parametrem musi by pozycja (indeks), w jakiej zostanie umieszczony nowy tekst. Drugim
parametrem musi by sam tekst do wstawienia. Oto przykad:
s1 = "Ja i Magosia wietn par byli!";
s1 = s1.Insert(s1.Length, " I co im po tym?");
Console.WriteLine(s1);
Join()
Czsto w trakcie zmagania si z jakim problemem programistycznym moesz si natkn na
konieczno poczenia elementw tablicy w jeden cig. Metoda Join() czy elementy
tablicy w jedn cao na podstawie podanego cznika (dowolny cig znakw). Prosty
przykad:
string[] Foo = { "Ala", "ma", "kota" };
string s1;
s1 = String.Join(" ", Foo);
Console.WriteLine(s1);
Split()
Metoda Split(), znana zapewne programistom Perla czy PHP, wykonuje czynno odwrotn
ni Join(). Rozbija mianowicie acuch znakw na podstawie podanego separatora i zapisuje
do tablicy kolejne elementy cigu.
Parametrem metody Split() jest lista separatorw w postaci tablicy typu char. Metoda
zwraca tablic typu string, w ktrej znajduj si odseparowane elementy:
string s1 = "Ala ma kota";
string[] Foo = s1.Split(new char[] { ' ' });
foreach (string Bar in Foo)
{
Console.WriteLine(Bar);
}
Console.Read();
Replace()
W wyniku wykonania takiego kodu na ekranie konsoli zostanie wywietlony napis Bartek
ma kota.
Remove()
Czasem moe zaistnie potrzeba usunicia danej frazy acucha. Wwczas z pomoc
przychodzi metoda Remove(). Pierwszym parametrem musi by indeks, od ktrego metoda
rozpocznie usuwanie tekstu. To jest waciwie jedyny wymagany przez ni parametr. Wtedy
usunie ona wszystko, co znajduje si za podanym indeksem. Istnieje jednak moliwo
okrelenia iloci tekstu, jaka ma zosta skasowana:
string s1 = "Ala ma kota";
Console.WriteLine(s1.Remove(0, 4));
SubString()
Metoda SubString pozwala na skopiowanie czci cigu, poczwszy od podanego znaku, i
zwrcenie skopiowanej wartoci. Jej budowa i zastosowanie s proste. Pierwszym
parametrem musi by pozycja, od ktrej rozpocznie si kopiowanie cigu znakw, a drugim
jest liczba znakw do skopiowania:
string s1 = "Ala ma kota";
/* wywietli napis: Ala */
Console.WriteLine(s1.Substring(0, 4));
ToUpper(), ToLower()
Zastosowanie tych metod jest proste. Pierwsza z nich (ToUpper()) zamienia wszystkie znaki
w acuchu na wielkie, a druga (ToLower()) na mae.
acuchy w WinForms
Co prawda biblioteka WinForms nie jest tematem tego rozdziau, ale chciabym tutaj
wspomnie o kilku jej tekstowych kontrolkach, ktre wykorzystuj mechanizmy acuchw.
Musisz sobie uwiadomi, e biblioteka WinForms, tak jak caa biblioteka klas .NET
Framework, opiera si na klasach. Komponenty (ktre rwnie s klasami) posiadaj
waciwoci oraz metody, z ktrych dua cz jest wanie typu System.String.
Podstawowe dwa komponenty suce do edycji tekstu to Textbox oraz RichTextBox.
Pierwsza z nich jest kontrolk jednoliniow suc do wpisywania prostych i krtkich notek.
Drugi komponent jest rozbudowan kontrolk tekstow miniedytorem tekstu. Moe
przechowywa dugie teksty, wywietla zawarto plikw
tekstowych.
Maksymaln dugo tekstu, jak moe przechowywa komponent RichTextBox, definiuje jego
waciwo MaxLength. Domylnie jest to 2147483647 znakw.
Komponent TextBox moe suy jako kontrolka wieloliniowa, lecz jest to moliwo rzadko
uywana. Jeeli mimo wszystko chcesz, aby komponent mg przechowywa wiele linii tekstu,
zmie waciwo Multiline na true.
Waciwo Text jest typu System.String, wic automatycznie na jej wartoci moemy
operowa tak jak na zwykych zmiennych typu string.
W komponencie RichTextBox sprawa jest nieco bardziej skomplikowana, bo i on sam jest
bardziej skomplikowany. Zasadniczo mamy moliwo korzystania z waciwoci Lines,
ktra w rzeczywistoci jest tablic typu string. Zasadniczo lepszym rozwizaniem bdzie
korzystanie z waciwoci Text typu System.String. Naley pamita, i w takim wypadku
znak \n jest odpowiedzialny za przejcie do nowej linii.
Listing 9.1 prezentuje prosty program WinForms sucy do wczytywania zawartoci pliku
tekstowego. Oprcz wczytania zawartoci do komponentu RichTextBox program wywietla
ciek do wybranego pliku w komponencie TextBox (rysunek 9.1).
Rysunek 9.1. Program wywietlajcy zawarto plikw tekstowych
using System.Data;
using System.Windows.Forms;
using System.IO;
namespace WinForms
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// wywietlenie okna Otwrz
openFileDialog1.ShowDialog();
// przypisanie cieki do pliku
textBox1.Text = openFileDialog1.FileName;
// odczytanie zawartoci pliku
FileStream FileStr =
(FileStream)openFileDialog1.OpenFile();
StreamReader Reader = new StreamReader(FileStr);
richTextBox1.Clear();
// przypisanie tekstu
richTextBox1.Text = Reader.ReadToEnd();
}
}
}
Klasa StringBuilder
Klasa StringBuilder (w odrnieniu od System.String) reprezentuje zmienny acuch.
Stosowanie klasy StringBuilder jest szczeglnie zalecane w sytuacjach, w ktrych musisz
czsto modyfikowa acuchy w swoim programie (np. w ptli). Klasa StringBuilder
zostaa zdefiniowana w przestrzeni nazw System.Text.
Do utworzenia obiektu tej klasy mona skorzysta z jednego z kilku przecionych
konstruktorw. W zalenoci od rodzaju konstruktora bdziesz musia poda wartoci dla
nastpujcych parametrw:
class Program
{
static void Main(string[] args)
{
Stopwatch Timer = new Stopwatch();
string Bar = "";
// rozpoczynamy odliczanie
Timer.Start();
for (int i = 0; i < 10000; i++)
{
Bar += "Ala ma kota ";
}
Timer.Stop();
Console.WriteLine("Czas wykonywania: {0} ms",
Timer.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
Ten prosty program dodaje w ptli warto do zmiennej Bar. Czas potrzebny na wykonanie
10 000 tys. powtrze ptli to ok. 9000 milisekund (w przypadku mojego procesora), czyli 9
sekund.
Aby przyspieszy dziaanie aplikacji, mona skorzysta z klasy StringBuilder, ktra
znajduje si w przestrzeni System.Text. Uycie tej klasy spowoduje przyspieszenie dziaania
aplikacji a do 3 milisekund! Rnic w wydajnoci wida wic bardzo wyranie.
Aby skorzysta z klasy StringBuilder, naley utworzy jej instancj i oczywicie
doda przestrze nazw System.Text. Przerobiony program z listingu 9.2 zaprezentowany
zosta na listingu 9.3.
Listing 9.3. Program napisany z uyciem klasy StringBuilder
using System;
using System.Diagnostics;
using System.Text;
namespace FooConsole
{
class Program
{
static void Main(string[] args)
{
Stopwatch Timer = new Stopwatch();
StringBuilder Bar = new StringBuilder();
// rozpoczynamy odliczanie
Timer.Start();
for (int i = 0; i < 10000; i++)
{
Bar.Append("Ala ma kota ");
}
Timer.Stop();
Console.WriteLine("Czas wykonywania: {0} ms",
Timer.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
Do sprawdzenia czasu wykonywania danego kodu uyem klasy Stopwatch, ktra znajduje si
w przestrzeni nazw System.Diagnostics. Podstawowe dwie metody tej klasy to Start(),
ktra rozpoczyna odliczanie, oraz Stop() oznaczajca zakoczenie pomiaru czasu. Czas
potrzebny na wykonanie danego kodu moemy odczyta z waciwoci ElapsedMilliseconds
(czas w milisekundach).
Formatowanie acuchw
Nieraz podczas czytania tej ksiki moge zauway, i stosowaem formatowanie
acuchw np.:
Console.WriteLine("Warto: {0}", Bar);
Chciabym na chwil zatrzyma si przy tym zagadnieniu i powici nieco wicej uwagi
moliwociom i korzyciom wynikajcym z uycia formatowania acuchw. Korzyci jest
przede wszystkim przejrzystszy kod. Ale nie tylko, bo jak si za chwil przekonasz, pojcie
formatowania acuchw to co wicej ni tylko podstawianie wartoci w okrelonym
miejscu cigu.
Owszem jest to podstawowe zadanie formatowania. W miejsce symboli zastpczych,
znajdujcych si pomidzy klamrami { oraz } w procesie wykonywania kodu zostan
podstawione wartoci odpowiadajce danemu indeksowi. Oto przykad:
Console.WriteLine("{0}, lat {1}, {2} cm wzrostu", "Jan
Kowalski", 42, 170);
Kady kolejny parametr metody WriteLine() jest numerowany od zera. Czyli parametr Jan
Kowalski ma indeks 0, parametr 42 indeks 1 itd. Taka instrukcja spowoduje wywietlenie
na ekranie konsoli tekstu Jan Kowalski, lat 42, 170 cm wzrostu.
Pomidzy klamrami naley umieci numer indeksu, nic nie stoi na przeszkodzie, aby takie
dane wywietli w dowolnej kolejnoci:
Console.WriteLine("{2} cm wzrostu, {1} lat, {0}, ", "Jan
Kowalski", 42, 170);
Jeeli chcemy wywietli w konsoli znak { lub }, musimy te symbole zapisa podwjnie:
Console.WriteLine("{{Ten tekst bdzie ujty w klamry}}");
Przeanalizujmy kolejny przykad. Symbol zastpczy moe zawiera parametr okrelajcy
rozmiar wyrwnania. Jest to liczba cakowita okrelajca szeroko danego pola oraz
wyrwnanie do prawej lub lewej strony. Spjrz na listing 9.4.
Listing 9.4. Symbole zastpcze z parametrem wyrwnania
using System;
namespace FooConsole
{
class Program
{
public struct User
{
public string Name;
public byte Age;
}
static void Main(string[] args)
{
User[] Bar = new User[2];
Bar[0].Name = "Jan Kowalski";
Bar[0].Age = 32;
Bar[1].Name = "Piotr Nowak";
Bar[1].Age = 56;
for (int i = 0; i < Bar.Length; i++)
{
Console.WriteLine("{0,-15} | {1,5}",
Bar[i].Name, Bar[i].Age
);
}
Console.ReadLine();
}
}
}
Specyfikatory formatw
Symbole zastpcze maj o wiele wiksze zastosowanie ni wspomniana moliwo
wyrwnywania wartoci. rodowisko .NET Framework definiuje zestaw symboli nazwanych
specyfikatorami formatw. Umoliwiaj one formatowanie wartoci liczbowych, daty i czasu
oraz wyliczeniowych, wedug wasnego upodobania.
Spjrz na poniszy kod:
int X = 110003242;
Console.WriteLine("{0:X}", X);
Symbol # oznacza dowoln liczb. Innymi sowy, symbol ten zostanie zastpiony kolejn
cyfr z liczby przekazanej jako parametr. Kolejne przykady:
System.Double Integer = 123.345;
// 123.3
Console.WriteLine("{0:#.#}", Integer);
// 123.3450
Console.WriteLine("{0:#.###0}", Integer);
Pozostae znaki
Console.ReadLine();
}
}
}
Typ System.Char
Typ char jzyka C#, bdcy odpowiednikiem klasy System.Char z CTS, suy do
przechowywania pojedynczych znakw typu Unicode. Warto zmiennej typu char musi by
zawarta w apostrofach:
char C = 'A';
Zmienna typu char nie moe zawiera wicej znakw ni jeden, gdy kompilator wskae bd
Too many characters in character literal.
Warto przypisywana do zmiennej typu char moe by zapisana w formie heksadecymalnej:
char
char
char
char
C1
C2
C3
C4
=
=
=
=
'\x0058';
(char)65;
'A';
'\t'; // znak tabulacji
\t czy \n).
IsDigit()
Zwraca true, jeeli znak zawarty w zmiennej jest liczb.
IsLetter()
Zwraca true, jeeli znak zawarty w zmiennej jest liter.
IsLower()
Zwraca true, jeeli znak zawarty w zmiennej jest ma liter.
IsSymbol()
Zwraca true, jeeli znak zawarty w zmiennej jest symbolem.
Zwraca true, jeeli znak zawarty w zmiennej mona zaliczy do tzw.
IsWhiteSpace()
biaych znakw (spacje, znaki nowej linii itp.).
Podsumowanie
Tematyka operowania na acuchach wbrew pozorom jest cakiem rozlega. Ja w tym
rozdziale opisaem podstawowe mechanizmy operowania na cigach znakw, w tym metody
suce do modyfikacji acuchw. Wspomniaem rwnie o klasie StringBuilder oraz o
formatowaniu acuchw, co z pewnoci pomoe Ci efektywnie wykorzysta moliwoci
platformy .NET w tym zakresie. Bardziej zaawansowanym operowaniem na acuchach jest
mechanizm wyrae regularnych (ang. regular expression), lecz ta tematyka wykracza poza
ramy niniejszej publikacji. Po wicej informacji na ten temat odsyam do dokumentacji
rodowiska .NET Framework.
Rozdzia 10
Biblioteka Windows Forms
Z dowiadczenia wiem, e pocztkujcym programistom najwicej frajdy sprawia wizualne
projektowanie aplikacji. Umieszczanie w programie wszelkiego rodzaju przyciskw,
kontrolek, czenie caoci w dziaajc aplikacj przynosi najwicej satysfakcji. W
poprzednich rozdziaach wikszo przykadw prezentowana bya na aplikacjach
konsolowych. Byo to celowe dziaanie, gdy uycie biblioteki wizualnej wymaga
zastosowania wikszej iloci kodu rdowego, konstrukcji, ktrych wczeniej nie znae.
Teraz, gdy nieobce jest Ci programowanie obiektowe, wiesz, czym s klasy i tablice, moemy
przej do programowania wizualnego. Odstawimy na bok aplikacje konsolowe, z ktrych
mao kto dzisiaj korzysta. Zajmiemy si budow programw na podstawie komponentw,
ktre s w dzisiejszych czasach podstaw do budowania aplikacji okienkowych, dziaajcych
w systemach Windows.
W gruncie rzeczy do tej pory powiedzielimy ju sobie cakiem sporo o aplikacjach
wizualnych. Potrafisz ju tworzy nowe projekty WinForms przy pomocy rodowiska Visual
C# Express Edition. W rozdziale 5. omwiem podstawowy kod aplikacji wizualnej, a w
rozdziale 6. wyjaniem, czym s zdarzenia. Wiesz ju, czym s waciwoci, w jaki sposb je
Podzesp System.Windows.Forms
Przestrze nazw System.Windows.Forms znajduje si w podzespole o tej samej nazwie. Jest
to podstawowa biblioteka suca do projektowania wizualnego. Kompilujc dany projekt z
uyciem kompilatora csc.exe, w linii komend musisz zaznaczy, i ma on by kompilowany
wraz z podzespoem System.Windows.Forms.dll. Jest to bardzo niewygodne, dlatego zalecam
korzystanie z wizualnych rodowisk typu Visual C# Express Edition. Podczas tworzenia
nowego projektu WinForms odpowiednie podzespoy umoliwiajce jego prawidow
kompilacj s wczane automatycznie. Moesz to sprawdzi, rozwijajc ga References w
oknie Solution Explorer (rysunek 10.1).
nam ono przegldanie zawartoci danych podzespow, przestrzeni nazw i typw. Cao jest
adnie przedstawiona w sposb hierarchiczny (rysunek 10.2).
Przestrze System.Windows.Forms
Najwaniejsz przestrzeni nazw w podzespole System.Windows.Forms.dll jest ta o takiej
samej nazwie (czyli System.Windows.Forms), wczana do kadego projektu typu
WinForms. Zawiera ona podstawowe klasy obsugi aplikacji, ale rwnie wszystkie klasy
komponentw. Musisz zda sobie spraw, e kady komponent jest jednoczenie klas, tak
sam, jak tworzylimy w rozdziale 5. Taka klasa zawiera elementy, o ktrych wspominaem
ju niejednokrotnie w tej ksice, czyli waciwoci, metody, zdarzenia, indeksery, typy
wyliczeniowe czy struktury. Oglnie, klasy biblioteki WinForms mona zakwalifikowa do
kilku kategorii, ktre omwiem w tabeli 10.1.
Tabela 10.1. Kategorie klasy przestrzeni System.Windows.Forms
Grupa
Opis
Komponenty
Kontrolki
Elementy
pozycjonowania
Menu i paski
narzdziowe
Okna dialogowe
Podstawowe klasy
Oczywicie caa biblioteka WinForms zbudowana jest na mechanizmie dziedziczenia oraz
polimorfizmu (jak mogoby by inaczej?), tote istnieje pewien zestaw klas bazowych dla
wszystkich komponentw, o ktrych powiniene wiedzie. Nie jest to wiedza niezbdna w
pracy nad komponentami, ale by moe ciekawostka dla osb, ktre chc wiedzie, jak to
wszystko jest zbudowane.
System.ComponentModel.Component
Klasa o dziwo nie znajduje si w przestrzeni System.Windows.Forms, lecz w
System.ComponentModel. Jest to podstawowa klasa dla wszystkich komponentw z
biblioteki WinForms, implementuje interfejs IComponent.
Waciwie nigdy nie bdziesz mia potrzeby uywania tej klasy samodzielnie, suy ona
jedynie jako klasa bazowa. Zawiera kilka elementw, m.in. metod Events() zwracajc list
zdarze przypisan do danego komponentu.
System.Windows.Forms.Control
Waciwoci klasy
Klasa Control zawiera cale mnstwo waciwoci publicznych oraz chronionych, ktre
dziedziczone s w klasach potomnych. Waciwoci te su do okrelania podstawowych
cech komponentw, takich jak pooenie, rozmiar, kolor itp. W tabeli 10.2 zawarem
podstawow list waciwoci, ktre z pewnoci bd przez Ciebie wykorzystywane
najczciej.
Tabela 10.2. Podstawowa lista waciwoci klasy Control
Waciwo
Opis
Waciwo typu bool okrela, czy dany komponent moe by rodzicem
AllowDrop
dla innych, tzn. czy za pomoc techniki przecignij i upu (ang. drag &
drop) mona w nim umieszcza inne kontrolki.
Okrela, w jaki sposb komponent ma zmienia swoje rozmiary, jeeli
Anchor
kontrolka znajdujca si w nim zmienia swj rozmiar.
Waciwo typu bool okrela, czy komponent ma mie rozmiar zgodny z
AutoSize
wymiarami treci, ktra si w nim znajduje.
BackColor
Umoliwia okrelenie koloru ta dla komponentu.
BackgroundImage Okrela obrazek, ktry bdzie uywany jako to komponentu.
Okrela kursor myszy, jaki ma by wywietlany, jeeli jej wskanik
Cursor
zostanie naprowadzony nad dan kontrolk.
Waciwo typu bool okrela, czy komponent bdzie wyczony
Enabled
(nieaktywna kontrolka jest specjalnie oznaczana, nie reaguje na
zdarzenia).
Umoliwia ustawienie czcionki, jaka bdzie wywietlana, jeeli kontrolka
Font
umoliwia wywietlanie tekstu.
ForeColor
Okrela kolor tekstu dla komponentu.
Okrela, czy w komponencie znajduj si inne kontrolki (tzw. kontrolki
HasChildren
potomne).
Height
Umoliwia odczytanie lub przypisanie szerokoci kontrolki.
Umoliwia okrelenie (w pikselach) odlegoci lewej krawdzi od obszaru
Left
formularza lub innego komponentu.
Location
Waciwo typu Point umoliwia okrelenie pooenia dla komponentu.
Name
Bardzo wana waciwo. Okrela nazw komponentu.
Umoliwia okrelenie lub odczytanie odstpw (minimalnej odlegoci)
Padding
wewntrz danej kontrolki.
Parent
Right
Size
TabIndex
Tag
Top
Visible
Width
System.Windows.Forms.Application
Klasa Application ma due znaczenie dla naszego programu. Zawiera bowiem bardzo
wan metod odpowiedzialn za wywietlenie gwnego formularza projektu.
Wykonajmy mae wiczenie pozwalajce napisa najprostszy program, ktry bdzie
wykorzystywa bibliotek WinForms. Nasza aplikacja bdzie wywietlaa jedynie puste okno
formularza.
Formularzem nazywamy okno systemu Windows.
1. Z menu File wybierz pozycj New Project.
2. Zaznacz pozycj Empty project i nacinij przycisk OK.
3. W oknie Solution Explorer zaznacz, a nastpnie kliknij prawym przyciskiem pozycj
References.
4. Kliknij Add Reference.
5. W oknie, w zakadce .NET odszukaj pozycj System, a nastpnie kliknij przycisk OK.
6. W ten sam sposb dodaj pozycj System.Windows.Forms.
7. Z menu Project wybierz pozycj Add New Item.
8. Zaznacz pozycj Empty file i kliknij OK.
Jak widzisz, kod jest prosty i krtki. Wywoujemy metod Run(), ktra jako parametru
wymaga instancji klasy Form. Klasa Form jest podstawow klas obsugi formularzy, dlatego
WinForms musi po niej dziedziczy.
Jeeli kod z listingu 10.1 oprcz okna formularza wywietla rwnie okno konsoli, musisz
sprecyzowa typ projektu. Z menu Project wybierz Properties. W zakadce Application
znajduje si lista rozwijana Output type. Wybierz z niej pozycj Windows Application.
Klasa Application ma wiele metod oraz waciwoci odpowiadajcych za obsug tzw.
komunikatw oraz wtkw. Ze wzgldu na ograniczenia objtoci tej ksiki, te tematy nie
zostan zaprezentowane. Klasa zawiera jednak dwie metody, ktre mog Ci si przyda w
dalszej pracy z jzykiem C#. Mam tu na myli metod Exit(), umoliwiajc zakoczenie
pracy oraz Restart(), ktra ponownie uruchamia aplikacj. Rozbudujmy nasz program z
listingu 10.1, dodajc do niego przyciski umoliwiajce zamknicie oraz ponowne
uruchomienie aplikacji.
Aby zrealizowa to zadanie, musimy umieci w programie kod tworzcy dwa komponenty
trybie projektowania w rodowisku Visual C#. Aby jednak utrudni sobie to zadanie,
wpiszmy odpowiedni kod rcznie.
Utworzenie nowego komponentu na formularzu polega zwyczajnie na utworzeniu nowej
instancji (np. komponentu Button) oraz ustawieniu odpowiednich waciwoci:
RestartBtn = new Button();
RestartBtn.Parent = this;
RestartBtn.Top = 10;
RestartBtn.Left = 10;
RestartBtn.Text = "Restartuj";
RestartBtn.Click += new System.EventHandler(OnRestareClick);
Kluczowe w tym kodzie jest przypisanie odpowiedniej wartoci dla waciwoci Parent.
Musimy okreli komponent macierzysty dla naszej kontrolki (bdzie to formularz), co
prezentuje listing 10.2.
Listing 10.2. Dynamiczne
tworzenie komponentw na formularzu
using System;
using System.Windows.Forms;
public class MyForm : Form
{
private Button RestartBtn;
private Button ExitBtn;
private void OnRestareClick(object sender, EventArgs e)
{
Application.Restart();
}
private void OnExitClick(object sender, EventArgs e)
{
Application.Exit();
}
public MyForm()
{
this.Text = Application.StartupPath;
RestartBtn = new Button();
RestartBtn.Parent = this;
RestartBtn.Top = 10;
RestartBtn.Left = 10;
RestartBtn.Text = "Restartuj";
RestartBtn.Click += new
System.EventHandler(OnRestareClick);
Rysunek 10.4 prezentuje dziaanie takiego programu. Zwr uwag, e pasek tytuowy
formularza zawiera ciek do uruchomionego programu. Odpowiada za to linia:
this.Text = Application.StartupPath;
Przykad dziaania
Zostawmy na chwil such teori, a zajmijmy si tworzeniem graficznego interfejsu
uytkownika (GUI ang. Graphical User Interface). Pamitasz, jak w rozdziale 6. pisalimy
gr Kko i krzyyk? Wwczas caa gra odbywaa si w trybie konsolowym. Poniewa cae
engine gry jest ju gotowe, wystarczy zaprojektowa interfejs, ktry obsugiwaby jej klas.
Do roboty!
Przygotowanie klasy
Utwrz nowy projekt aplikacji Windows Forms. Zapisz go na dysku, po czym skopiuj modu,
w ktrym znajdowaa si klasa GomokuEngine, do katalogu z projektem. Naley doda ten
modu do projektu:
1. Prawym przyciskiem kliknij w obrbie okna Solution Explorer.
2. Wybierz pozycj Add/Existing Item.
3. Wska plik, w ktrym znajduje si kod gry, i nacinij OK.
Projektowanie interfejsu
Zacznijmy od tytuu okna gwnego. Domylny napis Form1 nie odpowiada nazwie naszej
gry. W oknie Properties moesz zmieni warto waciwoci Text, wpisujc wasn nazw
okna np. Gomoku Win.
Jeeli okno Properties nie wywietla prawidowo waciwoci formularza, by moe nie jest
on zaznaczony. Kliknij wic okno formularza, aby wywietli list jego waciwoci.
Kolejna rzecz, jakiej potrzebujemy, to plansza do gry. W celu jej utworzenia proponuj
umieci na formularzu 9 komponentw typu Button. Na razie umie jeden komponent,
gdy musimy ustali waciwoci jego wygldu. Zacznijmy od rozmiaru. W oknie Properties
odnajd waciwo Size i rozwi jej ga. Warto Width zmie na 50, a Height na 30. Teraz
przyszed czas na zmian czcionki. Rozwi ga Font i zmie nastpujce pozycje:
Teraz uywajc kombinacji klawiszy Ctrl+C (kopiuj) oraz Ctrl+V (wklej) moesz
Rozwizania programistyczne
W kadym projekcie tworzenie interfejsu aplikacji to dopiero pocztek. Naszym zadaniem
jest odpowiednia obsuga klasy Gomoku, gdy cay silnik gry mamy ju gotowy. Zacznijmy
od rzeczy najprostszej, czyli oprogramowania zdarzenia Click komponentu btnStart (start
gry):
private void btnStart_Click(object sender, EventArgs e)
{
if (textPlayer1.Text.Length > 0 && textPlayer2.Text.Length
> 0)
{
GomokuObj.Player1 = textPlayer1.Text;
GomokuObj.Player2 = textPlayer2.Text;
GomokuObj.Start();
Ready = true;
}
}
{
MessageBox.Show("Podaj imiona graczy!");
return;
}
Point p = new Point();
p = (Point)(sender as Button).Tag;
(sender as Button).Text = GomokuObj.Active.Type ==
FieldType.ftCircle ? "O" : "X";
GomokuObj.Set(p.X, p.Y);
if (GomokuObj.Winner)
{
MessageBox.Show(String.Format("Brawo dla {0}",
GomokuObj.Active.Name), "Wygrana!");
}
}
Pierwsza instrukcja sprawdza, czy pole Ready ma warto false. Jeeli tak, wywietla
ostrzeenie, i nie podano imion graczy. Nastpnie tworzymy now struktur Point i
przypisujemy do niej informacje
z waciwoci Tag nacinitego przycisku. Kolejna instrukcja umieszcza na przycisku symbol
odpowiadajcy danemu graczowi, po czym wywouje metod Set() z odpowiednimi
parametrami. Na samym kocu instrukcja sprawdza, czy nie zakoczy gry, jeeli ktry z
uytkownikw wygra.
Omwienia wymaga rwnie metoda zdarzeniowa przycisku btnNewGame:
private void btnNewGame_Click(object sender, EventArgs e)
{
for (int i = 0; i < Controls.Count; i++)
{
if (Controls[i] is Button && (Controls[i] as
Button).Tag != null)
{
(Controls[i] as Button).Text = "";
}
}
GomokuObj.NewGame();
}
Jej zadaniem jest wywoanie metody NewGame(), lecz wczeniej czyci wartoci Text
przyciskw planszy. Waciwo Controls zwraca informacje na temat kontrolek
umieszczonych w tym przypadku na formularzu. W ptli sprawdzamy kad kontrolk i
jeeli jest typu Button oraz jej waciwo Tag nie jest pusta, czycimy zawarto jej
waciwoci Text. Kod rdowy gry (gwnego moduu aplikacji) zawarty jest na listingu
10.3.
Button).Tag != null)
{
(Controls[i] as Button).Text = "";
}
}
GomokuObj.NewGame();
}
private void button1_Click(object sender, EventArgs e)
{
if (!Ready)
{
MessageBox.Show("Podaj imiona graczy!");
return;
}
Point p = new Point();
p = (Point)(sender as Button).Tag;
(sender as Button).Text = GomokuObj.Active.Type ==
FieldType.ftCircle ? "O" : "X";
GomokuObj.Set(p.X, p.Y);
if (GomokuObj.Winner)
{
MessageBox.Show(String.Format("Brawo dla {0}",
GomokuObj.Active.Name), "Wygrana!");
}
}
}
}
Technika przecignij i upu (ang. Drag & Drop) z powodzeniem stosowana jest w wielu
programach komputerowych. Sam z pewnoci nieraz z niej skorzystae. Przykadowo:
przenoszenie plikw za pomoc kliknicia i przesuwania kursora myszy jest ju
wykorzystaniem techniki przecignij i upu! Dziki temu obsuga aplikacji jest
przyjemniejsza i bardziej intuicyjna. Poniewa obsuga tej techniki w rodowisku .NET
Framework nie naley do najprostszych, postanowiem zaprezentowa przykady jej
wykorzystania.
W naszej przykadowej aplikacji uytkownik bdzie mia moliwo przenoszenia elementw
pomidzy dwoma komponentami ListBox. Kontrolka ListBox suy do przechowywania
kolejnych linii tekstu (pozycji). Rysunek 10.7 prezentuje dziaanie takiej aplikacji.
if (effects != DragDropEffects.None)
{
(sender as ListBox).Items.RemoveAt((sender as
ListBox).SelectedIndex);
}
}
Najwaniejsza w tym kodzie jest metoda DoDragDrop(), ktra uruchamia proces przecigania
danych. Pierwszym parametrem s dane, ktre bd przecigane, czyli w naszym przypadku
zaznaczony element w ListBox. Drugim parametrem jest tryb przenoszenia. Metoda
zwraca obiekt klasy DragDropEffects.
Sami musimy zadba o usuwanie elementu z komponentu rdowego. Realizuje to metoda
RemoveAt() z waciwoci Items. W parametrze tej metody musimy poda numer pozycji do
usunicia. Usuwamy zaznaczon pozycj, wic skorzystamy z waciwoci SelectedIndex,
ktra dostarcza nam informacji o zaznaczonej pozycji.
Waciwo Items komponentu typu ListBox zwraca list pozycji (elementw) komponentw
w formie kolekcji.
Kolejnym krokiem bdzie obsuenie zdarzenia DragEnter, ktre wykonywane jest w
momencie, gdy kursor myszy z przeciganym elementem wejdzie w obszar kontrolki:
private void lb_DragEnter(object sender, DragEventArgs e)
{
StatusBar.Text = "Przesyam dane...";
if (e.Data.GetDataPresent(typeof(System.String)))
{
e.Effect = DragDropEffects.Move;
}
}
using
using
using
using
using
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
namespace DockApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
int selectedItem = lbSource.SelectedIndex;
object item = lbSource.SelectedItem;
if (selectedItem > 0)
{
lbSource.Items.RemoveAt(selectedItem);
lbDest.Items.Add(item);
}
}
private void btnRemove_Click(object sender, EventArgs
e)
{
int selectedItem = lbDest.SelectedIndex;
object item = lbDest.SelectedItem;
if (selectedItem > 0)
{
lbDest.Items.RemoveAt(selectedItem);
lbSource.Items.Add(item);
}
}
private void lb_DragEnter(object sender, DragEventArgs
e)
{
StatusBar.Text = "Przesyam dane...";
if (e.Data.GetDataPresent(typeof(System.String)))
{
e.Effect = DragDropEffects.Move;
}
}
if (effects != DragDropEffects.None)
{
(sender as ListBox).Items.RemoveAt((sender as
ListBox).SelectedIndex);
}
}
private void lb_DragDrop(object sender, DragEventArgs
e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
Object item =
(object)e.Data.GetData(typeof(System.String));
(sender as ListBox).Items.Add(item);
StatusBar.Text = "";
}
}
}
}
Tworzenie menu
W wielu aplikacjach, nie tylko tych przeznaczonych dla systemu Windows, obecne jest menu
gwne. Nie musz Ci chyba tumaczy, czym jest menu, ale warto wspomnie o tym, jak si
je tworzy w rodowisku Visual C# Express Edition. Wbrew pozorom, w rodowisku
wizualnym projektowanie menu jest bardzo proste i przyjemne.
Za wywietlanie i obsug menu odpowiada komponent MenuStrip. Umie ten komponent
na formularzu. Zostanie on umieszczony na pasku podrcznym (rysunek 10.9), gdy naley
do komponentw niewizualnych.
Waciwoci menu
Menu moesz rwnie edytowa, jeli klikniesz prawym przyciskiem ikon komponentu i
wybierzesz pozycj Edit Items. Okno Items Collection Editor daje moliwo okrelenia
szczegowych waciwoci dla menu, z ktrych najwaniejsze opisaem w tabeli 10.3.
Tabela 10.3. Waciwoci pozycji menu
Waciwo
Opis
BackColor
Umoliwia okrelenie koloru ta pozycji menu.
BackgroundImage
Umoliwia okrelenie obrazka ta.
BackgroundImageLayout Okrela pozycj i zachowanie obrazka ta.
Zmiana tej waciwoci na true spowoduje pojawienie si przy
Checked
pozycji zaznaczenia (charakterystycznego ptaszka).
Okrela sposb wywietlania pozycji (tekst i obrazek, tekst lub
DisplayStyle
obrazek).
Image
Umoliwia okrelenie ikony, ktra bdzie wywietlana w pozycji.
ImageAlign
Okrela pooenie obrazka.
ImageTransparent
Umoliwia okrelenie koloru ta obrazka (jeeli jest przezroczysty).
RightToLeft
Okrela, czy pozycje w menu bd wyrwnane do prawej.
TextAlign
Umoliwia szczegowe okrelenie pozycji tekstu w menu.
TextDirection
Okrela kierunek wywietlania tekstu (domylnie poziomy).
TextImageRelation
Umoliwia okrelenie pooenia tekstu wzgldem obrazka.
AutoSize
Dopasowanie rozmiaru elementu do jego zawartoci.
AutoToolTip
CheckOnClick
ToolTipText
DropDownItems
ShortcutKeys
ShowShortcutKeys
pozycji menu w trakcie projektowania wybierz pozycj Set Image, ktra rwnie wywietli
okno Select Resource (rysunek 10.13).
Skrty klawiaturowe
Aby nada naszemu menu bardziej profesjonalny charakter, naley niektrym pozycjom
przypisa skrty klawiaturowe. Umoliwi to szybkie wybranie danej pozycji przy pomocy
klawiatury, co znacznie uatwi prac z aplikacj. Do ustawiania skrtu klawiaturowego suy
waciwo ShortcutKeys. Dziki niej moemy wybra dowoln kombinacj klawiaturow,
ktra spowoduje wybranie danej pozycji (rysunek 10.15).
Menu podrczne
Paski narzdziowe
Paski narzdziowe (ang. tool bar) to rwnie czsto spotykany element interfejsu aplikacji.
Zawiera przyciski z najczstszymi opcjami programu ozdobione ikon. Podczas
projektowania aplikacji w menu powiniene umieszcza wszystkie opcje, jakie oferuje
program, a na pasku jedynie te najczciej uywane.
W bibliotece WinForms do tworzenia paskw narzdziowych moemy wykorzysta
komponent ToolStrip. Umieszczony na formularzu, automatycznie dopasowuje si do jego
grnej krawdzi. Zwr uwag na ma ikon wywietlan na pasku w trybie projektowania
(rysunek 10.16).
Pasek statusu
Pasek statusu rwnie czsto goci we wszelkiego rodzaju aplikacjach okienkowych. Jest to
pasek, ktry automatycznie dopasowuje si do dolnej krawdzi okna i suy do wywietlania
podstawowych informacji w trakcie dziaania programu.
W Windows Forms za wywietlanie paska statusu odpowiada komponent StatusStrip. Po
jego wywietleniu, podobnie jak w przypadku paskw narzdziowych, moemy umieci na
nim wiele elementw, w tym etykiet czy pasek postpu (rysunek 10.17).
Zakadki
Czsto gdy mamy rozbudowany interfejs, zachodzi potrzeba podzielenia go na kilka czci.
wietnie nadaje si do tego mechanizm zakadek, ktre moemy tworzy dziki kontrolce
TabControl.
TabControl (rysunek 10.18) jest komponentem-rodzicem. Oznacza to, e w jego wntrzu
Wystarczy je klikn i wybra z menu podrcznego pozycj Add lub Remove. Jeeli chcesz
edytowa waciwoci danej zakadki, musisz wybra waciwo TabPages z okna
waciwoci Properties. Waciwo TabPages oferuje wiele opcji, dziki ktrym bdziesz
mg ustawi cechy kadej zakadki z osobna. Kilka interesujcych waciwoci komponentu
TabControl prezentuje tabela 10.4.
Tabela 10.4. Najwaniejsze waciwoci komponentu TabControl
Waciwo
Opis
Okrela pooenie zakadek. Domylnie s one pooone u gry (Top), ale
Alingment moliwe s rwnie wartoci: Bottom (d), Left (lewa strona), Right (prawa
strona).
Okrela sposb prezentacji zakadek. Mona wybra warto Button (przyciski)
Appearance
lub Flat Buttons (paskie przyciski).
Jeeli waciwo ma warto true, zakadka zostanie podwietlona, gdy
HotTrack
naprowadzimy na ni kursor.
Okrela, czy tytu zakadki moe mieci si w wicej ni jednej linii (domylnie
Multiline
false nie moe).
SizeMode Tryb rozmiaru zakadek. Domylnie szeroko zakadki dopasowywana jest do
Kontrolki tekstowe
Osobicie dziel kontrolki tekstowe na jednowierszowe i wielowierszowe. Wielowierszow
kontrolk edycyjn jest komponent RichTextBox. Jednowierszow kontrolk edycyjn jest
np. TextBox. Do czsto uywanych kontrolek mona zaliczy rwnie:
ComboBox,
ListBox.
Ta pierwsza prezentuje kolejne linie tekstu w formie listy rozwijanej. Komponent ListBox
dziaa bardzo podobnie, tyle e kolejne linie prezentowane s w formie listy nierozwijanej.
Na nowej zakadce komponentu TabControl umieciem list rozwijan (ComboBox) oraz
komponent WebBrowser (rysunek 10.19). Ten drugi suy do wywietlania stron
internetowych.
IE.Navigate(comboBox1.Text);
comboBox1.Items.Add(comboBox1.Text);
}
}
Jeeli warunek zostanie speniony, wywoana bdzie metoda Navigate(), ktra nakazuje
przej do okrelonego URL wpisanego w komponencie ComboBox. Kolejna instrukcja dodaje
wpisany adres do listy. Musimy si w niej odwoa do waciwoci Items, ktra reprezentuje
kolekcj. Za dodanie nowej pozycji odpowiada metoda Add().
Kilka interesujcych waciwoci komponentu ComboBox zostao zaprezentowanych w tabeli
10.5.
Tabela 10.5. Waciwoci komponentu ComboBox
Waciwo
Opis
Reprezentuje styl listy rozwijanej. Warto Simple oznacza usunicie
DropDownStyle
ikony sucej do rozwijania listy. Warto DropDownList
uniemoliwia nadanie wartoci waciwoci Text.
DropDownHeight Reprezentuje wysoko listy (w pikselach).
DropDownWidth
Reprezentuje szeroko listy (w pikselach).
Oznacza maksymaln liczb pozycji, jaka moe znale si na licie
MaxDropDownItems
rozwijanej.
Sorted
Warto true oznacza, e pozycje na licie bd sortowane.
Komponent RichTextBox
Mona powiedzie, e komponent RichTextBox jest miniedytorem tekstu. Zawiera metody
suce do kopiowania, wycinania czy wklejania tekstu, jak rwnie takie, ktre umoliwi
zmian koloru zaznaczonego tekstu czy iloci wci.
Komponent RichTextBox idealnie nadaje si do reprezentowania duych porcji tekstu np.
zawartoci plikw tekstowych. Skoro pokazaem ju, w jaki sposb tworzy menu, paski
narzdziowe czy zakadki, moe warto by byo przeobrazi nasz program w co wicej ni
tylko pusty interfejs.
Oprogramujmy zdarzenia Click poszczeglnych pozycji menu.
Zacznijmy od pozycji Wytnij, Kopiuj, Wklej oraz Zaznacz wszystko. Wygeneruj zdarzenia
Click dla tych pozycji.
Jak widzisz, proces kopiowania czy wycinania tekstu jest bardzo prosty. Wystarczy wywoa
metod Cut(), aby wyci zaznaczony tekst, Copy(), aby go skopiowa, i Paste(), aby
wklei tekst ze schowka. Za zaznaczenie caego tekstu w komponencie RichTextBox
odpowiada metoda SelectAll().
Poniewa na pasku narzdziowym rwnie mamy ikony odpowiadajce za kopiowanie,
wycinanie i wklejanie, naley przypisa im odpowiednie procedury zdarzeniowe.
Inne ciekawe metody komponentu RichTextBox wymieniem w tabeli 10.6.
Tabela 10.6. Interesujce metody komponentu RichTextBox
Waciwo
Opis
AppendText()
Umoliwia dodanie tekstu na kocu aktualnej linii.
Zwraca warto true, jeeli w schowku s dane,
CanPaste()
ktre uytkownik moe wklei.
Clear()
Czyci zawarto komponentu RichTextBox.
Czyci informacje na temat ostatnio cofanych
ClearUndo()
operacji.
DeselectAll()
Usuwa ewentualne zaznaczenie tekstu.
Zwraca znak znajdujcy si w okrelonej pozycji
GetCharFromPosition()
tekstu.
GetFirstCharIndexFromLine()
Zwraca indeks pierwszego znaku z podanej linii.
GetFirstCharIndexOfCurrentLine() Zwraca indeks pierwszego znaku aktualnej linii.
LoadFile()
Umoliwia wczytanie zawartoci pliku.
Redo()
SaveFile()
Select()
Undo()
Okna dialogowe
Zapomnijmy na chwil o komponencie RichTextBox. Nasza przykadowa aplikacja ma
pozycje w menu suce do otwierania, zapisywania pliku. Aby zrealizowa to zadanie i
nada naszej aplikacji wicej profesjonalizmu, moemy skorzysta z okien dialogowych
Otwrz oraz Zapisz S to standardowe okna systemu Windows, ktre zapewne nieraz
widziae. Umoliwiaj one wskazanie pliku, ktry zostanie otwarty lub zapisany.
Komponenty umoliwiajce wywietlanie okien dialogowych znajduj si w kategorii
Dialogs okna Toolbox. Utwrz na formularzu komponenty OpenFileDialog oraz
SaveFileDialog. S to komponenty niewizualne, wic reprezentujce je ikony zostan
umieszczone w panelu podrcznym zakadki projektowania.
Wygeneruj zdarzenie Click pozycji Otwrz z menu gwnego. Kod metody zdarzeniowej
moe wyglda tak:
private void mmOpen_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK
&& openFileDialog1.FileName.Length > 0)
{
MyRichBox.LoadFile(openFileDialog1.FileName);
Text = AppName + " [" + openFileDialog1.FileName +
"]";
}
}
Jak widzisz, sposb otwarcia nowego dokumentu jest prosty. Przede wszystkim naley
wywietli okno dialogowe (metoda ShowDialog()). Jeeli uytkownik wskae plik i
nacinie przycisk OK, zmieniony zostanie tytu okna dialogowego, ale przede wszystkim
zawarto pliku zostanie zaadowana do komponentu (metoda LoadFile()).
Waciwo FileName komponentu OpenFileDialog zwraca ciek do pliku, ktry wybra
uytkownik.
Oprogramujmy teraz zdarzenie Click pozycji odpowiadajcej za zapisanie dokumentu
(pozycja Zapisz oraz Zapisz jako):
W programie zadeklarowaem pole prywatne FileName typu string. Jeeli warto tego pola
jest rwna null, naley wywoa okno dialogowe, w ktrym uytkownik musi poda ciek
oraz nazw pliku. Zapis zawartoci komponentu RichTextBox realizuje metoda SaveFile(),
w ktrej naley poda ciek do zapisu.
Metoda SaveFile() zapisuje plik w formacie RTF (ang. Rich Text Format), ktry akceptuje
potem take przy odczycie.
Pierwsza instrukcja nadaje warto null dla pola FileName (okrelajcego ciek aktualnie
edytowanego pliku). Kolejna instrukcja przypisuje do waciwoci Text warto pola
AppName, ktre okrela nazw programu:
private const string AppName = "Notepad Extra";
if (Modified)
{
DialogResult dr = MessageBox.Show("Plik nie zosta
zapisany. Zapisa?",
"Plik niezapisany",
MessageBoxButtons.YesNoCancel);
if (dr == DialogResult.Cancel)
{
return;
}
else if (dr == DialogResult.Yes)
{
mmSave_Click(sender, e);
}
Application.Exit();
}
}
Po wybraniu tej pozycji aplikacja zostanie zamknita. Uprzednio jednak program zapyta
uytkownika, czy zapisa zmiany w pliku, jeeli zosta on zmodyfikowany.
Metoda Show() z klasy MessageBox (ktra notabene bya czsto uywana przeze mnie w tej
ksice) zwraca informacje o nacinitym klawiszu w formie typu wyliczeniowego
DialogResult. W naszym przykadzie zawarto pliku zostanie zapisana tylko wtedy, gdy
uytkownik wybierze opcj Yes.
Podsumowanie
Opisanie caej biblioteki WinForms wymaga odrbnej publikacji. Ja w tym rozdziale
opisaem jedynie kilka podstawowych komponentw. Mam nadziej, e na tej podstawie
bdziesz w stanie wykorzysta uyteczno pozostaych komponentw, jakie udostpnia
biblioteka Windows Forms. Uwierz mi, e w poznaniu tej biblioteki due znaczenie odgrywa
dowiadczenie. Im czciej bdziesz wykorzystywa w swoich aplikacjach bibliotek
WinForms, tym lepiej j zgbisz i wkrtce nie bdzie ona krya przed Tob adnych
tajemnic.
Rozdzia 11
Podzespoy .NET
Odrobin historii
COM jest technologi stosunkowo now, bo powsta kilka lat temu. Wprowadzenie jej miao
na celu zapewnienie jednolitego standardu komunikowania si, tak aby np. (by jeszcze raz
posuy si wczeniejszym przykadem) programici mogli korzysta z moliwoci
przegldania stron WWW w swoich aplikacjach. Firma Microsoft wysza naprzeciw tym
potrzebom i utworzya modu obiektw (COM), ktry umoliwia udostpnianie innym
aplikacjom swoich metod.
ActiveX
ActiveX jest technologi opart na COM. Pozwala na tworzenie kontrolek .ocx lub .dll. W
rzeczywistoci ActiveX to obiekt COM, tyle e posiadajcy wasny interfejs (okna, kontrolki
itp.). Tak wic moglimy tworzy kontrolki ActiveX, wykorzystujc np. Delphi oraz jego
zalety projektowania wizualnego. Mona byo korzysta ze wszystkich komponentw i,
oglnie rzecz biorc, projektowanie byo atwiejsze ni w przypadku zwykych obiektw
COM.
Dodatkowo ActiveX pozwala na wygenerowanie kodu umoliwiajcego umieszczenie
aplikacji na stronie WWW.
Platforma .NET jest nastpczyni COM, ktra zakada integralno pomidzy programami.
Do tej pory programici mogli budowa
osobne kontrolki, ktre pniej dawao si wykorzystywa w innych aplikacjach. Wizao si
to z rejestracj tej kontrolki i dodawaniem odpowiednich wpisw w rejestrze Windows. W
.NET komunikacja midzy aplikacjami bdzie uatwiona dany program bdzie mg
dziedziczy po klasie z innego, obsugiwa jego wyjtki itp.
DCOM
DCOM jest akronimem sw Distributed Component Object Model. Technologia ta, rwnie
opracowana przez firm Microsoft, zakada moliwo komunikowania si pomidzy
poszczeglnymi kontrolkami COM za porednictwem internetu. Ta technologia rwnie
zostaa uznana za przestarza w stosunku do platformy .NET.
Podstawowy podzesp
Jak ju wiesz, podzespoy zawieraj przestrzenie nazw, a te z kolei kolejne klasy, struktury
czy typy wyliczeniowe. Wszystkie aplikacje .NET wykorzystuj podzesp mscorlib.dll.
Podzesp ten zawiera nie tylko wszystkie podstawowe typy wykorzystywane w tej
platformie, ale take klas bazow Exception oraz wiele innych wanych elementw.
Plik mscorelib.dll znajduje si w katalogu, w ktrym zainstalowane jest rodowisko .NET
Framework. Na moim komputerze jest to
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll.
Deasembler .NET
Deasembler jest programem dziaajcym odwrotnie ni asembler przeksztaca form
binarnej aplikacji do kodu poredniego IL. Wraz z pakietem .NET Framework SDK jest
dostarczany deasembler, ktry umoliwia analiz kodu binarnego PE i dokonanie konwersji
do kodu IL.
SDK to skrt od Software Developer Kit. Pakiet .NET Framework SDK zawiera narzdzia,
dokumentacj oraz wiele przydatnych rzeczy, ktre mog przyda si kademu programicie
.NET. Pakiet ten nie jest wymagany do prawidowego funkcjonowania aplikacji .NET. Moesz
go cign za darmo ze strony
http://msdn.microsoft.com/netframework/downloads/updates/default.aspx.
Deasembler, o ktrym mowa, znajduje si w katalogu C:\Program
Files\Microsoft.NET\SDK\v2.0\Bin (w zalenoci od konfiguracji) pod nazw Ildasm.exe
(rysunek 11.1).
Komponenty .NET
Gwn zalet .NET Framework jest niezaleno od platformy oraz od jzyka. W rozdziale
2. wspominaem o technologii Common Type System (wsplny system typw). Pisaem
wwczas, i dziki CTS moliwe jest komunikowanie
si pomidzy poszczeglnymi podzespoami.
W rodowisku Win32 moliwe jest podzielenie aplikacji na mniejsze jednostki. W tym celu
stosuje si biblioteki DLL. Biblioteki DLL mog eksportowa funkcje i procedury, ktre z
kolei mog by wykorzystywane w aplikacji EXE. Innym rozwizaniem jest zastosowanie
kontrolek COM. Uycie takiej kontrolki w programie take nie jest atwe, gdy przed
wykorzystaniem obiekt COM musi zosta zarejestrowany.
{ Vehicle }
procedure Vehicle.Breaks;
begin
Console.WriteLine('Wczam hamulce.');
end;
procedure Vehicle.SendMsg(const Message: String);
begin
{ sprawdzenie, jaki parametr zosta przekazany do procedury }
if Message = 'Left' then TurnLeft
else if Message = 'Right' then TurnRight
else if Message = 'Breaks' then Breaks
else Console.WriteLine('Nieprawidowa komenda.');
end;
procedure Vehicle.TurnLeft;
begin
Console.WriteLine('Skrcam w lewo.');
end;
procedure Vehicle.TurnRight;
begin
Console.WriteLine('Skrcam w prawo.');
end;
begin
{ empty }
end.
Jeeli masz na swoim komputerze zainstalowane rodowisko Delphi 8 lub nowsze, moesz
skompilowa w nim poniszy kod. Dla wszystkich tych, ktrzy nie maj zainstalowanego
rodowiska Delphi, na pycie CD doczonej do ksiki zamieciem wersj skompilowan.
Parametr przekazany do metody SendMsg() decyduje o tym, jaka procedura zostanie
wywoana z klasy Vehicle.
Przygotowanie komponentu C#
Przede wszystkim uruchom rodowisko Visual C# Express Edition i utwrz nowy projekt
aplikacji konsolowej. W oknie Solution odnajd ga References, kliknij j prawym
przyciskiem myszy i wybierz Add Reference. Dziki oknu Add Reference moemy doda do
programu odwoanie do innego komponentu .NET lub kontrolki COM. Nas interesuje
zakadka Browse pozwala ona wskaza skompilowany podzesp, do ktrego zostanie
utworzone odwoanie.
Zwr uwag, i przy pomocy instrukcji using musimy wczy do programu odpowiedni
przestrze nazw. Dalsza cz kodu powinna by dla Ciebie jasna. Rysunek 11.3 prezentuje
dziaanie takiego programu.
Powysze polecenie odniesie oczekiwany efekt, pod warunkiem e plik Assembly.dll oraz
demo.cs znajduj si w katalogu C:\katalog. Przecznik /r: okrela podzesp, z jakim
zostanie skompilowany nasz program. Dziki temu kompilator rozpoznaje typ Vehicle w
kodzie C#. Z kolei przecznik /out: okrela ciek, w ktrej zostanie umieszczona wersja
wynikowa naszego programu.
Jeeli kompilator nie wywietli adnego bdu, mona sprbowa uruchomi aplikacj
demo.exe.
Budowa podzespou
Powiedzielimy sobie, e podzesp to nie tylko poredni kod IL. W rzeczywistoci jego
zawarto mona podzieli na cztery elementy:
manifest,
metadane.
Atrybuty podzespou
W podzespoach mona zadeklarowa okrelone atrybuty opisujce dany podzesp. Mog
one by pniej odczytywane przez inne programy w celu weryfikacji numeru wersji aplikacji
oraz nazwy producenta. Innymi sowy, atrybuty s narzdziem programisty, ktry moe
decydowa o zawartoci manifestu.
Jeeli masz otwarty projekt aplikacji C#, niewane, czy Windows Forms, czy konsolowy,
przejd do okna Solution w gazi Properties, wybierz pozycj AssemblyInfo.cs i kliknij j:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through
the following
// set of attributes. Change these attribute values to modify
the information
// associated with an assembly.
[assembly: AssemblyTitle("AssemblyApp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssemblyApp")]
[assembly: AssemblyCopyright("Copyright 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this
assembly not visible
// to COM components. If you need to access a type in this
assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this
project is exposed to COM
[assembly: Guid("a0bf0a73-54d8-4b7b-9472-3a2c0980654f")]
// Version information for an assembly consists of the
following four values:
//
//
Major Version
//
Minor Version
//
Build Number
//
Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Zawarto tego moduu jest rwnie kompilowana wraz z pozostaym kodem. Modu ten
zawiera informacje o naszej aplikacji w formie atrybutw. Te informacje zostan
skompilowane do kodu poredniego IL.
Jak widzisz, umieszczanie atrybutw w kodzie programu jest do specyficzne. Atrybuty
umieszczane s w nawiasach kwadratowych, z uyciem sowa okrelajcego przeznaczenie
atrybutu (assembly):
[assembly: nazwa atrybutu]
W przypadku tego atrybutu do klasy naley przekaza jeden parametr w postaci typu string.
Jeeli chcesz, moesz zmieni zawarto atrybutw, m.in. podajc prawidow nazw
aplikacji oraz wersj czy autora.
Mechanizm refleksji
Powiedziaem ju, e dziki metadanym mamy moliwo odczytania informacji na temat
elementw umieszczonych w podzespole. Pokazywaem, jak to zrobi, korzystajc z opcji Go
to definition w menu podrcznym rodowiska Visual C# Express Edition.
Mechanizm refleksji umoliwia odczytanie informacji odnonie do metadanych. Z tego
mechanizmu korzysta m.in. rodowisko Visual C# Express Edition przy odczytywaniu
informacji z podzespou. Mamy moliwo przegldania zawartoci biblioteki klas FCL lub
jakiegokolwiek innego podzespou przy pomocy okna Object Browser (rysunek 11.4).
Framework.
Mechanizm refleksji pozwala na odczyt dowolnego podzespou .NET. Nie jest wic istotne, czy
program zosta napisany w C#, Delphi czy w C++. Po prostu program napisany w Delphi
bdzie zawiera wicej informacji, gdy kompilator Delphi dodatkowo wcza w aplikacj
wykonywaln zawarto przestrzeni Borland.Delphi.System.
Funkcja GetType
Kada klasa .NET posiada metod GetType(), ktra jest dziedziczona po klasie gwnej
System.Object. Owa metoda zwraca rezultat w postaci klasy System.Type. Dziki niej
mona odczytywa takie informacje jak nazwa podzespou, jego wersja, a take przestrze
adresowa, w jakiej si on znajduje. Tym zajmiemy si nieco pniej teraz pozostaniemy
jedynie przy odczytaniu nazwy danego typu:
using System;
namespace FooApp
{
class Program
{
static void Main(string[] args)
{
int I = 10;
double D = 10.5;
Console.WriteLine("Zmienna I jest typu: " +
I.GetType().ToString());
Console.WriteLine("Zmienna D jest typu: " +
D.GetType().ToString());
Console.Read();
}
}
}
Typy int czy double w C# s odpowiednikami typw .NET, rwnie s klasami, zatem
posiadaj metod GetType(), ktra zwraca informacje o obiekcie. W powyszym przykadzie
uyem metody ToString() do znakowego przedstawiania typu zmiennej. Dziaanie takiego
programu spowoduje wywietlenie na ekranie tekstu:
Zmienna I jest typu System.Int32
Zmienna D jest typu System.Double
Klasa System.Type
Jak powiedziaem wczeniej, metoda GetType() zwraca informacje w postaci obiektu
System.Type, z ktrego mona wyczyta wicej informacji na temat konkretnego obiektu
.NET.
Na formularzu WinForms umiemy teraz komponent ListBox oraz Button. Nazwijmy je,
odpowiednio, lbInfo oraz btnGetInfo (nazwy komponentw nie odgrywaj wikszej roli).
Zdarzenie Click przycisku powinno wyglda tak jak na listingu 11.3.
Listing 11.3. Wykorzystanie klasy System.Type
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WinForms
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnGetInfo_Click(object sender, EventArgs
e)
{
System.Type MyType;
MyType = sender.GetType();
lbInfo.Items.Add("Nazwa klasy: " +
MyType.FullName);
lbInfo.Items.Add("Przestrze nazw: " +
MyType.Namespace);
lbInfo.Items.Add("Nazwa podzespou: " +
MyType.Assembly.FullName);
lbInfo.Items.Add("Wersja podzespou: " +
MyType.Assembly.GetName().Version.ToString());
lbInfo.Items.Add("Nazwa pliku: " +
MyType.Module.Name);
}
}
}
adowanie podzespou
Program przedstawiony w poprzednim przykadzie pobiera informacje o komponencie typu
Button. Jest to do proste, lecz niezbyt przydatne. Okno Object Browser umoliwia
odczytanie informacji na temat dowolnego podzespou. Aby to wykona, mona wykorzysta
klas System.Reflection.Assembly.
Dla przypomnienia: dla kompilatora nie ma znaczenia, czy zadeklarowana zmienna bdzie
typu Assembly czy System.Reflection.Assembly.
Zaadowanie podzespou wie si zaledwie z jedn lini kodu wywoaniem metody
LoadFrom() z klasy System.Reflection.Assembly (w skrcie bd nazywa t klas po
prostu Assembly):
System.Reflection.Assembly AFile;
AFile = System.Reflection.Assembly.LoadFrom("Assembly.exe");
Taki zapis umoliwia nastpnie odczytanie klas znajdujcych si w podzespole oraz ich
waciwoci, zdarze itd.
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnLoad_Click(object sender, EventArgs e)
{
System.Reflection.Assembly AFile;
TreeNode ClassTree, PropTree, MethodTree,
FieldTree, EventTree;
// usunicie wszystkich gazi (jeeli s)
tvAssembly.Nodes.Clear();
// wywietlenie okna dialogowego
openFileDialog1.ShowDialog();
try
{
// pobranie nazwy wybranego podzespou oraz
zaadowanie go
AFile =
System.Reflection.Assembly.LoadFrom(openFileDialog1.FileName);
Text = "Reflection [" +
openFileDialog1.FileName + "]";
}
catch
{
MessageBox.Show("Nie mona zaadowa
podzespou!");
return;
}
// zaadowanie informacji o klasach znajdujcych
si w podzespole
System.Type[] MyType = AFile.GetTypes();
progressLoad.Maximum = MyType.Length;
for (int i = 0; i < MyType.Length; i++)
{
// utworzenie nowego wza o nazwie klasy z
podzespou
ClassTree =
tvAssembly.Nodes.Add(MyType[i].FullName);
// utworzenie nowej gazi o nazwie
Waciwoci
PropTree = ClassTree.Nodes.Add("Waciwoci");
for (int subI = 0; subI <
MyType[i].GetProperties().Length; subI++)
{
PropTree.Nodes.Add(MyType[i].GetProperties().GetValue(subI).To
String());
}
// utworzenie nowej gazi o nazwie Metody
MethodTree = ClassTree.Nodes.Add("Metody");
for (int subI = 0; subI <
MyType[i].GetMethods().Length; subI++)
{
MethodTree.Nodes.Add(MyType[i].GetMethods().GetValue(subI).ToS
tring());
}
// utworzenie nowej gazi o nazwie Pola
FieldTree = ClassTree.Nodes.Add("Pola");
for (int subI = 0; subI <
MyType[i].GetFields().Length; subI++)
{
FieldTree.Nodes.Add(MyType[i].GetFields().GetValue(subI).ToStr
ing());
}
// utworzenie nowej gazi o nazwie
Zdarzenia
EventTree = ClassTree.Nodes.Add("Zdarzenia");
for (int subI = 0; subI <
MyType[i].GetEvents().Length; subI++)
{
MethodTree.Nodes.Add(MyType[i].GetEvents().GetValue(subI).ToSt
ring());
}
progressLoad.Value = i;
}
progressLoad.Value = 0;
}
}
}
Tworzenie nowej pozycji w komponencie TreeView przypomina nieco tworzenie nowej linii
w kontrolce ListBox. Zmienna ClassTree stanowi niejako uchwyt, ktry bdzie
wykorzystywany do tworzenia kolejnych odgazie.
Kolejnym krokiem w dziaaniu programu jest odczytanie waciwoci, metod, zdarze, pl
oraz metod kadej z klas. W kadym przypadku proces ten jest niemal identyczny (rne s
jedynie nazwy sucych do tego funkcji):
PropTree = ClassTree.Nodes.Add("Waciwoci");
for (int subI = 0; subI < MyType[i].GetProperties().Length;
subI++)
{
PropTree.Nodes.Add(MyType[i].GetProperties().GetValue(subI).T
oString());
}
Przed prb kompilacji programu nie mona zapomnie o wczeniu do programu przestrzeni
nazw System.Reflection i System.Runtime.InteropServices.
Najpierw program sprawdza, czy element tablicy okrelony zmienn i jest typu
AssemblyTitleAttribute. Jeeli tak, nastpuje rzutowanie tego elementu tablicy oraz
wycignicie wartoci waciwoci Title. Kontrolka o nazwie edtText w rzeczywistoci jest
komponentem TextBox umieszczonym na formularzu.
Zastosowanie takiego mechanizmu byo konieczne ze wzgldu na to, i funkcja
GetCustomAttributes zwraca elementy w postaci tablicy typu System.Object. Naleao
wic ustali, czy rzutowanie na dan klas (np. AssemblyTitleAttribute) powiedzie si
std konieczno uycia operatorw is i as.
Wasne atrybuty
Powiedziaem, e atrybuty s danymi opisujcymi dane. Oprcz standardowych atrybutw
okrelajcych np. prawa autorskie do podzespou mona zadeklarowa wasne, ktre tak
samo bd odczytywane przez programy typu Reflection.
Do czego mog si przyda wasne atrybuty? Moliwoci s nieograniczone. Przykadowo:
umieszczanie w swoich programach takich informacji jak odnonik do strony WWW
zawierajcej kod XML danego programu. Gdy wszystkie Twoje aplikacje bd zawieray
odpowiedni atrybut z odnonikiem do pliku XML, to bd mogy zacieni wspprac w
pliku XML moe znajdowa si dodatkowa informacja dotyczca programu.
Inny przykad: notowanie bdw aplikacji. Mona oczywicie umieszcza komentarz przy
klasie, w ktrej poprawiono kod. Mona take uy atrybutu, np. w taki sposb:
[BugFixedAttribute("Adam", "19-05-2006", "Dodaem metod")]
[BugFixedAttribute("Janek", "21-06-2006", "Poprawka")]
class Program
{
static void Main(string[] args)
{
}
}
Taka klasa musi dziedziczy po Attribute. Jak widzisz, klasa BugFixedAttribute posiada
konstruktor z trzema parametrami. Jest to wany element programu, gdy kiedy uyjemy
atrybutu, jego wartoci zostan przypisane do parametrw konstruktora:
[BugFixedAttribute("Adam", "19-05-2006", "Dodaem metod")]
Uycie atrybutu w kodzie programu przypomina wywoanie metody klasy. Mamy rwnie
moliwo przekazania do niego okrelonych parametrw oddzielonych znakiem przecinka.
Istnieje moliwo jawnego przypisania parametrw atrybutu do danych waciwoci np.:
[BugFixedAttribute(Programmer = "Janek", Date = "21-06-2006",
Comment = "Poprawka")]
{
get
{
return FProgrammer;
}
set
{
FProgrammer = value;
}
}
public string Date
{
get
{
return FDate;
}
set
{
FDate = value;
}
}
public string Comment
{
get
{
return FComment;
}
set
{
FComment = value;
}
}
Wydaje mi si, e zapis z konstruktorem jest szybszy i przejrzystszy, aczkolwiek nie jest zbyt
czytelny dla programisty nieznajcego parametrw atrybutu.
Operator typeof jest tutaj wany, gdy zwraca typ danych (w tym przypadku klasy) w postaci
zmiennej typu System.Type. Po uzyskaniu typu mona wywoa metod
GetCustomAttributes(), pobierajc atrybuty programu.
Pierwszy parametr funkcji, GetCustomAttributes(), informuje o tym, e atrybuty maj by
jedynie typu BugFixAttributes. Jeli mamy tablic danych typu Object (Attrs), odczyt
atrybutw jest taki sam jak w poprzednio prezentowanym przykadzie.
Listing 11.5 zawiera peny kod rdowy programu odczytujcego atrybuty uyte w
programie.
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace AttrsApp
{
[AttributeUsage(AttributeTargets.All, AllowMultiple=true)]
class BugFixedAttribute : Attribute
{
private string FProgrammer;
private string FDate;
private string FComment;
public string Programmer
{
get
{
return FProgrammer;
}
set
{
FProgrammer = value;
}
}
public string Date
{
get
{
return FDate;
}
set
{
FDate = value;
}
}
public string Comment
{
get
{
return FComment;
}
set
{
FComment = value;
}
}
{
System.Object[] Attrs;
Attrs =
typeof(Foo).GetCustomAttributes(typeof(BugFixedAttribute),
true);
for (int i = 0; i < Attrs.Length; i++)
{
Console.WriteLine("-------");
Console.WriteLine("Programista: " + (Attrs[i]
as BugFixedAttribute).Programmer);
Console.WriteLine("Data: " + (Attrs[i] as
BugFixedAttribute).Date);
Console.WriteLine("Komentarz: " + (Attrs[i] as
BugFixedAttribute).Comment);
}
Console.Read();
}
}
}
Uycie atrybutu
components), czyli takich, ktre raz zarejestrowane zostan umieszczone w jednym gwnym
katalogu. Z takiego katalogu bdzie moga korzysta kada aplikacja, ktra zechce uy
naszego podzespou. Przestrze, w ktrej s rejestrowane takie podzespoy, nazywa si
Global Assembly Cache (w skrcie GAC) i znajduje si w katalogu C:\WINDOWS\assembly
(w moim przypadku).
Kady podzesp powinien posiada opis, nazw, informacj o prawach autorskich oraz co
bardzo wane numer wersji. Na jednej maszynie moe istnie kilka takich samych
podzespow o rnych numerach wersji. Program Global Assembly Cache Tool, kryjcy si
pod nazw gacutil.exe, umoliwia rejestracj danego podzespou jako globalnego,
wspuytkowanego.
Program gacutil jest wywoywany z poziomu wiersza polece. Aby zainstalowa dany
podzesp, naley wywoa program z opcj /i, tak jak to zrobiem poniej:
C:\Program Files\Microsoft.NET\SDK\v1.1\Bin>gacutil /i
c:\csharpexample\Assembly.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version
1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights
reserved.
Assembly successfully added to the cache
Program sn.exe take jest aplikacj wywoywan z poziomu wiersza polece. W celu
wygenerowania klucza naley uruchomi j z parametrem k oraz poda nazw pliku, do
ktrego klucz zostanie zapisany (tak jak to przedstawiem powyej).
Koncepcja strong names jest podobna do 128-bitowego klucza GUID (ang. Globally Unique
Identifier) w COM. GUID zapewnia unikalno klucza. Tymczasem wersja oraz nazwa
podzespou nie gwarantuj unikalnoci tak jak klucz. Dlatego te uywamy narzdzia sn.exe.
Wczenie klucza do podzespou moe nastpi za spraw uycia programu al.exe. Mona
take wczy klucz bezporednio w kodzie programu, uywajc atrybutu
AssemblyKeyFileAttribute.
WinCV
Nazwa programu jest skrtem od sw Windows Class Viewer (ang. podgld klas). Jest to
przydatny i prosty program sucy do odnajdywania i podgldu zawartoci klas .NET
(rysunek 11.8).
{{Image:csharp11.8.jpg}}
Rysunek 11.8. Program WinCV w trakcie dziaania
Gwnym elementem programu jest pole, w ktrym naley wpisa sowo kluczowe do
wyszukania. Na licie po lewej stronie zostan wywietlone klasy zawierajce to sowo
kluczowe.
Po zaznaczeniu danej klasy w gwnym oknie zostanie wywietlona lista znajdujcych si w
niej waciwoci, pl, zdarze oraz metod wraz z nazwami oraz typem parametrw.
.NET a COM
Pokazaem, w jaki sposb poszczeglne podzespoy mog komunikowa si ze sob oraz
wykorzystywa klasy z innych podzespow. Jednak cay ten proces odbywa si w obrbie
kodu zarzdzanego (managed code). W tym podrozdziale zaprezentuj sposb stosowania
kodu niezarzdzanego (kontrolki COM) w jzyku C# na .NET.
Import obiektu COM do aplikacji .NET jest dosy prosty. Obiekty Win32 COM mog zosta
zaimportowane do .NET za pomoc narzdzia o nazwie Tlbimp.exe, doczonego do .NET
Framework SDK.
Zasady importowania obiektw COM do .NET przedstawi na przykadzie kontrolki SAPI
(Microsoft Speech API). Najpierw odszukajmy na dysku plik sapi.dll. Potem z poziomu
wiersza polece trzeba uruchomi program Tlbimp.exe, ktry zaimportuje kontrolk COM do
.NET:
tlbimp "C:\Scieka do pliku\sapi.dll" /verbose /out:C:\Interop.SAPI.dll
System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
Interop.SAPI;
namespace SAPI
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnTalk_Click(object sender, EventArgs e)
{
SpVoice Voice;
Voice = new SpVoice();
Voice.Speak(richTextBox1.Text,
SpeechVoiceSpeakFlags.SVSFDefault);
}
}
}
PInvoke
.NET jest now platform programistyczn. Minie jeszcze sporo czasu, zanim programici
przystosuj swoje aplikacje do nowej platformy oraz obdarz j zaufaniem. Mimo i .NET
udostpnia dziesitki klas umoliwiajcych atwiejsze programowanie, w niektrych
przypadkach nadal konieczne moe okaza si wykorzystanie funkcji udostpnionych przez
WinAPI.
W wielu przypadkach uycie funkcji Win32 stanie si wrcz niezastpione, tak wic w tej
czci rozdziau zajmiemy si wykorzystaniem bibliotek Win32 DLL w aplikacjach .NET.
W tym celu bdziemy korzysta z mechanizmu zwanego Platform Invocation Service, czyli
Platform Invoke, zwanego w skrcie PInvoke (lub P/Invoke). Mechanizm w pozwala na
importowanie funkcji z bibliotek Win32 DLL za pomoc atrybutu [DllImport].
Dla przypomnienia powiem, i system Windows udostpnia interfejs programistyczny zwany
WinAPI (pisaem o tym w rozdziale 2.), ktry zawiera setki funkcji moliwych do
wykorzystania w naszych aplikacjach. Funkcje te zawarte s w tzw. bibliotekach DLL.
Biblioteki DLL to pliki z rozszerzeniem .dll, zawierajce skompilowany kod. Nie s to jednak
aplikacje wykonywalne (takie jak .exe).
Dla przykadu poka, w jaki sposb pobra nazw uytkownika (funkcja GetUserName())
oraz ciek do systemu Windows (funkcja GetWindowsDirectory()). Ich prototyp w kodzie
zarzdzanym wyglda tak:
[DllImport("kernel32.dll")]
static extern bool GetWindowsDirectory(StringBuilder lpBuffer,
ref int nSize);
[DllImport("Advapi32.dll")]
static extern bool GetUserName(StringBuilder lpBuffer, ref int
nSize);
Obie metody zostay opatrzone sowem kluczowym extern. Informuje ono kompilator, e
metody bd adowane z bibliotek zewntrznych. Metody zostay opatrzone atrybutem
DLLImport, okrelajcym nazw biblioteki, z ktrej adowane bd funkcje. Listing 11.6
zawiera program, ktry wykorzystuje funkcje Win32API.
Listing 11.6. Przykad uycia funkcji Win32 API
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace PInvoke
{
class Program
{
[DllImport("kernel32.dll")]
static extern bool GetWindowsDirectory(StringBuilder
lpBuffer, ref int nSize);
[DllImport("Advapi32.dll")]
static extern bool GetUserName(StringBuilder lpBuffer,
ref int nSize);
static void Main(string[] args)
{
StringBuilder Buffer = new StringBuilder(64);
Interesujce nas dane (cieka do systemu Windows oraz nazwa uytkownika) zostan
przypisane do zmiennej Buffer typu StringBuilder.
Aby wykorzysta funkcje Win32 API, naley zna ich budow (parametry oraz typy). Tego
moesz dowiedzie si z dokumentacji firmy Microsoft, znajdujcej si pod adresem
http://msdn.microsoft.com.
Taki atrybut nie posiada adnych dodatkowych parametrw. Normalnie jest moliwe
okrelenie konwencji wywoania parametrw (cdecl, stdcall itp.), nazwy adowanej
procedury bd funkcji oraz kodowania cigw znakowych (Unicode, ANSI String).
Parametry atrybutu DllImport mona okrela tak:
[DllImport("SimpleDLL.dll", CallingConvention =
CallingConvention.Stdcall, EntryPoint="About")]
procedury znajduje si cig znakw (String). Tym parametrem jest CharSet, ktry okrela
kodowanie:
W przypadku Win32 API wiele funkcji miao dwie odmiany jedn z parametrem typu
PAnsiChar, a drug z PWideChar. Dla przypomnienia: wszystkie cigi znakowe w .NET s
zapisane w Unicode, take typ String.
Wspomniaem tutaj o konwencji wywoania. Jest to zaawansowane zagadnienie zwizane z
przekazywaniem wartoci do parametrw funkcji, a konkretnie ich umieszczaniem w pamici.
Wyjanienie tych poj wykracza poza ramy niniejszej publikacji.
Zasadniczo technika PInvoke ma o wiele wiksze zastosowanie. Ja pokazaem jedynie prosty
przykad wywoania funkcji Win32 API, lecz ze wzgldu na rnice pomidzy systemem Win32
a .NET niekiedy uycie funkcji API moe okaza si o wiele trudniejsze. Jeeli jeste
zainteresowany technologi PInvoke, odsyam do dokumentacji platformy .NET.
Podsumowanie
Moliwo integracji poszczeglnych podzespow to wielka zaleta platformy .NET, dajca
ogromne moliwoci. Naley uwiadomi sobie, e rodowisko .NET Framework to nie tylko
biblioteka klas czy biblioteka WinForms, ale rwnie CLS, czyli wsplny jzyk
programowania. Dziki temu niewane jest, w jakim jzyku piszemy swoje aplikacje,
poniewa wygenerowany kod poredni zawsze bdzie taki sam, niezalenie od jzyka. Mam
nadziej, e po przeczytaniu niniejszego rozdziau masz pewn wiadomo moliwoci
pyncych ze wspdzielenia podzespow.
[1] W tej czci rozdziau terminem komponent .NET bd okrela podzesp (ang.
assembly), czyli zwyk aplikacj skompilowan do kodu IL i posiadajc rozszerzenie .exe
lub .dll.
Rozdzia 12
Pliki i obsuga strumieni
Czym jest plik
? Tego chyba nie trzeba wyjania adnemu uytkownikowi komputera. Istnieje kilka
rodzajw plikw: tekstowe, binarne, typowane itp. Pliki s wykorzystywane przez programy
do pobierania lub przechowywania informacji. Mog te zawiera binarne fragmenty
programu. Ten rozdzia bdzie powicony plikom i ich obsudze w C#. Zaczniemy od rzeczy
najprostszych, przechodzc do coraz bardziej zaawansowanych aspektw.
Biblioteka klas rodowiska .NET
Framework zawiera szereg klas umoliwiajcych obsug plikw oraz katalogw. Klasy te
znajduj si w przestrzeni nazw System.IO.
IO to potoczne okrelenie operacji wejcia-wyjcia (ang. input-output).
Czym s strumienie
Strumienie s specjaln form wymiany i transportu danych, obsugiwan przez klasy
przestrzeni System.IO. To okrelenie moe nie jest zbyt precyzyjne, ale zaraz postaram si
wyjani to szczegowo.
Dziki strumieniom mona w prosty sposb operowa na danych znajdujcych si w pamici
komputera, w plikach itp. Przykadowo, strumie moe by plikiem, pamici operacyjn lub
wspdzielonym zasobem
sieciowym.
Operacje na katalogach
Do operowania na katalogach wykorzystujemy klas Directory lub DirectoryInfo.
Directory moe by wygodnym sposobem prostego operowania na katalogach, gdy nie
wymaga tworzenia egzemplarza klasy. Jeeli jednak musimy wykona wiele operacji na
katalogach, wydajniejszym sposobem bdzie skorzystanie z klasy DirectoryInfo.
cieka C:\Foo\Bar zostanie utworzona, nawet gdy na dysku nie ma katalogu Foo.
Pamitaj o tym, aby w trakcie podawania cieek w acuchu korzysta z podwjnych
backslashw (\\). Podobnie jak w jzykach C/C++, kompilator po znaku \ oczekuje symbolu
specjalnego, takiego jak np. \n, ktry oznacza now lini.
Alternatywny kod korzystajcy z klasy DirectoryInfo, rwnie tworzcy nowy katalog,
wyglda nastpujco:
DirectoryInfo dInfo = new DirectoryInfo("C:\\Foo");
if (!dInfo.Exists)
{
dInfo.Create();
}
Klasa DirectoryInfo udostpnia przecion metod Delete(). Jej druga wersja moe
posiada parametr typu bool, ktry okrela, czy katalog bdzie usuwany nawet wwczas, gdy
posiada inne pliki i foldery (warto true).
Kopiowanie i przenoszenie
Podczas tworzenia aplikacji
przetwarzajcych z katalogami moe zaistnie konieczno skopiowania i (lub) przeniesienia
pewnych folderw. Okazuje si, e klasy Directory i DirectoryInfo udostpniaj tylko
metody do przenoszenia katalogw. Zaprezentuj rwnie technik umoliwiajc
kopiowanie caych katalogw, gdy nie wiedzie czemu takiej moliwoci brakuje w
bibliotece klas FCL.
Przenoszenie katalogw jest proste. Klasa Directory udostpnia metod Move(), ktra
przyjmuje dwa parametry katalog rdowy oraz docelowy:
Directory.Move("C:\\Foo", "C:\\Bar");
Przenoszenie jest proste. Naley jednak zastanowi si nad metod kopiowania katalogw,
ktr trzeba napisa samodzielnie. Metoda, ktr za chwil zaprezentuj, wykorzystuje
elementy nieomwione do tej pory omwi je w dalszej czci ksiki.
Metoda, ktra realizuje kopiowanie katalogu, przedstawiona zostaa poniej:
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.IO;
namespace WindowsApplication1
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnLoad_Click(object sender, EventArgs e)
{
DirectoryInfo dirInfo = new
DirectoryInfo(Application.StartupPath);
lbDirectory.Items.Add(
"Pena cieka: " + dirInfo.FullName);
lbDirectory.Items.Add(
"Katalog macierzysty: " + dirInfo.Parent);
lbDirectory.Items.Add(
"Data utworzenia: " +
dirInfo.CreationTime.ToShortDateString());
lbDirectory.Items.Add(
"Data ostatniego dostpu: " +
dirInfo.LastAccessTime.ToShortDateString());
lbDirectory.Items.Add(
"Data zapisu: " +
dirInfo.LastWriteTime.ToShortDateString());
lbDirectory.Items.Add(
"Atrybuty: " + dirInfo.Attributes.ToString());
}
}
}
Obsuga plikw
Tak jak omwione w poprzednim podrozdziale klasy Directory i DirectoryInfo su do
obsugi katalogw, tak File oraz FileInfo su do obsugi plikw. Z klasy File
skorzystaem ju wczeniej, podczas prezentowania sposobu na skopiowanie katalogu. Teraz
chciabym skupi si na podstawowych operacjach, jakich dokonujemy na plikach.
Tworzy ona nowy plik gotowy do zapisu tekstu z kodowaniem UTF-8. Oczywicie
Oczywicie utworzony w ten sposb plik bdzie pusty. Ani klasa File, ani FileInfo nie
oferuje metod sucych do zapisu lub odczytu danych z pliku. W tym celu mona skorzysta
z klasy StreamWriter, ktrej obiekt jest zwracany przez metod CreateText():
if (!File.Exists("C:\\foo.txt"))
{
StreamWriter sw = File.CreateText("C:\\foo.txt");
sw.WriteLine("Hello World!");
sw.Close();
}
W tym przykadzie metoda WriteLine() zapisuje now lini do pliku tekstowego. Taki plik
naley koniecznie zamkn, korzystajc z metody Close().
Usuwanie plikw jest rwnie proste jak usuwanie katalogw. Suy do tego metoda
Delete():
File.Delete("C:\\foo.txt");
Rysunek 12.2 prezentuje aplikacj, ktra odczytuje informacje o pliku i dodaje je kolejno do
komponentu typu ListBox.
{
File.CreateText("C:\\Foo.txt");
}
FileInfo F = new FileInfo("C:\\Foo.txt");
lbFile.Items.Add(
"Nazwa pliku: " + F.Name);
lbFile.Items.Add(
"Rozmiar pliku: " + F.Length);
lbFile.Items.Add(
"Nazwa katalogu: " + F.DirectoryName);
lbFile.Items.Add(
"Rozszerzenie: " + F.Extension);
lbFile.Items.Add(
"Atrybuty: " + F.Attributes.ToString());
lbFile.Items.Add(
"Czas utworzenia pliku: " +
F.CreationTime.ToShortDateString());
lbFile.Items.Add(
"Czas dostpu: " +
F.LastAccessTime.ToShortDateString());
lbFile.Items.Add(
"Czas ostatniej modyfikacji: " +
F.LastWriteTime.ToShortDateString());
File.Delete("C:\\Foo.txt");
}
}
}
Strumienie
O strumieniach moesz myle jako o cigach danych. W rodowisku .NET Framework
dostpnych jest wiele klas obsugujcych strumienie, w zalenoci od medium
przechowujcego te dane. I tak do obsugi strumieni plikw wykorzystujemy klas
FileStream, natomiast klasa MemoryStream reprezentuje strumienie znajdujce si w
pamici operacyjnej. Pomimo tych rnic wykorzystywanie tych klas jest niemale
identyczne.
Wspomniaem o klasach reprezentujcych dane. Jednake do odczytywania i zapisywania
danych do strumieni uywamy odrbnych klas StreamReader oraz StreamWriter. W
przypadku danych binarnych s to odpowiednio klasy BinaryWriter i BinaryReader.
Moliwoci stosowania tych klas w praktyce zostan zilustrowane przykadami w kolejnych
punktach.
cieki do pliku,
trybu otwarcia pliku,
trybu dostpu do pliku.
W drugim i trzecim parametrze okrelony zosta tryb otwarcia oraz dostpu do pliku. S to
typy wyliczeniowe, ktrych wartoci opisaem w tabeli 12.4 oraz 12.5.
Tabela 12.4. Wartoci typu wyliczeniowego FileMode
Warto
Opis
Tworzy lub otwiera plik i przechodzi na jego koniec. Wymaga utworzenia
Append
obiektu z parametrem FileAccess.Write.
Tworzy plik, a w razie gdy on ju istnieje, zastpuje jego dotychczasow
Create
zawarto.
CreateNew
Tworzy plik, a w razie gdy on ju istnieje, generuje odpowiedni wyjtek.
Otwiera nowy plik do odczytu. Jeeli plik nie istnieje, generowany jest
Open
wyjtek.
OpenOrCreate Otwiera plik, a jeeli ten nie istnieje tworzy nowy.
Truncate
Otwiera plik i czyci jego zawarto.
Powyszy kod tworzcy obiekt klasy FileStream prbuje otworzy plik. Jeeli ten nie
istnieje tworzy nowy. Plik jest otwierany zarwno do odczytu, jak i do zapisu.
Aby zapisa warto w pliku tekstowym, naley utworzy rwnie egzemplarz klasy
StreamWriter. W parametrze konstruktora tej klasy naley przekaza obiekt klasy
FileStream (patrz listing 12.3).
Listing 12.3. Przykad tworzenia oraz zapisania wartoci do pliku
using System;
using System.IO;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
FileStream fs = new FileStream("C:\\Foo.txt",
FileMode.OpenOrCreate, FileAccess.ReadWrite);
try
{
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("Hello World!");
sw.WriteLine("Bye!");
sw.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
Podobnie jak metoda WriteLine() z klasy Console wywietla now lini w oknie konsoli,
tak metoda WriteLine() z klasy StreamWriter zapisuje now lini w pliku tekstowym.
Naley wspomnie o tym, e wywoanie metody WriteLine() nie rwna si bezporedniemu
zapisaniu do pliku, lecz do bufora pamici. Naley pamita o wywoaniu metody Close()
lub Flush(), ktra czyci bufor.
Aby odczyta zawarto pliku tekstowego, moemy skorzysta z klasy StreamReader. Jej
uycie jest podobne jak w przypadku wczeniej wspominanej klasy StreamWriter. Listing
12.4 prezentuje przykad zaadowania do komponentu typu RichtextBox zawartoci pliku
tekstowego wskazanego przez uytkownika.
Listing 12.4. Przykad zaadowania zawartoci pliku
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.IO;
namespace WinForms
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
openDialog.ShowDialog();
FileStream fs = new
FileStream(openDialog.FileName,
FileMode.Open, FileAccess.Read);
try
{
StreamReader sr = new StreamReader(fs);
richText.Text = sr.ReadToEnd();
sr.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
Do operacji odczytu zawartoci pliku suy metoda ReadToEnd(). Odczytuje ona zawarto
caego pliku, zwracajc j w formie acucha string. Tak wic przy wikszych plikach uycie
tej metody moe wpyn na zawarto pamici, jaka jest uywana przez nasz aplikacj. W
przypadku wikszych plikw mona skorzysta z metody ReadLine(), ktra odczytuje
kolejne linie pliku tekstowego. T metod naley odtwarza w ptli:
while (!sr.EndOfStream)
{
richText.Text += sr.ReadLine();
}
Serializacja
Serializacja jest procesem umoliwiajcym zapisywanie informacji o obiektach klas w
plikach tekstowych lub binarnych. cilej mwic, jest procesem przeksztacania klas w
strumie bajtw, ktry moe by utrwalany na noniku danych, przesyany do innego procesu
lub nawet na inny komputer. Procesem odwrotnym jest deserializacja (ang. deserialization),
ktra polega na odczytaniu strumienia bajtw z pliku lub zdalnego komputera i
zrekonstruowaniu zawartej w nim klasy wcznie z jej zapisanym stanem.
Dziki serializacji moemy w kadej chwili zapisa stan, w jakim znajduje si obiekt, wraz z
wartociami jego elementw. Kiedy obiekt jest serializowany, rodowisko uruchomieniowe
CLR wewntrznie buduje graf obiektw. Umoliwia on temu rodowisku obsug
utrwalanych obiektw wraz ze wszystkimi obiektami, ktre s z nimi powizane.
Seralizowana klasa musi by opatrzona atrybutem [Serializable]. Serializowane s
wszystkie elementy klasy, pod warunkiem e nie zostay opatrzone atrybutem
[NonSerialized]:
[Serializable]
class Foo
{
int i;
string s;
[NonSerialized]
double d;
}
SOAP ang. Simple Object Access Protocol jest jzykiem opartym na XML,
wykorzystywanym przez usugi sieciowe do wywoywania procedur. Protok SOAP okrela
format przesyanych danych, nazwy parametrw itp. Jzyk XML zostanie omwiony w
kolejnym rozdziale ksiki.
Klasa SoapFormatter nie jest zalecana jako format przechowywanych danych. W wersji 2.0
biblioteki klas FCL ta klasa moe by nieobecna.
Przykad serializacji
Listing 12.5 zawiera przykadowy kod rdowy, ktry dokonuje procesu serializacji, a
nastpnie deserializacji danych. Serializowane dane s zapisywane na dysku C: pod nazw
data.dat.
Listing 12.5. Przykad serializacji
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp
{
public enum GenreEnum { Fish, Mammal };
[Serializable]
public struct Animal
{
public string Name;
public int Age;
public GenreEnum Genre;
}
class Program
{
static void Main(string[] args)
{
// deklaracja tablicy struktur
Animal[] MyPet = new Animal[2];
// wypenienie tablicy struktur
MyPet[0].Name = "Pies";
MyPet[0].Age = 10;
MyPet[0].Genre = GenreEnum. Mammal;
MyPet[1].Name = "Ryba";
MyPet[1].Age = 1;
MyPet[1].Genre = GenreEnum.Fish;
FileStream MyStream;
// utworzenie pliku, ktry bdzie zawiera
strumienie danych
MyStream = new FileStream("C:\\data.dat",
FileMode.Create);
Podsumowanie
Obsuga plikw we wasnej aplikacji wci, z uwagi na wiksz popularno systemw
bazodanowych, jest rzadziej wykorzystywana. Do przechowywania danych dotyczcych
aplikacji uywa si baz danych czy plikw XML (bdzie o nich mowa w kolejnym rozdziale).
Strumienie s jednak wykorzystywane dosy czsto w bibliotece klas FCL w wielu klasach,
dlatego warto zna choby ich podstawow obsug. Niekiedy moe si rwnie nadarzy
okazja do uycia plikw w formie bazy danych do przechowywania informacji. Wwczas
moesz powrci do tego rozdziau i przypomnie sobie sposoby obsugi rnych typw
strumieni.
Rozdzia 13
Obsuga formatu XML
XML jest stosunkowo now technologi zaproponowan przez najwiksze firmy
informatyczne (Microsoft i Borland) jako nowy standard przechowywania i wymiany
informacji.
XML jest skrtem od sw eXtensible Markup Language, co oznacza rozszerzalny jzyk
znacznikw. Zosta opracowany przez W3C (ang. World Wide Web
Consorcium).
HTML (ang. HyperText Markup Language) jest jzykiem znacznikw opisujcym struktur
dokumentu WWW. Projektant, aby utworzy stron WWW, musi zapisa pewne z gry
okrelone polecenia, tak aby byy rozpoznawane przez przegldark oraz odpowiednio
interpretowane. Dziki temu, korzystajc z przegldarek, moemy obserwowa dokumenty
pene odnonikw, obrazkw oraz animacji.
XML jest nieco inny tutaj to programista ustala nazwy znacznikw oraz ca struktur
dokumentw. Dziki temu tworzy unikalny format dokumentw, ktry moe by
odczytywany za pomoc dowolnego edytora tekstu.
XML jest czsto mylony z baz danych. Naley jednak podkreli, e XML chocia dziaa
podobnie nie jest baz danych. W przypadku baz danych kada platforma (MySQL,
PostgreSQL, Oracle) posiada wasn struktur danych i wasne formaty plikw, w ktrych s
gromadzone informacje. Co prawda uniwersalny jest tutaj jzyk zapyta SQL, za pomoc
ktrego komunikujemy si z baz danych, ale sam zapis i odczyt informacji z plikw
zapewnia aplikacja
np. MySQL. Plik XML jest natomiast zwykym plikiem tekstowym, dziki czemu kady
moe modyfikowa jego zawarto.
Obecnie istnieje wiele tzw. parserw, ktre pozwalaj na prost modyfikacj plikw XML.
Su one do odczytywania struktury takiego pliku, dziki czemu odciaj programist od
Niezaleno XML
XML jest niezaleny od platformy, jzyka programowania i sprztu (PC, Palmtop), jest wic
doskonaym i atwym formatem do przechowywania danych. Wemy jako przykad
notowania
spek giedowych przedstawiane na rnych portalach. Notowania te s zapisane na stronie
w formie tabel HTML, tak wic jedynym problemem w tym momencie jest odpowiednia
edycja kodu HTML, tak aby pozyska z niego potrzebne dane. Wystarczy jednak maa
zmiana w tym kodzie, aby wykorzystane narzdzie przestao poprawnie funkcjonowa. Jeeli
takie dane byyby przedstawiane jedynie w formie XML, znika problem niekompatybilnoci.
Wynika to z tego, e XML prezentuje jedynie tre, a nie form jak ma to miejsce w
HTML.
XHTML
XHTML jest nowym standardem, opracowanym take przez W3C, ktry w zamierzeniach ma
zastpi HTML i stanowi pewien porednik pomidzy jzykiem HTML a XML. Rozwj
HTML zosta wstrzymany i teraz do projektowania stron WWW zaleca si uywanie
XHTML.
XHTML jest bardziej uporzdkowany ni HTML. Usunito std wiele znacznikw HTML,
stawiajc na rozwj arkuszy stylw CSS. Innymi sowy, projektant powinien zrezygnowa z
wielu znacznikw HTML na rzecz CSS.
Budowa dokumentu
W tym podrozdziale mam zamiar opisa, jak wyglda budowa
plikw XML i jakie s ich wymagane elementy. Jako edytora mona tutaj uy dowolnego
programu pozwalajcego na zapisywanie tekstu, ja bd korzysta z edytora Visual C#
Express Edition.
Jeeli masz otwarty nowy projekt, z menu Project wybierz Add New Item. W oknie wybierz
pozycj XML File i nacinij Add. Do projektu dodany zostanie nowy plik, ktrego zawarto
wyglda tak:
<?xml version="1.0" encoding="UTF-8" ?>
Taki dokument mona zapisa pod dowoln nazw z rozszerzeniem *.xml. Pliki XML
otwiera si w przegldarce np. Internet Explorer aby sprawdzi poprawno
dokumentu.
Jeli dokument jest poprawny, Internet Explorer powinien wywietli jego tre, w
przeciwnym razie wskae bd w dokumencie.
Naley w tym momencie zaznaczy, e XML nie jest tak elastyczny jak HTML. Przegldarki
obecnie s w stanie zaakceptowa nieprawidowy zapis kodu HTML i mimo bdu poprawnie
zinterpretowa dany znacznik. Najmniejszy bd w XML spowoduje, e przegldarka
wywietli bdy w dokumencie.
Prolog
Specyficzna instrukcja dodana automatycznie przez rodowisko Visual C# Express Edition do
dokumentu to tzw. prolog. Jest to opcjonalna informacja dla parsera, informujca o typie
dokumentu oraz kodowaniu. Powinno si przykada du wag do owego nagwka i zawsze
stosowa go w swoich dokumentach.
Standardowa budowa prologu jest nastpujca:
<?xml version="1.0" encoding="UTF-8" ?>
Podstawowym warunkiem jest umieszczenie go midzy znacznikami <?xml oraz ?>. Opis
poszczeglnych parametrw prologu znajduje si w tabeli 13.1.
Tabela 13.1. Elementy skadowe prologu
<?xml ?>
Podstawowe instrukcje uywane przy deklaracji nagwka XML.
Wersja specyfikacji XML. Obecnie jedyn wersj owej specyfikacji jest
version="1.0"
1.0.
Wany nagwek, mwicy o kodowaniu dokumentu. Zalecam uywanie
kodowania Unicode (UTF-8), gdy zapewnia on poprawne wywietlanie
encoding="UTF-8"
wikszoci znakw. Godnym polecenia jest rwnie standard ISO-88592.
Dodatkowa warto okrelajca, czy dokument zawiera jakie odwoania
standalone="yes"
do innych plikw (yes).
Znaczniki
Budowa dokumentw HTML, XHTML oraz XML opiera si na znacznikach, zwanych take
tagami. W przypadku HTML oraz XHTML znaczniki musz mie z gry okrelon nazw. W
przypadku XML nie ma takich restrykcji, tak wic nazwa danego znacznika zaley ju od
programisty. Listing 13.1 przedstawia przykadow zawarto pliku XML.
Listing 13.1. Przykadowa zawarto pliku XML
<?xml version="1.0" encoding="ISO-8859-2" standalone="no" ?>
<jedzenie>
<pizza>
<nazwa>Hawajska</nazwa>
<cena>14,50</cena>
</pizza>
<pizza>
<nazwa>Peperoni</nazwa>
<cena>15,50</cena>
</pizza>
</jedzenie>
Tak zawarto moesz wklei do swojego edytora i zapisa. Po otwarciu takiego pliku w
przegldarce Internet Explorer powinien zosta wywietlony obraz taki jak na rysunku 13.1.
Budowa znacznikowa
Znaczniki s elementami o okrelonej nazwie, umieszczonymi pomidzy nawiasami (< oraz
>). Wyrniamy dwa rodzaje znacznikw otwierajcy oraz zamykajcy. W takim
przypadku w znaczniku zamykajcym musi si znale znak /:
<nazwa_znacznika> </nazwa_znacznika>
Podczas edycji znacznikw XML naley take zwrci uwag na ich nazwy nie mog si
zaczyna od cyfr oraz nie mog zawiera znakw $#%&*(/. Przykadowo, ponisza nazwa
znacznika nie jest poprawna:
<a12$%3> dane </a12$%3>
Elementy HTML
Format XML jest stworzony jedynie do przechowywania informacji, tak wic upikszajce
tekst znaczniki HTML nie maj dla XML znaczenia. Przykadowo, znacznik <b> suy w
HTML do pogrubiania tekstu. W XML taki znacznik bdzie zwykym tagiem,
niewpywajcym na sposb wywietlania tekstu:
<nazwa>Pizza <b>Peperoni</b> jest dobra</nazwa>
Znaczniki zagniedone
W zwizku z powyszym przykadem chciabym wspomnie o znacznikach zagniedonych.
Element <nazwa> bdzie gwnym znacznikiem zawierajcym tekst Pizza jest dobra.
Natomiast w powyszym przypadku znacznikiem zagniedonym bdzie <b>, zawierajcy
tekst Peperoni.
Znaki specjalne
Podczas wpisywania informacji pomidzy znacznikami nie mona uywa znakw < oraz >.
Aby umieci te znaki pomidzy tagami, naley zastosowa pewien trik, polegajcy na
zastpieniu ich innymi wartociami (tabela 13.2).
Tabela 13.2. Znaki specjalne XML
Znak specjalny Naley zastpi
<
>
&
<
>
&
"
'
Atrybuty
Kady element moe dodatkowo wykorzystywa atrybuty przybierajce okrelone wartoci:
<nazwa id="101">Peperoni</nazwa>
W tym przykadzie pizza Peperoni posiada atrybut id o wartoci 101. Atrybuty naley
wpisywa midzy apostrofami lub w cudzysowie. W danym elemencie mona umieci
dowoln liczb parametrw.
Tak samo jak w przypadku nazw elementw, nazwy atrybutw nie mog zawiera
niedozwolonych znakw (!@#^&*), nawiasw itp.
Znaczniki puste
Istnieje moliwo deklarowania elementw w sposb specyficzny uwzgldniajc jedynie
atrybuty, bez zamykania danego znacznika. W takim przypadku na kocu nazwy elementu
musi znale si znak /.
<nazwa id="101" nazwa_pizzy="Peperoni" />
Podstawowa terminologia
Czasami wyrazy tag, znacznik, element czy wze s uywane zamiennie, gdy czowiek jest
w stanie domyli si ich znaczenia. Pragn jednak sprecyzowa pewne pojcia.
Znacznik inaczej moemy nazywa tagiem jest to specjalnie sformatowany cig znakw,
np.:
<table>
<p>
Elementy mog by take puste tak moliwo przewiduje specyfikacja XML lub
XHTML:
<br />
Wze gwny
Analizujc listing 13.1, mona zauway, e cay pozostay kod XML znalaz si w wle
gwnym . Jest to podstawowa zasada: wszystkie pozostae wzy musz zosta
umieszczony w jednym gwnym korzeniu (ang. root). Trzeba o tym pamita podczas
tworzenia wasnych dokumentw XML.
Komentarze
Jak w kadym jzyku takim jak HTML czy XHTML, take i w XML istniej komentarze.
Komentarze nie s uwzgldniane przez parser i musz by zawarte pomidzy znakami <!-oraz -->, tak samo jak to ma miejsce w dokumentach HTML:
<!--komentarz-->
Przestrzenie nazw
Konstrukcja przestrzeni nazw (ang. namespace) pozwala na deklaracj wicej ni jednego
zbioru znacznikw.
Najlepiej przedstawi to na przykadzie. W pewnym projekcie istnieje dokument XML, do
Co prawda nie jest to czsto uywana konstrukcja, ale warto o niej wiedzie. Wspominam o
tym, poniewa chciabym w tym momencie zwrci uwag na moliwo deklarowania
przestrzeni nazw http://www.w3.org/XML/1998/namespace o aliasie xml. Przestrze ta
zawiera atrybut lang, ktry moe by stosowany dla kadego elementu dokumentu bez
koniecznoci jawnego okrelania przestrzeni xml. Dziki temu mona np. w dokumencie
XML stosowa wzy wielojzyczne, co uatwia proces tworzenia wielojzycznego
dokumentu:
<ns2:pizza xmlns:ns2="http://4programmers.net">
<ns2:nazwa>Peperoni</ns2:nazwa>
<ns2:cena xml:lang="PL">15,50</ns2:cena>
<ns2:cena xml:lang="EU">4</ns2:cena>
</ns2:pizza>
W powyszym przykadzie jeden element ma atrybut xml:lang o wartoci PL, a drugi EU.
Dziki temu moemy np. ustali cen pizzy w zotwkach oraz w euro.
DTD
Mona mwi o dwch rodzajach dokumentu poprawnie sformatowanym oraz
poprawnym. Dokument poprawnie sformatowany to taki, w ktrym wszystkie znaczniki s
zamknite, a elementy prawidowo zagniedone.
Dokument poprawny musi z kolei zawiera dodatkowo deklaracj DTD, czyli Document Type
Declaration. Polecenia DTD mog si znale bezporednio w dokumencie XML lub w
osobnym pliku. Instrukcje DTD, najprociej mwic, su do opisu tego, z czego skada si
dokument XML. Instrukcje DTD s unikalne i proste listing 13.2 przedstawia dokument
XML wraz z opisem struktury.
Listing 13.2. Dokument XML wraz z deklaracj DTD
<?xml version="1.0" encoding="ISO-8859-2" standalone="no" ?>
<!DOCTYPE jedzenie [
<!ELEMENT nazwa (#PCDATA)>
Pocztek instrukcji DTD musi rozpoczyna si fraz <!DOCTYPE, a koczy ]>. Pomidzy
tymi instrukcjami znajduj si opisy elementw i oglnie struktury pliku XML.
Deklaracja elementu
Kluczow czci DTD jest deklaracja typu elementu znajdujcego si w kodzie XML. Owa
deklaracja jest wykonywana przy uyciu jednej linii, ktra rozpoczyna si fraz <!ELEMENT:
<!ELEMENT nazwa (#PCDATA)>
Nastpnie naley poda nazw elementu oraz zawarto w tym przypadku #PCDATA.
Tabela 13.3 przedstawia frazy, ktre mog posuy w opisie DTD do okrelania danego
elementu.
Tabela 13.3. Frazy DTD opisujce zawarto elementu
Fraza
Opis
Przykad
DTD: <!ELEMENT nazwa EMPTY> XML: <nazwa
EMPTY
Pusty element
/>
Pomidzy znacznikami
(#PCDATA) znajduje si zwarto
(zwyky tekst)
Dany element posiada
(child)
inny zagniedony
element o nazwie child
Dany element posiada
(Seq1,Seq2) zagniedone elementy
Seq1 oraz Seq2
(child*)
(child+)
(child?)
Znaki *, ? oraz +
okrelaj, ile razy element
child moe wystpi: *
zero lub wicej razy, +
jeden raz lub wicej, ?
zero lub jeden raz
(child1 |
child2)
Deklaracja atrybutu
DTD jest rwnie uywane do deklarowania atrybutw, jakie s wykorzystywane przy
elementach. Ich budowa jest nastpujca:
<!ATTLIST Element Atrybut Typ Domylna_warto>
W przypadku gdy atrybutw jest wiele, mona okreli ich domylne wartoci w nastpujcy
sposb:
<!ATTLIST Element Atrybut Typ Domylna_warto>
Typ Domylna_warto>
Gdzie:
Typ tekstowy
Typ tekstowy jest deklarowany z uyciem sowa kluczowego CDATA:
<!ATTLIST nazwa id CDATA 'acuch'>
Typ wyliczeniowy
Opisanie atrybutu DTD w sposb wyliczeniowy daje wybr, pewien zakres wartoci, jakie
moe on przyjmowa. Przyjrzyjmy si poniszemu fragmentowi kodu:
<?xml version="1.0" encoding="ISO-8859-2" standalone="no" ?>
<!DOCTYPE
<!ELEMENT
<!ELEMENT
<!ATTLIST
]>
jedzenie [
nazwa (#PCDATA)>
pizza (nazwa)>
nazwa id (101|102|103) '103'>
<jedzenie>
<pizza>
<nazwa>Hawajska</nazwa>
<cena>14,50</cena>
</pizza>
<pizza>
<nazwa id="101">Peperoni</nazwa>
<cena>15,50</cena>
</pizza>
</jedzenie>
W takim przypadku atrybut id bdzie mg przyjmowa warto 101, 102 lub 103, lecz
wartoci domyln bdzie 103. Naley take zwrci uwag, e pierwszy element <nazwa>
nie ma zadeklarowanego atrybutu id. W zwizku z tym, gdy wywietlimy kod pliku XML w
przegldarce Internet Explorer, nada ona automatycznie atrybut id brakujcym elementom.
Teraz w gwnym pliku XML naley wczy deklaracj DTD w nastpujcy sposb:
<!DOCTYPE jedzenie SYSTEM "jedzenie.dtd">
Encje tekstowe
Encje tekstowe musz zosta zadeklarowane w sekcji DTD. Peni one w dokumencie XML
funkcj szablonu. Dziki encjom programista unika cigego wpisywania tej samej frazy w
tych samych miejscach kodu XML. Dziaanie encji mona porwna do staych jedna staa
moe by pniej uyta w dalszej czci programu, tak wic uatwia to ewentualn zmian
danych wystarczy wwczas tylko zmiana jednej staej. Listing 13.3 prezentuje sposb
wykorzystania encji tekstowych.
Listing 13.3. Prezentacja wykorzystania encji tekstowych
<?xml version="1.0" encoding="ISO-8859-2" standalone="no" ?>
<!DOCTYPE
<!ELEMENT
<!ELEMENT
<!ATTLIST
jedzenie [
nazwa (#PCDATA)>
pizza (nazwa)>
nazwa id (101|102|103) '103'>
<nazwa>&Special;</nazwa>
<cena>&Price;</cena>
</pizza>
<pizza>
<nazwa id="101">Peperoni</nazwa>
<cena>15,50</cena>
</pizza>
</jedzenie>
Dziki encjom zostay utworzone nowe znaczniki &Special; oraz &Price;. Encj deklaruje
si w nastpujcy sposb:
<!ENTITY Special "Hawajska">
Budowa tego elementu jest prosta: encj rozpoczyna fraza <!ENTITY, po ktrej wystpuje
nazwa oraz warto midzy znakami apostrofw. W tym momencie wstawienie znacznika
&Special; w kodzie XML spowoduje zamienienie go na warto owej encji.
Naley zwrci uwag na znak rednika na kocu (&Special;). Jak ju wspominaem, w XML
liczy si precyzja i takich szczegw nie mona pomin.
W kodzie XML mona uywa zarwno cudzysowu ("), jak i apostrofw (').
XSD
Do weryfikacji poprawnoci dokumentu XML stosowane jest DTD lub XSD (ang. XML
Schema Definition) standard opisu dokumentu zalecany przez W3C. Konsorcjum W3C
preferuje wykorzystanie XSD zamiast DTD, poniewa schemat ten korzysta z jzyka XML.
XSD definiuje elementy, atrybuty oraz typy danych zawartych w dokumencie XML. XSD jest
bardziej uyteczny oraz rozbudowany ni DTD, a przede wszystkim opracowany i zalecany
przez W3C.
Idea utworzenia XSD zostaa zapocztkowana przez firm Microsoft, ale w kocu w roku
2001 patronat nad powstaniem i utworzeniem standardu obja organizacja W3C.
Przypominam, e XML opracowano w roku 1998.
Utwrzmy nowy plik tekstowy i nazwijmy go jedzenie.xsd. Ten plik tekstowy naley
umieci w tym samym katalogu co przykadowy dokument XML. Bdzie on zawiera
instrukcje XSD. Przyjmujemy wic, e deklaracje XSD zostan umieszczone w osobnym
pliku. Na listingu 13.4 znajduje si przykadowy kod z instrukcjami XSD. Przypominam, e
deklaracje (instrukcje) XSD s napisane w jzyku XML i to jest podstawowa rnica
pomidzy nim a DTD.
Nagwek XSD
Na samym pocztku naley zaznaczy, e XSD korzysta z przestrzeni nazw XML, ktr
trzeba okreli na samym pocztku. Wszystkie schematy XSD musz mie gwny wze
<schema>, taki jak ten poniej:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.programowanie.org"
elementFormDefault="qualified">
Moe nie by zupenie identyczny, ale wany jest tutaj element o nazwie <schema>, ktry jest
obowizkowy w dokumencie XSD. Jak wida, uywamy tutaj przestrzeni nazw, deklarujc
http://www.w3.org/2001/XMLSchema o aliasie xs.
Elementy XSD
XSD w rzeczywistoci jest charakterystycznym kodem XML, np.:
<xs:element name="nazwa" type="xs:string" />
<nazwa>Peperoni</nazwa>
Wartoci atrybutu name musi by nazwa elementu w kodzie XML, a atrybut type okrela
rodzaj (typ) danych umieszczonych pomidzy tagami. Opisany element naley do elementw
prostych, czyli takich, ktre zwieraj jakie dane (tekst, liczby, wartoci typu boolean), nie
mog jednak zawiera innych znacznikw oraz atrybutw.
Oto przykad schematu XSD opisujcego kod XML, ktry zawiera zagniedone znaczniki:
<xs:element name="pizza">
<xs:element name="nazwa" type="xs:string" />
<xs:element name="cena" type="xs:float" />
</xs:element>
Typy danych
Ju si dowiedziae, e schematy XSD wymagaj podania typu danych elementw XML,
ktre opisuj. W poprzednich przykadach uyem typu string oraz float. Tabela 13.4
prezentuje kompletn list typw danych.
Tabela 13.4. Typy danych
Typ
Opis
string Cig tekstowy, czyli cig znakw
float 32-bitowe liczby rzeczywiste
Typy proste
Czym jest typ prosty? Jest to element, ktry zawiera jedynie tekst dane proste. Moe nie
jest to do koca poprawne sformuowanie, bo element prosty moe take zawiera wartoci
numeryczne typu boolean itp.
Deklarowalimy ju schematy opisujce proste elementy XML:
<xs:element name="nazwa" type="xs:string" />
W miejsce atrybutu default mona uy fixed. Podana w tym atrybucie warto nie bdzie
jedynie domylna nie bdzie jej mona pniej zmieni!
Atrybuty
Na pocztku moesz by nieco sceptyczny, bowiem atrybuty elementu XML w schemacie
XSD s opisywane jako element. Wane jest zrozumienie tego zdania. Przypumy, e mamy
fragment kodu XML o takiej postaci:
<pizza name="Hawajska" />
Jest to prosty, zwyky znacznik zawierajcy atrybut name. Teraz schemat opisujcy zarwno
ten element, jak i atrybut wyglda nastpujco:
<xs:element="pizza" type="xs:string" />
<xs:attribute="name" type="xs:string" />
</xs:element>
Schematy opisujce atrybuty, tak samo jak te opisujce elementy, mog zawiera wartoci
domylne (czyli parametry default oraz fixed).
Wszystkie atrybuty elementu XML domylnie s opcjonalne. Nie jest konieczne ich
deklarowanie w trakcie tworzenia dokumentu XML. Mona to zmieni, deklarujc schemat
opisujcy element:
<xs:element="pizza" type="xs:string" />
<xs:attribute="name" type="xs:string" use="optional" />
</xs:element>
Powysza deklaracja okrela, i atrybut name elementu pizza bdzie opcjonalny tak samo
jak w poprzednim przykadzie. Taki zapis nie jest konieczny, gdy jak ju powiedziaem
atrybuty XML domylnie s opcjonalne.
Mona natomiast okreli, i dany atrybut ma by wymagany! W miejsce sowa optional
naley wpisa required:
<xs:element="pizza" type="xs:string" />
<xs:attribute="name" type="xs:string" use="required" />
</xs:element>
Restrykcje
Restrykcje s elementem XSD, ktry pozwala na przypisanie szczegowych informacji
odnonie danego elementu XML np. maksymalnej wartoci, jaka moe by zawarta w
danym elemencie. Oto przykad nadania restrykcji dla elementu XML price:
<xs:element="price">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="10" />
<xs:maxInclusive value="50" />
<xs:restriction>
</xs:simpleType>
</xs:element>
Restrykcje tego typu prostego mwi, i warto elementu price musi by wiksza od 10, ale
jednoczenie mniejsza od 50. Opis pozostaych restrykcji znajduje si w tabeli 13.5.
Tabela 13.5. Restrykcje moliwe do uycia w schematach XSD
Restrykcja
Opis
length
Opisuje dugo danego elementu.
minLength
Minimalna dugo, jak moe przybra dany element.
maxLength
Maksymalna dugo, jak moe przybra dany element.
pattern
Maska, zaawansowane opcje zwizane z wyraeniami regularnymi.
enumeration Warto musi by jedn z podanych.
minInclusive Warto elementu musi by wiksza lub rwna liczbie podanej w schemacie.
minExclusive Warto elementu musi by wiksza od liczby podanej w schemacie.
maxInclusive Warto elementu musi by mniejsza lub rwna liczbie podanej w schemacie.
maxExclusive Warto elementu musi by mniejsza od liczby podanej w schemacie.
Powyszy schemat mwi, i wartoci elementu pizza moe by albo Peperoni, albo
Hawajska.
Typy zoone
Typy zoone maj inne zagniedone znaczniki. By moe ju domylasz si, jak wyglda
schemat typw zoonych. Oto przykadowy znacznik XML:
<pizza>
<nazwa>Hawajska</nazwa>
<cena>14,50</cena>
</pizza>
Oczywicie nic nie stoi na przeszkodzie, aby poczy zalety baz danych oraz XML,
przechowujc kod XML w bazie danych w polu typu BLOB lub TEXT.
XSL
Kolejnym terminem, o ktrym warto wspomnie, jest XSL (ang. eXtensible Stylesheet
Language). Jest to jzyk, ktry opisuje przeksztacenia dokumentw XSL. Jest to waciwie
rodzina jzykw, bo skadaj si na ni:
DOM
DOM, czyli Document Object Model, jest standardem opracowanym przez W3C,
definiujcym dostp do dokumentw XML oraz HTML. w standard zosta przeniesiony na
rzeczywisty zbir interfejsw (API) i funkcji w wielu parserach czy np. przegldarkach
internetowych. DOM definiuje struktur dokumentu oraz sposb manipulowania nim. Od
czasu opublikowania specyfikacji DOM
powstao wiele jej implementacji. W rodowisku Win32 moglimy korzysta z kontrolki
COM, udostpniajcej interfejsy obsugujce XML.
W .NET bdziemy korzystali z przestrzeni nazw System.XML i znajdujcych si w niej klas.
W DOM dokumenty posiadaj logiczn struktur podobn do drzewa, ktre zawiera wzy.
Np. poniszy fragment kodu XML w DOM bdzie mia struktur podobn do tej z rysunku
13.2:
<jedzenie>
<pizza>
<nazwa>Hawajska</nazwa>
<cena>14,50</cena>
</pizza>
<pizza>
<nazwa>Peperoni</nazwa>
<cena>15,50</cena>
</pizza>
</jedzenie>
SAX
SAX (ang. Simple API for XML) jest podobnie jak DOM interfejsem programistycznym
definiujcym dostp do plikw XML. O interfejsie SAX mwi si, e jest sterowany
zdarzeniami. Oznacza to, e SAX parsujc zawarto dokumentu, napotyka na znaczniki i
inicjalizuje rozmaite czynnoci, ktrych implementacja naley do programisty. Programista
musi wic zainicjalizowa procedury obsugi (ang. handlers) konkretnych znacznikw.
Podstawow rnic pomidzy DOM a SAX jest sposb odczytywania dokumentw. SAX
czyta dokumenty stosunkowo maymi porcjami, parsuje je i w razie koniecznoci
wywouje procedury obsugi. SAX nadaje si wic do analizy bardzo duych dokumentw
XML.
DOM jednorazowo wczytuje cay dokument do pamici, w ktrej tworzy drzewo gazi.
Oznacza to due obcienie dla pamici w przypadku wikszych dokumentw. DOM nadaje
si wic do obsugi maych oraz rednich plikw, a SAX jest wrcz stworzony do odczytu
duych dokumentw. SAX ma jednak du wad nie umoliwia zapisywania dokumentw
XML, lecz suy jedynie do ich odczytu.
Korzystanie z System.XML
W prezentowanych tutaj przykadach bd posugiwa si podzespoem System.XML.dll, w
ktrym znajduje si przestrze nazw System.XML dodajmy j wic do naszego programu.
Gwn klas, ktr bdziemy stosowa do adowania i zapisywania dokumentu, jest
System.XML.XMLDocument, jednak przestrze System.XML definiuje o wiele wicej klas
okrelajcych np. wze (ang. node) XML.
catch (XmlException e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
}
}
Jak wida, konstrukcja jest prosta. Metoda Load() aduje plik XML do pamici. Warto take
wiedzie o metodzie Save(), ktra zapisuje do podanego pliku zawarto edytowanego
dokumentu XML.
Warto te zwrci uwag na obsug wyjtku XMLException. Klasa XMLDocument ma
wbudowany mechanizm walidacji, ktry sprawdza poprawno dokumentu XML. Dziki
temu w razie jakich nieprawidowoci w kodzie XML zostanie wywietlony komunikat
bdu.
Do odczytania zawartoci pliku XML mona take uy metody LoadXML.
nnerText);
NewXmlNode.Nodes.Add("Skadniki: " +
XmlDoc.GetElementsByTagName("skladnik").Item(i).InnerText);
NewXmlNode.Nodes.Add("Cena: " +
XmlDoc.GetElementsByTagName("cena").Item(i).InnerText);
}
}
catch (XmlException ex)
{
MessageBox.Show(ex.Message);
}
}
Na samym pocztku dziaania programu naleao okreli, ile elementw pizza zawiera nasz
plik XML. Skorzystaem przy tym z waciwoci Count klasy XMLNodeList:
XmlDoc.GetElementsByTagName("pizza").Count
</pizza>
<pizza>
<nazwa>Peperoni</nazwa>
<cena>20,00</cena>
<skladnik>peperoni, cebula</skladnik>
</pizza>
</jedzenie>
<cena rozmiar="rednia">20,00</cena>
<cena rozmiar="dua">27,00</cena>
<skladnik>ser, szynka, ananasy</skladnik>
</pizza>
<pizza>
<nazwa>Peperoni</nazwa>
<cena rozmiar="maa">15,50</cena>
<cena rozmiar="rednia">19,50</cena>
<cena rozmiar="dua">26,50</cena>
<skladnik>peperoni, cebula</skladnik>
</pizza>
<pizza>
<nazwa>Barbacue</nazwa>
<cena rozmiar="maa">19,10</cena>
<cena rozmiar="rednia">26,00</cena>
<cena rozmiar="dua">31,50</cena>
<skladnik>bekon, kawaki kurczaka, cebula, czerwona
papryka, ser</skladnik>
</pizza>
</jedzenie>
nnerText);
NewXmlNode.Nodes.Add("Skadniki: " +
XmlDoc.GetElementsByTagName("skladnik").Item(i).InnerText);
PriceNode = NewXmlNode.Nodes.Add("Ceny");
for (int j = i * 3; j <= (i * 3) + 2; j++)
{
Node =
XmlDoc.GetElementsByTagName("cena").Item(j);
PriceNode.Nodes.Add(
Node.Attributes.GetNamedItem("rozmiar").InnerText + ": " +
Node.InnerText
);
}
}
}
catch (XmlException ex)
{
MessageBox.Show(ex.Message);
}
}
Pocztek tej metody jest identyczny jak tej z listingu 13.5. Problemy zaczynaj si pniej.
Zdefiniowano 3 rne ceny dla kadej pizzy, kolejna ptla for musi przetworzy trzy kolejne
wzy, odczytujc ich atrybuty. Skoro liczba wzw <cena> w wle <pizza> jest znana,
wyliczenie liczby iteracji opiera si na rachunku matematycznym:
for (int j = i * 3; j <= (i * 3) + 2; j++)
W celu skrcenia zapisu do zmiennej Node jest przypisywany rezultat dziaania funkcji
GetElementsByTagName("cena").Item(j). Nastpnie w celu pobrania wartoci atrybutu
naley skorzysta z metody GetNamedItem():
PriceNode.Nodes.Add(Node.Attributes.GetNamedItem("rozmiar").In
nerText + ": " + Node.InnerText);
Klasa Reader posiada metod Read() (ktra zwraca warto typu Boolean), dziki ktrej
ptla moe po kolei odczyta wszystkie elementy:
using System;
using System.Xml;
namespace XmlApp
{
class Program
{
static void Main(string[] args)
{
XmlTextReader Reader = new
XmlTextReader("C:\\demo.xml");
while (Reader.Read())
{
Console.WriteLine(Reader.Name + ' ' +
Reader.Value);
}
Reader.Close();
Console.Read();
}
}
}
Waciwo Name zwraca nazw elementu, a Value jego warto. Takie rozwizanie ma
pewne wady, poniewa klasa XmlTextReader nie rozpoznaje typw znacznikw, czego
rezultatem moe by odczytanie przez ni wszystkich znacznikw rwnie tych
zamykajcych. Aby temu zapobiec, omawiana klasa wykorzystuje waciwo NodeType,
dziki ktrej mona odczyta jedynie interesujce nas elementy. Waciwo NodeType moe
przybra nastpujce wartoci:
Tworzenie pliku
Do tej pory zajmowalimy si jedynie odczytem wzw i atrybutw. Nie wspomniaem w
ogle o tworzeniu plikw XML, a taka moliwo w modelu DOM istnieje. Do tworzenia
plikw XML, w tym wzw oraz atrybutw, suy klasa XMLTextWriter.
Podczas tworzenia klasy XMLTextWriter w parametrze konstruktora naley poda ciek do
pliku, ktry ma by utworzony w wyniku dziaania programu. Utworzenie i zwolnienie klasy
w najprostszym przypadku wyglda nastpujco:
XmlTextWriter Writer = new XmlTextWriter("C:\\output.xml",
null);
Powyszy program utworzy prosty dokument XML, majcy jeden wze gwny root:
<?xml version="1.0"?><root />
Sprawa jest prosta: wze gwny pizze zawiera jeden element pizza. Do wygenerowania
takiego tekstu jest wymagany nastpujcy fragment kodu C#:
using System;
using System.Xml;
namespace XmlApp
{
class Program
{
static void Main(string[] args)
{
XmlTextWriter Writer = new
XmlTextWriter("C:\\output.xml", null);
Writer.Formatting = Formatting.Indented;
Writer.Indentation = 4;
Writer.WriteStartDocument();
Writer.WriteStartElement("pizze");
Writer.WriteStartElement("pizza");
Writer.WriteString("Hawajska");
Writer.WriteEndElement();
Writer.WriteEndElement();
Writer.WriteEndDocument();
Writer.Close();
}
}
}
Jak wida, rezultat dziaania takiego kodu zgadza si z tym, co zostao wygenerowane:
wcicia w kodzie maj wielko czterech spacji, a wze gwny nosi nazw pizze zgodnie
z tym, co zaprogramowalimy.
Dane, ktre maj znale si w znaczniku pizza, s okrelone jako parametr metody
WriteString().
using System;
using System.Xml;
namespace XmlApp
{
class Program
{
static void Main(string[] args)
{
String[,] PizzaArray =
{
{ "Peperoni", "14,50" },
{ "Hawajska", "15,00" }
};
XmlTextWriter Writer = new
XmlTextWriter("C:\\output.xml", null);
Writer.Formatting = Formatting.Indented;
Writer.Indentation = 4;
Writer.WriteStartDocument();
Writer.WriteStartElement("pizze");
for (int i = 0; i < PizzaArray.GetLength(1); i++)
{
Writer.WriteStartElement("pizza");
Writer.WriteStartElement("nazwa");
Writer.WriteString(PizzaArray[i, 0]);
Writer.WriteEndElement();
Writer.WriteStartElement("cena");
Writer.WriteString(PizzaArray[i, 1]);
Writer.WriteEndElement();
Writer.WriteEndElement();
}
Writer.WriteEndElement();
Writer.WriteEndDocument();
Writer.Close();
}
}
}
<cena>14</cena>
</pizza>
<pizza>
<nazwa>Hawajska</nazwa>
<cena>15,50</cena>
</pizza>
</pizze>
Dodawanie atrybutw
Zmodyfikujmy aplikacj, tak aby wraz z elementem cena by dodawany atrybut o nazwie
rozmiar. Oto zmodyfikowany fragment poprzedniego programu:
Writer.WriteStartElement("cena");
Writer.WriteAttributeString("rozmiar", "maa");
Writer.WriteString(PizzaArray[i, 1]);
Writer.WriteEndElement();
Writer.WriteEndElement();
Dziki takiemu zabiegowi do wza cena zostanie doklejony atrybut rozmiar o wartoci
maa:
<cena rozmiar="maa">15,50</cena>
Zarwno klasa XmlTextWriter, jak i XmlTextReader nie korzystaj z modelu DOM. Zostay
one zaprojektowane w taki sposb, aby dostarcza mechanizmy odczytu oraz zapisu przy jak
najmniejszym nakadzie zasobw.
Dokumentacja XML
XML jest bardzo uniwersalnym formatem. rodowisko Visual C# Express Edition i jzyk C#
umoliwiaj tworzenie komentarzy XML. Su one do opisywania kodu, klas, metod, pl
czy waciwoci. Na podstawie takich komentarzy kompilator C# umoliwia wygenerowanie
dokumentacji w formacie XML. Oto przykad uycia komentarzy XML:
/// <summary>
/// Klasa MyFoo (przykad)
/// </summary>
class MyFoo
{
/// <summary>
/// Pomocnicze pole
/// </summary>
static string MyBar;
/// <summary>
/// Przykadowa metoda Foo
/// </summary>
/// <param name="Bar">Wany parametr</param>
static void Foo(string Bar)
{
}
/// <summary>
/// Waciwo Bar
/// </summary>
static string Bar
{
get
{
return MyBar;
}
}
}
Jak widzisz, element <summary> okrela opis danego elementu, aczkolwiek moliwe jest
rwnie opisywanie parametrw metod (element <param>).
Tworzenie komentarzy XML w rodowisku Visual C# Express Edition jest bardzo proste i
przyjemne. Wystarczy nad danym elementem trzykrotnie wcisn przycisk /, aby rodowisko
wygenerowao ca otoczk dla komentarza (czyli znaczniki XML).
Z menu Project wybierz pozycj Properties. Wybierz zakadk Build i zaznacz pozycj XML
documentation file. W polu tekstowym moesz okreli ciek, pod ktr kompilator bdzie
zapisywa dokumentacj XML. Kompilujc w ten sposb projekt, spowodujemy utworzenie
dokumentacji kodu, np.:
<?xml version="1.0"?>
<doc>
<assembly>
<name>FooApp</name>
</assembly>
<members>
<member name="T:FooApp.MyFoo">
<summary>
Klasa MyFoo (przykad)
</summary>
</member>
<member name="F:FooApp.MyFoo.MyBar">
<summary>
Pomocnicze pole
</summary>
</member>
<member name="M:FooApp.MyFoo.Foo(System.String)">
<summary>
Przykadowa metoda Foo
</summary>
<param name="Bar">Wany parametr</param>
</member>
<member name="P:FooApp.MyFoo.Bar">
<summary>
Waciwo Bar
</summary>
</member>
</members>
</doc>
Podsumowanie
XML jest bez wtpienia technologi rewolucyjn. Jej du zalet jest niezaleno i
moliwo atwego przeksztacenia dokumentw do innego formatu danych. Dziki licznym
parserom w do prosty sposb mona odczyta zawarto dokumentu XML, a nawet go
zmodyfikowa. Zreszt modyfikowanie pliku XML jest moliwe nawet za pomoc zwykego
edytora tekstu.
Rozdzia ten stanowi jedynie wprowadzenie do technologii XML, jest to bowiem bardzo
obszerny temat. Platforma .NET udostpnia ponadto wiele klas, o ktrych tu nie
wspominaem, a ktre take pomagaj w tworzeniu i obrbce dokumentw XML. Jeeli jeste
zainteresowany transformacj dokumentw XML do formatu HTML, powiniene si
zainteresowa specyfikacj XSL.