You are on page 1of 613

PRIJEVOD ETVRTOG IZDANJA

Programiranje C#

Jesse Liberty

Prijevod:
Ana Anti
Marko Grgi

E x p e rt
IT

0 REILLY
Programiranje C#, prijevod
etvrtog izdanja
Jesse Liberty

Nakladnik: Dobar Plan,


Zagreb
Za nakladnika: Tomislav
Komik
Urednik: Aleksandar
Dragosavljevi Prilagodba
omota: Maja Halapija
Tisak: Denona,
Zagreb

Copyright 2005 Dobar Plan, Zagreb. Autorizirani


prijevod engleskog izdanja knjige Progratntning C# ,
Fourth Edition 0 Reilly Media, Ine. Ovaj prijevod je
objavljen i prodaje se s dozvolom 0 Reilly Media, Ine.
koja je vlasnik svih prava za objavljivanje i prodaju.
Iako je tijekom prijevoda i pripreme ove knjige za tisak
uloen veliki trud kako bi se izbjegle pogreke, autor i
izdava ne preuzimaju odgovornost za pogreke ili
propuste niti za tetu koja bi mogla nastati upotrebom
informacija iz ove knjige.

CIP - Katalogizacija u
publikaciji Nacionalna i
sveuilina knjinica -
Zagreb

UDK 004.43 C#(035)

LIBERTY, Jesse
Programiranje C#
/ Jesse
liberty ; prijevod Ana Anti, Marko
Grgi.
- Zagreb : Dobar Plan, 2005.

Prijevod djela: Programming


C#, 4th ed. - Kazalo.

I5BN 953-95207-1-1
I. C# programski jezik --
Prirunik 451124026
L J

ISBN 953-95207-1-1 3 2 1
Sadraj

Predgovor ........................................................................ ix

Dio I JezikC#

1. ProgramskijezikC#i.NETkostur .......................................... 3
Platforma .NET 3
.NET kostur 4
Prevoenje i MSIL 6
Jezik C# 6
2. Poetak:HelloVVorld" ............................................................. 9
Klase, objekti i tipovi 9
Razvoj programa Hello World 16
Koritenje programa za ispravljanje pogreaka 20
3. Osnove programskog jezika C # ............................................... 23
Tipovi 23
Varijable i konstante 28
Izrazi 35
Bijeli prostor 36
Iskazi 37
Operatori 52
Naredbe za pretprocesor 60
4. Klase i objekti ............................................................... 64
Definiranje klasa 65
Stvaranje objekata 70
Koritenje statikih lanova 77
Unitavanje objekata 81
Prosljeivanje parametara 84
Preoptereivanje metoda i konstruktora 90
Uahurivanje podataka sa svojstvima 93
Polja readonly 97
5. Nasljeivanje i polimorfizam ............................................. 99
Specijalizacija i generalizacija 99
Nasljeivanje 102
Apstraktne klase 109
Korijen svih klasa: Object 113
Pakiranje i raspakiravanje tipova 115
Ugnjeivanje klasa 117
6. Preoptereivanje operatora ................................................120
Koritenje kljune rijei operator 120
Podrka ostalim .NET jezicima 121
Stvaranje korisnih operatora 121
Logiki parovi 122
Operator jednakosti 122
Operatori pretvaranja 123
7. Strukture ............................................................................129
Definiranje struktura 129
Stvaranje struktura 132
8. Suelja ........................................................................ 136
Definiranje i implementiranje suelja 137
Pristupanje metodama suelja 145
Premoivanje implementacija suelja 153
Eksplicitna implementacija suelja 156
9. Polja, indekseri i kolekcije .................................................... 166
Polja 166
Iskaz foreach 171
Inicijaliz'iranje elemenata polja 172
Indekseri 184
Suelja kolekcija 193
Ogranienja 196

vi | Sadraj
List<T> 201
Redovi 212
Stogovi 214
Rjenici 217
10. Nizovi i regularni izrazi ........................................................ 220
Nizovi 221
Regularni izrazi 235
11. Obrada izn im k i ........................................................................ 245
Izbacivanje i hvatanje iznimki 246
Objekti Exception 255
Prilagoene iznimke 258
Ponovno izbacivanje iznimki 261

12. Delegati i dogaaji ........................................................ 266


Delegati 267
Dogaaji 281
Koritenje anonimnih metoda 294
Dohvat vrijednosti iz vieodredinih delegata 295

Dio II Programiranje na jeziku C#

13. Programiranje Windows aplikacija ................................ 305


Izrada jednostavnog Windows Forms obrasca 306
Izrada Windows Forms aplikacija 310
XML komentari za dokumentaciju 334
14. PristuppodacimakrozADO.NET ....................................... 337
Relacijske baze podataka i SQL 337
Objektni model ADO.NET-a 341
Poetak rada s ADO.NET-om 343
Koritenje OLE DB upravljanih izvora podataka 346
Rad s kontrolama za podatke 349
15. Programiranje ASP.NET aplikacija i Web usluga 356
Razumijevanje Web Forms obrazaca 357
Izrada Web Forms obrasca 361
Dodavanje kontrola 365

Sadraj | vii
Povezivanje podataka 367
Web usluge 377
SOAP, WSDL i otkrivanje 377
Izrada Web usluge 378
Stvaranje posrednika 383
16. Sastavljanje u cjelinu .......................................................... 389
Ukupni dizajn 389
Izrada klijenta Web usluge 389
Prikazivanje rezultata 399
Pretraivanje po kategorijama 408

Dio III. CLRi.NET kostur

17. Sklopovi i rad s inaicama .................................................415


PE datoteke 415
Metapodaci 415
Sigurnosna granica 416
Manifesti 416
Sklopovi s vie modula 417
Privatni sklopovi 425
Dijeljeni sklopovi 425
Global Assembly Cache 428
Izrada dijeljenog sklopa 428
18. Atributi i refleksija ........................................................... 431
Atributi 431
Refleksija 438
19. Rasporeivanje i rad na daljinu ...................................... 448
Aplikacijske domene 449
Kontekst 458
Rad na daljinu 460
20. Dretve i sinkronizacija .................................................. 471
Dretve 472
Sinkronizacija 480
Stanja natjecanja i zastoji 489

viii | Sadraj
21. Tokovi p o d a ta k a ............................................... 491
Datoteke i mape 492
itanje i upisivanje podataka 502
Asinkroni ulaz i izlaz 510
Ulaz i izlaz podataka preko mree 514
Web tokovi 533
Serijalizacija 536
Izolirano spremite 544
22. .NET i COM programiranje .............................. 548
Uvoenje ActiveX kontrola 548
Uvoenje COM komponenata 556
Izvoenje .NET komponenata 564
P/Invoke 567
Pokazivai 570
Dodatak: C# kljune rijei.......................................................... 575

Kazalo ....................................................................................... 581

Sadraj | ix
Predgovor

Otprilike svakih deset godina pojavi se nova tehnologija koja promijeni na stav prema
razvoju aplikacija. Poetkom 1980-ih pojavio se operacioni sustav Unix koji se mogao
pokrenuti na stolnom raunalu, a tvrtka AT&T razvila je moan novi programski jezik
C. Poetkom 1990-ih pojavio se operativni sustav Windows i jezik C++. Svaka nova teh-
nologija predstavljala je veliku promjenu u pristupu programiranju. 2000. godine uslije-
dio je novi val koji je donio .NET i C# , a do kraja 2005. godine donjet e i .NET 2.0.
Microsoft se kladio 1 na .NET. Kada tako velika i utjecajna tvrtka potroi milijarde
dolara i promijeni cjelokupnu strukturu poduzea kako bi podravala novu platformu,
to ne ostaje nezapaeno meu programerima. .NET e ustvari promijeniti va stav
prema programiranju. Ukratko, radi se o novoj razvojnoj platformi ija je svrha olakati
objektno orijentiran razvoj za Internet. Programski jezik koji je izabran za ovu plat-
formu je C# , smiljen na temelju ranijeg iskustva sa jezicima C (odline performanse),
C++ (objektno orijentirana struktura), Java (sakupljanje otpada, visoka razina sigur-
nosti) i Visual Basic (brz razvoj), s namjerom stvaranja novog jezika koji e u potpuno-
sti odgovarati vieslojnim Web aplikacijama temeljenim na komponentama.
C# 2.0, jezik odabran za .NET 2005, sadri aurirane alate i novi snani razvojni oko-
li. On predstavlja najvee dostignue Microsoftovog ulaganja u istraivanje i razvoj.
Jednostavno je sjajan.

0 knjizi
Ova knjiga je prirunik za C# i pisanje .NET aplikacija s pomou jezika C# .
Ako ste programer koji se dobro slui jezikom C# 1.1 i samo elite saznati koje su nove
znaajke jezika C# 2 .2 , odloite ovu knjigu i kupite Visual C# 2005: A Developers
Notebook (u izdanju 0 Reilly Media, Ine.).
Ako, s druge strane, elite poboljati svoje vjetine programiranja ili se dobro sluite
drugim programskim jezicima kakvi su C++ ili Java, ak i ako se nikad niste upoznali
s drugim programskim jezikom, ovo je prava knjiga za vas.

xi
to vam treba za koritenje ove knjige
Od beta izdanja programa Visual Studio Whidbey (2005) pristup radovima tvrtke
Microsoft mnogo je jednostavniji. Dostupno vam je nekoliko mogunosti: ba kao to
Visual Studio ima mnogo inaica, tako su i demo inaice .NET-a 2 .0 i Visual Studija
2005 dostupne u razliitim oblicima:

Preuzimanje SDK
Beta SDK, zajedno s prevoditeljima za pokretanje u naredbenom redu, dokumen-
tacijom i drugim alatima, moe se besplatno preuzeti na adresi http://msdn.micm-
soft.com/netframework/downloads/updates/default.aspx. Datoteka za preuzimanje
nije velika, ali bit e vam potreban vlastiti program za ureivanje koda (moete
koristiti bilo koji, od Notepada do SharpDevelopera).

Express izdanja
Microsoft je izdao i ogoljene inaice platforme Visual Studio koje su male pa ih
lako moete preuzeti s Internetra i pokrenuti. Express inaice moete preuzeti
na adresi http://lab.msdn.microsoft.com/vs2005. Za veinu primjera iz ove knjige
koristite Visual C # Express. Za neke primjere bit e vam potreban Visual Web
Developer Express, a za neke ADO.NET primjere morat ete instalirati SQL Ser-
ver Express ili MSDE (Microsoft Data Engine).
Beta i Community Tech Preview
Pune inaice platforme Visual Studio 2005 dostupne su za preuzimanje u dva for-
mata: Community Technology Preview (CTP) koji djeluje pomalo nedovreno te
potpuno beta izdanje. U vrijeme pisanja ove knjige, CTP formati bili su dostupni
MSDN pretplatnicima na Visual Studio Professional, Standard i Team Systems.
Beta 1 inaica Visual Studija Professional takoer je dostupna MSDN pretplat-
nicima, a ostali plaaju samo trokove potarine. Vie informacija potraite na
http://lab.msdn.microsoft.com/vs2005lgetldefault.aspx.
Mono
Projekt Mono je razvojna platforma otvorenog izvornog koda koja se temelji na
.NET-u. Sponzorira ju tvrtka Novell, a moe se koristiti na operativnim sustavima
Linux, Mac OS X i drugima. Iako je trenutna inaica namijenjena za .NET 1.1,
moete ju instalirati s podrkom za neke .NET 2 .0 znaajke. Vie informacija
potraite na http://www.mono-project.com/about/index.html.
etvrto izdanje Programiranja C# moete koristiti za sve navedene okolie. Slike zaslona
mogu se, meutim, razlikovati jer je svaki od ovih okolia u drugoj inaici.

Kako je knjiga organizirana


Prvi dio knjige bavi se pojedinostima jezika, u drugom dijelu moete nauiti kako se
piu .NET programi, a u treem je dijelu opisan nain koritenja C# .NET Common
Language Runtimea i Framevvork Class Libraryja.

xii | Predgovor
Dio I, Jezik C#
Poglavlje 1, Programski jezik C# i.NET kostur predstavlja uvod ujezik C# i platformu
.NET.

Poglavlje 2, Poetak: Hello World opisuje jednostavan program kao uvod u sljedea
poglavlja te vam predstavlja Visual Studio IDE i razne koncepte jezika C# .
U Poglavlju 3, Osnove programskog jezika C# , opisane su osnove jezika, od ugraenih
tipova podataka do kljunih rijei.

Klase definiraju nove tipove i programeru omoguuju proirivanje jezika kako bi bolje
mogao oblikovati problem koji pokuava rijeiti. U Poglavlju 4, Klase i objekti, opisane
su komponente koje ine bit jezika C# .

Klase mogu biti kompleksna predoenja i apstrakcije pojava iz stvarnog svijeta. Pogla-
vlje 5, Nasljeivanje i polimorfizam, bavi se odnosima izmeu klasa.
U Poglavlju 6, Preoptereivanje operatora, moete nauiti kako svojim korisniki defi-
niranim tipovima moete dodati operatore.
Poglavlja 7 i 8 su uvod u Strukture i Suelja, kategorije srodne klasama. Strukture su
jednostavni objekti koji su neto ogranieniji od klasa, a manje su zahtjevni za opera-
tivni sustav i memoriju. Suelja su poput ugovora - opisuju kako e klasa funkcionirati
tako da drugi programeri mogu koristiti vae objekte na dobro definiran nain.
Objektno orijentirani programi mogu stvoriti veliki broj razliitih objekata. esto ih je
prikladno grupirati i s njima zajedniki raditi, a C# prua opsenu podrku za kolek-
cije. U Poglavlju 9, Polja, indekseri i kolekcije, opisane su klase kolekcija koje prua
Framevvork Class Library, nove generike kolekcije i nain na koji programeri mogu
stvarati vlastite tipove kolekcija s pomou generika.
Poglavlje 10, Nizovi i regularni izrazi, opisuje nain upotrebe jezika C# za rad s tekstu-
alnim nizovima i regularnim izrazima. Veina Windows i Web programa komunicira
s korisnikom, a nizovi imaju kljunu ulogu u korisnikom suelju.
U Poglavlju 11, Obrada iznimki, objanjeni su postupci obrade iznimki koji pruaju
objektno orijentiran mehanizam za rjeavanje moguih hitnih sluajeva".
I Windows i Web aplikacije reagiraju na dogaaje. U jeziku C# dogaaji su vani
lanovi jezika. Poglavlje 12, Delegati i dogaaji posveeno je nainu obrade dogaaja i
koritenju delegata (objektno orijentiranih mehanizama povratnih poziva koji su
sigurni za tip) kao podrke za obradu dogaaja.

Dio II, Programiranje na jeziku C#


Drugi se dio bavi pisanjem .NET programa: samostalnih aplikacija s
Windows Forms i Web aplikacija s Web Forms. Uz to, u drugom je dijelu opisan rad
s bazama podataka i nain stvaranja Web usluga.

Predgovor | xiii
Na vrhu .NET infrastrukture nalazi se apstrakcija operativnog sustava ija je svrha
da olaka razvoj objektno orijentiranog softvera. U ovaj gornji sloj pripadaju i ASP.
NET i Windows Forms. ASP.NET ukljuuje Web Forms za brzi razvoj Web aplikacija
i Web usluge za izradu Web objekata bez korisnikog suelja. Web usluga je distribui-
rana aplikacija koja prua funkcionalnost preko standardnih Web protokola, najee
XML-a i HTTP-a.
C# prua model brzog razvoja aplikacija koji je dosad postojao samo u jeziku Visual
Basic. Poglavlje 13, Programiranje Windows aplikacija, opisuje nain koritenja RAD
modela za stvaranje Windows programa profesionalne kvalitete s pomou razvojnog
okolia Windows Forms.
Bez obzira jesu li namijenjene za Web ili klasini operacioni sustav, veina aplikacija se
temelji na obradi velike koliine podataka. Poglavlje 14, Pristup podacima kroz ADO.
NET, objanjava ADO.NET sloj u .NET kosturu i nain interakcije s Microsoft SQL
posluiteljem i ostalim izvorima podataka.
Predmet poglavlja 15, Programiranje ASP.NET aplikacija i Web usluga, su dva dijela
ASP.NET tehnologije: Web Forms i Web Services.
U Poglavlju 16, Sastavljanje u cjelinu, kombinira se velik broj vjetina obraenih u dru-
gom dijelu kako biste nauili razvijati skup integriranih aplikacija.

Dio III,CLRi.NETkostur
Izvedbeni okoli je sredina u kojoj se izvode programi. Common Language Runtime
(CLR) je sr platforme .NET. On sadri sustav tipizacije podataka koji se primjenjuje
na cijeloj platformi i koji je zajedniki svim jezicima koje.NET podrava. CLR je odgo-
voran za postupke poput upravljanja memorijom i brojanja referenci objekata.
Druga kljuna znaajka .NET CLR-a je sakupljanje otpada. Za razliku od tradicional-
nog C/C++ programiranja, u jeziku C# programer nije odgovoran za unitavanje obje-
kata. Beskrajni sati provedeni u traenju objekata koji se vie ne koriste sada su stvar
prolosti. Kada se objekti vie ne koriste, CLR e za vama obaviti ienje. Metoda
sakupljanja otpada u CLR-u provjerava ima li u gomili objekata bez referenci i oslo-
baa memoriju koju ti objekti zauzimaju.
Platforma .NET i biblioteka klasa proiruju se prema viim razinama sve do platforme
srednje razine na kojoj se nalazi infrastruktura klasa za podrku, zajedno s tipovima
za komunikciju izmeu procesa, XM L, rad s dretvama, ulaz i izlaz podataka, zatitu,
dijagnostiku itd. Srednji sloj sadri i komponente za pristup podacima koje se skupno
nazivaju ADO.NET.
U treem dijelu ove knjige obraen je odnos izmeu jezika C# , CLR-a i biblioteke
klasa kostura.

xiv | Predgovor
U Poglavlju 17, Sklopovi i rad s inaicama, opisane su razlike izmeu privatnih i javnih
sklopova te nain stvaranja i rada sa sklopovima. Na platformi .NET sklop je skup
datoteka koji korisniku izgleda kao jedinstvena DLL ili izvedbena datoteka. Sklop je
osnovna jedinica za ponovnu upotrebu, rad s inaicama, zatitu i primjenu.
.NET sklopovi sadre iscrpne metapodatke o klasama, metodama, svojstvima, doga-
ajima i ostalim elementima. Oni se prevode u program i automatski dohvaaju kroz
refleksiju. U Poglavlju 18, Atributi i refleksija, objanjeno je dodavanje metapodataka
u kod, stvaranje prilagoenih atributa i pristup metapodacima s pomou refleksije.
U ovom poglavlju moete pronai i informacije o dinamikom pozivanju pri kojem se
metode pozivaju sa kasnim povezivanjem.

.NET kostur projektiran je kako bi pruio podrku distribuiranim aplikacijama i apli-


kacijama temeljenim na Webu. Komponente napisane na jeziku C# mogu se nalaziti
unutar drugih procesa na istom stroju ili na drugim umreenim strojevima ili na Inter-
netu. Rasporeivanje je tehnika interakcije s objektima koji su u biti negdje drugdje,
a rad na daljinu predstavlja tehniku komunikacije s takvim objektima. Te tehnike
objanjene su u Poglavlju 19, Rasporeivanje i rad na daljinu.
FCL prua opsenu podrku za asinkroni ulaz i izlaz podataka i druge klase koje eks-
plicitnu manipulaciju dretvama ine nepotrebnom. C# , meutim, prua podrku za
dretve i sinkronizaciju, o emu govori poglavlje 20.

U poglavlju 21 objanjeni su Tokovi podataka, mehanizam koji ne slui samo za inter-


akciju s korisnikom, ve i za uzimanje podataka s Interneta. Ovo poglavlje sadri i
potpuni pregled C# podrke za serijalizaciju - mogunost zapisivanja objekata na disk
i njihovog ponovnog itanja.
Poglavlje 22, .NET i COM programiranje, bavi se interoperabilnou, mogunou
interakcije sa COM komponentama koje su stvorene izvan upravljanog okolia .NET
kostura. Mogue je pozivanje komponenata iz C# aplikacija u COM i pozivanje COM
komponenata u C# . Ti su postupci objanjeni u poglavlju 22.
Na kraju knjige nalazi se dodatak A s kljunim rijeima jezika C# .

Kome je knjiga namijenjena


etvrto izdanje knjige Programiranje C# napisano je za programere koji ele razvijati
aplikacije za platformu .NET. Mnogi od vas vjerojatno ve imaju iskustva u programi-
ranju s jezicima C++, Java ili Visual Basic (VB). Drugi itatelji moda imaju iskustva s
drugim programerskim jezicima, dok neki moda uope nemaju programerskog isku-
stva, ali su radili s HTML-om i ostalim Web tehnologijama. Ova knjiga namijenjena je
svima vama, no moda e vam biti neto tea za razumijevanje ako nemate nikakvog
iskustva u programiranju.

Predgovor | xv
Ako ste prethodno radili s jezicima C, C++, VB 6 ili Java, u sljedeim odjeljcima
moete pronai usporedbu osnovnih svojstava tih jezika s jezikom C# . U cijeloj knjizi
postoje napomene posebno napisane za vas.

C# 2.0 u usporedbi sa C# 1.1


U jeziku C# , razvojnom okoliu i .NET kosturu mnogo toga se promijenilo od inaice
1.1. Sve su uinjene kako bi se smanjila koliina koda koji morate napisati te kako biste
se laki fokusirali na izradu robusnih aplikacija.
Ova knjiga obuhvaa i te promjene, no ona nije napisana kao vodi za programera koji
se bavi jezikom C# 1.1 i samo eli saznati vie o promjenama koje su uvedene u inaici
2.0. Usprkos tome. takve promjene nastojat u posebno istaknuti u daljnjem tekstu.

C# u usporedbi s Visual Baskom .NET


Osnovna premisa .NET kostura je da su svi jezici jednaki. No, sukladno rijeima Geor-
gea Orvvella, neki jezici sujednakiji od drugih. C# je izvrstan jezik za .NET razvoj. Pri-
mijetit ete kako je rije o svestranom, robusnom i dobro projektiranom jeziku. C# je
i jezik koji se trenutno najvie koristi u lancima i vodiima za .NET programiranje.
Mogue je da e velik broj VB 6 programera radije izabrati svladavanje jezika C# nego
nadogradnju svog znanja naVB.N ET. Prijelaz s VB 6 na VB.NET je vjerojatno jednako
teak kao i prijelaz s VB6 na C# , a treba uzeti u obzir i injenicu da programeri koji
rade u jeziku C# , koliko god to bilo nepravedno, imaju vee mogunosti zarade od
VB programera. VB programeri nikad zapravo nisu dobili panju i naknadu koju zaslu-
uju, a C# im prua jedinstvenu mogunost za potencijalno unosan prijelaz.
U svakom sluaju, ako imate iskustva s VB jezikom, odabrali ste pravu knjigu jer ovdje
moete pronai upute koje e vam olakati prijelaz s jednog jezika na drugi.

C# u usporedbi s Javom
Java programeri e na C# vjerojatno gledati s mjeavinom osjeaja strepnje, veselja i
ogorenosti. Postoji miljenje da je C# kopija" jezika Java. Ne bih htio komentirati
vjerski rat izmeu Microsofta i onih koji mu nisu naklonjeni. Jedino u napomenuti
kako je jezik C# mnogo toga nauio od jezika Java, ali je i Java mnogo toga nauila
od C++, koji je svoju sintaksu preuzeo od jezika C, a C je izgraen po uzoru na starije
programske jezike. Svi mi stojimo na ramenima divova.
C# Java programerima nudi jednostavan prijelaz: sintaksa je vrlo slina, a semantika
je poznata i jednostavna. Java programeri e se za uinkovito koritenje jezika C#
vjerojatno morati usredotoiti na razlike izmeu ta dva jezika. U knjizi sam te razlike
pokuao dodatno istaknuti (pogledajte napomene za Java programere u poglavljima).

xvi | Predgovor
C# u usporedbi sjezikom C ++
Programiranje na jezicima C i C++ za platformu .NET je mogue, no nije jednostavno
niti prirodno. Iskreno, deset sam godina radio kao C++ programer, napisao sam dese-
tak knjiga o njemu i radije bih otiao zubaru nego radio s upravljanim C++. Moda
se radi samo o tome da je C# mnogo jednostavniji. U svakom sluaju, nakon to sam
poeo koristiti C# , vie se nisam osvrtao.

Meutim, budite paljivi: postoji mnogo malih zamki koje sam nastojao vidljivo oznaiti.

Pravila oznaavanja koritena u ovoj knjizi


U knjizi se koriste sljedee tipografske konvencije:
Kurziv se koristi za:
Putanje te nazive datoteka i programa
Internet adrese, kao to su nazivi domena i URL adrese
Nove termine na mjestima na kojima su definirani

Pismo iste irine se koristi za:

Redove naredbi i opcije koje treba unijeti doslovno


Nazive i kljune rijei u primjerima programa, ukljuujui nazive metoda, varija-
bli i klasa

Pismo is t e irine u kurzivu se koristi za:


Zamjenjive elemente, poput varijabli ili neobaveznih elemenata, unutar redova
sintakse ili koda

Podebljano pismo iste irine se koristi za:


Isticanje unutar programskog koda

Posebnu panju obratite na napomene koje su od teksta odvojene ovim sliicama:

Ovo je savjet. On sadri korisne dodatne informacije o odreenoj


temi.

Ovo je upozorenje. Ono vam pomae u rjeavanju i izbjegavanju


problema.

Predgovor | xvii
Podrka
Kao autor, prua m stalnu podrku za svoje knjige na Web stranici ija je adresa:
http://www.LibertyAssociates.com
Na toj stranici moete preuzeti i izvorni kod za sve primjere iz knjige Programiranje
C#. Tamo ete moi pristupiti forumu o knjizi s posebnim dijelom za pitanja u vezi s
jezikom C#. Prije postavlja nja pitanja proitajte FAQ i datoteke s ispravka ma. Ako i
na kon to proitate te dokumente jo uvijek imate pitanje, postavite ga na forumu.
Najuinkovitiji nain za dobiva nje pomoi je postavlja nje vrlo preciznog pitanja ili
ak pisa nje kratkog progra ma koji ilustrira podruje koje vas zanima ili zabrinjava.
Korisno je iprovjeriti razliite diskusijske grupe i forume na Internetu. Microsof t nudi
irok raspon diskusijskih grupa, a DevelopMentor (http://discuss.develo p.com) odrava
iznimno korisan e-mail foru m.

Zahvale
Prije nego to nastavim, moram se posebno zahvaliti lanu Griffithsu koji je temeljito
recenzirao tekst i dao strune savjete. Ubraja se u skupinu ugodnijih i pametnijih ljudi
s kojima suraujem.
Ovo je etvrto izdanje knjige i previe mi je prijatelja i itatelja pomoglo unaprijediti ju
da bi ih mogao sve nabrojati. Ali, posebno mora m spomenuti sljedee osobe: Donald
Xie, Dan Hurwitz, Seth Weiss, Sue Lynch, Cliff Gerald, Tom Petr, Jim Culbert, Mike
Woodring, Eric Gu nnerson, Rob Howard, Piet Obermeyer, Jonathan Hawkins, Peter
Drayton, Braci Merrill, Ben Albaha ri, Susan Warren, Bram Bischof i Kent Quirk.
John Osborn omoguio mi je suradnju s izdavakom kuom O'Reilly, za to u mu
uvijek biti zahvalan. Valerie Quercia, Claire Cloutier i Tatiana Diaz napravile su velik
posao na prethodnim inaica ma, a nadogradnju na C# 2.0 predvodio je Brian Jepson.
Rob Romano je izradio brojne nove ilustracije i poboljao stare. Tim O'Reilly je pruio
podrku i resurse i za to sam mu zahvalan.
Javili su mi se m nogi itatelji i obavijestili nas o pravopisnim i manjim pogrekama
u prva tri izda nja. Vrlo smo im zahvalni, a posebno bi htio spomenuti sljedee itate-
lje: Peter Adams, Sol Bick, Brian Cassel, Steve Charbonneau, Ronal Chu, John Cor-
ner, Duane Corpe, Kevin Coupland, Randy Eastwood, Glen Fischer, Larry Fix, Andy
Gaskall, Dave Fowler, Vojimir Golem, David Kindred, Steve Kirk, Bob Kilne, Theron
LaBounty, Arnn Landy, Jeremy Lin, Chris Linton, Mark Melhado, Harry Martyros-
sian, Jason Mauss, Stephen Nelson, Harold Norris, Tim Noll, Mark Phillips, Marcus
Rahilly, Paul Reed, Christian Rodriguez, David Solum, Paul Schwartzburg, Erwing
Steininger, Fred Talmadge, Steve Thompson, Greg Torrance, Ted Volk, John Watson,
Walt White i Seen Sai Yang.

xviii I Programiranje .NET komponenata


Naporno smo radili kako bi u etvrtom izdanju ispravili sve takve pogreke. Pomno
smo pregledali knjigu kako bi se uvjerili da se nisu pojavile nove pogreke i da se sav
kod prevodi i ispravno pokree u Visual Studiju 2005. Ako pronaete kakvu pogreku,
provjerite datoteke s popisanim pogrekama na Web stranici (http://www.LibertyAs-
sociates.com) i ako ste otkrili novu pogreku slobodno poaljite poruku na adresu
jliberty@ libertyassociates.com.

Predgovor | xix
POGLAVLJE 1

Programski jezik C# i .NET kostur

C# 2 .0 ima ulogu jezika za .NET razvoj koji e biti jednostavan, siguran, moderan,
objektno orijentiran, usmjeren na Internet i visokih performansi. C# sada je potpun
jezik i na njega su primijenjena iskustva iz tri protekla desetljea. Kao to u djeci
moete vidjeti osobine njihovih roditelja, baka i djedova, u jeziku C# lako moete
zapaziti utjecaj jezika Java, C++, Visual Basic (VB) i drugih ali i lekcije nauene od
prve pojave jezika C# .
Glavni predmet ove knjige je jezik C# i njegova upotreba kao alata za programiranje
na platformi .NET, posebno u razvojnom okoliu Visual Studio.NET 2005 (potpuna
ili Express Edition inaica).

Mnogi programi u ovoj knjizi pisani su kao konzolne aplikacije (a ne


kao Windows ili Web aplikacije) kako bi se lake usredotoili na zna-
ajke jezika bez nepotrebnih pojedinosti o korisnikom suelju.

Ako koristite Mono ili neku drugu inaicu jezika C # koju nije pro-
izvela tvrtka Microsoft, vjerojatno ete uvidjeti kako svi programi iz
ove knjige ispravno funkcioniraju, iako su testirani samo na odobrenoj
Microsoft inaici.

Ovo je poglavlje uvod u jezik C# i platformu .NET, ukljuujui .NET kostur.

Platforma .NET
Kada je u srpnju 2000 . Microsoft najavio C# , njegova premijera bila je dio mnogo
veeg dogaaja: najave platforme .NET. C# 2.0 predstavlja zrelu fazu jezika i podu-
dara se s izdanjem nove generacije alata za .NET.
Platforma .NET je razvojni kostur koji daje novi API za usluge i API-je starijih inaica
operativnog sustava Windows, a istovremeno kombinira brojne razliite tehnologije
koje je Microsoft razvio tijekom kasnih 1990-ih. To ukljuuje komponentne usluge

3
COM+, posveenost XML-u i objektno orijentiranom dizajnu, podrku za nove pro-
tokole Web usluga kao to su SOAP, WSDL i UDDI te fokusiranje na Internet, a sve to
integrirano unutar arhitekture Distributed interNet Applications (DNA).
Microsoft je ogromne resurse posvetio razvoju platforme .NET i njoj srodnih tehno-
logija. Dosadanji rezultati tog rada uistinu su impresivni. Ako nita drugo, .NET je
ogromnog raspona. Platforma se sastoji od tri grupe proizvoda:
Skupa jezika, koji ukljuuje C# i VB, skupa razvojnih alata u kojem se nalazi i
Visual Studio .NET, iscrpna biblioteka klasa za izradu Web usluga te Web i Win-
dows aplikacija, kao i Common Language Runtime (CLR) za izvoenje objekata
izraenih unutar ovog kostura.
Dvije generacije posluitelja .NET Enterprise: ve objavljenih posluitelja i onih
koji trebaju biti objavljeni u sljedee dvije ili tri godine
Novih ureaja koji nisu kuna raunala, a podravaju .NET

.NET kostur
Microsoft .NET podrava ne samo neovisnost jezika, ve i integraciju jezika. To znai
da moete nasljeivati iz klasa, hvatati iznimke i iskoristiti prednosti polimorfizma u
razliitim jezicima. .NET kostur vam to omoguava kroz specifikaciju Common Type
System (CTS) koju sve .NET komponente moraju potivati. Na primjer, u .NET-u je
sve objekt odreene klase koja izvodi iz korijenske klase System.Object. CTS podrava
opi koncept klasa, suelja i delegata (koji podravaju povratne pozive).
Uz to, .NET sadri i Common Language Specification koja prua niz osnovnih pravila
potrebnih za integraciju jezika. CLS postavlja minimalne zahtjeve koje jezik mora ispu-
njavati da bi bio.NET jezik. Prevoditelji koji su u skladu s CLS-om stvaraju objekte
koji mogu meusobno djelovati. Frameivork Class Library (FCL) moe u potpunosti
koristiti bilo koji jezik koji je u skladu s CLS-om.
.NET kostur se nalazi iznad operativnog sustava, a to moe biti bilo koja inaica Win-
dowsa i sastoji se od razliitih komponenata koje trenutno ukljuuju:
Pet slubenih jezika: C# , VB, Visual C++, Visual J# i JScript.N ET
CLR, objektno orijentiranu platformu za Windows i Web razvoj zajedniku svim
navedenim jezicima
Razliite povezane biblioteke klasa koje se zajedniki nazivaju Framework Class
Library
Na slici 1-1 prikazane su komponente .NET kostura.

Zbog arhitekture CLR-a, operacioni sustav moe biti bilo koja inaica operacionog sustava Unix ili neki
skroz drugi operacioni sustav.

4 | Programiranje C#
.NET kostur

WebServices Web Forms l^^findovi^^onns

Podatkovne i XML klase


(AD0.NET, SQl, XSLJ, XPnth,XML, itd .)

Osnovne klase kostura


(Ulaz/izlaz, nizovi, mree, sigurnost, dretve, tekst, refleksija, kolekcije, itd.)

Common Language Runtime


(pronalaenje pogreaka, iznimke, provjera tipova, JITprevoditelji)

Windows

Slika 1-1. Arhitektura .NET kostura

Najvanija komponenta u .NET kosturu je CLR koja prua okruenje za izvoenje pro-
grama. CLR sadri virtualni stroj koji je u mnogo emu slian Java virtualnom stroju.
Na vioj razini CLR aktivira objekte, na njima izvodi sigurnosne provjere, pohranjuje ih
u memoriju, izvodi ih i odlae u otpad (Common Type System takoer je dio CLR-a).
Na slici 1-1 sloj iznad CLR-a je skup klasa kostura, iza kojeg slijedi dodatni sloj podat-
kovnih i X M L klasa, zatim jo jedan sloj klasa namijenjenih Web uslugama te Web
i Windows obrascima. Ove klase zajedniki ine FCL, jednu od najveih biblioteka
klasa u povijesti koja prua objektno orijentiran API za sve funkcionalnosti uahurene
u platformi .NET. Sa preko 4000 klasa, FCL omoguava brzi razvoj stolnih aplikacija,
klijent/posluitelj aplikacija te drugih Web usluga i apliakcija.
Skup osnovnih klasa, odnosno najnia razina FCL-a, slian je skupu klasa u jeziku
Java. Te klase podravaju ulaz i izlaz, rad s nizovima, upravljanje sustavom zatite,
mrenu komunikaciju, upravljanje dretvama, rad s tekstom, refleksiju, kolekcije itd.
Iznad te razine nalazi se sloj klasa koje proiruju osnovne klase tako da podravaju rad
s podacima i XML-om. Podatkovne klase podravaju rad s podacima koji se uvaju
u bazama podataka. Te klase ukljuuju Structured Query Language (SQL) klase koje
omoguavaju rad s podacima kroz standardno SQL suelje. .NET kostur podrava i
brojne druge klase koje omoguavaju rad s X M L podacima te njihovo pretraivanja i
prevoenje.

Kao proirenje osnovnih klasa kostura te podatkovnih i XML klasa, postoji i sloj klasa
koje su namijenjene izradi aplikacija s pomou tri razliite tehnologije: Web Services,
Web Forms i Windows Forms. Web Services ukljuuju velik broj klasa koje podra-
vaju razvoj jednostavnih distribuiranih komponenata koje e raditi ak i uz upotrebu
vatrozida i NAT posluitelja. Budui da Web Services kao temeljne komunikacijske

Poglavlje 1: Programski jezikC# i.NET kostur | 5


protokole koriste HTTP i SOAP, ove komponente u kiberprostoru podravaju metodu
Plug and Play.
Web Forms i Windows Forms omoguavaju primjenu tehnika za brzi razvoj aplikacija
za izradu Web i Windows aplikacija. Jednostavno povucite i ispustite kontrole na obra-
zac, dvaput pritisnite kontrolu i upiite kod koji e odgovoriti na dogaaj.
Iscrpniji opis .NET kostura potraite u knjizi .NET Erameu/ork Essentials u izdanju0
Reilly Media.

Prevoenje i MSI L
Na platformi .NET programi se ne prevode u izvedbene datoteke, oni se prevode u
sklopove koji sadre Microsoft Intermediate Language (MSIL) upute koje CLR zatim
pretvara u strojni kod i izvodi. MSIL (esto se koristi samo kratica IL) datoteke koje
proizvodi C# gotovo su identine IL datotekama koje proizvode ostali jezici podrani
u .NET platformi. Kljuna osobina CLR-a je da je on zajedniki: isti izvedbeni okoli
podrava razvoj na jezicima C# i VB.NET.
C# kod se prevodi u IL prilikom izrade projekta. IL se sprema u datoteku na disku.
Kada pokrenete program, IL se ponovno prevodi s prevoditeljem Just In Time (JIT).
Rezultat je strojni kod koji procesor moe izvesti.
Standardni se JIT prevoditelj pokree na zahtjev. Kada se metoda pozove, JIT prevodi-
telj analizira IL i proizvodi iznimno uinkovit strojni kod koji se vrlo brzo izvodi. Dok
se program izvodi prevoenje se provodi samo po potrebi, a jednom preveden kod se
sprema u privremenu memoriju da bi se mogao ponovno upotrijebiti. .NET aplikacije
sa svakim novim pokretanjem postaju sve bre jer se koristi ve prevedeni kod.
CLS znai da svi .NET jezici proizvode slian IL kod. To omoguava da se objektima
stvorenim u jednom jeziku moe pristupiti i iz drugog jezika. Stoga je mogue osnovnu
klasu stvoriti u jeziku VB.NET, a iz nje izvoditi u C# .

Jezik C#
Jezik C# je iznenaujue jednostavan. Sadri oko 80 kljunih rijei i desetak ugrae-
nih tipova, ali je vrlo izraajan kada je rije o implementaciji modernih programerskih
koncepata. C # prua svu podrku potrebnu za strukturirano, objektno orijentirano
programiranje temeljeno na komponentama kakvo biste i oekivali od modernog
jezika sagraenog na temeljima jezika C++ i Java. U inaici 2 .0 dodani su mu i mnogi
vani elementi koji su ranije nedostajali, poput generika i anonimnih metoda.

C++ programeri obratite panju: generici u C # su ekvivalent predlo-


cima. Iako su C # generici neto jednostavniji i uinkovitiji od C++
predloaka. Oni smanjuju koliinu koda ponovnom upotrebom zajed-
nikog koda tijekom izvoenja, ali je njihova fleksibilnost neto manja
od fleksibilnosti C++ predloaka.

6 | Programiranje C#
Jezik C# razvio je mali tim koji predvode dva istaknuta Microsoftova inenjera,
Anders Hejlsberg i Scott Wiltamuth. Hejlsberg je poznat i kao autor jezika Turbo
Pascal koji je bio popularan na kunim raunalima te kao voa tima koji je razvio
Borland Delphi, jedan od prvih uspjenih integriranih razvojnih okolia za klijent/
posluitelj programiranje.
U sreditu svakog objektno orijentiranog jezika je njegova podrka za definiranje klasa
i rad s klasama. Klase definiraju nove tipove, to omoguava proirivanje jezika kako
biste bolje modelirali problem koji pokuavate rijeiti. C# sadri kljune rijei za dekla-
riranje novih klasa, njihovih metoda i svojstava te za implementaciju uahurivanja,
nasljeivanja i polimorfizma - tri stupa objektno orijentiranog programiranja.
U C# , sve to je potrebno za deklariranje klase nalazi se u samoj deklaraciji. Defini-
cije C# klasa ne zahtijevaju posebne datoteke zaglavlja niti IDL (Interface definition
Language) datoteke. tovie, C # podrava novi XML stil dokumentiranja koji pojed-
nostavljuje izradu dokumentacije.

C# podrava i suelja, koja su poput ugovora s klasom o pruanju usluga koje suelje
zahtijeva. U C# klasa moe nasljeivati samo iz jedne roditeljske klase, ali moe imple-
mentirati vie suelja. Kada implementira suelje, C# klasa ustvari obeava pruiti
funkcionalnosti koje suelje zahtijeva.

C# prua i podrku za strukture (engl. structs), koncept ije se znaenje znaajno


promijenilo u odnosu na C++. U C# struktura je ogranien, jednostavan tip koji pri-
likom instanciranja manje optereuje operacioni sustav i memoriju od obine klase.
Struktura ne moe nasljeivati iz klase niti se iz nje moe nasljeivati, ali struktura
moe implementirati suelje.
C# prua potpunu podrku za delegate (engl. delegate) kako bi se omoguilo neiz-
ravno pozivanje metoda. U drugim jezicima, poput C++, moete pronai slinu funk-
cionalnost (npr. u pokazivaima na metode lanice), ali delegati su referentni tipovi
koji uahuravaju metode s posebnim potpisima i povratnim tipovima.

C# nudi komponentno orijentirane znaajke, poput svojstava, dogaaja i deklara-


tivnih konstrukcija (kao to su atributi). Komponentno orijentirano programiranje
podrava spremite metapodataka u kojem se nalazi kod za klasu. Metapodaci opi-
suju klasu, zajedno s njenim metodama i svojstvima, kao i njene sigurnosne potrebe i
druge atribute, poput mogunosti serijalizacije. Prevedena klasa je, dakle, samostojna
jedinica. Stoga, okruenju koje zna proitati metapodatke i kod klase nisu potrebne
druge informacije za koritenje klase. Ako se koristi C# i CLR, klasi se mogu dodati
prilagoeni metapodaci stvaranjem prilagoenih atributa. Isto tako, metapodaci klase
se mogu proitati s pomou CLR tipova koji podravaju refleksiju.

Prevoenjem koda zapravo stvarate sklop (engl. assembly). Sklop je kolekcija datoteka
koju programer vidi kao jednu dinamiki povezanu biblioteka (DLL) ili izvrnu dato-
teku (EXE). Na .NET platformi sklop je osnovna jedinica za ponovnu upotrebu, pra-
enje inaica, zatitu i razmjetaj.

Poglavlje 1: Programski jezike# i.NET kostur | 7


Naposljetku treba napomenuti kako C# daje podrku i za:
Izravan pristup memoriji s pomou pokazivaa u stilu jezika C++
Kljune rijei za odvajanje takvih operacija kao opasnih
Upozoravanje CLR sakupljaa otpada da ne sakuplja objekte na koje pokazuju
pokazivai dok se ne oslobode

8 | Programiranje C#
POGLAVLJE 2

Poetak: Hello World"

Knjige o programiranju se tradicionalno zapoinju programom Hello WorId.


U ovom poglavlju napisat emo, prevesti i pokrenuti jednostavan program Hello
World napisan u jeziku C# . Analiza ovog kratkog programa pokazat e najvanije
znaajke jezika C# .
U primjeru 2-1 pokazani su osnovni elementi vrlo jednostavnog C # programa.

Primjer 2-1. Jednostavan program Hello World ujeziku C#


class Hello
{
static void Main()
{
// Koristi objekt konzole sustava
System.Console.WriteLine("Hello World");
}
}
Prevoenjem i pokretanjem ovog koda u konzoli e se ispisati rijei Hello W orld.
Prije hego to ga prevedemo i pokrenemo, pogledajmo paljivije ovaj jednostavan
program.

Klase, objekti i tipovi


Bit objektno orijentiranog programiranje je stvaranje novih tipova. Tip (engl. type)
predstavlja neku stvar. Ponekad je ta stvar apstraktna, poput tablice podataka ili
dretve, a ponekad je neto opipljivija, poput gumba u prozoru. Tip definira opa svo-
jstva i ponaanje stvari.
Ako program u prozoru koristi tri instance tipa gumba - na primjer, gumbe OK,
Cancel i Help - svaki gumb moe imati svoju veliinu. Slino tome, svi gumbi e se
jednako ponaati, iako se nain na koji oni implementiraju ta ponaanja moe razliko-
vati. Dakle, pojedini gumbi se mogu razlikovati iako svi pripadaju istom tipu.

9
Kao to je to sluaj u veini objektno orijentiranih programskih jezika, tip je u C#
definiran klasom, a pojedinane instance te klase nazivaju se objektima. U kasnijim
poglavljima objanjeno je da u jeziku C # postoje i drugi tipovi koji nisu klase, na
primjer enumeratori (engl. enums), strukture (engl. structs) i delegati (engl. delegates),
ali zasad emo se usredotoiti na klase.
Program Hello World deklarira jedan tip: klasu Hello. Ako u C # elite definirati
tip, deklarirate ga kao klasu koristei kljunu rije cla ss, date mu naziv - u ovom
sluaju ,,Hello - i zatim definirate njegova svojstva i ponaanja. Definicije svojstava i
ponaanja klase u C # moraju se nalaziti u vitiastim zagradama ({}).

C++ programeri obratite panju: iza zatvorene zagrade ne stoji toka

Metode
Klasa ima svojstva i ponaanja. Ponaanja su definirana s metodama lanicama, a
svojstva su opisana u poglavlju 3.
Metoda )t funkcija u vlasnitvu klase. Zapravo, metode se ponekad i nazivaju funkcijama
lanicama. Metode lanice definiraju to klasa moe uiniti ili kako se ponaa. Meto-
dama se obino daju nazivi akcija koje izvode, na primjer WriteLine() ili AddNumbers().
Meutim, u ovdje navedenom primjeru metoda klase ima poseban naziv, Main(), koji ne
opisuje akciju, ali CLR-u govori kako je ovo glavna, odnosno prva metoda za klasu.

C++ programeri obratite panju: Main() se u C # pie velikim poetnim


slovom i mora biti lanica klase, a ne globalna lanica. Main() moe
vratiti in t ili void.

CLR prilikom pokretanja programa poziva Main(). Main() je ulazna toka programa i
svaki C# program je mora imati.'
Deklaracije metoda su zapravo ugovori izmeu autora i korisnika metode. Autor i
korisnik metode e vjerojatno biti isti programer, ali to ne mora uvijek biti sluaj. Moe
se dogoditi da jedan lan razvojnog tima napie metodu, a da je drugi koristi.
a*
Napomena za jav a programere: Main() je ulazna toka svakog C # pro-
grama, to je u odreenoj mjeri slino metodi run() u Java apletu ili
metodi Main() u Java programu.

Tehniki je u C# mogue imati nekoliko Main() metoda. U tom sluaju morate upotrijebiti preklopnik
/main u odzivniku da biste zadali klasu u kojoj se nalazi Main() metoda koja e sluiti kao ulazna toka za
program.

10 | Programiranje C#
Za deklariranje metode trebate zadati tip povratne vrijednosti i iza njega navesti ime.
U deklaracijama metoda obavezne su i zagrade, bez obzira na to prihvaa li metoda
parametre ili ne. Na primjer:
int myMethod(int sie)

deklarira metodu myMethod() koja ima jedan parametar - cjelobrojnu vrijednost koja
e se unutar metode koristit kao sie. Ova metoda vraa cjelobrojnu vrijednost. Tip
povratne vrijednosti korisniku metode govori koju e vrstu podataka metoda vratiti
kad zavri.

Neke metode uope ne vraaju vrijednosti. Za takve se metode kae da vraaju void,
to se posebno definira kljunom rijeju void. Na primjer:
void myVoidMethod();

deklarira metodu koja vraa void i nema parametara. U C# morate uvijek deklarirati
tip povratne vrijednosti ili void.

Komentari
C# program moe sadrati i komentare. Pogledajte prvi red iza otvorene vitiaste
zagrade ranije navedene glavne metode:
// Koristi objekt konzole sustava

Tekst poinje s dvije kose crte (//). One oznaavaju komentar. Komentar je napomena za
programera koja ne utjee na izvoenje programa. C# podrava tri vrste komentara.
Prva upravo prikazana vrsta govori da se sav tekst s desne strane oznake za komentar
treba smatrati komentarom, sve do zavretka tog reda. Ta se vrsta naziva komentarom
u C++ stilu.
Druga vrsta komentara naziva se komentar u C stilu i poinje otvorenom oznakom
za komentar (/*) a zavrava zatvorenom oznakom za komentar (*/). Na taj se nain
komentari mogu prostirati u vie redova i nije potrebno umetati znakove // na poetku
svakog reda, kao to je pokazano u primjeru 2-2.

Primjer 2-2. Komentari u vie redova


namespace Helloklorld
{
class Helloklorld
{
static void Main()
{
/* Koristi objekt konzole sustava
kao to je objanjeno u tekstu */
System.Console.IVriteLine("Hello klorid");
}
}
}

Poglavlje 2: Poetalc:Hello World" | 11


Komentari u C++ stilu mogu se ugnijezditi unutar komentara u C stilu. Iz tog se
razloga komentari u C++ stilu koriste kad god je to mogue, a komentari u C stilu
samo za privremeno izdvajanje dijelova koda iz projekta.
Trea, i posljednja, vrsta komentara koju C# podrava koristi se kako bi se kodu pridruila
vanjska XM L dokumentacija. Ta vrsta komentara objanjena je u poglavlju 13.

Konzolne aplikacije
Hello W orld je primjer konzolne aplikacije (engl. console applicatiori). Konzolna
aplikacija obino nema grafikog korisnikog suelja - ne postoje izbornici, gumbi,
prozori i slino. Tekst se unosi i prikazuje u standardnoj konzoli (najee je to DOS
prozor pod Windowsima). Na poetku knjige bavit emo se samo konzolnim aplikaci-
jama kako bi se lake usredotoili na sam jezik. Kasnija poglavlja obrauju i Windows
i Web aplikacije te emo se tamo usredotoiti na alate za izradu grafikog korisnikog
suelja koje nudi Visual Studio .NET.
Metoda Main() iz naeg primjera jednostavno ispisuje tekst Hello W orld" na stan-
dardni izlaz (DOS prozor). Standardnim izlazom upravlja objekt Console. On ima
metodu WriteLine() koja uzima niz (skup znakova) i ispisuje ga na standardni izlaz.
Kada pokrenete ovaj program, u DOS prozoru na monitoru raunala pojavit e se
rijei Hello World.
Metoda se poziva operatorom toka (.). Stoga, kako biste pozvali metodu WriteLine()
objekta Console, napiite Console.W riteL ine(...) i u zagrade upiite niz koji elite
ispisati.

Imenski prostori
Console je samo jedan od brojnih tipova koji su dio .N ET FCL-a. Svaka klasa ima svoj
naziv, stoga FCL sadri na tisue naziva, kao to su ArrayList, Hashtable, FileDialog,
DataException, EventArgs itd. Postoje stotine, tisue, ak i desetine tisua naziva.

To predstavlja problem. Nijedan razvojni inenjer ne moe upamtiti sve nazive koji se
koriste na platformi .N ET te ete prije ili kasnije stvoriti objekt i dati mu naziv koji
se ve koristi. to e se dogoditi ako klasu Hashtable nabavite od drugog dobavljaa
i zatim otkrijete da je ona u sukobu s klasom Hashtable koju prua platforma .NET?
Upamtite, u C # svaka klasa mora imati jedinstven naziv a nazive gotovih klasa koje
ste kupili obino ne moete promijeniti.
Rjeenje ovog problema lei u upotrebi imenskih prostora. Imenski prostor (engl.
namespace) ograniava doseg naziva, ograniavajui njegovu smislenost samo na
definirani imenski prostor.*

* ' Napomena za C++ programere: imenski prostori u C++ odvajaju su ope-


ratorom za odvajanje dosega ( ::), dok se u C # koristi operator toka (.).
--------- Napomena za Java programere: imenski prostori pruaju brojne pogod-
nosti paketa.

12 | Programiranje C#
Recimo da je Jim inenjer. Rije inenjer11 moe znaiti mnogo toga i biti prilino
zbunjujua. Projektira li on zgrade? Pie programe? Izrauje vlakove?
Izraz moemo pojasniti tako da kaemo on je znanstvenik" ili on je strojarski
inenjer". C # programer moe objasniti kako je Jim Science.engineer, a ne train.
engineer. Imenski prostor (u ovom sluaju Science i train) ograniava doseg sljedee
rijei. On stvara prostor" unutar kojeg rije ima znaenje.
Nadalje, Jim moda nije bilo kakav science.engineer. Moda je Jim diplomirao na
sveuilitu M IT i ima titulu softverskog inenjera, a ne inenjera graevinarstva. To
znai da se objekt 3 im moe odreenije definirati kao Science. software.engineer. Ova
klasifikacija ukazuje kako je imenski prostor software smislen unutar imenskog pros-
tora science, a engineer je u ovom kontekstu smislen unutar imenskog prostora soft-
ware. Ako kasnije saznate kako je Charlotte transportation.train.engineer, odmah
e vam biti jasno kojoj ona vrsti inenjera pripada. Istovremeno mogu postojati dva
objekta engineer, svaki unutar svog imenskog prostora.
Slino tome, ako se ustanovi kako na platformi .NET postoji klasa Hashtafale unutar
imenskog prostora System.Collections, a vi ste unutar imenskog prostora Prog.Csharp.
DataStructures takoer stvorili klasu Hashtable, nee doi do sukoba jer svaka klasa
postoji unutar svog imenskog prostora.

U primjeru 2-1 naznaeno je da se klasa Console nalazi unutar imenskog prostora


System s pomou sljedeeg koda:
Systera. Console. Write Line();

Operator toka (.)


U primjeru 2-1 operator toka (.) koristi se za pristup metodi (i podacima) unutar
klase (u ovom sluaju metodi WriteLine()) te za ogranienje naziva klase na odreen
imenski prostor (u ovom sluaju Console se nalazi unutar imenskog prostora System).
To funkcionira jer u oba sluaja kopamo" kako bismo pronali tono ono to traimo.
Najvia razina je imenski prostor System (u kojem se nalaze svi objekti System koje
prua FCL). Tip Console postoji unutar tog imenskog prostora, a metoda WriteLine()
je metoda lanica tipa Console.

U veini sluajeva imenski se prostori dijele na potprostore. Na primjer, imenski pros-


tor System sadri vie podreenih imenskih prostora kao to su Data, Configuration,
Collections i tako dalje, dok je sam imenski prostor Collections podijeljen na mnogo
podreenih imenskih prostora.

Imenski prostori mogu pomoi u organizaciji i razvrstavanju tipova. Kada piete kom-
pleksni C # program, korisno je stvoriti vlastitu hijerarhiju imenskih prostora, koja
moe biti vrlo duboka. Svrha imenskih prostora je da vam pomognu da metodom
podijeli i vladaj" lake koristite sloenu hijerarhiju objekata.

Poglavlje 2: Pofetak:Hello World" | 13


Kljuna rije using
Umjesto da rije System napiete ispred rijei Console, moete navesti da ete koristiti
tipove iz imenskog prostora System tako da direktivu:
using System;

napiete na poetku popisa, kako je prikazano u primjeru 2-3.

Primjer 2-3. Koritenje kljune rijei using


using System;
class Hello
{
static void Main()
{
//Console iz imenskog prostora System
Console.WriteLine("Hello Horld");
}
}
Moda ste primijetili da je direktiva using System napisana ispred definicije klase
Hello. Visual Studio . N E T 2 0 0 5 u svaku konzolnu aplikaciju ukljuuje tri iskaza using
(System, System.Collections.Generic i System.Text).

Iako moete izjaviti da koristite imenski prostor System, za razliku od nekih drugih
jezika, ne moete izjaviti da koristite objekt System.Console. Primjer 2-4 se nee pre-
vesti.

Primjer 2-4. Kod koji se ne m oe prevesti (nije doputen u C# )


using System.Console;
class Hello
{
static void Main()
{
//Console iz imenskog prostora System
WriteLine("Hello Horld");
}
}
Generira se pogreka u prevoenju:
error CS0138: A using namespace directive can only be applied
to namespaces; 'System.Console' is a type not a namespace

Ako koristite Visual Studio, uvidjet ete da ste napravili pogreku, jer
kad unesete using System i iza toga stavite toku, Visual Studio .N ET
2005 e navesti popis valjanih imenskih prostora na kojem se ne nalazi
Console.

Koritenje kljune rijei using moe smanjiti koliinu koda koji trebate napisati, ali
moe i ponititi prednosti imenskih prostora jer zagauje doseg brojnim nazivima koji

14 | Programiranje C#
se preklapaju. Najee rjeenje je koritenje kljune rijei using uz ugraene imenske
prostore i one koje ste sami stvorili, ali ne i uz komponente koje ste nabavili od neke
tree strane.

Neki programeri uvijek ispisuju cijelu putanju kroz imenske prostore


do objekta (npr. System.Console.WriteLine(), a ne samo Consoie.Urite-
Line()) i to koriste kao dodatak dokumentaciji. To je neupotrebljiv pris-
tup ako se koriste sloene hijerarhije imenskih prostora.

Razlikovanje velikih i malih slova


U C# razlikovanje velikih i malih slova je obavezno, to znai da writeLine nije isto
to i WriteLine, a oba su razliita od WRITELINE. Naalost, za razliku od VB-a, C# raz-
vojni okoli nee ispraviti pogreno unesena velika i mala slova. Ako istu rije jednom
unesete malim, a drugi put velikim slovima, unijet ete pogreku u program koju ete
kasnije teko pronai.

Koristan trik je oznaiti naziv u kojem su pogreno napisana jedino


mala i velika slova i zatim pritisnuti Ctrl-Space. Znaajka Intellisense
e umjesto vas ispraviti pogreno napisana slova.

Kako biste sprijeili ovakve pogreke na kojima se nepotrebno gube vrijeme i ener-
gija, trebate sastaviti pravilnik za imenovanje varijabli, metoda, konstanti itd. U ovoj
se knjizi varijable imenuju ,,deva zapisom (engl. camel notation) (npr. imeNekeVari-
jable), a metode, konstante i svojstva Pascalovim zapisom (npr. NekaMetoda).

Pascalov zapis se od ,,deva zapisa razlikuje samo po tome to u njemu


nazivi poinju velikim slovom.
Microsoft je razvio smjernice za pisanje koda koje je korisno pogledati
(one su esto sve to vam je zapravo potrebno). Moete ih preuzeti sa
adrese:
http://msdn.microsoft.com/library/default.aspturfc/library/en-us/
cpgenref/html/cpconNETFrameworkDesignGuidelines.asp

Kljuna rije static


Metoda Main() prikazana u primjeru 2-1 ima jo jedno odreenje. Odmah ispred
deklaracije povratnog tipa void (koji, sjeate se, oznaava da metoda ne vraa nikakvu
vrijednost) moete pronai kljunu rije static:
static void Main()

Kljuna rije static govori kako Main() moete pozvati bez prethodnog stvaranja
objekta tipa Hello. Ova pomalo kompleksna tema bit e detaljnije obraena u kasnijim
poglavljima. Jedan od problema koji se javljaju prilikom uenja novog programskog
jezika je potreba za koritenjem sloenijih znaajki prije njihova potpuna razumi-
jevanja. Za sada deklaraciju metode Main() moete smatrati arobnom formulom.

Poglavlje 2: Poetak:Hello World" | 15


Razvoj programa Hello World"
Postoje najmanje dva naina na koje moete napisati, prevesti i pokrenuti programe iz
ove knjige: s pomou Visual Studio .NET IDE-a ili s pomou programa za ureivanje
teksta i prevoditelja koji se pokree u odzivniku.
Iako programe moete razvijati izvan Visual Studia .NET, IDE vam prua razne pred-
nosti. One ukljuuju podrku za uvlaenje redova, Intellisense, oznaavanje bojama
i integraciju s datotekama za pomo. Najvanije od svega, IDE sadri moan alat za
ispravljanje pogreaka i mnotvo drugih alata.
U ovoj knjizi je pretpostavljeno da ete koristiti Visual Studio .NET. Meutim, upute
su vie fokusirane na jezik i platformu nego na alate. Sve navedene primjere moete
kopirati u program za ureivanje teksta poput Notepada ili Emacsa, spremiti ih kao
tekstualne datoteke s nastavkom imena .cs i prevesti s pomou C # prevoditelja za
odzivnik koji se nalazi u kompletu .N ET Framework SDK (ili kompletima razvojnih
alata kompatibilnih s platformom .NET kao to su Mono ili Microsoft Shared Source
CLI). Napominjemo kako neki od primjera navedenih u kasnijim poglavljima koriste
Visual Studio .N ET alate za izradu Windows i Web obrazaca, no ak i te primjere
moete runo upisati u Notepad ako elite ii teim putem.

Ureivanje programa Hello World"


Za izradu programa Hello World u IDE-u, odaberite Visual Studio .N ET s izbornika
Start ili odgovarajuu ikonu na radnoj povrini, zatim odaberite File -* New -* Project.
Time ete otvoriti prozor New Project (slika 2-1). Ako prvi put koristite Visual Studio,
prozor New Project moda e se sam pojaviti.

* r $ffHeBoWoHd| .7. ..
J u f* 2 U C :\^cu m e rtsa n S e ttin g s \A m in is tra to r\M y Docum ents\Visual S tudo\P rojecl

S o W lo n N e S lH e lk M o H d P , 0 to B f to ry fo r S o U io n i
----- vi'*- v sfV i

m
iSr
Slika 2-1. Izrada C # konzolne aplikacije u Visual Studiju .NET

16 J Programiranje C#
Za otvaranje aplikacije odaberite Visual C# u prozoru Project Types, a u prozoru
Templates Console Application (ovaj korak ne morate izvesti ako koristite Visual C#
Express Edition - izravno odaberite Console Application).
Sada moete upisati naziv projekta i odabrati mapu u koji ete spremati datoteke. Pritis-
nite OK i otvorit e se novi prozor u koji moete upisati kod iz primjera 2-1 (slika 2-2).

v n ollo W orld - M icro so f t O ev e Jup nifint E n v iro n m cn t


m <fc B * f fin b u a D jt Io o b M ndov B dp
sa m
I.10. a - E 5 H I X Ui
g ,cs < mw B -lj k , 1 1 ; S I V
St ^/Program.cs
^jH;l|
oWorid.Ptc-giarn mIJc^Mair^sticr^]
I l'iiB flc eg io n U sin g d i r e c t i v e s
0
^ SolutiwiVWioWld, (l project)
0HelloW oH d

m
! h u s in g S y s tem ;
u s in g 3 y s t e i T i . C o l l e c c io n s . 0 e n e r i
I*] - fc jj P roperties
fi- g j References

[ - u s in g S y s te r o .T e x t ;

- tfe n d re g io n
^ SoU ion Explorer | l ^ ,'Cl&jV'9W |~
^ B n am esp ac e H e U o tJo rld

c l a s s P ro g ram Program.cs FdeProperties

t a c i : v o id H a i n ( s t r i n g [ ] a rg s)

{ B ,Advanced' Compie
^BuMAction
:}Copyto Output IFalse
Custom Tod
D>* Custom Tod Nan
&fflSjCv-> c
t-bFile Mame Program.es
$ Full Path ' C:\Docijmonts and 5

s
s gpeisw lW KM f
r *-<>.\ _________ .*> & ?. "A i

Slika 2-2. Editor u koji moete upisati program

Primijetit ete kako Visual Studio .NET imenski prostor stvara prema navedenom
nazivu projekta (HelloWorld) i dodaje direktivu using za System, System.Collections.
Generic i System.Text jer e tipovi iz tih imenskih prostora biti potrebni za gotovo svaki
program koji budete pisali.

Visual Studio .NET stvara klasu Program koju slobodno moete preimenovati. Kada
klasi mijenjate naziv, dobro je promijeniti i naziv datoteke (Classl.cs). Ako promijen-
ite naziv datoteke, Visual Studio e automatski umjesto vas promijeniti naziv klase.
Za reprodukciju primjera 2-1 promijenite ime datoteke Program.es (nalazi se u pro-
zoru Solution Explorer) u hello.es te promijenite ime Program u HelloWorld (ako imena
promijenite obrnutim redoslijedom, Visual Studio e promijeniti ime klase u hello).
Na kraju, Visual Studio 2005 stvara kostur programa kako biste lake zapoeli s
radom. Za reprodukciju primjera 2-1 uklonite argumente (string[ ] args) iz metode
Main(). Zatim sljedea dva reda kopirajte u tijelo metode Main():

Poglavlje 2: Poetak:Hello World" | 17


Gradska knjinica
// Koristi objekt konzole sustava
System.Console.WriteLine("Hello World");

Ako ne koristite Visual Studio .NET, otvorite Notepad, unesite kod iz primjera 2-1 i
datoteku spremite kao tekstualnu datoteku hello.es.

Prevoenje i pokretanje programaHello World"


Visual Studio prua mnogo naina za prevoenje i pokretanje programa Hello
World. Skoro svaki zadatak moete obaviti pritiskom na gumb na alatnoj vrpci Visual
Studija, odabirom opcije izbornika ili u mnogim sluajevima upotrebom preaca na
tipkovnici.

Preaci na tipkovnici mogu se postaviti odabirom opcije izbornika


Tools -* Options -* Keyboard. U ovoj knjizi pretpostavljeno je da koris-
tite zadane postavke.

Za prevoenje programa Hello W orld moete, na primjer, pritisnuti Ctrl-Shift-B ili


odabrati opciju izbornika Build -> Build Solution. Moete i pritisnuti gumb Build na
alatnoj vrpci Build (da biste je vidjeli moda ete morati desnom tipkom mia pritis-
nuti alatnu vrpcu). Alatna vrpca Build prikazana je na slici 2-3. Gumb Build nalazi se
sasvim lijevo i istaknut je.

Slika 2-3. Alatna vrpca Build

Za pokretanje programa Hello W orld bez ispravljanja pogreaka moete na tip-


kovnici pritisnuti Ctrl-F5, odabrati Debug -> Start W ithout Debugging ili pritisnuti
gumb Start Without Debugging na alatnoj vrpci Build, kao to je prikazano na slici 2-
4 (moda ete morati prilagoditi svoju alatnu vrpcu kako bi ovaj gumb bio dostupan).
Program moete pokrenuti bez da ga prethodno prevedete (ovisno o postavkama pod
Tools -* Options) - IDE e spremiti datoteku, prevesti je i pokrenuti (pritom e moda
od vas zatraiti doputenje za svaki korak).

Slika 2-4. Gufnb Start Without Debugging

U prvom redu preporuam da prouite razvojni okoli Visual Studija


2005. To je za vas, kao .N ET razvojnog inenjera, osnovni alat i vrlo je
vano da se njime znate dobro sluiti. Vrijeme koje uloite u upozna-
vanje okolia Visual Studia viestruko e vam se isplatiti u sljedeim
mjesecima.

18 | Programiranje C#
Pravovrem eno prevoenje
Prevoenjem program a hello.es koristei esc stvara se izvedbena datoteka. Upamtite
da su u .exe datoteci instrukcije zapisane u M SIL-u, koji je opisan u poglavlju 1.
Zanim ljivo je da, ako ovu aplikaciju napiete u V B.N ET -u ili bilo kojem drugom
jeziku sukladnom s .N E T C L S-om , dobit ete vie-m anje isti MS1L. Razlike izmeu
IL ko d a stvorenog u razliitim jezicim a praktiki ne postoje.
Uz to to stvara IL kod, prevoditelj stvara i segm nt ,exe datoteke sam o za itanje u
koji um ee standardno W in 3 2 izvedbeno zaglavlje. Prevoditelj zadaje ulaznu toku
unutar segm enta sam o za ita nje. Program za uitavanje prelazi na tu toku kada
pokrenete program , ba kao i kod svakog drugog programa za W indowse.
O peracioni sustav, m eutim , ne moe izvesti IL kod i ta ulazna toka slui samo za
prijelaz do .N ET J1 T prevoditelja (koji je takoer opisan u poglavlju 1). J1 T prevoditelj
generira originalne instrukcije za procesor kakve se mogu pronai u obinim ,exe
datotekam a. K ljuno svojstvo J1 T prevoditelja zapravo je da se metode prevode samo
prilikom koritenja, kad su na redu za izvoenje.

Za prevoenje i pokretanje programa Hello World s pomou C# prevoditelja za odzivnik


koji se nalazi u paketima .NET Framevvork SDK, Mono (http://www.mono-project.com) i
Shared Source CLI (http://msdn.microsoft.com/net/sscli/) pratite sljedee korake:
1. Spremite primjer 2-1 u datoteku hello.es.
2. Otvorite .NET naredbeni prozor (Start - Programs - Visual Studio .NET -> Visual
Studio Tools - Visual Studio Command Prompt). Ako koristite UNIX, trebate
pokrenuti tekstualnu konzolu, xterm ili neto to e vam prikazati odzivnik ljuske.
3. U naredbenom redu zadajte sljedeu naredbu ako koristite .NET ili Shared
Source CLI C # prevoditelja:
esc /debug hello.es

Ako koristite Mono, zadajte sljedeu naredbu:


mes -debug hello.es

Ovim korakom pravi se EXE datoteka. Ako program sadri pogreke, prevoditelj
e o njima izvijestiti u prozoru odzivnika. Preklopnik /debug u kod umee sim-
bole tako da EXE datoteku moete pokrenuti pod programom za ispravljanje
pogreaka ili pogledati brojeve redova koda u tragovima stoga (trag stoga ete
dobiti ako program generira pogreku koju ne obraujete).
4. Za pokretanje programa pod .NET-om unesite:
hello

Ako koristite Shared Source CLI zadajte ovu naredbu:


clix hello.exe

a ako koristite Mono onda ovu:


mono hello.exe

Sada biste u konzoli trebali vidjeti rijei Hello World.

Poglavlje 2: Poetak: Hello World" | 19


Koritenje programa za ispravljanje pogreaka
Alat za ispravljanje pogreaka vjerojatno je najvaniji alat u svakom razvojnom okoliu.
Visual Studio ima vrlo moan alat za ispravljanje pogreaka i vrijeme koje potroite na
njegovo svladavanje viestruko e vam se isplatiti. Osnove ispravljanja pogreaka vrlo
su jednostavne. Tri osnovne vjetine su:
* Kako postaviti toku prekida te kako doi do nje
* Kako ui u poziv metode i prijei preko njega
* Kako provjeriti i promijeniti vrijednost varijable, podatka lana itd.

Ovo poglavlje ne ponavlja kompletnu dokumentaciju alata za ispravljanje pogreaka,


ali ove su vjetine toliko vane da se jednostavno moraju ukratko objasniti.
Alat za ispravljanje pogreaka isti cilj moe se koristiti na razliite naine, obino preko
opcija izbornika, gumba itd. Najjednostavniji nain postavljanja toke prekida je pri-
tiskom na lijevu marginu programskog koda. IDE toku prekida oznaava crvenom
bojom, kao na slici 2-5.

1
1 for ( int i = 0; i < 3; i++ )
{

)
>
)

Slika 2-5. Toka prekida

rW r Za objanjenje alata za ispravljanje pogreaka potreban nam je prim -


jer koda. Ovdje naveden kod uzet je iz poglavlja 5 i nije potrebno da
TIX razumijete kako funkcionira (iako e C++ i Java programeri vjerojatno
shvatiti o emu se radi).

Za pokretanje alata za ispravljanje pogreaka moete odabrati Debug -> Start ili jed-
nostavno pritisnuti F5. Program se zatim prevodi i izvodi do toke prekida, na njoj se
zaustavlja, a uta strelica oznaava sljedei iskaz za izvoenje, kao to je prikazano
na slici 2-6.

for ( int i 0; i < 3; i++ )

0 (winArray[i] .Dra*rtJindow();
}
}
)

Slika 2-6. Dosegnuta toka prekida

20 | Programiranje C#
Nakon to ste doli do toke prekida, provjera vrijednosti razliitih objekata je
jednostavna. Na primjer, vrijednost varijable i vidjet ete ako iznad nje postavite
pokaziva mia i priekate nekoliko trenutaka (slika 2-7).

Slika 2-7. Prikaz vrijednosti objekta

U IDE alatu za ispravljanje pogreaka postoji i niz korisnih prozora, kao to je prozor
Locals u kojem su prikazane vrijednosti svih lokalnih varijabli (slika 2-8).

> c o u n te r 0 in t
Gfl w in {V irtu a lM e th o d s.W in d o w } VirtualM ethods.VVindow
B lb {V irtu a lM etho ds.LlstB o x) V irtu a lM e th o d s.listB o x
b {V irtu a lM e th o d s.B u tto n ) V irtua lM ethod s.B utton
fJ * w inArray {Dimensions:[33^ VirtualMethods.Vtfnovvl]

] A u to s j L o c a ls J | p W a tch 1

Slika 2-8. Prozor Locals

Ugraeni tipovi, poput cjelobrojnih vrijednosti, prikazuju vrijednost, ali objekti prika-
zuju sv,oj tip i uz njih je prikazan znak plus (+). Te objekte moete proiriti kako biste
vidjeli njihove unutarnje podatke (slika 2-9). Vie o objektima i njihovim unutarnjim
podacima moete saznati u poglavljima koja slijede.

a {V irtu alM etho ds.B utto n). V irtua lM ethod s.B utton
a ^ w ln A rra y {D im e n sio n s:{3 )) V irtudlM ethods.W indo(v{)
M S 0 [0 ] {V irtu a lM e th o d s.W in d o w ) V irtua lM ethod s,W ind ow

{V irtu a lM e th o d s.listB o x > Virtua lM ethods.ListB ox


4 in t
3 in t
iV k tu a lM e rh o d s J liiU o n l____ V irhialM ethnrfc u/IndniAi A n ,r..a lM i iM
| 3 A u to s j Locals | j p W a t c h t

Slika 2-9. Detaljne informacije o objektu u prozoru Locals

Poglavlje 2: Poetak:,,Hello World" | 21


U sljedeu metodu moiete ui pritiskom na F l l . U o.om sluaju sljedea metoda je
OrawWindow() klase VJindovi, kao to je prikazano na slici 2-10.

t ; V irtiu M 'tM t IBobueeidE)' HletosoM Deuelopment enyiit.tima.il


ZA' Vto* Oebu, 0a W
ntto*
& i *9. .0*. ;
.v:BL
tScfcjbon&rploror-So... ' U
' Vliliiriini'thnfir f f }
v'- ^,^1^ kfbJS
tSSSSSSS iS : ~ .... V; a.vW) j
i
SokAton'VktUftMetho'K(
11*
t h i s . c o p - to p ; { 3 VjrtoalMcthod
th is .le t t - 5i tB 'MProperi**
Jl ffl iaSI Referent*
// c v w il- t :s th e v tn d ov
>|
']I viituaM
ethods
p'ufclic V i r t u a l v o id DrawindOW<)

Ca n ao l e.M r l te L ln e( "Bindons drauing indov ' , (Ol, (1),


top, leit );

Slika 2-10. Vlazka u metodu


Moete primijetiti kako jesljedei iskaz na redu za izvoenjeWriteLine()uDrawWindow().
Prozor Autos se aurirao i u njemu je prikazano trenutno stanje objekata.
O alatu za ispravljanje pogreaka treba nauiti jo mnogo toga, ali ovaj kratki uvod tre-
bao bi vam biti dovoljan za poetak. Mnogo programerskih pitanja moe se rijeiti tako
d : ; e kratke probu p r o g * , pregleda, ih u alatu aa ? p r i .
Dobar alat za ispravljanje pogreaka je, na neki nacin, najmoniji alat za samostalno
u e n je p ro gra m sko g je z ik a .

22 | Programiranje C#
POGLAVLJE 3

Osnove programskog jezika C#

U poglavlju 2 prikazan je vrlo jednostavan program napisan u jeziku C# . Taj je mali


program toliko sloen da smo morali preskoiti nekoliko vanih pojedinosti. U ovom
poglavlju te su pojedinosti objanjene detaljnijim prikazom sintakse i strukture samog
jezika C# .
Ovo poglavlje opisuje sustav tipova u jeziku C# , pravei razliku izmeu ugraenih
tipova (int, bool itd.) i korisniki definiranih tipova (tipova koje sami stvarate u obliku
klasa i suelja). U njemu su objanjene i osnove programiranja poput stvaranja i kori-
tenja varijabli i konstanti. Nadalje, tu su objanjene enumeracije, nizovi, identifikatori,
izrazi i iskazi.
U drugom dijelu ovog poglavlja moete pronai objanjenja i primjere koritenja iskaza
za kontrolu tijeka i f , switch, while, do.. .while, for i foreach. Objanjeni su i opera-
tori, ukljuujui logike, relacijske, matematike operatore i operatore pridruivanja.
Slijedi uvod u imenske prostore i kratak vodi kroz C# pretprevoditelj.
Iako je glavni zadatak jezika C # stvaranje objekata i rad s njima, najbolje je poeti s
osnovnim dijelovima: elementima od kojih se objekti stvaraju. U te se elemente ubrajaju
ugraeni tipovi koji su temeljni dio jezika C# , kao i sintaktiki elementi jezika C# .

Tipovi
C # je vrlo tipiziran jezik. U takvom jeziku morate deklarirati tip (engl. type) svakog
objekta kojeg stvorite (npr. cjelobrojnih vrijednosti, brojeva s pominim zarezom,
nizova, prozora, gumba itd.), a prevoditelj e sprijeiti pojavu pogreaka tako to e
inzistirati da objektima budu pridrueni samo podaci odgovarajueg tipa. Tip objekta
govori prevoditelju koliko je objekt velik (npr. int oznaava objekt od 4 bajta) i koje su
njegove mogunosti (npr. gumbi se mogu povui, pritisnuti i tako dalje).

23
Napomena za C # 1.1 programere: sve do inaice 2 platforma .N ET bila
je vrlo tipizirana u svemu osim u kolekcijama. Uvoenjem generika
i*, stvaranje vrlo tipiziranih klasa kolekcija sada je jednostavno, kao to
je prikazano u poglavlju 9.

Poput jezika C++ i Java, C # tipove dijeli u dva skupa: ugraene (engl. intrinsic ) tipove
koji su dio samog jezika i koris n i ki d efin ira n e tipove koje definira programer.
C # skup tipova dijeli na jo dvije kategorije: v rijed n osn e i referen tne tipove.' Osnovna
razlika izmeu vrijednosnih i referentnih tipova je nain spremanja njihovih vrijedno-
sti u memoriju. Vrijednosni tip uva svoju vrijednost u memoriji dodijeljenoj na stogu
(engl. stack).

Napomena za C i C++ programere: u C # ne postoji eksplicitni pokaza-


telj da je objekt referentnog tipa (tj. ne koristi se operator &). Takoer,
u C # obino se ne koriste pokazivai (iznimku ovog pravila potraite
u poglavlju 22).

Ako imate vrlo velik objekt, njegovo smjetanje na gomilu ima mnogo prednosti. U
poglavlju 4 objanjene su prednosti i nedostaci rada s referentnim tipovima. Ovo se
poglavlje bavi ugraenim vrijednosnim tipovima koji su dostupni u C# .

U C # veliina i format pohrane za razliite ugraene tipove (npr. int)


ne ovise o platformi i jednaki su u svim .N ET jezicima.

C # podrava tipove p o k a z iv a a u C++ stilu, ali oni se koriste samo kod rada s neu-
pravljanim kodom. To je kod koji nije stvoren na platformi .NET, na primjer, COM
objekti. (Rad s COM objektima objanjen je u poglavlju 22.)

Rad s ugraenim tipovima


C # nudi izobilje ugraenih tipova koji se oekuju od modernog programskog jezika,
i svaki od njih preslikava se u temeljni tip kojeg podrava .NET CLS. Preslikavanje
C # primitivnih tipova u temeljni .N ET tip osigurava da se objekt stvoren u C # moe
koristiti naizmjenino s objektima napravljenim u bilo kojem drugom jeziku koji je u
skladu s .N ET CLS-om , primjerice u VB.NET-u.

Svi ugraeni tipovi, osim Object (koji je detaljnije opisan u poglavlju 5) i String (opisan u poglavlju 10) su
vrijednosni tipovi. Svi korisniki definirani tipovi, osim struktura (pogledajte poglavlje 7) i enumeriranih
tipova (pogledajte poglavlje 3) su referentni tipovi.

24 | Programiranje C#
Napomena za Java programere: C # ima iri raspon osnovnih tipova
od jezika Java. Treba istaknuti decimalni tip u C # koji je koristan za
financijske izraune.

Svaki tip ima odreenu i nepromjenjivu veliinu. Za razliku od C++, veliina int tipa u
C# je uvijek 4 bajta jer se on preslikava u Int32 u .NET CLS-u. U tablici 3-1 popisani
su ugraeni vrijednosni tipovi dostupni u jeziku C# .

Tablica 3-1. Vrijednosni tipovi u C#

Veliina (u
Tip bajtovima) .NETtip Opis

Byte 1 8yte Bez predznaka (vrijednostod 0 do 255)

Char 2 Char Unicode znak

Bool 1 Boolean True iliFalse

Sbyte 1 SByte 5 predznakom (vrijednostiod -128 do 127)

Short 2 Intl6 5 predznakom (vrijednostiod -32,768 do 32,767)

Ushort 2 UIntl6 Bez predznaka (vrijednostiod 0 do 65,535)

Int 4 Int32 Cjelobrojnevrijednostispredznakom od -2,147,483,648 do


2,147,483,647

Uint 4 UInt32 Cjelobrojnevrijednosti bez predznaka od 0 do 4,294,967,295

Float 4 Single Brojspominim zarezom. uva vrijednostiod otprilike+/- 1.5*1(HSdo


otprilike3.4I38sa sedam znaajnih znamenki.
Double 8 Double Brojspominim zarezom dvostruke preciznosti. uva vrijednosti od
otprilike+/- 5.0*10 w do otprilike+/-1.8*10308sa 15-16 znaajnih
znamenki.

decimal 16 Decimal Stalna preciznostdo 28 znamenki ipoloajem decimalnog zareza. Kori-


stise u financijskimizraunima izahtijeva prefiks,,mili,,M".
Long' 8 Int64 Cjelobrojnevrijednostispredznakom od -9,223,372,036,854,775,808
do 9,223,372,036,854,775,807
Ulong 8 UInt64 Cjelobrojnevrijednostibez predznaka od 0 do Oxffffffffffffffff

Napomena za C i C++ programere: u C # vrijednost Boolean varijabli


moe biti samo true ili false. Cjelobrojne vrijednosti se ne izjednaavaju
sa Boolean vrijednostima u C # i ne postoji implicitno pretvaranje.

Uz ove primitivne tipove C # ima jo dva vrijednosna tipa: enum (objanjen kasnije u
ovom poglavlju) i struct (objanjen u poglavlju 4). U poglavlju 4 objanjene su i druge
pojedinosti vrijednosnih tipova, poput primjene vrijednosnih tipova kao referentnih
tipova u postupku koji je poznat pod nazivom pakiranje (engl. boxing) i injenice da
vrijednosni tipovi ne nasljeuju.

Poglavlje 3: Osnove programskog jezika C# | 25


Stog i gom ila
Stog (engl. stack) je podatkovna stru ktu ra koja se koristi za sprem anje elem enata na
naelu posljednji unutra prvi van (poput gom ile tanjura u restoranu). Stog se odnosi
na podruje memorije koje podrava procesor i u koje se sprem aju lokalne varijable.
U C # vrijednosni tipovi (npr. cjelob rojn e vrijednosti) se alociraju na stog. Za nji-
hovu vrijednost se rezervira podru je u m em oriji i na to se podru je referira imenom
varijable.
Referentni tipovi (npr. objekti) rasporeuju se na gom ilu. G om ila (engl. heap) je pod-
ruje memorije koje se koristi za alociran je prostora ob jektim a. Kad se objekt alocira
na gomilu, vraa se njegova adresa i zatim pridruuje referenci.
O bjekti na stogu se unitavaju kad izau iz dosega. O kvir stoga je obino definiran
metodom. Ako lokalnu varijablu deklarirate unutar m etode (kao to je objanjeno
kasnije u ovom poglavlju), objekti koje stavite na stog unutar te m etode bit e uniteni
kad metoda zavri.
O bjekti na gomili se sakupljaju u otpad kratko nakon unitavanja posljednje reference
koja ukazuje na njih.

rt *
Napomena za C i C++ programere: C # upravlja svom memorijom s
pomou sustava za sakupljanje otpada - ne postoji operator za brisanje.

Odabir ugraenog tipa


Odluku o tome koju ete vrstu cjelobrojne vrijednosti (short, int ili long) koristiti
obino donosite na temelju veliine vrijednosti koju elite spremiti. Na primjer, ushort
moe sadrati samo vrijednosti od 0 do 65,535 , dok uint moe sadrati vrijednosti
od 0 do 4,294,967,295.
Memorija je razmjerno jeftina, dok je vrijeme jednog programera sve skuplje. Veinu
vremena ete varijable deklarirati kao in t, osim ako ne postoji dobar razlog da uinite
drukije.
a
Cjelobrojne vrijednosti su esto bre od manjih tipova jer su moderni
procesori optimizirani za rad s njima.

Float, double i decimal nude razliite stupnjeve veliine i preciznosti. Float je dobar za
veinu malih necjelobrojnih vrijednosti. Uzmite u obzir da prevoditelj pretpostavlja
da je svaki broj s decimalnim zarezom double ako ne kaete drukije. Za doslovnu
dodjelu tipa float iza broja dodajte slovo f (pojedinosti pridruivanja vrijednosti litera-
lima objanjene su kasnije u ovom poglavlju):
float someFloat = 57f;

26 | Programiranje C#
Tip char p redstavlja U nicode z n ak . ch ar sp e cija ln i sim b o li m ogu biti jed n o sta v n i zn a -
kovi, U n ico d e ili kon trolni z n a ko v i (engl. e s cap e ch aracters ) sm je ten i izm eu a p o -
strofa. N a p rim jer, Aje je d n o sta v a n z n a k , dok je \u004l U nicode znak. K o n tro ln i zn a -
kovi su p o se b n i to ken i ko ji se sa s to je od dva z n a k a , od k o jih je prvi ob rn u ta kosa
crta . N a p rim jer, \t je vodoravni tabu lator. U o b iaje n i k o n tro ln i znakovi navedeni su
u ta blici 3 - 2 .

Tablica 3-2. Uobiajeni kontrolni znakovi

Znak Znaenje

V Apostrof

\" Navodnik

\\ Obrnuta kosa crta


\0 Nuli

\a Upozorenje

\b Brisanjeunatrag

\f Nova stranica

\n Novi red

\r Prijelazu sljedei red

\t Vodoravni tabulator

\v Okomiti tabulator

Pretvaranje ugraenih tipova


Objekti jednog tipa mogu se implicitno ili eksplicitno pretvoriti u objekte drugog
tipa. Implicitne se pretvorbe dogaaju automatski - prevoditelj sve radi umjesto vas.
Eksplicitne se pretvorbe dogaaju kad vrijednost pretvorite" u drugi tip. Semantika
eksplicitne pretvorbe je Hej! prevoditelju, znam to radim!" To se ponekad naziva
udarac velikim ekiem" i moe biti vrlo korisno ili vrlo bolno, ovisno o tome nalazi
li se va palac na avlu.

Napomena za VB6 programere: u jeziku VB6 se nizovi znakova i zna-


kovni tip mogu lako pomijeati. Znak se tretira kao niz znakova duljine
1. No, u C # tipovi su dobro definirani. Kako biste varijabli char pridru-
ili literal, morate ga umetnuti izmeu apostrofa.
Uzmite u obzir i injenicu da VB 6 metode za pretvorbu izmeu znaka i
njegove odgovarajue ASCII vrijednosti (Chr() i Asc()) ne postoje u C# .
Kako biste char konvertirali u odgovarajuu ASCII vrijednost, navedite
ju kao in t (cjelobrojnu vrijednost):
(in t)'A '
Da biste broj pretvorili u char, navedite ga kao char:
(char)65

Poglavlje 3: Osnove programskog jezika C# | 27


Implicitne se pretvorbe dogaaju automatski i u njima se informacije ne mogu izgubiti.
Na primjer, short in t (koji je veliine dva bajta) moete jednostavno pretvoriti u int
(koji je veliine etiri bajta). Bez obzira na to koja se vrijednost nalazi u short, ona se
nee izgubiti prilikom pretvorbe u in t:
short x = 5;
int y = x; // Implicitna pretvorba

Ako provedete obrnutu pretvorbu, moe lako doi do gubitka informacija. Ako je vri-
jednost u in t vea od 32 767 njen dio se moe izgubiti prilikom pretvorbe. Prevoditelj
nee izvesti implicitnu pretvorbu iz in t u short:
short x;
int y = 5 0 0 ;
x = y; // Nee se prevesti

Morate izvesti eksplicitnu pretvorbu koristei operator:


short x;
int y = 5 0 0 ;
x = (short) y; // 0K

Svi ugraeni tipovi sami definiraju vlastita pravila pretvorbe. Ponekad je zgodno defini-
rati pravila pretvorbe za korisniki definirane tipove, a to je objanjeno u poglavlju 5.

Varijable i konstante
Varijabla (engl. variable ) je lokacija u memoriji s tipom. U prethodnim su primjerima
i x i y varijable. Varijablama se mogu pridruiti vrijednosti koji se mogu programski
promijeniti.

WriteLine()
.N E T kostu r sadri korisnu metodu za ispis na zaslon. Pojedinosti m etode System.
C onsole.W riteLine() bit e pojanjene kasnije u knjizi, ali osnove su jednostavne. Pozo-
vite m etodu kako je prikazano u prim jeru 3 -1 , unosei niz koji elite ispisati na kon-
zoli i param etre koji e se zam ijeniti. U sljedeem prim jeru:

System.Console.WriteLine("Aiter assignmervt, mylnt: {0}", mylnt);

niz A fter assignment, mylnt se ispisuje kao takav, iza ega slijedi v rijednost varija-
ble mylnt. Lokacija param etra zam jene { 0 } zadaje gdje e se prikazati vrijednost prve
izlazne varijable mylnt - u ovom sluaju, na kraju niza. U sljedeim poglavljim a saznat
ete m nogo vie o WriteLine().

Varijablu moete napraviti tako to ete deklarirati njezin tip i zatim joj dodijelite naziv.
Moete ju inicijalizirati prilikom deklariranja, a novu vrijednost jo j moete dodijeliti
bilo kada, jednostavnom promjenom vrijednosti. To je prikazano u primjeru 3-1.

28 | Programiranje C#
primjer 3-1- Inicijalizacija varijable i pridruivanje vrijednosti varijabli
(tregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace InitializingVariables
{
class Values
{
static void Main()
{

int mylnt = 7;
Sytem.Console.WriteUne(Initialized, mylnt: {o}",
mylnt);

mylnt = 5;
System.Console.WriteLine("A-fter assignment, mylnt: { o }\
mylnt);

}
}
}

Visual Studio 2 0 0 5 za svaki program stvara imenski prostor i iskaz


using (kao i podruje upotrebe). Kako bi se utedilo na prostoru, to je
izostavljeno u veini primjera koda, iako je ukljueno u primjere koje
moete preuzeti s prezentacija 0 Reilly ili LibertyAssociates.com.

Ovdje se varijabla mylnt inicijalizira sa vrijednosti 7, ta se vrijednost prikazuje, varija-


bli se dodjeljuje vrijednosti 5 koja se zatim ponovno prikazuje.

Napomena za VB6 programere: u C # tip podatka dolazi ispred naziva


varijable.

Obavezno pridruivanje
U jeziku C # pridruivanje je obavezno: to jest, varijable se moraju inicijalizirati ili im
se mora pridruiti vrijednost prije koritenja. Kako biste isprobali ovo pravilo, promi-
jenite red koji inicijalizira mylnt u primjeru 3-1 u:
int mylnt;

i spremite promijenjeni program prikazan u primjeru 3-2.

Poglavlje 3: Osnove programskog jezika C# | 29


*__
* Napomena za C i C++ programere: u C # , svakoj se varijabli prije upo-
trebe mora pridruiti konana vrijednost.

Primjer 3-2. Koritenje varijable koja nije inicijalizirana


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace UninitializedVariable
{
class UninitializedVariable
{
static void Main(string[] args)
{
int mylnt;
System.Console.WriteLine
("Uninitialized, mylnt: {0}", mylnt);
mylnt = 5;
System.Console.WriteLine("Assigned, mylnt: {0}", mylnt);

}
}
}

Kada pokuate prevesti ovaj ispis, C # prevoditelj e prikazati poruku o pogreci pri-
kazanu na slici 3-1.

J D escrlpUon . __ ^ ; | . . F fe . . - i C olum n I
1 U se o f u nassigned local v a riab le 'm y ln t' U n in itia lized V ariable.es 17 33

Slika 3-1. Poruka o pogreci nastaloj zbog koritenja varijable kojoj nije dodijeljena vrijednost

Ako dvaput pritisnete poruku o pogreci razvojni okoli e postaviti kursor na mjestu
pogreke u kodu.
U C# nije doputeno koritenje varijabli koje nisu inicijalizirane. Znai li to da svaku
varijablu u programu morate inicijalizirati? Zapravo, ne. Ne morate stvarno inicija-
lizirati varijablu, ali joj prije koritenja morate dodijeliti vrijednost. U primjeru 3-3
prikazana je ispravna inaica programa.

30 | Programiranje C#
Primjer 3-3. Pridruivanje bez inicijaliziranja
#region Using directives

using System;
using System.Colle ctio ns.G eneric;
using System.Text;

#endregion

namespace Assi gning Without I nitiali z ing

^ class Ass igning Withou t Initiali zi ng

static void Main(string[] args)


{
int mylnt;
mylnt = 7;
SystenuConsole.WriteLine(Assigned, mylnt: {o}'', mylnt);
mylnt = 5;
System.Console.WriteLine(''Reassigned, mylnt: {0}", mylnt);

}
}
}

Konstante
K onstanta (engl. constan t ) je varijabla ija se vrijednost ne moe promijeniti. Varijable
predstavljaju moan alat, ali ponekad je potrebna tono odreena vrijednost, vrijed-
nost za koju elite biti sigurni da e ostati nepromijenjena. Na primjer, u programu za
simuliranje kemijskog eksperimenta trebate koristiti toke vrelita i ledita na Fahren-
heitovoj skali. Program e biti jasniji ako varijable u koje se vrijednosti spremaju nazo-
vete FreezingPoint i BoilingPoint, ali ne elite dopustiti da se njihove vrijednosti pro-
mijene. Kako to sprijeiti? Rjeenje je koritenje konstanti.
Postoje tri vrste konstanti: literali (engl. literals ), sim bolike k on stan te i enu m eracije
(engl. en um erations). U izrazu:
x = 32;

vrijednost 32 je literal. 32 uvijek ima vrijednost 32. Toj konstanti ne moete dodijeliti
drugu vrijednost. Koliko god se trudili ne moete postii da 32 predstavlja vrijednost 99.
Simbolike konstante pridruuju naziv konstantnoj vrijednosti. Simbolilytkonstantu
moete deklarirati s pomou kljune rijei const i sljedee sintakse;/
const tip identifikator=vrijednost;

Konstanta se prilikom deklariranja mora inicijalizirati, a kad je jednom inicijalizirana,


ne moete ju mijenjati. Na primjer:
const int FreezingPoint = 3 2 ;

Poglavlje 3: Osnove programskog jezika C# | 31


U ovoj deklaraciji, 32 je literal, a FreezingPoint je simbolika konstanta tipa int. U
primjeru 3 -4 prikazana je upotreba simbolikih konstanti.

Primjer 3-4. Upotreba simbolikih konstanti


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

(tendregion

namespace SymbolicConstants
{
class SymbolicConstants
{
static void Main(string[] args)
{
const int FreezingPoint = 32; // Stupnjevi Fahrenheita
const int BoilingPoint = 212;

System.Console.WriteLine("Freezing point of water: {o}",


FreezingPoint);
System.Console.WriteLine(''Boiling point of water: {o}",
BoilingPoint);

BoilingPoint = 2 1 ;

}
}
)
U primjeru 3-4 stvorene su dvije simbolike cjelobrojne konstante; FreezingPoint i Boi-
lingPoint. Nazivi konstanti napisani su Pascalovim stilom, no to nije obavezno.
Svrha ovih konstanti je da u svim potrebnim izrazima koriste vrijednosti 32 i 212 za
toke ledita i vrelita ali, budui da konstante imaju nazive, one prenose mnogo vie
znaenja. Ako odluite program prebaciti na Celzijusovu skalu, konstante moete
ponovno inicijalizirati prilikom prevoenja na 0 i 100. Ostatak koda trebao bi nor-
malno funkcionirati.
Kako biste se uvjerili da se konstanti ne moe ponovno dodijeliti vrijednost, pokuajte
ukloniti oznaku komentar iz posljednjeg reda u kodu (prikazan podebljanim slovima).
Prilikom ponovnog prevoenja trebala bi se javiti pogreka prikazana na slici 3-2.

! | Description i File j Une ' Cojumn i


Q 1 The left-hand side of an assignment must SymbolicConstants.cs 23 3
be a variable, property or indexer

Slika 3-2. Upozorenje koje se pojavljuje kada konstanti pokuate ponovno dodijeliti vrijednost

32 | Programiranje C#
Enumeracije
Enumeracije (engl. enumerations) su iznimno korisna alternativa konstantama. Enu-
meracija je samostojan vrijednosni tip koji se sastoji od skupa imenovanih konstanti
(naziva se i enumeratorski popis).
U primjeru 3 -4 stvorili ste dvije povezane konstante:
const int FreezingPoint = 32;
const int BoilingPoint = 212;

Popisu m oete d o d a ti razne druge k o risn e ko n sta n te, poput:


const int Lightlacketlfeather = 60;
const int SwimmingWeather = 72;
const int WickedCold = o;

Ovaj postupak je pomalo zamoran i izmeu navedenih konstanti ne postoji logika


veza. Za rjeavanje tog problema u C # dostupna je enumeracija:
enum Temperatures
{
WickedCold = 0 ,
FreezingPoint = 32,
LightlacketWeather = 60,
SwimmingWeather = 72,
BoilingPoint = 212,
}

Iza svake enumeracije nalazi se temeljni tip koji moe biti bilo koji tip cjelobrojne vri-
jednosti (integer, short, long itd.) osim char. Tehnika definicija enumeracije je:
[atributi] [modifikatori] enum identifikator
[:osnovni tip] {enumeratorski popis};

Neobavezni modifikatori i atributi opisani su kasnije u knjizi. Zasad emo se usredo-


toiti na ostatak ove deklaracije. Enumeracija poinje kljunom rijei enum iza koje
obino slijedi identifikator, na primjer:
enum Temperatures

Osnovni tip je temeljni tip enumeracije. Ako izostavite ovu neobaveznu vrijednost (to
se esto dogaa), ona e poprimiti vrijednost int, ali slobodno moete koristiti bilo
koji tip cjelobrojne vrijednosti (npr. ushort, long) osim char. Sljedei odlomak, na pri-
mjer, deklarira enumeraciju cjelobrojnih vrijednosti bez predznaka (uint):
enum ServingSizes :uint
{
Small = 1 ,
Regular = 2,
Large = 3
}
Obratite panju na enumeratorski popis kojim zavrava deklaracija. On sadri pridru-
ene konstante za enumeraciju koje su odvojene zarezima.

Poglavlje 3: Osnove programskog jezika C# | 33


U primjeru 3-5 prikazan je prepravljen primjer 3 -4 u kojem se koristi enumeracija.

Primjer 3-5. Pojednostavljivanje koda koritenjem enumeracija


ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

ttendregion

namespace EnumeratedConstants
{
class EnumeratedConstants
{
enum Temperatures
{
WickedCold = 0,
FreezingPoint = 32,
Light3acketWeather = 60,
SwimmingWeather = 72,
BoilingPoint = 212,
}
static void Main(string[] arg)
{
System.Console.WriteLine("Freezing point of water: {0}",
(int)Temperatures.FreezingPoint);
System.Console.WriteLine("Boiling point of water: {0},
(int)Temperatures.BoilingPoint);
}
}
}
Kao to moete primijetiti, enum se mora kvalificirati tipom enumeracije (npr. Tempera-
tures. WickedCold). Vrijednost enumeracije se podrazumijevano prikazuje s pomou
simbolikog naziva (kao to su BoilingPoint i FreezingPoint). Kada elite prikazati
vrijednost enumerirane konstante, konstantu morate pretvoriti u njen temeljni tip
(int). Cjelobrojna vrijednost se prosljeuje u WriteLine i prikazuje.

Svakoj konstanti u enumeraciji odgovara brojana vrijednost - u ovom sluaju cijeli


broj. Ako je ne postavite drugaije, enumeracija poinje nulom, a svaka sljedea vrijed-
nost je za jedan vea od prethodne.

Ako stvorite sljedeu enumeraciju:


enum SomeValues
{
First,
Second,
Third = 2 0 ,
Fourth
}

34 | Programiranje C#
v rijedn ost First b it e o, Second e im ati vrijedn o st l, Third 20, a Fourth 21.

E n u m eracije su fo rm a ln i tip ovi stoga je za pretvorbu tipa en u m era cije u tip cje lo b ro jn e
vrijedn o sti p o tre b n a e k sp licitn a pretvorba.

Napomena za C++ programere: u C # se enumeracije koriste na malo


drugaiji nain nego u C++, tako d a je pridruivanje tipu enumeracije
iz cjelobrojne vrijednosti ogranieno, ali omoguava unaprjeenje enu-
meracije u cjelobrojnu vrijednost za dodjeljivanje enumeracije cjelobroj-
noj vrijednosti.

Nizovi znakova
Gotovo je nemogue napisati program u jeziku C # bez upotrebe nizova. U objektu
niza nalazi se niz znakova.
Varijabla niza se deklarira kljunom rijei string, isto kao kod stvaranja instance bilo
kojeg objekta:
string myString;

Literal niza se pravi stavljanjem navodnika ispred i iza znakovnog niza:


"Hello World"

Varijabla niza se obino inicijalizira literalom niza:


string myString = "Hello World";

Nizovi su detaljnije opisani u poglavlju 10.

Identifikatori
Identifikatori (engl. identifiers) su nazivi koje programeri odaberu za svoje tipove,
metode, varijable, konstante, objekte itd. Identifikator mora poinjati slovom ili
donjom crtom.
Standardna Microsoft praksa imenovanja predlae ,,deva zapis (malo poetno slovo,
npr. nekolme) za nazive varijabli i Pascalov zapis (veliko poetno slovo, npr. NekoDrugo-
Ime) za nazive metoda i veinu drugih identifikatora.

Microsoft vie ne preporua koritenje maarskog zapisa (npr. iNekiln-


teger) i donjih crta (npr. Neka Vrijednost).

U identifikatorima se velika i mala slova razlikuju pa tako C# myVariable i MyVariable


tretira kao dva razliita imena varijabli.

Izrazi
Iskazi koji su jednaki nekoj vrijednosti zovu se izrazi (engl. expressions). Iznenaujue
velik broj iskaza je jednak nekoj vrijednosti. Na primjer, pridruivanje:

Poglavlje 3: Osnove programskog jezika C# | 35


myVariable = 57;
I
je izraz. Jednako je dodijeljenoj vrijednosti koja, u ovom sluaju, iznosi 57.
Obratite panju na prethodni iskaz koji vrijednost 57 pridruuje varijabli myVariable.
Operator pridruivanja (=) ne provjerava jednakost. Njime se ono stoje na desnoj strani
(57) dodjeljuje onome Stoje na lijevoj strani (myVariable). Svi C # operatori (ukljuujui
one za pridruivanje i jednakost) objanjeni su kasnije u ovom poglavlju.
Budui da je myVariable = 57 izraz koji iznosi 57, moe se koristiti kao dio drugog
operatora pridruivanja, npr:
mySecond\/ariable = myVariable = 57;

U ovom se iskazu vrijednost literala 57 pridruuje varijabli myVariable. Vrijednost tog


pridruivanja (57) se zatim dodjeljuje drugoj varijabli, mySecondVariable. Vrijednost 5 7
je, dakle, pridruena objema varijablama. Dakle, jednim iskazom moete inicijalizirati
beskonaan broj varijabli i pridruiti im istu vrijednost:
a = b = c = d = e = 2 0 ;

Bijeli prostor
U jeziku C # se razmaci, fabulatori i novi redovi smatraju bijelim prostorom" (engl.
whitespace ) (nazvani tako jer se vidi samo bjelina stranice na kojoj je napisan kod).
Dodatni bijeli prostor u C # iskazima se uglavnom zanemaruje. Moete napisati:
myVariable = 5;

ili:
myVariable = 5;

i prevoditelj e oba iskaza jednako tretirati.

Iznimka ovog pravila je bijeli prostor unutar nizova koje se ne zanemaruju. Ako
napiete:
Console.WriteLine("Hello World")

svaki razmak izmeu Hello" i ,,World bit e tretiran kao znak u nizu.
Koritenje bijelog prostora uglavnom je intuitivno. Treba ga koristiti tako da kod bude
to itljiviji programeru, a prevoditelju to ionako nije bitno.
Postoje situacije u kojima je upotreba bijelog prostora vrlo bitna. Iako je izraz:
int x = 5;
isti kao:
int x=5;

nije isti kao:


intx=5;
Prevoditelj zna d a je bijeli prostor sa svake strane operatora pridruivanja suvian, ali

36 | Programiranje C#
rostor izmeu deklaracije tipa in t i naziva varijable x nije suvian, ve je obavezan.
To nije iznenaujue: bijeli prostor prevoditelju omoguuje razlikovanje kljune rijei
in t i nekog nepoznatog termina in tx . Izmeu i n t i x moete dodati koliko god elite
bjelina, ali mora postojati najmanje jedna (obino razmak ili tabulator).

Napomena za VB programere: zavretak reda u C # nema posebnog


znaenja. Iskazi zavravaju tokom zarez, ne znakovima za novi red.
Ne postoji znak za nastavak reda jer nije potreban.

Iskazi
Potpuna programska uputa u C# naziva se is ka zo m (engl. statem en t). Programi se
sastoje od nizova C# iskaza. Svaki iskaz mora zavravati tokom zarez (;). Na pri-
mjer:
int x; // iskaz
x = 23; // jo jedan iskaz
int y = x; // i jo jedan iskaz

C# iskazi se procjenjuju redom. Prevoditelj poinje na vrhu popisa iskaza i kree se


prema kraju. To bi bilo prilino jednostavno i ograniavajue kad ne bi postojalo gra-
nanje. U C# programu postoje dvije vrste grananja: bezu vjetno g ran anje (engl. uncon-
ditional bran ch in g ) i uvjetno g rananje (engl. cond ition al branching).

Na tijek programa utjeu i petlje i iteracijski iskazi koji se oznaavaju kljunim rijeima
for, while, do, in i foreach. Iteracija je objanjena kasnije u ovom poglavlju. Prvo emo
objasniti neke osnovnije metode uvjetnog i bezuvjetnog grananja.

Iskazi bezuvjetnog grananja


Bezuvjetno grananje se moe postii na dva naina. Prvi je pozivanjem metode. Kad
prevoditelj naie na naziv metode, on zaustavlja izvoenje tekue metode i prelazi
na novu pozvanu" metodu. Kada ta metoda vrati vrijednost, izvoenje originalne
metode se nastavlja tono na redu ispod poziva metode. Pogledajmo primjer 3-6.

Primjer 3-6. Pozivanje metode


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CallingAMethod
{
class CallingAMethod
1

Poglavlje 3: Osnove programskog jezika C# | 37


Primjer 3-6. Pozivanje metode (nastavak)
static void Main()
{
Console.WriteLine("In Main! Calling SomeMethod()..
SomeMethod();
Console.WriteLine("Back in Main().");
}
static void SomeMethod()
{
Console.Writeline("Greetings from SomeMethod!");
}
}
}
Tijek programa poine s Main() i nastavlja se do poziva SomeMethod(). U toj se toki
program grana na drugu metodu. Kad se ona zavri, program nastavlja s redom iza
poziva te metode.
Drugi nain bezuvjetnog grananja je s pomou jedne od kljunih rijei za bezuvjetno
grananje: goto, break, continue, return ili throw. Dodatne informacije o prva tri iskaza
navedene su kasnije u ovom poglavlju. Iskaz return vraa kontrolu pozivajuoj metodi.
Posljednji iskaz, throw, objanjen je u poglavlju 11.

Iskazi uvjetnog grananja


Uvjetno grananje stvara se uvjetnim iskazom na koji upuuju kljune rijei i f , else i
switch i do njega moe doi samo kad je uvjetni iskaz istinit.

Napomena za C i C + + programere: za razliku od jezika C i C + + u


kojima se bilo koji izraz moe koristiti kao uvjetni, u C # svi uvjetni
izrazi moraju se usporeivati sa Boolean vrijednostima.

Iskazi if...else
Iskazi i f . . .e lse oznaavaju grananje temeljeno na uvjetu. Uvjet je izraz koji se testira
na poetku iskaza i f . Ako je uvjet istinit, izvodi se iskaz (ili blok iskaza) u glavnom
dijelu i f iskaza.
i f iskazi mogu sadrati dodatni iskaz else. On se izvodi samo ako je izraz na poetku
iskaza i f neistinit:
if (izraz)
iskazi
[else
iskaz2]

Ovakav opis iskaza i f moete pronai u dokumentaciji prevoditelja. On pokazuje da


i f iskaz uzima Boolean izraz (izraz koji moe biti istinit ili neistinit) unutar zagrada
i izvodi iskazi ako je izraz istinit. Upamtite da iskazi moe biti i blok iskaza unutar
vitiastih zagrada.

38 | Programiranje C#
M oete p rim ije titi i da je isk az e l s e n e obav ezan, bu dui da se n alazi u n u tar u glatih
zagrada. Ia k o je tim e o b ja n je n a sin ta ksa iska za i f , u p otreba e b iti ja sn ija pogledate
li p rim jer 3-7.

Primjer 3 - 7. Iskazi if..else


using System;
class Values
{
static void Main()
{
int valueOne = 10;
int valueTwo = 20;

if ( valueOne > valueTwo )


{
Console.WriteLine(
"ValueOne: {0} larger than ValueTwo: fi}",
valueOne, valueTwo);
}
else
{
Console.WriteLine(
"ValueTwo: {0} larger than ValueOne: {1}",
valueTwo,valueOne);
}

valueOne = 3 0 ; // Postavlja valueOne vilje

if ( valueOne > valueTwo )


{
valueTwo = valueOne++;
Console.WriteLine("\nSetting valueTwo to valueOne value, ");
Console.WriteLine("and incrementing ValueOne.\n'');
Console.WriteLine("ValueOne: {o} ValueTwo: {l}",
valueOne, valueTwo);
}
else
{
valueOne = valueTwo;
Console.WriteLine("Setting them equal. );
Console.WriteLine("ValueOne: ,{o} ValueTwo: {i}",
valueOne, valueTwo);
}
}
}
U primjeru 3-7 prvim se iskazom if testira je li valueOne vee od valueTwo. Relacijski
operatori poput vee od (>), manje od (<) i jednako (==) prilino su jednostavni za
koritenje.

Testiranje je li valueOne vee od valueTwo daje neistinit rezultat (jer je valueOne jednako
10, a valueTwo je jednako 2 0 ,stoga valueOne nije vee od valueTwo). Poziva se iskaz else
koji ispisuje iskaz:

Poglavlje 3: Osnove programskog jezika C# | 39


ValueTwo: 20 is larger than ValueOne: 10

Drugi je iskaz i f istinit i svi iskazi u bloku i f se procjenjuju, to uzrokuje ispis sljedea
dva reda:
Setting valueTwo to valueOne value,
and incrementing ValueOne.

ValueOne: 31 ValueTwo: 30

Blokovi iskaza
Blok iskaza moe se napisati na svakom m jestu na kojem C # oeku je iskaz. Blok
iskaza je skup iskaza sm jeten u vitiaste zagrade.
Stoga um jesto:
if (nekillvjet)
nekilskaz;

m oete napisati:
if(nekillvjet)
{
iskazledan;
iskazDva;
iskazTri;
}

Ugnijeeni iskazi if
Iskazi i f se esto ugnjeuju kako bi se upravljalo sloenim uvjetima. Pretpostavimo
da trebate napisati program za provjeru temperature koji vraa sljedee informacije:
Ako je temperatura 32F ili nia, program bi vas trebao upozoriti o ledu na
cestama.
Ako temperatura iznosi tono 32F, program bi vam trebao rei kako moe doi
do zaleivanja.
Postoji mnogo dobrih naina za pisanje ovakvog programa. U primjeru 3 -8 pokazan
je pristup u kojem se koriste ugnijeeni iskazi i f .

Primjer 3-8. Ugnijeeni iskazi if


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

40 l Programiranje C#
Primjer 3-8. Ugnijeeni iskazi if (nastavak)
namespace Nestedlf

class Nestedlf
{
static void Main()
{
int temp = 3 2 ;

if ( temp <= 32 )
{
Console.WriteLine( "Narning! Ice on road!" );
if ( temp == 32 )
{
Console.WriteLine(
"Temp exactly freezing, beware of water." );
}
else
{
Console.WriteLine( "Watch for black ice! Temp: {0 }", temp );
} // zavrava else
} // zavrava if (temp <= 3 2 )
} // zavrava main
} U zavrava class
} // zavrava namespace

Nisu svi operatori jednaki


Pogledamo li paljivije drugi iskaz i f u primjeru 3 - 8 , prim jetit em o uobiajeni pro-
blem. O vaj iskaz i f testira je li temperatura jednaka 32:
i f (temp == 32)

U jezicim a C i C + + ovakav iskaz moe biti opasan. Neiskusni programeri esto umje-
sto.operatora jedn akosti koriste operator pridruivanja te tako napisu iskaz:
if (temp = 3 2 )

Tu je pogreku teko prim ijetiti, a takvim bi se iskazom temp pridruio broj 32 i 3 2 bi se


vratio kao vrijednost iskaza pridruivanja. Budui da su u C i C + + sve vrijednosti koje
nisu nula istin ite, iskaz i f bio bi istinit. Tako bi se temp pridruila vrijednost 32, bez
obzira na to je li temp izvorno imao tu vrijednost ili ne. Ovakve pogreke su prilino
este i lako ih je previdjeti - no razvojni inenjeri jezika C # su ih predvidjeli!
U C # ovaj je problem rijeen tako to iskazi i f prihvaaju sam o Boolean vrijednosti.
Vrijednost 32 koju je vratilo pridruivanje nije Boolean vrijednost (ve cjelobrojna
vrijednost) i u C # vrijednost 32 nee se autom atski pretvoriti u true. Tako bi ova
pogreka bila prim ijeena tijekom prevoenja to je velik napredak u odnosu na C ++,
uz nisku cijenu jednostavne zabrane im plicitnih pretvorbi cjelobrojnih vrijednosti u
Boolean vrijednosti.

Poglavlje 3: Osnove programskog jezika Cft | 41


Logika u primjeru 3-8 provjerava je li temperatura manja ili jednaka 32F. Ako je tako,
ispisuje se upozorenje:
if (temp <= 32)
{
Console.WriteLine("Warning! Ice on road!");

Program zatim provjerava je li temperatura jednaka 32F. Ako je, ispisuje se jedna
poruka; ako nije, temperatura mora biti manja od 32F i program ispisuje drugu
poruku. Primijetite da je drugi iskaz i f , ugnijeen unutar prvog i f , tako da je logika
iza else budui da je ustanovljeno kako je temperatura manja ili jednaka 32F i pri-
tom nije jednaka 32F, onda mora biti manja od 32F.

Iskazi switch: alternativa ugnijeenim iskazima if


Ugnijeeni iskazi i f teko se itaju, teko ih je ispravno napisati i u njima ispraviti
pogreke. Kada vam je dostupan irok raspon mogunosti, iskaz switch predstavlja
itljiviju alternativu. Logika iskaza switch glasi odaberi odgovarajuu vrijednosti i
djeluj u skladu s tim.
switch (izraz)
{
case konstanta-izraz:
iskaz
iskaz na koji se prelazi
[default: iskaz]
}
Kao to moete primijetiti, izraz se, kao i kod iskaza i f , stavlja unutar zagrada na
poetku iskaza switch. Svaki sluaj zahtijeva izraz konstante tj. literal, simboliku
konstantu ili enumeraciju.
Ako je sluaj zadovoljen, izvodi se iskaz (ili vie njih) povezan s tim sluajem. Iza toga
mora slijediti iskaz na koji se prelazi. Obino se koristi break koji izvedbu premjeta
izvan iskaza switch. Moe se koristiti i iskaz goto koji oznaava preskakanje do sljede-
eg sluaja, kao to je prikazano u primjeru 3-9.

Primjer 3-9. Iskaz su/itch


(tregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace SwitchStatement
{
class SwitchStatement
{

42 | Programiranje C#
Primjer 3-9. Iskaz switch (nastavak)
static void Main( string[] args )

^ const int Democrat = 0;


const int LiberalRepublican = 1;
const int Republican = 2;
const int Libertarian = 3;
const int NewLeft = 4;
const int Progressive = 5;

int myChoice = Libertarian;

switch ( myChoice )
{
case Democrat:
Console.WriteLine( "Vou voted Democratic.Nn" );
break;
case LiberalRepublican: // propada kroz
//Console.WriteLine(
//"Liberal Republicans vote Republican\n");
case Republican:
Console.WriteLine( "Vou voted Republican.\n" );
break;
case NewLeft:
Console.WriteLine( "NewLeft is now Progressive" );
goto case Progressive;
case Progressive:
Console.WriteLine( "Vou voted Progressive.\n" );
break;
case Libertarian:
Console.WriteLine( "Libertarians are voting Republican" );
goto case Republican;
default:
Console.WriteLine( "You did not pick a valid choice.Nn" );
break;
}
Console.WriteLine( "Thank you for voting." );
}
}
}
U ovom primjeru pravimo konstante za razliite politike stranke. Varijabli myChoice
pridruuje se jedna vrijednost (Libertarian) i program se prebacuje na tu vrijednost.
Ako je myChoice jednaka vrijednosti Democrat, ispisuje se iskaz. Primijetit ete da ovaj
sluaj zavrava s break. break je iskaz za preskakanje kojim se iz iskaza switch preba-
cujemo na prvi sljedei red koji ispisuje Thank you for voting".

Poglavlje 3: Osnove programskog jezika C# | 43


Napomena za VB programere: ekvivalent C # iskaza switch je VB 6 iskaz
Se le ct Case. Takoer, za razliku od V B 6 koji vam doputa testiranje
' j J * , 4 cijelog raspona vrijednosti s pomou jednog iskaza Case, C # sintaksa to
ne doputa. Sljedea dva iskaza Case su sintaktino ispravna u VB6:
Case Is > 1 0 0
Case 50 to 60

Ti iskazi, meutim, nisu valjani u C# . U C # moete testirati samo izraz


s jednom konstantom . Za testiranje cijelog raspona morate zasebno
testirati svaku vrijednost i propasti" do zajednikog bloka case.

Ispod vrijednosti LiberalRepublican nema iskaza i ta vrijednost propada do sljede-


eg sluaja: Republican. Ako je vrijednost LiberalRepublican ili Republican, izvode
se iskazi Republican. Ovakvo je propadanje mogue samo ako iskaz nema tijelo. Ako
izvuete iz komentara WriteLine() iz LiberalRepublican, program se nee prevesti.

V N apomena za C i C ++ programere: propadanje do sljedeeg sluaja


nije mogue, osim ako iskaz case nije prazan. Stoga, moete napisati
sljedee:
sluaj l: I I propadanje je mogue
sluaj 2:

U ovom primjer slu a j l je prazan. Ne moete, meutim, napisati:


sluaj 1:
NekaAkcija();
// propadanje nije mogue
sluaj 2:

Ovdje unutar slu a j 1 postoji iskaz i propadanje nije mogue. Ako


elite da slu a j 1 propadne do slu a j 2, morate izriito upotrijebiti
goto:
sluaj 1:
NekaAkcija();
goto iskaz 2; // eksplicitno propadanje
sluaj 2:

Ako vam je potreban iskaz, ali iza toga elite izvesti drugi sluaj, moete koristiti iskaz
goto, kao to je prikazano u sluaju NewLeft:
goto case Progressive;

Iskaz goto vas ne mora prebaciti na prvi sljedei sluaj. U sljedeoj instanci izbor
Libertarian takoer sadri goto, ali njime se ovaj put prebacuje nazad sve do sluaja
Republican. Budui d aje naa vrijednost postavljena na Libertarian, dogaa se upravo
ovo. Ispie e iskaz Libertarian, dolazi do prebacivanja na sluaj Republican, ispisuje
se taj iskaz, zatim se dolazi do break, to nas prebacuje izvan switch sve do posljednjeg
iskaza. Izlaz svega ovog je:
Libertarians are voting Republican
You voted Republican.

Thank you for voting.

44 | Programiranje C#
Obratite panju na uvjet default iz primjera 3-9:
default:
Console.WriteLine(
"You did not pick a valid choice.Nn");

Ako ni jedan od sluajeva ne odgovara, pozvat e se sluaj default koji e korisnika


upozoriti na pogreku.

Iskaz svvitch i nizovi


U prethodnom primjeru vrijednost prebacivanja bila je integralna konstanta. C#
prua mogunost prebacivanja na string, to doputa da napiete:
case "libertarian :

Ako se nizovi podudaraju, ulazi se u iskaz case.

Iteracijski iskazi
C# nudi irok raspon iteracijskih iskaza, ukljuujui petlje for, while i do..,while te
petlje foreach (nove u C jezicima, no ve poznate VB programerima). Uz to, C# podr-
ava iskaze za preskakanje goto, break, continue i return.

Iskaz goto
Iskaz goto je izvor iz kojeg potjeu svi ostali iteracijski iskazi. Na alost, radi se o
sjemenu iz kojeg je potekao pageti kod i beskonana zbunjenost. Najiskusniji pro-
grameri vjerojatno se jee od iskaza goto, ali kako ne biste ostali neinformirani, evo
kako se on koristi:
1. Stvorite oznaku.
2. Odete na tu oznaku.

Oznaka je identifikator iza kojeg slijedi dvotoka. Naredba goto obino se povezuje s
uvjetom, kao to je prikazano u primjeru 3-10.

Primjer 3-10. Upotreba iskaza goto


ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace UsingGoTo
{
class UsingGoTo
{
static void Main( string[] args )

Poglavlje 3: Osnove programskog jezika Cif | 45


Primjer 3-10. Upotreba iskaza goto (nastavak)

{
int i = 0;
repeat: // Oznaka
Console.WriteLine( "i: {0}", i );
i++;
if ( i < 10 )
goto repeat; // Zlodjelo
return;
}
}
}
Kad biste pokuali nacrtati tijek kontrole u programu u kojem se esto koriste iskazi
goto, rezultat, brojne ukriene i preklopljene linije, vjerojatno bi vas podsjetio na tanjur
pun pageta. Otuda i potjee naziv pageti kod. Taj je fenomen doveo do nastanka
alternativnih rjeenja, poput petlje while. Mnogi programeri dre da se goto treba
koristiti samo u najjednostavnijim problemima, u suprotnom je nastali kod iznimno
zbunjujui i teak za odravanje.

Petlja vvhile
Semantika petlje while je dok je ovaj uvjet istinit, obavljaj taj posao11. Sintaksa je:
while (izraz) iskaz

Izraz je, kao i inae, bilo koji iskaz koji vraa vrijednost. Iskazi wbile zahtijevaju izraz
koji odgovara Boolean vrijednosti (true/false), a takav iskaz moe naravno biti i blok
iskaza. Primjer 3-11 aurira primjer 3-10 upotrebom petlje while.

Primjer 3-11. Upotreba petlje u/hile


Sregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

itendregion

namespace WhileLoop
{
class WhileLoop
{
static void Main( stringf] args )
{
int i = 0;
while ( i < 1 0 )
{
Console.WiiteLine( "i: {o}", i );
i++;
}

46 | Programiranje C#
P r im je r 3 -1 1 - U p o t r e b a p e t l j e w h ile ( n a s ta v a k )

return;
}
}
}
Kod iz primjera 3-11 daje identine rezultate kao kod iz primjera 3-10, ali je njegova
logika neto jasnija. Iskaz while je jednostavan i kompletan, a ima znaenje dok je i
manje od 10, ispisuj ovu poruku i poveavaj i .

Primijetit ete da petlja while testira vrijednost i prije ulaska u petlju. Time se osigu-
rava da se petlja nee pokrenuti ako je testirani uvjet netoan. Stoga, ako se i inicijali-
zira s U , petlja se uope nee pokrenuti.

Petlja do...while
Iskaz while se moda uope nee izvesti ako testirani uvjet bude neistinit. Ako elite
osigurati da se iskaz izvede barem jednom, koristite petlju do.. .while:
do iskaz while izraz

Izraz je bilo koji iskaz koji vraa vrijednost. U primjeru 3-12 prikazana je petlja do...
while.

Primjer 3-12. Petlja do...while


ttzegion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace DoWhile
{ '
class Doklhile
{
static void Main( stringj] args )
{
int i = 1 1 ;
do
{
Console.WriteLine( "i: {o}", i );
i++;
} while ( i < 10 );
return 0 ;
}
}
}

Ovdje se i inicijalizira s 11 i test while nee uspjeti, ali tek nakon to se tijelo petlje
jednom izvede.

Poglavlje 3: Osnove programskog jezika C# | 47


Petlja for
Ako paljivo pogledate petlju while u primjeru 3-11, primjetit ete uzorak koji se esto
moe susresti u iterativnim iskazima: inicijalizacija varijable (i = 0), testiranje varija-
ble (i < o), izvoenje niza iskaza i poveavanje varijable (i++). Petlja fo r omoguava
kombiniranje tih koraka u jedan iskaz petlje:
for ([inicijalizatori]; [izraz]; [iteratori]) iskaz

Petlja fo r prikazana je u primjeru 3-13.

Primjer 3-13. Petlja for


(tregion Using directives

using Sysi:em;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ForLoop
{
class ForLoop
{
static void Main( string[] args )
{
for ( int i = 0; i < 1 0 0 ; i++ )
{
Console,Write( "{0 } ", i );

if ( i % 10 == 0 )
{
Console.WriteLine( "Yt{0}", i );
} '
}
return ;
}
}
}

Izlaz:
0 0
i ;! 3 4 5 6 7 8 9 10 10
n 12 13 14 15 16 17 18 19 20 20
21 22 23 24 25 26 27 28 29 30 30
31 32 33 34 35 36 37 38 39 40 40
41 42 43 44 45 46 47 48 49 50 50
51 52 53 54 55 56 57 58 59 60 60
61 62 63 64 65 66 67 68 69 70 70
71 72 73 74 75 76 77 78 79 80 80
81 82 83 84 85 86 87 88 89 90 90
91 92 93 94 95 96 97 98 99

48 | Programiranje C#
Ova petlja k oristi op era to r m odulo (engl. m odulus) koji je opisan kasn ije u ovom pogla-
vlju V rijedn ost i se isp isu je sve d o k i n ije v ie k ra tn ik od 10.

i f ( i % 10 == o)

Z atim se ispisuje tabulator, a iza njega vrijednost. Stoga su desetice (20, 30, 40 itd.)
ispisane uz desni rub izlaza.
<r,
Napomena za VB programere: u C # se varijable iz petlje deklariraju
unutar zaglavlja iskaza fo r ili foreach a ne prije poetka iskaza. To
znai da su u dosegu samo unutar bloka i da ih se ne moe pozivati
izvan petlje. Iskaz foreach detaljnije je objanjen u poglavlju 9.

P o jed in a n e v rije d n o s ti se isp isu ju s p o m o u C o n so le .W rite () koja je slin a W rite-


L in e (), ali ne u n osi z n a k za novi red, to dop u ta n astav ak ispisa u istom redu.

Treba istaknuti nekoliko stvari: u petlji for uvjet se testira prije izvoenja iskaza. Stoga se
u primjeru i inicijalizira kao 0, zatim se provjerava je li manje od 100. Budui da je iskaz
i < 1 0 0 toan, izvodi se iskaz unutar petlje for. Nakon izvoenja i se poveava (i++).
Upamtite da je d oseg varijable i unutar petlje for (to jest, varijabla i je vidljiva samo
unutar petlje for). Primjer 3-14 se nee prevesti.

Primjer 3-14. Doseg varijabli deklariranih unutar petlje for


(tregion Using directives

using System;
using Systera.Collections.Generic;
using System.Text;

#endregion

namespace ForLoopScope
{ '
class ForLoopScope
{
s ta tic void Main( strin g[] args )
{
for ( int i = 0; i < 100; i++ )
{
Console.Write( "{0 } " , i ) ;

i f ( i % 10 == 0 )
{
Console.WriteLine( " \ t{ o j", i ) ;
}
}
Console.Writeline( "\n Final value of i: {o}", i );
}
}
}

Poglavlje 3: Osnove programskog jezika C# | 49


Podebljani red se ne moe prevesti jer varijabla i nije dostupna izvan dosega petlje for.

Bijeli prostor i vitiaste zagrade


Upotreba bijelog prostora u programiranju prilino je kontroverzna. Na primjer,
ova se petlja for:
for (int i=o;i<loo;i++)

i f (i%lo == o)

Console.WriteLine("\t{o}", i);

moe zapisati s vie razmaka izmeu operatora:


for ( int i = o; i < loo; i++ )

if ( i % 10 == o )
{
Console.WriteLine("Yt{o}", i);
}
}
Upotreba bjelina veinom je stvar osobnog ukusa. Visual Studio 2005 omoguava da
koritenje bijelog prostora konfigurirate odabirom opcije Tools -> Options -> C# ->
Formatting -* Spacing.

Iskaz foreach
Iskaz foreach je novina u porodici C jezika. On se koristi za petlje kroz elemente polja
ili kolekcije. Ovaj iznimno koristan iskaz detaljnije emo obraditi u poglavlju 9.

Iskazi continue i break


Ponekad se morate vratiti na poetak petlje bez izvoenja preostalih iskaza u petlji.
Iskaz continue uzrokuje preskakanje preostalih koraka u petlji.
Druga strana medalje je mogunost naputanja petlje i izravnog zavravanja svih osta-
lih zadataka u petlji. Za tu se svrhu koristi iskaz break.

tirati

U primjeru 3-15 prikazana je mehanika iskaza continue i break. Ovaj kod mi je pred-
loio jedan od mojih recenzenata, Donald Xie, a slui razvoju sustava prometne signa-
lizacije. Signali se simuliraju unosom brojeva i velikih slova s pomou tipkovnice te
upotrebom Console.ReadLine() koja ita teksta s tipkovnice.

50 | Programiranje C#
IrniiS:
Algoritam je jednostavan: primitak nule znai normalne uvjete i daljnje akcije nisu
potrebne, osim za biljeenje dogaaja (u tom sluaju program jednostavno ispisuje
poruku na konzoli, a stvarna aplikacija mogla bi u bazu podataka unijeti zapis s vre-
menskom oznakom). Po primitku signala za prekid (simulira ga slovo ,,A) problem se
biljei i proces se prekida. Na kraju, za bilo koji drugi dogaaj pokree se alarm, koji
moda obavjetava policiju (ovaj primjer ne obavjetava policiju, ali ispisuje poruku
za uzbunu na konzoli). Ako je signal ,,X, pokree se alarm, ali se prekida i petlja
while.

P rim je r 3 - 1 5 . U p o t r e b a i s k a z a continue i break

#region Using direetives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ContinueBreak
{
class ContinueBreak
{
static void Main( stringf] args )
{
string signal = "o"; // Inicijalizira s neutralnim stanjem
while ( signal != "X" ) // X oznaava zaustavljanje
{
Console.Write( "Enter a signal: " );
signal = Console.ReadiineO;

// Obavlja neki posao, nema veze


// koji je signal primljen
Console.WriteLine( "Received: {o}", signal );

if ( signal == "A" )
{
// Pogreno - prekida obradu signala
// biljei problem i prekida.
Console.WriteLine( "Fault! AbortNn" );
break;
}

if ( signal == "o" )
{
// Normalno stanje
// biljei i nastavlja
Console.WriteLine( "Ali is well.\n" );
continue;
}

// Problem. Poduzima akciju i zatim biljei problem

P oglavlje 3 : Osnove program skog je zik a C# | 51


Primjer 3-15. Upotreba iskaza continue i break ( nastavak)
II nakon toga nastavlja.
Console.WriteLine( "{0} -- raise alarm!\n",
signal );
} I I kraj while
} I I kraj main
} // kraj class
} I I kraj namespace

Svrha ove vjebe je pokazati kako se prilikom primanja signala Aizvodi akcija u iskazu
i f i zatim program izlazi iz petlje bez pokretanja alarma. Kad je signal 0, alarm se tako-
er ne treba pokrenuti te program nastavlja na poetku petlje.

Operatori
Operator je simbol koji uzrokuje da C # pokrene akciju. Primitivni tipovi (npr. int)
podravaju razne operatore kao to su pridruivanje, poveavanje i tako dalje.

Operator pridruivanja (= )
U ranijem odjeljku ovog poglavlja, Izrazi11, prikazana je upotreba operatora pridrui-
vanja. Ovaj simbol uzrokuje promjenu vrijednosti operanda s lijeve strane operatora
u vrijednost koja se nalazi s desne strane operatora.

Matematiki operatori
U C # koristi se pet matematikih operatora: etiri za standardne izraune i peti za vra-
anje ostatka pri dijeljenju cjelobrojne vrijednosti. Upotreba tih operatora objanjena
je u sljedeim odjeljcima.

Jednostavni aritmetiki operatori (+ , * , /)


C # nudi operatore za jednostavne aritmetike operacije: zbrajanje (+), oduzimanje
(-), mnoenje (*) i dijeljenje (/). O ni funkcioniraju na oekivani nain, uz moguu
iznimku pri dijeljenju cjelobrojnih vrijednosti s nulom.
Prilikom dijeljenja jedne cjelobrojne vrijednosti s drugom, C # dijeli poput uenika u
etvrtom razredu osnovne kole: odbacuje decimalni ostatak. Stoga, 17 podijeljeno sa
4 vraa vrijednost 4 (17/4=4, uz ostatak 1). C # ima poseban operator modulo (koji je
opisan u sljedeem odjeljku) za vraanje ostatka.
C # vraa decimalne vrijednosti prilikom dijeljenja brojeva s pominim zarezom, bro-
jeva s dvostrukom preciznou i decimalne brojeve.

Operator modulo (%) za vraanje ostataka


Za vraanje ostatka pri dijeljenju cijelih brojeva koristite operator modulo (%). Na pri-
mjer, iskaz 17%4 vraa 1 (ostatak nakon dijeljenja cijelih brojeva).

52 | P ro g ra m ira n je C#
Operator modulo mnogo je korisniji nego to se to na prvi pogled ini. Kada upotrije-
bite modulo n na broju koji je viekratnik od n, rezultat je 0. Stoga je 80%10 = o jer je
80 cjelobrojni viekratnik od 10. Ta injenica omoguava postavljanje petlji u kojima
se akcija primjenjuje svaki n-ti put, testirajui na brojau je li %n jednako 0. Ova je
strategija korisna za petlju for, kao to je opisano ranije u ovom poglavlju. Utjecaj dije-
ljenja na cijele brojeve, brojeve s pominim zarezom, brojeve dvostruke preciznosti i
decimalne brojeve prikazanje u primjeru 3-16.

Primjer 3-16. Dijeljenje i operator modulo


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

endregion

namespace OivisionModulus
{
class DivisionModulus
{
static void Main( stringj] args )
{
int ii, i2;
float fl, f 2 ;
double dl, d2;
decimal decl, dec2;

11 = 17;
12 = 4;
fl = I7f;
f2 = 4f;
dl = 17;
' d2 = 4;
decl = 17;
dec2 = 4;
Console.WriteLine( "lnteger:\t{0}\nfloat:\t\t{l}",
ii / i2, fl / f2 );
Console.WriteLine( "double:\t\t{o}\ndecimal:\t{l}",
dl / d2, decl / dec2 );
Console.WriteLine( "\nModulus:\t{o}", ii % 12 );

1
}
}
Pogledajmo sljedei red iz primjera 3-16:
Console.WriteLine("Integer;\t{0 }\nfloat:\t\t{l}\n",
il/i2, fl/f2);

On poinje pozivanjem Console.WriteLine() i prosljeuje sljedei nepotpuni niz:

P oglavlje 3: Osnove program skog je zik a C# | 53


"In te g e r :\t{0}\n

To e ispisati znakove Integ er:, zatim tabulator (Vt), prvi parametar ({o}) i znak za
novi red (\n). Sljedei odlomak niza:
floa t:\t\t{l} \ n

vrlo je slian. On ispisuje float:, zatim dva tabulatora (radi osiguravanja poravnanja),
sadraj drugog parametra ({ l}) i ponovno znak za novi red. Obratite panju i na slje-
dei red:
Console. WriteLine( "\nModulus:\t{ 0 } ", il%iz);

Ovaj put niz poinje znakom za novi red, to uzrokuje preskakanje reda tono prije
ispisa niza Modulus:.

Operatori za uveavanje i umanjivanje


Uobiajen zahtjev u programiranju je dodavanje vrijednosti varijabli, oduzimanje vri-
jednosti od varijable ili neka druga promjena brojane vrijednost te pridruivanje nove
vrijednosti istoj varijabli. Moda ete ak rezultat trebati dodati sasvim drugoj varija-
bli. Ti su sluajevi opisani u sljedea dva odjeljka.

Operatori za izraun i ponovnu dodjelu


Pretpostavimo kako varijablu mySalary elite uveati za 5 000 . To moete uiniti ako
napiete:
mySalary = mySalary + 5000;

Dodavanje se provodi prije pridruivanja i ponovno pridruivanje rezultata izvornoj


varijabli u potpunosti je doputeno. Dakle, nakon to se ova operacija izvede, vrijed-
nost mySalary bit e uveana za 5 000 . Ovu vrstu pridruivanja moete kombinirati
sa svim matematikim operatorima:
mySa!ary = mySalary * 5000;
mySalary = mySalary - 5000;

i tako dalje.
Potreba za uveavanjem i umanjivanjem varijabli je toliko esta da C # sadri posebne
operatore za automatsko pridruivanje. U te se operatore ubrajaju +=, -=, /= i %= koji
kombiniraju zbrajanje, oduzimanje, mnoenje, dijeljenje i modulo s automatskim pri-
druivanjem. Stoga prethodne primjere moete napisati i na sljedei nain:
mySaJary += 5000;
mySalary *= 5000;
mySalary -= 5 0 0 0 ;

Time se mySalary uveava za 5 000 , mnoi s 5 000 i od varijable mySalary se oduzima


5 000.

54 | P ro g ra m ira n je C#
Budui da je uveavanje i umanjivanje vrijednosti za 1 vrlo esta potreba, C # (kao i C
; C++) ima i dva posebna operatora. Za uveavanje vrijednosti za 1 koristite operator
++, a za umanjivanje vrijednosti za jedan koristite operator -
Stoga, ako varijablu myAge elite uveati za 1, moete napisati:
myAge++;

Operatori prefiksa i sufiksa

Kako biste dodatno zakomplicirali stvari, varijablu moete uveati i rezultat dodijeliti
drugoj varijabli:
firstValue = secondValue++;

Sad se postavlja pitanje elite li pridruivanje prije uveavanja vrijednosti ili nakon
njega? Drugim rijeima, ako secondValue poinje s vrijednosti 1 0 , elite li da na kraju
i firstValue i secondValue imaju vrijednost 11 ili elite da firstValue ima vrijednost 10
(poetna vrijednost), a da secondValue ima vrijednost 1 1 ?

C# (ba kao i C i C++) nudi dvije vrste operatora za uveavanje i umanjivanje: prefix
\sufx. Stoga moete napisati:
firstValue = secondValue++; // Sufiks

to e prvo izvesti pridruivanje, a zatim uveanje (firstValue=lO, secondValue=ll).


Moete napisati i:
firstValue = ++secondValue; // Prefiks

to e prvo izvesti uveavanje, a zatim pridruivanje (firstValue=ll, secondValue=ll).


Vano je razumjeti razliite uinke ova dva operatora koji su prikazani u primjeru

Primjer 3-1 7. Uveavanje s pomou operatora prefix i suftx


ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace PrefixPostfix
{
class PrefixPostfix
{
static void Main( stringf] args )
{
int valueOne = 10;
int valueTwo;
valueTwo = valueOne++;
Console.WriteLine( "After postfix: {o}, {i}", valueOne,
valueTwo );

P oglavlje 3: Osnove program skog je z ik a C# | 55


Primjer 3-17. Uveavanje s pom ou operatora prefix i sufix (nastavak)
valueOne = 20;
valueTwo - ++valueOne;
Console.WriteLine( "After prefix: {0}, {l}", valueOne,
valueTwo );

}
}
}

Relacijski operatori
Relacijski se operatori koriste za usporedbu dvije vrijednosti i vraaju Boolean vrijed-
nost (istinito ili neistinito). Operator vee od (>)vraa istinitu vrijednost ako je vrijed-
nost s lijeve strane operatora vea od vrijednosti s desne strane. Stoga 5>2 vraa true,
a 2>5 vraa -false.
Relacijski operatori jezika C # prikazani su u tablici 2-3. Ona pretpostavlja dvije vari-
jable: bigValue i smallValue, gdje bigValue ima vrijednost 100, a smallValue vrijednost
50.

T ablica 3 - 3 . R e la c ijsk i o p e r a t o r i je z ik a C # (p retp o sta v lja s e d a j e bigValue = 100


i smallValue = 50)

Naziv Operator Iskaz Vrijednost iskaza


Jednako == bigValue == 100 true
bigValue == 8 0 false

Nijejednako f= bigValue != 1O0 false

bigValue != 80 true

Vee od > bigValue > smallValue true

Vee ilijednako >= bigValue >= smallValue true


smallValue >= bigValue false

Manje od < bigValue < smallValue false

Manje ilijednako <= smallValue <= bigValue true


bigValue <= smallValue
false

Relacijski operatori funkcioniraju na uobiajeni nain. Meutim, obratite panju na


operator jednakosti (==) koji se pie s dva uzastopna znaka jednako (=) (tj. izmeu njih
ne smije biti razmaka). C # prevoditelj taj par znakova tretira kao jedan operator.
Operator jednakosti jezika C # (==) testira jednakost objekata s obje strane operatora.
On vraa Boolean vrijednost (true ili fa lse ) pa iskaz:
myX == 5;

v ra a v rije d n o s t tr u e a k o i sa m o a k o v a rija b la myX im a v rije d n o st 5.

56 | P ro g ra m ira n je C#
O perator pridruiva.nja (=) s. e esto mi>je----a----s-- Jo/pV e.UrVaVtoIUro1 1m
7-, jeUdUndat VkUobsltl i (==)
1 IV
D ru gi se saston od dva 7 naka ip r ln a ^ e ; -------
D gl Se sast0 , od dva znaka jed n akosti, a prvi od sam o jednog.

Upotreba logikih operatora s uvjetima


Iskazi i f (objanjeni ranije u ovom poglavlju) testiraju istinitost uvjeta. esto ete tre-
bati testirati jesu li oba od dva uvjeta istinita, je li istinit samo jedan ili su oba neistinita.
C# za to nudi skup logikih operatora koji su prikazani u tablici 3-4. U ovoj tablici
pretpostavljene su dvije varijable, x i y, od kojih * ima vrijednost 5 , a y vrijednost 7 .

Tablica 3 - 4 . L o g i k i o p e r a to r i je z ik a C # (p retp ostav lja s e d a j e x = 5 a y = 7 )

& Operator Iskaz Vrijednostiskaza


I && (x == 3) && (y == 7 ) fa ls e
IL I N (* == 3) || (y == 7) true

N E ! (x = 3) true

Operator I testira jesu li oba iskaza istinita. Prvi red u tablici 3 -4 sadri primjer koji
prikazuje upotrebu operatora I: y 1 J
(x == 3 ) && (y == 7 )

Cijeli je izraz neistinit jer je neistinita jedna strana (x == 3 ).

Kod operatora IL I jedna ili obje strane moraju biti istinite. Izraz je neistinit samo ako
su obje strane neistinite. Stoga, u sluaju primjera iz tablice 3-4:
(x = = 3 ) || (y == 7 )

cijeli je izraz istinit jer je istinita jedna strana (y==7).

primjeru-310"3 S!<aZje ^ je im z " eistinit 1obrnut0' Dakle, u navedenom

(x - 3)

S d a i ; : d ; z v ; K ,ira n i izr ( - s n tinit ^ ^

Prednost operatora
S a p L T < * < * * . A K Pri-
myVariable = 5 + 7 * 3 ;

n Td eln elj-trebt Pr0Cijen'^ tH PeJrat0ra (= +1 PWqena moena Primjer, ii s lijeva


Se; njedn0St 5 dodij elila varijabli myVariable, zatim bi se 7 zbrojilo
oirn P mn0i]0 s 3 - no ta bi se vrijednost zatim odbacila. To
oito nije pravi nain.

P o g lavlje 3 : Osnove programskogjezika Of | 57


Pravila prednosti prevoditelju govore koje operatore treba prvo procijeniti. Kao to
je to sluaj i u algebri, mnoenje ima prednost ispred zbrajanja, stoga je 5+7*3 jed-
nako 26, a ne 36. 1 zbrajanje i mnoenje imaju prednost pred pridruivanjem pa e
prevoditelj prvo rijeiti matematiku, a tek nakon toga rezultat (26) pridruiti varijabli
myVariable.

Za promjenu redoslijeda operacija se u C# , kao i u algebri, koriste zagrade. Stoga rezul-


tat moete promijeniti ako napiete:
myVariable = (5+7) * 3;

Izravna procjena
P ogledajm o slje d e i o d lo m a k kod a:

int x = 8;
if ((* == 8) U (y 1 2 ))

Ovdje naveden iskaz i f pom alo je sloen. Cijeli iskaz i f nalazi se unutar zagrada,
kao i svi iskazi i f u jeziku C $ . Stoga, da bi iskaz i f bio istin iti, sve unutar vanjskih
zagrada mora biti istinito.
U nutar vanjskih zagrada nalaze se dva iskaza (x==8) i (y==l2), koji su odvojeni operato-
rom ILI (||). Budui da je x jedn ak o 8 , prvi iskaz (x 8) je istin it. Drugi iskaz (y==i2)
ne treba se provjeravati je r za istinitost cijelog izraza nije bitno je li y jedn a ko 12.
Pogledajmo i ovaj odlom ak koda:
in t x = 8;
if ((x == 5) && (y == 12))
Ponovno se drugi iskaz ne mora provjeravati. Budui da je prvi iskaz neistinit, I mora
im ati vrijednost neistinito (oba provjerena izraza m oraju biti istin ita kako bi iskaz I
bio istinit).
U ovakvim slu ajevim a, C # e skratiti provjeru. D rugo testiranje se uope nee
provesti.

Zbog ovakvog grupiranja elemenata pridruivanja, prevoditelj zbraja 5 i 7, rezultai


mnoi s 3 i zatim dobijenu vrijednost (36) dodjeljuje varijabli myVariable. Saeti prika;
prednosti operatora moete pronai u tablici 3-5.

Tablica 3-5. Prednost operatora

Kategorija Operatori

Primarni ( x ) x . y x - > y f ( x ) a [ x ] x++ x - - n e w ty p e o f s i z e o f ch e cke d un checked


s t a c k a l lo c
Unarni + -!~ + + x -x (T )x * x & x

Mnoenje + /%
Zbrajanje + -
Pomak

58 | P ro g ra m ira n je C#
Tablica 3-5- Prednost operatora (nastavak)

f kategorija Operatori
Relacijski < > < = > = isas
jednakost

Logiko I &
Logikoekskluzivno ILI A

Logiko ILI

Uvjetno I &&
Uvjetno ILI II
Uvjetni
Pridruivanje = *= / = + = -= = & = a = |=

U nekim sloenim jednadbama moda ete morati ugnijezditi zagrade kako biste
osigurali ispravan redoslijed operacija. Pretpostavimo da elite saznati koliko sekundi
moja obitelj izgubi svakog jutra. Ispostavlja se da odrasli svakog jutra potroe 20
minuta uz kavu i 10 minuta itajui novine. Djeca na ljenarenje potroe 30 minuta,
a na svau 10 minuta.
Evo mog algoritma;
(((minOrinkingCof-fee + minRead ing N ewspape r )* nu mAdu lts ) +
((minDaivdling + min Arguin g) * numChildren)) * secondsPerMinute.

lako ovaj algoritam funkcionira, teko ga je proitati i ispravno sastaviti. Sljedei nain
koritenja meuvarijabli mnogo je jednostavniji:
wastedByEachAdult = minDrinkingCoffee + minReadingNeivspaper;
wastedByAHAdults = wastedByEachAdult * numAdults;
wastedByEachKid = minDawdling + minArguing;
wastedByAUKids = wastedByEachKid * numChildren;
wastedByFamily = wastedByAllAdults + wastedByAllKids;
totalSeconds = wastedByFamily * 60;

U drugom se primjeru koristi mnogo vie varijabli, ali je taj primjer mnogo lake
proitati i razumjeti te (najvanije od svega) u njemu ispraviti pogreke. Prilikom pre-
gledavanja ovog programa alatom za ispravljanje pogreaka moete lako uoiti meu-
vrijednosti i provjeriti jesu li one ispravne.

Ternarni operator
Iako veina operatora zahtjeva jedan (npr. myValue++) ili dva izraza (npr. a+b) postoji
jedan operator koji zahtjeva tri: ternarni operator (?:):
uvjetni izraz ? izrazi : izraz2

Ovaj operator procjenjuje uvjetni izraz (izraz koji vraa vrijednost tipa bool), a zatim
poziva iz razi ako je uvjetni izraz vratio istinitu vrijednost ili izraz 2 ako je vraena nei-
stinita vrijednost. Logika je ako je ovo istinito, uini prvo, u suprotnom uini drugo".
To je prikazano u primjeru 3-18.

P o g lavlje 3: Osnove program skog je zika C# | 59


Primjer 3-18. Ternarni operator
#region Using directives

using System;
using System.Collections.Ceneric;
using System.Text;

#endregion

namespace TernaryOperator
{
class TernaryOperator
{
static void Main( string[] args )
{
int valueOne = 10;
int valueTwo = 2 0 ;

int maxValue = valueOne > valueTwo ? valueOne : valueTwo;

Console.WriteLine( "ValueOne: {0}, valueTwo: {l}, maxValue: {2}",


valueOne, valueTwo, maxValue );

}
}
}
U Primjeru 3-18 ternarni se operator koristi za testiranje je li valueOne vee od valu-
eTwo. Ako je to sluaj, vrijednost valueOne se dodjeljuje cjelobrojnoj varijabli maxValue.
U suprotnom se vrijednost valueTwo dodjeljuje maxValue.

Naredbe za pretprocesor
U svim dosad navedenim primjerima prevodili ste cjelokupni program. Ponekad je,
meutim, potrebno prevesti samo dijelove programa - na primjer, ovisno o tome ispra-
vljate li pogreke ili prevodite finalni kod.
Prije nego to je kod preveden, pokree se drugi program koji se naziva pretprocesor
i priprema program za prevoditelj. Pretprocesor provjerava postoje li u kodu posebne
naredbe za pretprocesor koje uvijek poinju znakom ljestve (# ). Ove naredbe omogu-
avaju zadavanje identifikatora i testiranje njihova postojanja.

Definiranje identifikatora
Naredba #define DEBUG definira identifikator pretprocesora DEBUG. Iako se druge direk-
tive za pretprocesor mogu nalaziti bilo gdje unutar koda, identifikatori se moraju defi-
nirati prije svakog drugog koda, ukljuujui iskaze using.

60 | P ro g ra m ira n je C#
0
D d ^ n c Z C i C + + P m 8 m m e r e : C # P retprocesor primjenjuje s a m o
p o d s k u p C + + pretprocesora i ne podrava m a k r o naredbe.

Moete p ro v jeriti je li DEBUG d efin ira n s iskazo m # i f . D a kle, m oete napisati:


#define DEBUG

1 1 .. . normalni kod na koji pretprocesor ne djeluje

#if DEBUG
// kod koji e biti ukljuen u uklanjanje pogreaka
#else

#endifd ^ ^ UkljUen ^ ne radi isPr^ l ja nj e pogreaka

1 1 .. . normalni kod na koji pretprocesor ne djeluje

Kad se p re tp ro ce so r p o k re n e , v idjet e iskaz # def.ne i zab iljeiti id en tifika to r DEBUG


P retprocesor p resk a e u o b ia je n C # kod i p ron ala zi blo k # i f - # e ls e - # endif.

Jskaz i f pro vjerava p o sto ji li id e n tifik a to r DEBUG ko ji uistinu p o sto ji i stoga se kod
meu f , , 1 prevodi p,gram - ali se kod lzmeu , , ej di ' J J
Taj se kod uope ne pojavljuje u sklopu - kao da ga uope niste n. napisali. '

Dk i kaZ " t0 j6 S t ^ Ste t6Stlrali P St0ji H >de n tifik a to r koji n ije p o sto ja o
kod izm eu # i f i # e ls e se ne bi prevodio , za razliku od koda izm eu # i f i ifen dif.
**
r*&-
Pretprocesor ne utjee na kod koji se ne nalazi izmeu #if/#endif i on
t se prevodi u program.
ik '

Ponitavanje definiranih identifikatora


k o ^ "rani dentifikator moete Por,ititi s pomou #undef. Pretprocesor prolazi kroz
#undef illdo3 Prema, dnU St ga S identifikator definira od iskaza #define do iskaza
#undef ih do zavretka programa. Stoga, ako napiete:
#define DEBUG

#if DEBUG
// ovaj e se kod prevesti
#endif

#undef DEBUG

# if DEBUG
// ovaj kod se nee prevesti
#endif

k p ' o n t o n " ' ' i ' * defil,lran0) dra* i

Poglavlje 3: Osnove programskog jezika C# | 61


#if, #elif, #else i #endif
Za pretprocesor ne postoji naredba switch, ali naredbe # e lif i # else pruaju veliku
prilagodljivost. Naredba # e lif omoguava logiku u suprotnom-ako tj. ako DEBUG
onda prva akcija, u suprotnom ako TEST onda druga akcija, inae trea akcija":
ftif DEBUG
I I prevodi ovaj kod ako je definirano debug
#elif TEST
I I prevodi ovaj kod ako debug nije definirano
I I ali je definirano TEST
#else
I I prevodi ovaj kod ako ni DEBUG ni TEST
I I nije definirano
#endif

U ovom primjeru pretprocesor prvo provjerava je li definiran identifikator DEBUG. Ako


jest, prevodit e se kod izmeu #if i #elif, a ostatak koda sve do #endif se nee pre-
voditi.
Ako (i samo ako) identifikator DEBUGnije definiran, pretprocesor e provjeriti je li defi-
niran TEST. Pretprocesor e postojanje identifikatora TEST provjeriti samo ako DEBUG
nije definiran. Ako je TEST definiran, prevodit e se kod izmeu direktiva #elif i #else.
Ako se ustanovi kako ni TEST ni DEBUG nisu definirani, prevodit e se kod izmeu iskaza
#else i #endif.

#region
Naredba itregion polje teksta oznaava komentarom. Ona se u prvom redu upotre-
bljava da bi se alatima kao sto je Visual Studio .N ET omoguilo izdvajanje odreenog
dijela koda i njegovo saimanje u programu za ureivanje tako da se vidi samo naredba
itregion i njen komentar.
Kad, na primjer, piete Windows aplikaciju (sto je objanjeno u poglavlju 13), Visual
Studio stvara podruje za kod koji e upisati razvojni inenjer. Kad se podruje proiri,
ono izgleda poput podruja prikazanog na slici 3-3 (napomena: na slici je radi lakeg
snalaenja podruje istaknuto i oznaeno etverokutom).
Moete vidjeti podruje oznaeno naredbama itregion i ifendregion. Meutim, kad
podruje samete, vidjet ete samo komentar podruja (Windows Form Designer gene-
rated code), kao to je prikazano na slici 3 -4 .

62 | Programiranje C#
S' |f.-hyu'ii U irid ous Focin D e s i g n e r y-ru ;t.H L-d c o d e

jr. i v u t c v o>rl I n i t - u v l Lr-eCcvropoirem; O

D I .
t h i s . j U i t o S c a l e B u s e S i s e - ncw 5 ? s t e j n . l ov ikj S i . i t ( S , 1 3 ) ;
c U i a . C l i e n L 3 i c e " n t c S y s t c . P r a u m g . S i s e (2 5 2 , 2 7 3 ) ;
ibia.Neune - "Formi";
t b i 3 . T e x t ' " F o r m i" ;
t M s . L o a d +* n a v S y o r e i o . E v e n t H a n d l e r ( th l a . F o r m i L o a d ) ;

Slika 3-3. Proirivanje podruja koda u Visual Studiju

I r i n d o t a For;^ I i e a i g r .a r g c n e r a t & d csdoj

Slika 3-4. Saeto podruje koda

Poglavlje 3: Osnove programskogjezika C# | 63


POGLAVLJE 4
Klase i objekti

U poglavlju 3 objasnili smo primitivne tipove koji su dio jezika C# , poput in t, long
i char. Bit jezika C # zapravo je mogunost stvaranja novih, sloenih tipova koje defi-
nira sam programer, a koji jasno preslikavaju objekte od kojih se sastoji problem koji
pokuavate rijeiti.
Upravo je ova mogunost stvaranja novih tipova glavna karakteristika objektno orijen-
tiranih jezika. Nove tipove u jeziku C # zadajete deklariranjem i definiranjem klasa.
Tipove moete definirati i s pomou suelja (engl. interfaces ), to je detaljnije obja-
njeno u poglavlju 8. Instance klase nazivaju se objekti (engl. objects). Objekti se stva-
raju u memoriji prilikom izvoenja programa.
Razlika izmeu klase i objekta jednaka je razlici izmeu koncepta psa i stvarnog psa
koji vam moda sjedi kraj noge dok ovo itate. Definiciji psa ne moete baciti tap, to
moete uiniti samo s instancom.
Klasa Dog opisuje kakvi su psi: oni imaju svoju teinu, visinu, boju oiju, boju dlake,
narav i tako dalje. Karakteriziraju ih i akcije koje mogu izvesti, na primjer, mogu jesti,
hodati, lajati i spavati. Odreeni pas (npr. moj pas Milo) ima odreenu teinu (31 kg)
i visinu (55 cm), boju oiju (crna), boju dlake (uta), narav (pravi aneo) i tako dalje.
On ima iste sposobnosti kao svi psi (iako oni koji ga poznaju misle kako primjenjuje
samo metodu jedenja).
Ogromna prednost klasa u objektno orijentiranom programiranju je da su u njima
osobine i sposobnosti entiteta uahurene (engl. encapsulated ) u jedinstvenoj, samo-
stojnoj i samoodrivoj jedinici koda. Kada, na primjer, elite sortirati sadraj instance
padajueg popisa, samo popisu kaete da se sortira. Kako e to uiniti nije bitno - sve
to je bitno je da je sortiranje izvedeno. Uahurivanje, zajedno s polimorfizmom (engl.
polymorphism) i nasljeivanjem (engl. inheritance), je jedan od tri osnovna naela
objektno orijentiranog programiranja.
Stari programerski vic kae: Koliko je objektno orijentiranih programera potrebno
da bi se promijenila arulja? Odgovor: nijedan, arulji jednostavno kaete da se sama
promijeni (drugi odgovor: nijedan, Microsoft je standard promijenio na mrak).

64

I
U ovom su poglavlju opisane znaajke jezika C # koje se koriste za zadavanje novih
klasa. Elementi klase - ponaanja (engl. behaviors) i svojstva (engl. properties) - zajed-
niki se nazivaju lanovima klase (engl. class members). U ovom poglavlju opisat emo
kako se metode koriste za definiranje ponaanja klase i kako se stanje klase odrava u
varijablama lanicama (koje se esto nazivaju polja (engl.fields)). Pored toga, poglavlje
se bavi i svojstvima koja razvojnim inenjerima izgledaju poput metoda, a klijentima
klase kao polja.

Definiranje klasa
Za definiranje novog tipa ili klase prvo treba deklarirati, a zatim definirati pripadajue
metode i polja. Klasa se deklarira s pomou kljune rijei class. Potpuna sintaksa
glasi:
[atributi] [modifikator pristupa] class identifikator [:osnovna klasa [,suelja(e)]]
(tijelo klase}

Atributi su objanjeni u poglavlju 8; modifikatori pristupa su objanjeni u sljedeem


odjeljku (kao modifikator pristupa klase obino se koristi kljuna rije public). identi-
fikator je naziv klase koji unesete. Neobavezna osnovna klasa objanjena je u poglavlju
5. Definicije lanova od kojih se sastoji tijelo klase piu se izmeu vitiastih zagrada
({})

Napomena za C i C++ programere: u C # definicija klase ne zavrava


tokom-zarez, no ako je sluajno dodate, program e se svejedno pre-
vesti.

U C# se sve dogaa unutar klase. Dosad, meutim, nismo instancirali nijednu instancu
klase; to jest, nismo stvorili nijedan objekt. Koja je razlika izmeu klase i instance te
klase? Kako bismo odgovorili na to pitanje, krenut emo od razlike izmeu tipa in t i
varijable tipa int. Moete, na primjer, napisati:
int mylnteger = 5;

no ne moete napisati:
int = 5;

Vrijednost se ne moe dodijeliti tipu. Ona se dodjeljuje objektu koji pripada tom tipu
(u ovom sluaju, varijabli tipa int).
Prilikom deklariranja nove klase programer definira svojstva svih objekata koji pripa-
daju toj klasi, kao i njihova ponaanja. Na primjer, stvarate li okoli s prozorima trebat
ete stvoriti standardne elemente prozora (u Windows programiranju oni se nazivaju
kontrole (engl. Controls)) kako biste korisniku pojednostavili interakciju s aplikacijom.
Jedna od zanimljivijih kontrola je padajui popis koji prikazuje niz opcija i korisniku
omoguava da odabere neke od njih.

Poglavlje 4: Klase i objekti | 65


Padajui popisi imaju razliite karakteristike - na primjer, visinu, irinu, poloaj i boju
teksta. Programeri od padajuih popisa obino oekuju i odreena ponaanja: oni se
mogu otvoriti, zatvoriti, sortirati i tako dalje.

Objektno orijentirano programiranje dozvoljava stvaranje novog tipa, ListBox, u kojem


su uahurene sve ove karakteristike i mogunosti. Takva klasa kao lanove moe imati
varijable height, width, location i text_ color te metode so rt(), add(), remove() itd.
Tipu ListBox ne moete dodijeliti podatke. Prvo morate stvoriti objekt tog tipa, kao u
sljedeem odlomku koda:
ListBox myListBox;

Kad ste stvorili instancu ListBox, njenim poljima moete dodijeliti podatke.

Uzmimo za primjer klasu koja slui za praenje i prikaz vremena. Unutarnje stanje
klase mora moi prikazati tekuu godinu, mjesec, datum, sat, minutu i sekundu.
Vjerojatno ete htjeti da klasa vrijeme prikazuje u razliitim formatima. Takvu klasu
moete implementirati definiranjem jedne metode i est varijabli, kao to je prikazano
u primjeru 4-1.

Primjer 4-1. Jednostavna klasa za prikazivanje vremena


Kregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace TimeClass
{
public class Time
{
// privatne varijable
int Vear;
int Month;
int Date;
int Hour;
int Minute;
int Second;

// javne metode
public void DisplayCurrentTime()
{
Console.WriteLine(
"stub for DisplayCurrentTime" );
}
}

public class Tester

66 | Programiranje C#
p imjer 4-1- J ednostavna ^ asa za Pokazivanje vremena (nastavak)

^ static void Main()


{
Time t = new Time();
t .DisplayCurrentT ime();

<*.

S
Prilikom prevoenja ove klase primit ete upozorenja o tome da se

varijable lanice Time (Vear, Mgnth itd.) nikad ne koriste. Zasad zanema-
*' rite ta upozorenja (iako upozorenja uglavnom nije dobro zanemarivati,
osim ako niste potpuno sigurni o emu se radi i zato ih moete zane-
mariti). U ovom sluaju pravimo samo kostur klase Time i, da se radi o
stvarnoj klasi, ove lanove bi koristili u drugim metodama.

Jedina metoda deklarirana unutar definicije klase Time je DisplayCurrentTime(). Tijelo


metode je definirano unutar definicije same klase. Za razliku od ostalih jezika (kao
to je C++), C# ne zahtijeva da se metode deklariraju prije nego to se definiraju^ niti
podrava stavljanje deklaracija i koda u razliite datoteke (u C# ne postoje datoteke
zaglavlja). Sve metode jezika C # definiraju se u kodu, kao to je prikazano u primjeru
4-1 kod DisplayCurrentTime().

Metoda DisplayCurrentTime() definirana je tako da vraa void tj. ona metodi koja ju
pozove nee vratiti vrijednost. Zasad je tijelo ove metode izbrisano.
D efinicija k la se Time zavrava d e k la ra c ijo m n e ko lik o v a rija b li la n ic a : Year, Month,
Date, Hour, Minute i Second.

Nakon zatvorene zagrade definira se druga klasa, Tester. Ona sadri ve poznatu
metodu Main(). U metodi Main() je stvorena instanca varijable Time i njena je adresa
dodijeljena objektu t . Budui d aje t instanca varijable Time, Main() moe s objektima
tog tipa iskoristiti metodu DisplayCurrentTime() i pozvati je kako bi se prikazalo vri-
jeme:
t.DisplayCurrentTime();

Modifikatori pristupa
Modifikator pristupa zadaje koje metode drugih klasa mogu vidjeti i koristiti odreenu
varijablu ili metodu unutar klase. U tablici 4-1 moete vidjeti saetak modifikatora
pristupa u jeziku C# .

Poglavlje 4: Klase i objekti | 67


Tablica 4-1. Modifikatori pristupa

Modilikator pristupa Ogranienja


public Nema ogranienja. lanovi ogranieni modifikatorom public vidljivisu svim metodama izsvih
klasa.

private lanovima klase A koji su oznaeni modifikatorom p r i v a t e mogu pristupiti samo metode klase A.

protected lanovima klase A koji su oznaeni modifikatorom p r o t e c t e d mogu pristupiti metode klase A i
metode onih klasa koje su izvedene iz klase A.

internal lanovima klase A koji su oznaeni modifikatorom i n t e r n a l mogu pristupiti metode svih klasa
iz sklopa klase A.

protected lanovima klase A koji su oznaeni modifikatorom p r o t e c t e d i n t e r n a l mogu pristupiti


internal metode klase A, metode klasa koje su izvedene iz klase A i metode svih klasa iz sklopa klase A. Ovo
je zapravo p r o t e c t e d ILI i n t e r n a l (ne postoji koncept p r o te c te d I i n t e r n a l ) .

lanove klase je poeljno oznaiti modifikatorom private. To znai da njihovoj vri-


jednosti mogu pristupiti samo metode koje su lanice te klase. Budui da je private
podrazumijevana razina pristupa, ne morate je eksplicitno navoditi, ali vam prepo-
ruujem da to ipak uinite. Dakle, deklaracije varijabli iz primjera 4-1 trebale su biti
napisane na sljedei nain:
// privatne varijable
private int Vear;
private int Month;
private int Date;
private int Hour;
private int Minute;
private int Second;

Klasa Tester i metoda DisplayCurrentTime() deklarirane su kao public kako bi ih


mogle koristiti druge klase.

Dobra je programerska praksa izriito zadati dostupnost svih metoda


i lanova klase. Iako se moete osloniti na injenicu da se sve klase
prema zadanim postavkama deklariraju kao private, izriito zadavanje
pristupa oznaava svjesnu odluku i olakava pregled koda.

Argumenti metoda
Metode mogu imati beskonaan broj param etara.' Popis parametara nalazi se iza
naziva metode unutar zagrada, a ispred svakog parametra stoji njegov tip. Sljedeom
se deklaracijom, na primjer, definira metoda MyMethod() koja vraa vrijednost void
(tj. ne vraa nikakvu vrijednost) i koja ima dva parametra: cjelobrojnu vrijednost i
gumb.

Termini argument" i parametar" se esto koriste za istu stvar iako neki programeri inzistiraju na razli-
kovanju deklaracije parametra i argumenta proslijeenih prilikom pozivanja metode.

68 | Programiranje C#
void MyMethod (int firstParam, Button secondParam)
{
II ...
}
Parametri unutar tijela metode funkcioniraju kao lokalne varijable, kao da ste ih dekla-
rirali u tijelu metode i inicijalizirali ih s proslijeenim vrijednostima. U primjeru 4-2
prikazan je nain prosljeivanja vrijednosti u metodu u ovom sluaju radi se o vri-
jednostima tipa int i float.

Primjer 4-2. Prosljeivanje vrijednosti u SomeMethodO


Kregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace PassingValues
{
public class MyClass
{
public void SomeMethodf int firstParam, float secondParam )

Console.Writeline(
"Here are the parameters received: {o}, {i}",
firstParam, secondParam );
}
}

public class Tester


{
static void Main()
{
int howManyPeople = 5;
float pi = B.i4f;
MyClass mc = new HyClass();
mc.SomeMethod( howManyPeople, pi );
}
}
}
Metoda SomeMethod() prihvaa int i float iprikazuje ih koristei Console.WriteLine(). Para-
metri firstParam i secondParam se tretiraju kao lokalne varijable unutar SomeMethod().

Napomena za VB6 programere: metode jezika C # ne doputaju dekla-


riranje dodanih argumenata. Umjesto toga za stvaranje metoda koje
5$ deklariraju druge kombinacije argumenata morate koristiti preoptere-
ivanje metode. Vie informacija potraite u odjeljku Preoptereivanje
metoda i konstruktora'1u ovom poglavlju.

Poglavlje 4: Klase i objekti | 69


U pozivajuoj metodi (Main) stvaraju se i inicijaliziraju dvije lokalne varijable (how-
ManyPeople i pi). One se kao parametri prosljeuju SomeMethod( ). Prevoditelj howManyPe-
ople preslikava u firstParam, a pi u secondParam na temelju njihova poloaja u popisu
parametara.

Stvaranje objekata
U poglavlju 3 objanjena je razlika izmeu vrijednosnih tipova i referentnih tipova.
Primitivni tipovi jezika C # (in t, char itd.) su vrijednosni tipovi i stvaraju se na stogu.
Za razliku od njih, objekti su referentni tipovi i stvaraju se na gomili s pomou kljune
rijei new. Na primjer:
Time t = new Time();

t zapravo ne sad ri v rije d n o s t za o b je k t Time, ve sa m o a d resu tog (neim enov ano g)


o b je k ta koji je stvo ren na g o m ili, t je sa m o referen ca tog o b je k ta .

a*
Napomena za VB6 programere: dok se u VB 6 kljune rijei Dim i Newne
bi trebale koristiti u istom redu jer to naruava izvedbu, u C # to nije
sluaj. Stoga u C # ne postoji zapreka koritenju kljune rijei new prili-
kom deklariranja varijable objekta.

Konstruktori
U primjeru 4-1 primijetit ete kako iskaz kojim se stvara objekt Time izgleda kao da
poziva metodu:
Time t = new Time();

Metoda se zapravo i poziva svaki put kad instancirate objekt. Ta se metoda naziva
konstruktor i morate ju definirati u okviru definicije klase ili dopustiti da je CLR osi-
gura umjesto vas. Zadatak konstruktora je stvaranje objekta koji je definiran klasom
i njegovo postavljanje u ispravno stanje. Prije pokretanja konstruktora objekt je samo
jo jedan dio memorije. Nakon to se konstruktor izvede u memoriji e stajati valjana
instanca klase type.
Klasa Time iz primjera 4-1 ne definira konstruktora. Ako konstruktor nije deklariran,
prevoditelj e ga sam pruiti. Podrazumijevani konstruktor stvara objekt, ali ne izvodi
nikakvu drugu akciju.
Varijable koje su lanice klase inicijaliziraju se u bezazlene vrijednosti (cjelobrojne vri-
jednosti se inicijaliziraju s nulom, nizovi s praznim nizom itd.).' U tablici 4-2 nalazi se
popis vrijednosti koje se podrazumijevano dodjeljuju primitivnim tipovima.

Kad piete vlastiti konstruktor vidjet ete da su vrijednosti inicijalizirane prije pokretanja konstruktora.
Postoje dva koraka u proesu stvaranja objekta - prvo se izvodi magija" na razini CLR-a koja resetira sva
polja i ini sve to je potrebno uiniti kako bi se napravio odgovarajui objekt i zatim se prelazi na kon-
struktor koji ste pruili (ako jeste).

70 | Programiranje C#
Tablica 4-2. Primitivni tipovi i njihove podrazumijevane vrijednosti

Tip Podrazumijevana vrijednost

brojani (int, long, itd.) o

logiki False

char ,\o' (nuli)

enum o

reference nuli

U veini sluajeva ete konstruktor definirati sami i proslijediti mu argumente kako bi


mogao postaviti objekt u poetno stanje. Za potrebe primjera 4-1 pretpostavimo kako
elite upisati tekuu godinu, mjesec, datum i tako dalje, tako da objekt bude stvoren
sa smislenim podacima.

Za definiranje konstruktora deklarirajte metodu s istim imenom kao i klasa u kojoj


je deklarirate. Konstruktori nemaju povratni tip i obino se deklariraju kao javni
(public). Ako postoje argumenti za prosljeivanje, popis argumenata definirajte kao i
za bilo koju drugu metodu. U primjeru 4-3 prikazano je deklariranje konstruktora za
klasu Time koja prihvaa jedan argument, objekt tipa DateTime.

Primjer 4-3. Deklariranje konstruktora


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace DeclaiingConstructor
{
public class Time
{

// privatne varijable lanice


int Year;
int Month;
int Date;
int Hour;
int Minute;
int Second;

// javne metode za pristupanje


public void DisplayCurrentTime()
{
System.Console.WriteLine( "{o}/{l}/{ 2 } {3}:{4}:{5}",
Month, Date, Year, Hour, Minute, Second );
}

Poglavlje 4: Klase i objekti | 71


Primjer 4-3. Deklariranje konstruktora (nastavak)
11 konstruktor
public Time( System.DateTime dt )
{

Year = dt.Year;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute;
Second = dt.Second;
}
}
public class Tester
{
static void Main()
{
System.DateTime currentTime = System.DateTime.Now;
Time t = new Time( currentTime );
t.DisplayCurrentTime();
}
}
}
U ovom primjeru konstruktor prihvaa objekt DateTime i inicijalizira sve pripadajue
varijable na temelju vrijednosti unutar objekta. Kad konstruktor zavri, objekt Time
postoji i vrijednosti su inicijalizirane. Kad se u Main() pozove DisplayCurrentTime(),
prikau se vrijednosti.
Pokuajte jedno od pridruivanja izdvojiti u komentar i ponovno pokrenuti program.
Vidjet ete kako je prevoditelj varijablu lanicu inicijalizirao u 0. Cjelobrojne varijable
lanice se postavljaju na 0 ako im ne dodijelite neku drugu vrijednost. Upamtite, inci-
jalizacija vrijednosnih tipova (npr. cjelobrojnih vrijednosti) se ne moe ponititi. Ako
ne zadate to konstruktor treba raditi, on e pokuati uiniti neto bezazleno.
U primjeru 4-3 objekt DateTime je stvoren u metodi Main() klase Tester. Ovaj objekt,
koji daje biblioteka System, nudi nekoliko javnih vrijednosti - Year, Month, Day, Hour,
Minute i Second - koje odgovaraju privatnim varijablama objekta Time. Uz to, objekt
DateTime nudi i statiko svojstvo Now koje je referenca instance objekta DateTime ini-
cijaliziranog s trenutnim vremenom.
Pogledajte istaknuti dio koda unutar Main() gdje je objekt DateTime stvoren pozivanjem
statikog svojstva Now. Now stvara vrijednost DateTime koja se u ovom sluaju kopira
u varijablu currentTime na stogu.

Varijabla currentTime se kao parametar prosljeuje konstruktoru Time. Parametar kon-


struktora Time, dt, je kopija objekta DateTime.

72 | Programiranje C#
Inicijalizatori
Vrijednosti varijabli lanica mogu se inic.jalizirati u incijalizatoru (engl. initializer)
umjesto u svakom konstruktoru. Inicijalizator moete stvoriti pridruivanjem poetne
vrijednosti elanu klase:
private int Second = 30; // inicijalizator

Pretpostavimo da je semantika naeg objekta Time takva da se, bez obzira na posta-
vljeno vrijeme, sekunde uvijek iniijaliziraju na 30. Kad bi klasu Time ponovno napisali
tako da koristi incijahzator, bez obzira na to koji se konstruktor pozove vrijednost
Second bi se uvijek micijalizirala eksplicitno s pomou konstruktora ili implicitno s
pomou lnicijanzatora. Pogledajte primjer 4-4.

ri r
U primjeru 4 -4 koristi se preoptereeni (engl. overloaded ) konstruktor,
<> 4 . ? .Zna<rI da P stoie dvije inaice konstruktora koje se razlikuju po
_ tk *. broju i tipu parametara. Preoptereeni konstruktori detaljnije su obja-
njeni kasnije u ovom poglavlju.

Primjer 4-4. Koritenje inicijalizatora


(tregion Using direetives

using System;
using System.Collections.Ceneric;
using 5ystem.Text;

kendregion

namespace Initializer
{
public class Time
{
// privatne varijable clanice
jirivate int Year;
private int Month;
private int Date;
private int Hour;
private int Minute;
private int Second = 30; // inicijalizator

// javne metode za pristupanje


public void DisplayCurrentTime()

System.DateTime now = System.DateTime.Now;


System.Console.WriteLine(
"\nDebug\t: {0}/{i}/{2 } {3}:{4}:{5}",
now.Month, now.Day, now.Year, now.Hour,
now.Minute, now.Second );

System.Console.WriteLine( "Time\t: {o}/{l}/{2 } {3 >-f4 > fs>"


Month, Date, Year, Hour, Minute, Second ); 1 '

Poglavlje 4: Klase i objekti | 73


Primjer 4-4. Koritenje inidjalizatora (nastavak)

11 konstruktori _
public Time( Sy st em . D at e T im e dt )

{
Year = dt.Vear;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute; ....
Second = dt.Second; //eksplicitno pridruivanje

}
public Time( int Year, int Month, int Date,
int Hour, int Minute )

{
this.Year = Vear;
this.Month = Month;
this.Date = Date;
this.Hour = Hour;
this.Minute = Minute;

}
public class Tester

static void Main()

{ Sy st em .D at eT im e curr en t T im e = System.Date Time. Now;


Ti me t = new Time ( c u rr en t T im e );
t .DisplayCurrentTime();

Time t2 = new Time( 2005, 11 i 18 > H > 45 ),


12.DisplayCurrentT ime();

je s 3 0 :
private int Second = 30; // inicijalizator

Ako za"Second ne bude proslijeena vrijednost, ona e se prilikom stvaranja t2 posta-

viti na 30:

Time t2 = new Time(2005,n,l8,ll>45);


t 2 .DisplayCurrentTime();

74 | Programiranje Ctt
jVfeutim, ako je Second dodijeljena vrijednost, kao to je to uinjeno u konstruktoru
(koji prihvaa objekt DateTime, podebljan u ispisu), ta e vrijednost zamijeniti inicija-
liziranu vrijednost.
Prilikom prvog pozivanja D isplay C urrentTim e() p o zivam o k o n stru k to r k o ji p rih v aa
objekt DateTime, a seku n de se in icija liz ira ju s 54. P rilik o m dru gog pozivanja poziva se
m etoda, izriito p o stav ljam o v rijem e na 1 1 :4 5 (b ez seku ndi) i dalje preuzim a in icija-
lizator.

Da u programu nije bilo inicijalizatora i da se za Second vrijednost nije dodijelila na


neki drugi nain, CLR bi vrijednost inicijalizirao s 0.

Napomena za C++ programere: u C # nema konstruktora za kopi-


ranje, pa se semantika kopiranja postie implementacijom suelja
ICloneable.

Suelje ICloneable
.N E T kostur definira suelje IC lo n ea b le radi podrke konceptu konstruktora za kopi-
ranje (suelja su detaljnije obraena u poglavlju 8). Ovo suelje definira jednu jedinu
metodu: C lo n e (). Klase koje podravaju zamisao konstruktora za kopiranje trebale
bi implementirati IC lo n ea b le, a zatim implementirati ili plitku kopiju (pozivanje Mem-
berwiseClone) ili duboku kopiju (npr. pozivajui konstruktora za kopiranje i runo
kopirajui sve lanove).
class SomeType: ICloneable
{
public Object Clone()
{
return MemberwiseClone(); // plitka kopija
}
}

Kljuna rije this


Kljuna rije th is pokazuje na trenutnu instancu objekta. Referenca this (ponekad se
naziva i pokaziva this') je skrivena referenca koja se prosljeuje svim metodama klase
koje nisu statike. Preko reference this svaka metoda moe referirati druge metode i
varijable objekta.
Referenca th is koristi se na razne naine. Prvi je kvalifikacija lanova instance koji su
inae skriveni parametrima, kao to je to sluaj u sljedeem primjeru:

Pokaziva je varijabla koja sadri adresu objekta u memoriji. C# ne koristi pokazivae s upravljivim objek-
tima. Neki C++ programeri toliko su se navikli na pokaziva this da ga (neispravno) koriste i u C#.

Poglavlje 4: Klase i objekti | 75


public void SomeMethod (int hour)
{
this.hour = hour;
}
U ovom primjeru SomeMethod () prihvaa parametar (hour) s istim nazivom kakvog ima
varijabla lanica klase. Referenca t h i s se koristi za rjeavanje vieznanosti naziva,
t h is .h o u r referira varijablu lanicu, a hour referira parametar.

Dobra strana ovakvog stila je odabir dobrog naziva varijable koji se zatim koristi i za
parametar i za varijablu lanicu. Loa je strana koritenje istog naziva za parametar i
varijablu lanicu, to ponekad moe biti zbunjujue.
Drugi nain upotrebe reference th is je prosljeivanje trenutnog objekta kao parametra
u drugu metodu. Na primjer:
class myClass

^ public void Foo(OtherClass otherObject)


{
otherObject.Bar(this);
}
}
Ovaj primjer potrebno je pojasniti. Imamo metodu myClass.Foo. U tijelu metode
poziva se metoda Bar instance OtherClass i prosljeuje joj se referenca do tekue
instance myClass. To metodi Bar omoguava rad s javnim metodama i lanovima
tekue instance myClass.
Trei nain upotrebe reference th is je s indekserima, to je objanjeno u poglavlju 9.
etvrti nain na koji se referenca th is moe koristiti je za pozivanje jednog preoptere-
enog konstruktora iz drugog, na primjer:
class myClass

public myClass(int i) { //... }


public myClass() : this(42) { //... }
}
U ovom primjeru podrazumijevani konstruktor s pomou kljune rijei th is poziva
preoptereeni konstruktor koji uzima cjelobrojnu vrijednost.
Naposljetku, kljuna rije th is moe se koristiti i za eksplicitno pozivanje metoda i
lanova klase, kao oblik dokumentacije:
public void MyMethod(int y)
{ '
int x = 0;
X = 7 ; // pridruuje lokalnoj varijabli
y = 8; // pridruuje parametru
this.z = 5; // pridruuje metodi lanici
this.Draw(); // poziva metodu lanicu
}

76 | Programiranje C#
U navedenim primjerima koritenje reference this je suvino, ali moe dodatno poja-
sniti namjeru programera, a da pritom ne uini nikakvu tetu (osim mogueg optere-
enja koda).

Koritenje statikih lanova


lanovi klase (varijable, metode, dogaaji, indeksi itd.) mogu biti ili lanovi instance
ili statiki lanovi. lanovi instance povezani su s instancama tipa, dok se statiki la-
novi smatraju dijelom klase. Statikom lanu moete pristupiti s pomou naziva klase
u kojoj je deklariran. Na primjer, pretpostavimo kako postoji klasa Button i kako su
instancirani objekti te klase btnUpdate i b tn D e le te . Pretpostavimo i kako klasa Button
ima statiku lanicu SomeMethod( ) . Za pristup statikoj metodi napiite:
Button.SomeMethod();

a ne:
btnUpdate.SomeMethod();

U C# nije doputen pristup statikoj metodi i varijabli lanici putem instance i to e


generirati pogreku prevoditelja (C++ programeri posebno pripazite na to).
Neki jezici prave razliku izmeu metoda klase i drugih (globalnih) metoda koje su
dostupne izvan konteksta bilo koje klase. U C # ne postoje globalne metode samo
metode klase, ali odgovarajui rezultat moete postii definiranjem statikih metoda
unutar klase.

'' I Napomena za VB6 programere: kljuna rije static u C# nije isto to i


J ^ kljuna rije Static uVB6 i VB.NET. U VB, kljuna rije Static dekla-
V*' rira vari!ablu koje j e dostupna samo metodi u kojoj je deklarirana. Dru-
gim rijeima, varijablu Static ne dijele razliiti objekti iz njene klase
(tj. svaka instanca varijable Static ima svoju vrijednost). Ova varija-
bla, meutim, postoji tijekom trajanja programa koji njenoj vrijednosti
doputa da traje od jednog poziva metode do drugog.
U C# kljuna rije static oznaava lana klase. Odgovarajua kljuna
rije u VB je Shared.

Statike m eto de uglavn om d jelu ju p o p u t g lo b a ln ih m eto da je r ih m oete p ozvati a da


nemate in sta n cu o b je k ta . P re d n o st sta ti k ih m eto da pred glo baln im m eto da m a je da
je naziv o gra n ie n na klasu u k o jo j se p o ja v ljuje, stoga ne dolazi do p re op tereen ja
globalnog im en sko g p ro stora v elikim b ro je m naziva m etoda. T o m oe b iti k o risn o za
upravljanje sloenim p ro g ra m im a , a naziv k la se se uvelike ponaa kao im en ski pro stor
za statike m eto de k o je se n a la z e u n u ta r klase.

f S .'T ^ e n o ; btnUPdate 1 btnDelete > Pravo varijable koje referiraju na neimenovane


radi na f m ' Radl Jednostavnosti ne su ovdje navedene kao nazivi objekata, no upamtite kako se
radi samo o kraticama za nazive varijabli koji referiraju na neimenovane instance na gomili1'.

Poglavlje 4: Klase i objekti I 77


Uz to, statikim metodama se kao parametri mogu proslijediti lanovi instance (ili
same mogu stvoriti takve instance unutar statike metode). Kako im doseg nije globa-
lan, ve je ogranien na klasu, one imaju pristup privatnim lanovima instance.

Odupritese elji da u programu napravite jednu klasu u koju ete sm je-


stiti sve metode. To je mogue, ali nije preporuljivo i protivi se naelu
uahurivanja u objektno orijentiranom programiranju.

Pozivanje statikih metoda


Metoda Main() je statika. Kae se da statike metode djeluju na klasu, a ne na instancu
klase. One nemaju referencu th is jer ne postoji instanca na koju treba pokazati.

Napomena za Java programere: u C # nije doputeno pozivanje stati-


kih metoda kroz varijable instanci.

Statike metode ne mogu izravno pristupiti lanovima koji nisu statiki. Kako bi
metoda Main() pozvala metodu koja nije statika, ona mora instancirati objekt. Pogle-
dajte ranije navedeni primjer 4-2.
SomeMethod() je nestatika metoda MyClass. Kako bi Main() pristupila toj metodi, ona
prvo mora instancirati objekt tipa MyClass i zatim pozvati metodu kroz taj objekt.

Koritenje statikih konstruktora


Ako klasa deklarira statiki konstruktor moete biti sigurni da e se on pokrenuti prije
stvaranja bilo koje instance klase.'

Trenutak pokretanja statikog konstruktora ne moete tono znati,


no to e se dogoditi nakon pokretanja programa i prije stvaranja
prve instance. Zbog toga se ne moe pretpostaviti (niti utvrditi) da je
instanca stvorena.

Na primjer, klasi Time iz primjera 4 -4 moete dodati sljedei statiki konstruktor:


static Time()
{
Name = "Time";
} .

Zapravo, CLR jami da e pokrenuti statiki konstruktor prije b ilo k a k v e operacije s klasom. Dalje, on
samo jami da e p okren u ti izvoenje konstruktora, ali ne i da e zav riti njegovo izvoenje. Mogue je
zamisliti primjer u kojem su dvije klase ovisne jedna o drugoj. Umjesto da ue u slijepu ulicu, CLR moe
pokrenuti konstruktore u razliitim dretvama tako da ispuni jamstvo da e bar zapoeti izvoenje kon-
struktora pravilnim redoslijedom.

78 | Programiranje C#
Prim ijetit e te k a k o ispred sta ti k o g ko n stru kto ra nem a m od ifik ato ra pristu pa (npr.
public). K o rite n je m o d ifik a to ra p ristu pa uz sta ti k e k o n stru k to re n ije d op u ten o.
Nadalje, b u du i da se radi o sta tiko j m etodi la n ici, ne m o ete p ristu piti v arijab la m a
lanicama k o je nisu sta ti k e pa se Name m ora de klarira ti k a o sta tika v arijabla:
private static string Name;

Posljednja p ro m jen a je d odav anje sljedeeg reda u D isplay C u rrentTim e():


public void DisplayCurrentTime()
{
System.Console.WriteLine(',Name: {o}", Name);
System.Console.Writel_ine("{0}/{l}/{2} {3}:{4}:{5}",
Month, Date, Vear, Hour, Minute, Second);
}
Nakon svih p ro m je n a , izlaz je sljedei:
Name: Time
11/27/2005 7:52:54
Name: Time
11/18/2005 11:45:30

(Va e se izlaz razlikovati ovisno o datumu i vremenu pokretanja koda.)


lako ovaj kod funkcionira, stvaranje statikog konstruktora nije obavezno za postiza-
nje ovog cilja. Umjesto toga moete koristiti incijalizator:
private static string Name = "Time";

kojim se dobija isti rezultat. Statiki su konstruktori, meutim, korisni za pripremu


koja se ne moe postii inicijalizatorom, a treba se izvesti samo jednom.

> Napomena za Java programere: u C # se statiki konstruktor koristi na


j , onim N i t i m a na kojima se u jeziku Java koristi statiki inicijalizator.

Pretpostavimo, na primjer da u starom DLL-u postoji dio neupravljivog koda. Za taj


kod elite napraviti omota klase. Moete pozvati LoadLibrary u statikom konstruk-
toru i inicijalizirati tablicu preskakanja (engl. jump table) unutar statikog konstruk-
tora. Obrada starog koda i rad s neupravljivim kodom objanjen je u poglavlju 22.

Statike klase
U C# ne postoje globalne metode ili konstante. Moda ete morati stvarati male
pomone klase ija je jedina svrha da sadravaju statike lanove. Zanemarimo li
posljedice tog postupka, ako uistinu stvorite takvu klasu, trebat ete izbjei stvara-
nje instanci. Klasu oznaite sa S t a t i c kako biste osigurali da se nee stvoriti nijedna
instanca klase. Statike su klase zapeaene i stoga se ne mogu stvoriti izvedeni tipovi
klase S t a t i c . Upamtite kako statike klase ne moraju sadravati nestatike lanove ili
imati konstruktor.

Poglavlje 4: Klase i objekti | 79


Koritenje statikih polja
Uobiajen nain za prikaz upotrebe statikih varijabli lanica je praenje broja instanci
koje trenutno postoje za klasu. To je prikazano u primjeru 4-5.

Primjer 4-5. Koritenje statikih polja za brojanje instanci


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace StaticFields
{
public class Cat
{
private static int instances = 0;

public Cat()
{
instances++;
}

public static void HowManyCats()


{
Console.Writetine( "{0} cats adopted",
instances );
}
}
public class Tester
{
static void Main()
{
Cat.HowHanyCats();
Cat frisky = new Cat();
Cat.HowManyCats();
Cat whiskers = new Cat();
Cat.HowManyCats();
}
}
}
Klasa Cat je svedena na najosnovnije elemente. Stvorena je statika varijabla lanica
instances i inicijalizirana je s 0. Ako pogledate primjer, vidjet ete kako se statiki
lan smatra dijelom klase, a ne lanom instance te ga prevoditelj ne moe inicijalizi-
rati prilikom stvaranja instance. Stoga, ako elite incijalizirati statiki lan, morate
osigurati eksplicitni inicijalizator. Prilikom stvaranja dodatnih instanci Cats (unutar
konstruktora) broj se poveava.

80 | Programiranje C#
Statike metode za pristup statikim poljima
Podatke lanove nije dobro oznaiti s public. To se odnosi i na statike varijable la-
nice. Jedn o je rjeenje oznaiti statikog lana s p rivate, kao to smo to m aloprije
uinili s instartces. Stvorena je javna metoda HowManyCats() koja omoguava pristup
ovom privatnom lanu.

Unitavanje objekata
Budui da C # osigurava sakupljanje otpada, objekte ne morate sami unitavati. Meu-
tim, ako objekt kontrolira neupravljive resurse, te resurse ete morati eksplicitno oslo-
boditi kad vam vie nisu potrebni. Implicitnu kontrolu neupravljivih resursa omogu-
ava destruktor kojeg sakuplja otpada poziva kada je objekt uniten.

-**r----
Napomena za C i C++ programere: destruktor se ne pozva obavezno
4, nakon to objekt izae iz dosega, ve kad bude sakupljen kao otpad
TL-*. (to se moe dogoditi mnogo kasnije). To se naziva i nedeterministika
finalizacija.

Destruktor bi trebao osloboditi samo one resurse na koje se objekt oslanja i ne bi


trebao referencirati druge objekte. Ako se radi samo o upravljivim referencama, ne tre-
bate, i ne biste smjeli, implementirati destruktor. On je potreban samo za rukovanje
neupravljivim resursima. Budui da koritenje destruktora ima svoju cijenu, njegova
implementacija se preporua samo u metodama u kojima je obavezan (tj. metodama
koje troe vrijedne neupravljive resurse).
Destruktor objekta se ne moe izravno pozvati. To e uiniti sakuplja otpada.

Kako destruktori funkcioniraju


Sakuplja otpada ima popis objekata koji imaju destruktora. Taj se popis aurira pri-
likom svakog stvaranja ili unitavanja takvog objekta.
Kad se objekt s popisa sakupi kao otpad, on se stavlja u red zajedno s drugim objek-
tima koji ekaju unitavanje. Sakuplja otpada e nakon izvoenja destruktora saku-
piti objekt i aurirati red, kao i odgovarajui popis objekata za unitavanje.

C# destruktor
Destruktor jezika C # sintaktiki nalikuje C++ destruktoru, no ponaa se drugaije.
C# destruktor deklarirajte znakom tilda, na sljedei nain:
~MyClass(){}

Poglavlje 4: Klase i objekti | 81


U C # ova sintaksa predstavlja preacza deklarairanje metode Finalize() koja se pove-
zuje sa svojom osnovnom klasom. Stoga, ako napiete:
~MyClass()
{
// obavlja posao
}
C# prevoditelj e to prevesti kao:
protected override void Finalize()
{
try
{
// obavlja posao
}
finally
{
base.Finalize();
}
}

Razlika izmeu unitavanja i odlaganja


Eksplicitno pozivanje destruktora nije doputeno. Destruktora e pozvati sakuplja
otpada. Ako koristite vrijedne neupravljane resurse (poput identifikatora datoteka)
koje elite to prije zatvoriti i odloiti, trebate implementirati suelje IDisposable'
(vie o sueljima moete nauiti u poglavlju 8). Prilikom implementacije suelja IDi-
sposable() trebate obavezno definirati metodu Dispose() koja e obavljati sva vana
ienja. Dostupnost metode Dispose() klijentima omoguava da rade na naelu Ne
ekaj na pozivanje destruktora, ve oisti odmah.
Ako pruite metodu Dispose() trebate sprijeiti da sakuplja otpada pozove destruktor
objekta. To moete uiniti pozivanjem metode GC.SuppressFinalize() prosljeujui
pokaziva th is objekta. Destruktor tada moe pozvati metodu Dispose(). Moete, na
primjer, napisati sljedee:
using System;
class Testing : IDisposable
{
bool is_disposed = false;
protected Virtual void Dispose(bool disposing)
{
if (!is_disposed) // Odlaze samo jednom!
{
if (disposing)
{
Console.WriteLine(

Veinu vremena neete trebati izravno stvarati klase koje slue za rad s neupravljivim resursima poput
sirovih identifikatora. Moda ete, meutim, koristiti klase omotae poput FileStream ili Socket, ali te
klase ne implementiraju IDisposable pa u tom sluaju trebate u klasi implementirati IDisposable (ali ne i
finalizator). Metoda Dispose e za sve resurse koji se trebaju ukloniti pozvati Dispose.

82 1 Programiranje C#
"Not in destructor, OK to reference other objects");
}
// izvodi ienje za ovaj objekt
Console.WriteLine("Disposing..
}
this.is_disposed = true;

}
public void Dispose()
{
Dispose(true);
// govori sakupljau otpada da ne finalizira
GC.SuppressFinalize(this);
}
~Testing()
{
Dispose(false);
Console.WriteLine(In destructor.);
}
}

Implementiranje metode CloseO


Za neke objekte moda ete radije omoguiti klijentu da pozove metodu Close(). Na
primjer, ta metoda vjerojatno ima vie smisla za objekte datoteka nego metoda Dis-
pose(). Metodu Close() moete implementirati stvaranjem privatne metode Dispose()
i javne metode Close() koju ete napisati tako da poziva Dispose().

Iskaz using
Kako bi klijenti lake ispravno odloili objekte, u C# je dostupan iskaz using koji osi-
gurava najranije mogue pozivanje metode Dispose(). Trebate deklarirati objekte koje
koristite i zatim napraviti doseg za njih s pomou vitiastih zagrada. Kada se dosegne
zatvorena vitiasta zagrada automatski e se pozvati metoda Dispose() za objekt, kao
to je prikazano u primjeru 4-6.

Primjer 4-6. Iskaz using


#region Using directives

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

#endregion

namespace usingStatement
{
class Tester
{

Poglavlje 4: Klase i objekti | 83


Primjer 4-6. Iskaz using (nastavak)
public static void Main()
{
using ( Font theFont = new Font( "Arial", 10.Of ) )
{
// koristi theFont

} // prevoditelj e pozvati Dispose na theFont

Font anotherFont = new Font( "Courier", 1 2 .of );

using ( anotherFont )
{
// koristi anotherFont

} // prevoditelj poziva Dispose na anotherFont


}
}
}
U prvom dijelu ovog primjera objekt Font je stvoren unutar iskaza using. Kad iskaz
using zavri, za objekt Font pozvat e se metoda Dispose().

U drugom dijelu primjera objekt Font je stvoren izvan iskaza using. Kada odluimo upo-
trijebiti upravo to pismo stavljamo ga unutar iskaza using. Kad iskaz zavri, ponovno
se poziva Dispose ().
Drugi je pristup prilino opasan. Ako se izbaci iznimka nakon stvaranja objekta, no
prije poetka bloka using, objekt se nee odloiti. Drugo, varijabla ostaje u dosegu i
nakon zavretka bloka using, no ona nee uspjeti ako joj se pristupi.
Iskaz using titi i od neoekivanih iznimki. Dispose() se poziva bez obzira na to kako
kontrola naputa iskaz using. Stvara se implicitni blok try-finally (pogledajte pogla-
vlje 11 za vie informacija).

Prosljeivanje parametara
Prema zadanim postavkama vrijednosni tipovi se u metode prosljeuju po vrijednosti.
To znai da se, prilikom prosljeivanja objekta vrijednosti metodi, unutar te metode
stvara privremena kopija objekta. Kad metoda zavri, kopija se unitava. Iako je pro-
sljeivanje po vrijednosti uobiajen postupak, ponekad se objekti prosljeuju i po refe-
renci. C # za prosljeivanje objekata vrijednosti u metodu po referenci nudi parametar
ref, a za prosljeivanje varijable ref bez prethodne inicijalizacije postoji modifikator
out. C # podrava i modifikator params koji metodi doputa prihvaanje promjenjivog
broja parametara. Kljuna rije params poblie je objanjena u poglavlju 9.

84 | Programiranje C#
Prosljeivanje po referenci
M e t o d e m o g u vratiti s a m o j e d n u vrijednost (iako ta vrijednost m o e biti kolekcija vri-
jednosti). P o g l e d a j m o p o n o v n o k l a s u Time i m e t o d u GetTitne() koj a v ra a sate, m i n u t e
isek unde.

Napomena za Java programere: u C # se za osnovne tipove poput int


(cjelobrojna vrijednost) ne trebaju koristiti klase omotai. Umjesto njih
se koriste parametri referenci.

B u d u i d a nije m o g u e vratiti tri vrijednosti, m o d a se m o g u proslijediti tri p ar a m e t r a ,


pustiti d a m e t o d a m odif icira p a r a m e t r e i z a t i m provjeriti rezultat u p oz iv no j me to di .
U primjeru 4-7 je p r i k a z a n o v a k a v pristup.

primjer 4-7. Vraanje vrijednosti u parametrima


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

ffendregion

namespace ReturningValuesInParams
{
public class Time
{
// privatne varijable lanice
private int Year;
private int Month;
private int Date;
private int Hour;
private int Minute;
private int Second;

// javne metode za pristupanje


public void DisplayCurrentTime()
{
System.Console.WriteLine( "{0 }/{l}/{2 } {3}:{4}:{5}'',
Month, Date, Year, Hour, Minute, Second );
}

public int GetHour()


{
return Hour;
}

public void GetTime( int h, int m, int s )

h = Hour;
m = Minute;

Poglavlje 4: Klase i objekti | 85


Primjer 4-7. Vraanje vrijednosti u parametrima (nastavak)
s - Second;
}
// konstruktor
public Time( System.DateTime dt )
{
Vear = dt.Year;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute;
Second = dt.Second;
}
}
public class Tester
{
static void Main()
{
System.DateTime currentTime = System.DateTime.Now;
Time t = new Time( currentTime );
t .DisplayCurrentT ime();

int theHour = 0;
int theMinute = 0;
int theSecond = 0;
t.GetTime( theHour, theMinute, theSecond );
System.Console.WriteLine( "Current time: {0}:{l}:{2}",
theHour, theMinute, theSecond );
}
}
}
Obratite pozornost Current time u izlazu je 0:0:0. Ovaj pokuaj oito nije dao eljene
rezultate. Problem je u parametrima. U GetTime() se prosljeuju tri cjelobrojna para-
metra, zatim se parametri u GetTime() modificiraju, ali kad se vrijednostima ponovno
pristupi u Main() one nisu promijenjene. To se dogodilo zbog toga to cijeli brojevi
pripadaju vrijednosnom tipu, to znai da se prosljeuju po vrijednosti; u GetTime()
se nalazi kopija. Mi te vrijednosti trebamo proslijediti po referenci.
Za to su potrebne dvije male promjene. Prvo parametre metode GetTime() promijenite
tako da oznaavaju kako se radi o parametrima ref:
public void GetTime(re-f int h, ref int m, ref int s)
{
h = Hour;
m = Minute;
s = Second;
}

86 | Programiranje C#
Z a t i m modificirajte p oz i v GetTime() k a k o bi se i a r g u m e n t i proslijedili p o referenci:

t.GetTimefref theHour, re-f theMinute, ref theSecond);

Ako izostavite drugi korak u kojem se argumenti oznaavaju kljunom rijei ref, pre-
voditelj e javiti da se argument ne moe pretvoriti iz int u ref int.
Novi rezultat pokazuje tono vrijeme. Deklariranjem parametara kao ref parametara
prevoditelju dajete uputu da ih proslijedi po referenci. Umjesto stvaranja kopije, para-
metar u GetTime() je referenca do iste varijable (theHour) koja je stvorena u Main().
Kada te vrijednosti promijenite u GetTime(), promjena e se odraziti i u Main().

Upamtite kako su re f parametri reference stvarnih vrijednosti. To je kao da date


uputu, Evo, radi na ovom". Za razliku od njih, parametri vrijednosti su kopije. Njima
odgovara uputa, Evo radi na ovom potpuno jednakom primjerku1'.

Svladavanje definitivnog pridruivanja s pomou parametara out


C# namee definitivno pridruivanje (engl. definite assignment) koje zahtjeva da se
prije upotrebe svim varijablama dodijeli vrijednost. Ako u primjeru 4-7 ne inicijali-
zirate theHour, theMinute i theSecond prije nego to ih kao parametre proslijedite Get-
Time(), prevoditelj e javiti pogreku. Meutim, sprovedena inicijalizacija samo posta-
vlja njihove vrijednosti na 0 prije nego to se proslijede metodi:
int theHour = 0;
int theMinute = 0 ;
int theSecond = 0 ;
t.GetTime( ref theHour, ref theMinute, ref theSecond);

Inicijalizacija ovih vrijednosti ini se beskorisnom jer ih odmah prema referenci pro-
sljeujete GetTime gdje e se promijeniti. No, ako to ne uinite, javit e se sljedee
pogreke prevoditelja:
Use of unassigned local variable 'theHour'
Use of unassigned local variable 'theMinute'
Use of unassigned local variable 'theSecond'

U C# za ovu situaciju postoji modifikator parametra out. On uklanja zahtjev za inici-


jalizacijom parametra reference. Parametri za GetTime(), na primjer, metodi ne daju
nikakve informacije. Oni su samo mehanizam za dobivanje informacija od metode.
Stoga, ako sva tri parametra oznaimo s out, eliminirat emo potrebu za njihovom
inicijalizacijom izvan metode. Unutar pozvane metode out parametrima mora biti
dodijeljena vrijednost prije nego to metoda vrati. Navedene su promijenjene deklara-
cije parametara za GetTime().
public void GetTime(out int h, out int m, out int s)

h = Hour;
m = Minute;
s = Second;
}

Poglavlje 4: Klase i objekti j 87


Ovo je novi poziv metode u Main():
t.GetTime( out theHour, out theMinute, out theSecond);

Dakle, vrijednosni tipovi se metodama prosljeuju po vrijednosti. Parametri re f se


koriste za prosljeivanje vrijednosnih tipova po referenci. To omoguava uzimanje
njihove modificirane vrijednosti u pozivnoj metodi. Parametri out se koriste samo za
vraanje informacija iz metode. Primjer 4-7 je modificiran u primjeru 4 -8 gdje se kori-
ste sva tri parametra.

Primjer 4-8. Koritenje parametara in, out i ref


ifregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace InOutRef
{
public class Time
{
// privatne varijable lanice
private int Year;
private int Month;
private int Date;
private int Hour;
private int Minute;
private int Second;

// javne metode za pristupanje


public void DisplayCurrentTime()
{
System.Console.WriteLine( "{0}/{l}/{2} {3}:{4}:{5}",
Month, Date, Year, Hour, Minute, Second );
}
public int GetHour()
{
return Hour;
}

public void SetTime( int hr, out int min, ref int sec )
{
// ako je proslijeeno vrijeme >= 30
// poveava minute i postavlja sekunde na o
// u suprotnom ne mijenja nita
if ( sec >= 30 )
{
Minute++;
Second = 0;
}

88 | Programiranje C#
Primjer 4-8. Koritenje parametara in , o u t i r e f (nastavak)
Hour = hr; // postavlja na proslijeenu vrijednost

// prosljeuje minute i sekunde natrag


min = Minute;
sec = Second;

}
// konstruktor
public Time( System.DateTime dt )
{
Year = dt.Year;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute;
Second = dt.Second;
}
}
public class Tester
{
static void Main()
{
System.DateTime currentTime = System.DateTime.Now;
Time t = new Time( currentTime ); t
.DisplayCurrentT ime();

int theHour = 3;
int theMinute;
int theSecond = 2 0 ;

t.SetTime( theHour, out theMinute, ref theSecond );


System.Console.WriteLine(
"the Minute is now: {0} and {l} seconds",
theMinute, theSecond );

theSecond = 40;
t.SetTime( theHour, out theMinute, ref theSecond );
System.Console.WriteLine( ''the Minute is now: " +
"{0 } and {l} seconds",
theMinute, theSecond ); -
}
}
}
SetTime izgleda pomalo neprirodno, ali prikazuje sva tri tipa parametara. theHour se
prosljeuje kao parametar vrijednosti. Njegova jedina funkcija je postavljanje varijable
lanice Hour i s pomou tog se parametra ne vraa nikakva vrijednost.
ref parametar theSecond se koristi za postavljanje vrijednosti u metodi. Ako je theSe-
cond vei ili jednak 30 , varijabla lanica Second se ponovno postavlja na nulu a varijabla
lanica Minute se poveava.

Poglavlje 4: Klase i objekti | 89


Kada ko ristite param etre reference, m orate zadati r e f i za poziv i za
odredite.

Naposljetku, parametar theMinute se u metodu prenosi samo da bi se vratila vrijednost


varijable lanice Minute, pa je stoga oznaen kao parametar out.
Potpuno je razumljivo da se theHour i theSecond moraju inicijalizirati. Njihove su vri-
jednosti potrebne i koriste se. theMinute nije potrebno inicijalizirati jer se radi o para-
metru out koji slui samo za vraanje vrijednosti. Pravila koja su se isprva inila arbi-
trarnim i muiavim sada napokon imaju smisla. Vrijednosti se trebaju inicijalizirati
samo kada njihova poetna vrijednost ima smisla.

Preoptereivanje metoda i konstruktora


esto e vam biti potrebne dvije razliite metode ali sa istim imenom. Najei pri-
mjer takvog imenovanja je kad imate vie konstruktora. Konstruktori iz dosad nave-
denih primjera imali su po jedan parametar: objekt DateTime. Bilo bi korisno kada
bismo objekte Time mogli postaviti na arbitrarno vrijeme prosljeivanjem vrijednosti
za godinu, mjesec, datum, sat, minuti i sekundu. Jo bi korisnije bilo kada bi neki kli-
jenti koristili jedan konstruktor, a ostali neke druge konstruktore. Preoptereivanje
metode omoguava upravo to.
Potpis (engl. signature) metode definiran je njenim nazivom i popisom parametara.
Dvije se metode razlikuju po svojim potpisima ako imaju razliita imena ili razliite
popise parametara. Prva se metoda u sljedeem kodu od druge razlikuje prema broju
parametara, a druga se od tree razlikuje po tipu parametara:
void myMethod(int pl);
void myMethod(int pl, int p 2 );
void myMethod(int pl, string si);

Klasa moe imati neogranien broj metoda, ali se njihovi potpisi moraju meusobno
razlikovati.
U primjeru 4-9 prikazana je klasa Time sa dva konstruktora: jednim koji prihvaa
objekt DateTime i drugim koji prihvaa est cjelobrojnih vrijednosti.

Primjer 4-9. Preoptereivanje konstruktora


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace OverloadedConstructor
{

90 | Programiranje C#
p n m j e r 4-9. P r e o p t e r e iv a n j e k o n s t r u k t o r a ( n a s t a v a k )

public class Time

H privatne varijable lanice


private int Year;
private int Month;
private int Date;
private int Hour;
private int Minute;
private int Second;

// javne metode za pristupanje


public void DisplayCurrentTime()
{
System.Console.WriteLine( " { 0 } / { l } / { 2 } {3}:{4}:{5}",
Month, Date, Year, Hour, Minute, Second );
}
// konstruktori
public Time( System.DateTime dt )
{
Year = dt.Year;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute;
Second = dt.Second;

public Time( int Year, int Month, int Date,


int Hour, int Minute, int Second )
{
this.Year = Year;
this.Month = Month;
this.Date = Date;
, this.Hour = Hour;
this.Minute = Minute;
this.Second = Second;
}
}

public class Tester


{
static void Main()
{
System.DateTime currentTime = System.DateTime.Now;

Time ti = new Time( currentTime );


t .DisplayCurrentTime();

Time t2 = new Time( 2005, 11, 18 , 1 1 , 03, 30 );


t2 .DisplayCurrentTime();

Poglavlje 4: Klase i objekti | 91


Kao to moete primijetiti, klasa Time u primjeru 4-9 ima dva konstruktora. Kad bi se
potpis metode sastojao samo od naziva metode, prevoditelj ne bi znao koji konstruktor
treba pozvati prilikom konstrukcije t i i t2. Budui da se u potpisu nalaze i tipovi argu-
menata metode, prevoditelj moe poziv konstruktora za t i uskladiti s konstruktorom
iji potpis zahtjeva objekt DateTime. Isto tako, prevoditelj moe poziv konstruktora za
t 2 povezati s metodom konstruktora u ijem je potpisu navedeno est argumenata.
Prilikom preoptereivanja metode morate promijeniti njen potpis (tj. naziv, broj i tip
parametara). Moete promijeniti i povratni tip, no to nije obavezno. Ako promijenite
samo povratni tip, preoptereenje metode se nee izvesti, a stvaranje dvije metode s
istim potpisom i razliitim povratnim tipovima generirat e pogreku prevoditelja
(pogledajte primjer 4-10).

Primjer 4-10. P reop tereivale metode s razliitim povratnim tipovima


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace VaryingReturnType
{
public class Tester
{
private int Triple( int val )
{
return 3 * val;
}
private long Triple( long val )
{
return 3 * val;
)
public void Test()
{
int x = 5;
int y = Triple( x );
System.Console.Writeline( "x: {0} y: {l}", x, y );

long lx = 1 0 ;
' long ly = Triple( lx );
System.Console.WriteLine( "lx: {o} ly: {l}", lx, ly );

}
static void Main()
{
Tester t = new Tester();

92 | Programiranje C#
* Primjer 4-10. Preoptereivanje metode s razliitim povratnim tipovima (nastavak)
t.Testf);

}
}
}
u ovom primjeru klasa Tester preoptereuje metodu T riple(), tako da jedna prihvaa
cjelobrojnu vrijednost, a druga long. Dvije metode Triple() imaju razliite povratne
tipove. Iako to nije obavezno, u ovom sluaju je vrlo korisno.

Uahurivanje podataka sa svojstvima


Svojstva doputaju klijentima da pristupe stanju klase kao da izravno pristupaju
poljima lanovima, dok zapravo implementiraju pristup kroz metodu klase.
Taj je postupak savren. Klijentu treba izravan pristup stanju objekta, a eli izbjei
metode. Dizajner klase, meutim, eli u lanovima klase sakriti unutarnje stanje i
osigurati izravan pristup putem metode.

Odvajanjem stanja klase od metode koja tom stanju pristupa, dizajner moe prema
potrebi promijeniti unutarnje stanje objekta. Prilikom prvog stvaranja klase Time, vri-
jednost Hour mogla bi se spremiti kao varijabla lanica. Kad se klasa prepravi, vrijed-
nost Hour moe se izraunati ili preuzeti iz baze podataka. Ako klijent ima izravan
pristup izvornoj varijabli lanici Hour, prebacivanje na izraun vrijednosti prekinulo
bi njegov rad. Odvajanjem i prisiljavanjem klijenta da proe kroz metodu (ili svojstvo)
klasa Time moe promijeniti nain na koji upravljanja internim stanjem bez naruava-
nja koda klijenta.

Svojstva zadovoljavaju oba uvjeta: klijentu pruaju jednostavno suelje jer izgledaju
kao varijable lanice. Meutim, implementiraju se kao metode, omoguavajui sakri-
vanje podataka koje zahtijeva dobar objektno orijentiran dizajn, kao to je prikazano
u primjeru 4-11.

Primjer 4-11. Koritenje svojstva


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

Dendregion

namespace UsingAProperty

public class Time


{
// privatne varijable lanice
private int year;

Poglavlje 4: Klase i objekti | 93


Primjer 4-11. Koritenje svojstva (nastavak)

private int month;


plivate int date;
private int hour;
private int minute;
private int second;

// javne me to de za pristup anje


public vo id D isplay Cur re nt Ti me ()

{
System . C on so le . W ri te Li ne(
"TimeVt: {0}/{l}/{2} {3}:{4}:{5}",
month, date, year, hour, minute, second );

}
// konstruktori
public Time( S y s t em . D at e T im e dt )

{
year = dt.Year;
month = dt.Month;
date = dt.Day;
hour = dt.Hour;
minute = dt.Minute;
second = dt.Second;
}
// stvara svojstvo

public int Hour


{
get
{
return hour;
}

set
{
hour = value;
}
}
}
public class Tester
{
static void Main()

System.DateTime currentTime = System.DateTime.Now;


Time t = new Time( currentTime );
t .DisplayCurrentTime();

int theHour = t.Hour;


System . C on so le . W ri te Li ne( "\ nRetr i eved the hour: {0}\n

th eHour );

94 | Programiranje C#
primjer 4-11. Koritenje svojstva (nastavak)
theHour++;
t.Hour = theHour;
System.Console.WriteLine( "Updated the hour: {0}\n", theHour );

}
}
}
2a deklariranje svojstva napiite tip i naziv svojstva i iza njih stavite par vitiastih
zagrada. Unutar zagrada moete deklarirati pristupnike (engl. accessors) get i set, Nije-
dan od njih nema eksplicitne parametre, iako metoda se t() ima implicitni parametar
value, kao to je prikazano u sljedeem primjeru.
U primjeru 4-11, Hour je svojstvo. Deklaracija tog svojstva stvara dva pristupnika: get
i set.
public int Hour
{
get
{
return hour;
}

set
{
hour = value;
}
1
Svaki pristupnik ima svoje tijelo koje uzima i postavlja vrijednost svojstva. Vrijednost
svojstva moe biti pohranjena u bazi podataka (u tom e sluaju glavni dio pristupnika
uiniti ono to je potrebno za interakciju s bazom podataka) ili moe biti pohranjena
u privatnoj varijabli lanici;
private int hour;

Pristupnik get
Tijelo pristupnika get slino je metodi klase koja vraa objekt tipa svojstva. Svojstvo
Hour iz navedenog primjera slino je metodi koja vraa in t. Ono vraa vrijednost pri-
vatne varijable lanice u kojoj je pohranjena vrijednost svojstva:
get
{
return hour;
}
U ovom primjeru vraa se lokalna varijabla lanica int, ali mogli biste isto tako uzeti
cjelobrojnu vrijednost iz baze podataka ili je izraunati.
Prilikom svakog itanja svojstva poziva se pristupnik get;
Time t = new Time(currentTime);
int theHour = t.Hour;

Poglavlje 4: Klase i objekti | 95


U ovom se primjeru vraa vrijednost svojstva Hour objekta Time i poziva pristupnik get i
radi izdvajanja vrijednosti koja se zatim dodjeljuje lokalnoj varijabli.

P ristu p n ik set
Pristupnik set postavlja vrijednost svojstva i slian je metodi koja vraa void. Prilikom
definiranja pristupnika set morate koristiti kljunu rije value koja e predstavljati
argument ija se vrijednost prosljeuje i koju svojstvo sprema:
set
{
hour = value;
}
U ovom se primjeru ponovno za spremanje vrijednosti svojstva koristi privatna vari-
jabla lanica, no pristupnik set moe prema potrebi zapisivati u bazu podataka ili
aurirati druge varijable lanice.
Kada svojstvu dodijelite vrijednost, automatski se poziva pristupnik set te se impli-
citni parametar value postavlja na vrijednost koju dodijelite:
theHour++;
t.Hour = theHour;

Dvije glavne prednosti ovog pristupa su to klijent moe izravno raditi sa svojstvima,
bez rtvovanja neprikosnovenosti sakrivanja podataka i uahurivanja u dobrom
objektno orijentiranom programiranju, te mogunost da autor svojstva osigura valja-
nost podataka.

M o d ifik a to ri p ristu p a svojstvu


Modifikator pristupa (protected, internal, private) moe se postaviti i za reguliranje
pristupa pristupnicima get ili set. Da biste to uinili, svojstvo mora sadrati i pristup-
nik set i pristupnik get, a modificirati moete samo jedno od njih. Modifikator mora
biti restriktivniji od razine pristupa samog svojstva ili indeksera (stoga, za razliku od
privatnog svojstva, pristupniku get ili set javnog svojstva moete dodati protected):
public strirg MyStrirg
{
protected get { return myString; }
set { myString = value; }
}
U ovom je primjeru pristup pristupniku get ogranien na metode ove klase i klasa koje
su izvedene iz nje, dok je pristupnik set javan.

Modifikator pristupa ne moete primijeniti na suelje (pogledajte pogla-


vlje 8) niti u eksplicitnu implementaciju lana suelja. Uz to, ako pre-
m oujete virtualno svojstvo ili indeks (to je objanjeno u sljedeem
odjeljku), modifikator pristupa mora odgovarati modifikatoru pristupa
osnovnog svojstva.

96 | Programiranje C#
Polja readonly
, vi Moda ete trebati stvoriti inaicu klase Time koja e pruati javne statike vrijednosti
d--"' kkojeo j e e pred
r stavljati tekue vrijeme i datum. U primjeru 4-12 ilustriran je
jednostavan
i^ p g r is tu p ovom problemu.

' P rim jer 4 - 1 2 . K o r i t e n je s t a t i k i h j a v n i h k o n s t a n t i

ffregion Using di rectives

using System;
using Sys tem.Coll ections.C e n e r i c ;
\ using Sy stem .Tex tJ

. Sendregion

. namespace StaticPublicConstants
{'
public class RightNow
, {
// javne varijable lanice
public static int Year;
public static int Month;
public static int Date;
| public static int Hour;
public static int Minute;
public static int Second;

static RightNow()
{
System.DateTime dt = System.DateTime.Now;
Year = dt.Year;
Month = dt.Month;
Date = dt.Day;
, Hour = dt.Hour;
Minute = dt.Minute;
Second = dt.Second;
}
}

public class Tester


{
static void Main()
{
System.Console.WriteLine( "This year: {0 }",
RightNow.Year.ToString() );
RightNow.Year = 2 0 0 6 ;
System.Console.WriteLine( "This year: {o}'',
RightNow.Year.ToString() );

}
. 1

Poglavlje 4: Klase i objekti I 97


Ovakav kod dobro funkcionira dok se ne promijeni jedna od vrijednosti. Kao to
moete vidjeti u primjeru, vrijednost RightNow. Year se moe promijeniti u, na primjer,
2 0 0 6 . Oito ne elimo da se to dogodi.

Bilo bi dobro statike vrijednosti oznaiti kao konstante, ali to nije mogue jer se one
inicijaliziraju tek kad se izvede statiki konstruktor. U C# za ovu svrhu postoji kljuna
rije readonly. Ako deklaracije varijabli lanica klase promijenite na sljedei nain:
public static readonly int Year;
public static readonly int Month;
public static readonly int Date;
public static readonly int Hour;
public static readonly int Minute;
public static readonly int Second;

i zatim u komentar izdvojite ponovno pridruivanje u Main():


// RightNow.Year = 2006; // pogreka!

program e se ispravno prevesti i izvesti.

98 I Programiranje C#
POGLAVLJE 5

Nasljeivanje i polimorfizam

U prethodnom smo poglavlju pokazali kako se novi tipovi mogu stvoriti deklariranjem
klasa. Ovo poglavlje bavi se odnosom izmeu objekata iz stvarnog svijeta i modelira-
njem tih odnosa u kodu. Glavna tema ovog poglavlja je specijalizacija koja se u jeziku
C# implementira s pomou nasljeivanja. U ovom je poglavlju objanjeno i kako se
instance vie specijaliziranih klasa mogu tretirati kao da je rije o instancama opih
klasa, a taj se postupak naziva polimorfizmom (engl. polymorphism). Poglavlje zavr-
ava objanjenjem zapeaenih klasa koje se ne mogu specijalizirati: apstraktnih klasa
koje postoje samo kako bi se specijalizirale te korijena svih klasa, klase Object.

Napomena za VB6 programere: kao i VB.NET, C# prua potpuno


objektno orijentiranu tehnologiju, ukljuujui nasljeivanje, polimor-
fizam i uahurivanje. Te su teme relativna nepoznanica VB6 progra-
merima. Trebate ih paljivo prouiti jer one utjeu na klase i dizajn
aplikacije.

Specijalizacija i generalizacija
Klase i njihove instance (objekti) ne postoje u vakuumu, ve u mrei meusobne ovi-
snosti i odnosa, kao to mi, drutvene ivotinje, ivimo u svijetu odnosa i kategorija.
Odnos to je je jedna vrsta specijalizacije. Kada kaemo kako je pas sisavac, to znai
kako je pas specijalizirana vrsta sisavca. On ima sve osobine sisavca (raa ive mlade,
doji ih mlijekom, ima dlaku), ali su te osobine specijalizirane na poznate osobine poro-
dice canine domesticus. Maka je takoer sisavac. Iz toga moemo zakljuiti da make
i psi imaju neke zajednike osobine koje pripadaju opim osobinama sisavaca, ali se
make i razlikuju od pasa u onim osobinama koje su specijalizirane za make.
Odnosi specijalizacije i generalizacije su proporcionalni i hijerarhijski. Proporcionalni
su jer je specijalizacija suprotno od generalizacije. Stoga su pas i maka specijalizacije
sisavca, a sisavac je generalizacija psa i make.
Ti su odnosi i hijerarhijski jer ine stablo odnosa u kojem se specijalizirani topovi f
granaju iz vie generaliziranih tipova. to se po hijerarhiji vie pomiete prema gore, |
to j e generalizacija vea. Kako biste generalizirali osobinu da i make i psi raaju ive j
mlade, pomiete se do sisavaca. Pomicanje kroz hijerarhiju prema dolje predstavlja j
specijalizaciju. Stoga je maka specijalizacija sisavca koja ima pande (osobina) i prede
(ponaanje). |
Slino tome, ako kaete da ListBox i Button jesu Controls, naznaujete kako Controls i
imaju osobine i ponaanja koja se mogu pronai u oba tipa. Drugim rijeima, Control "
generalizira zajednike osobine ListBox i Button, dok ListBox i Button specijaliziraju
posebne osobine i ponaanja.

0 UML-u
Unified Modeling Language (UML) je standardizirani jezik za opis sustava ili
naina poslovanja. Dio UML-a koji je koristan za svrhe ovog poglavlja je skup dija-
grama koji se koriste za dokumentiranje odnosa izmeu klasa.
U UML-u klase su predstavljene okvirima. Naziv klase nalazi se na vrhu okvira, a
(prema izboru) metode i lanovi se mogu popisati unutar okvira. U UML-u se odnosi
specijalizacije (na primjer) modeliraju na nain prikazan na slici 5-1. Strelica kree iz
klase koja je specijalizirana i pokazuje generaliziranu klasu.

Slika 5-1. Odnos ,,toje

Dvije klase esto imaju zajedniku funkcionalnost, a ta se zajednika svojstva zatim


faktoriraju u zajedniku osnovnu klasu. Kod sastavljen na taj nain jednostavniji je
za odra'vanje i moe se lake koristiti u drugim projektima. Pretpostavimo kako ste
poeli stvarati objekte na nain prikazan na slici 5-2.

100 | Programiranje C#
Slika 5-2. Izvoenje iz Control

Nakon rada s gumbima RadioButtons, CheckBoxes i Commandshvatite kako postoje odre-


ene zajednike osobine i ponaanja koja su vie specijalizirana od Control, ali su ope-
nitija od ostale tri. Te zajednike osobine i ponaanja moete faktorirati u zajedniku
osnovnu klasu Button i promijeniti hijerarhiju nasljeivanja, kao to je prikazano na
slici 5-3. Ovo je primjer koritenja generalizacije u objektno orijentiranom razvoju.

Ovaj UML dijagram prikazuje odnos izmeu klasa i pokazuje kako su i klasa ListBox
i Button izvedene iz Control, a da je klasa Button dalje specijalizirana u klase CheckBox
i Command. Nadalje, klasa RadioButton je izvedena iz CheckBox. Moete, dakle, rei kako
RadioButtonjest CheckBox koji je Button, a Buttons su Controls.

Ovo nije jedina, niti vjerojatno najbolja, organizacija ovih objekata, ali predstavlja
dobar uvod u prikaz meusobnih odnosa tipova (klasa).
<*

lako ovo zapravo pokazuje organizaciju nekih hijerarhija kontrola, ja

B ^ sam osobno vrlo skeptian prema svakom sustavu u kojem model ne


!J odraava moju percepciju stvarnosti. Kada kaem da je RadioButton
CheckBox,
moram dugo i naporno razmiljati ima li to smisla. Recimo
da je RadioButton neka vrsta polja za potvrivanje. Toje polje za potvrdu
koje podrava idiom meusobno iskljuivih opcija. S obzirom na to, ova-
kva hijerarhija pomalo je nategnuta i moe biti znak labavog dizajna.

Poglavlje 5: Nasljeivanje i polimorfizam I 101


Nasljeivanje
Odnos specijalizacije se u C # obino primjenjuje kroz nasljeivanje. Nasljeivanje nije
jedini nain za primjenu specijalizacije, ali je najei i najprirodniji nain za imple-
mentiranje takvog odnosa.
Izjava da ListBox nasljeuje iz (izvodi iz) Control oznaava da je rije o specijalizaciji
klase Control. Control se naziva osnovnom klasom, a ListBox izvedenom klasom. Dru-
gim rijeima, ListBox svoje osobine i ponaanja izvodi iz Control i zatim se specijali-
zira prema vlastitim potrebama.

Implementacija nasljeivanja
U C # se izvedena klasa stvara dodavanjem dvotoke iza naziva izvedene klase, iza
ega slijedi naziv osnovne klase:
public class ListBox : Control

Ovim se kodom deklarira nova klasa ListBox koja izvodi iz klase Control. Dvotoka
se moe proitati kao izvodi iz.
a *
Napomena za C++ programere: u C# ne postoji privatno i zatieno
nasljeivanje.

Izvedena klasa nasljeuje sve lanove osnovne klase - varijable i metode lanice.

Polimorfizam
Nasljeivanje ima dva vana aspekta. Jedan je ponovna upotreba koda. Kada stvorite
klasu ListBox moi ete ponovno upotrijebiti dio logike iz osnovne klase (Control).
Drugi aspekt nasljeivanja moda je jo vaniji: to je polimorfizam. Poli znai mnogo,
a m orf znai oblik. Polimorfizam dakle predstavlja mogunost koritenja vie oblika
tipa, bez obzira na njihove pojedinosti.
Kada telefonska kompanija vaem telefonu poalje signal zvona, ona ne zna kakav se
tip telefona nalazi na drugoj strani linije. Moda imate starinski telefon u kojem pose-
ban motor pokree zvono, a moda imate elektronski telefon ije je zvonjenje zapravo
digitalna glazba.
Vaa telefonska kompanija poznaje samo osnovni tip Phone i oekuje da svaka
instanca" ovog tipa moe zvoniti. Kad telefonska kompanija vaem telefonu naredi
da zazvoni, ona od telefona jednostavno oekuje da uini pravu stvar". Telefonska
kompanija va telefon, dakle, tretira polimorfno.

Stvaranje polimorfnih tipova


Kako biste stvorili metodu koja podrava polimorfizam, trebate je samo oznaiti s
Virtual u njenoj osnovnoj klasi. Na primjer, kako biste metodu DrawWindow() u klasi

102 | Programiranje C#
' Control iz primjera 5-1 oznaili kao polimorfnu, jednostavno u njenu deklaraciju
dodajte kljunu rije Virtual na sljedei nain:
public Virtual void DrawWindow()

Sada e svaka izvedena klasa moi implementirati vlastitu inaicu DrawWindow(). Kako
biste to uinili, jednostavno premostite virtualnu metodu osnovne klase koristei
kljunu rije override u definiciji metode izvedene klase i zatim dodajte novi kod za
tu premoenu metodu.
Usljedeem odlomku iz primjera 5-1 (koji je naveden kasnije u ovom odjeljku) ListBox
je izvedena iz Control i implementira vlastitu inaicu metode DrawWindow():
public override void DrawWindow()
{
base.DrawWindow(); // poziva osnovnu metodu
Console.WriteLine ("Writing string to the listbox: {o}",
listBoxContents);
}
Kljuna rije override prevoditelju govori da je ova klasa namjerno premostila nain
rada metode DrawWindow(). Na slian je nain ova metoda premoena i u drugoj klasi,
Button, koja je takoer izvedena iz Control.

U tijelu primjera 5-1 prvo ete stvoriti tri objekta: Control, ListBox i Button. Zatim ete
za svaki od njih pozvati metodu DrawWindow():
Control win = new Control(l,2);
ListBox lb = new ListBox(3,4,"Stand alone list box")j
Button b = new Button(5,6);
win.DrawWindow();
lb.DrawWindow();
b.DrawWindow();

Ovo funkcionira na oekivani nain. Ispravan DrawWindow() objekt se poziva za svaki.


Do sad nije uinjeno nita polimorfno. arolija poinje kada stvorite polje Control
objekata. Budui da ListBox jest Control, slobodno je moete staviti u polje Control.
Button takoer moete staviti u polje Control objekata jer i Button jest Control:
Control[] winArray = new Control[3];
winArray[o] = new Control(l,2 );
winArray[l] = new ListBox(3 ,4 ,"List box in array'');
winArray[2 ] = new Button(5,6);

to e se dogoditi kad za svaki objekt pozovete metodu DrawWindow()?


for (int i = 0 ;i < 3 ; i++)
{
winArray[i].DrawWindow();
}
Prevoditelj zna samo da ima tri Control objekta i da ste za svaki pozvali metodu Draw-
Window(). Da DrawWindow niste oznaili s Virtual, metoda DrawWindow() iz Control bi
se pozvala tri puta. Meutim, kako ste DrawWindow() oznaili s Virtual i budui da
izvedene klase premouju tu metodu, kad pozovete DrawWindow na polje prevoditelj

Poglavlje 5: Nasljeivanje i polimorfizam | 103


odreuje izvedbeni tip stvarnih objekata (Control, List8ox i Button) i za svaki pozivi
odgovarajuu metodu. To je bit polimorfizma. Potpuni kod za ovaj primjer prikazan
je u primjeru 5-1.
A 4
rV r- Ovaj ispis koristi polje, a polje je kolekcija objekata istog tipa. lano-
vima polja moete pristupiti koristei operator indeksa:
*7L-*.
// postavlja vrijednost elementa
// s pomakom 5
MyArray[5] = 7;

Prvi element polja ima indeks 0. Koritenje polja u ovom primjeru trebalo
bi biti prilino intuitivno. Polja su iscrpnije objanjena u poglavlju 9.

Primjer 5-1. Koritenje virtualnih metoda


Using Vi rtua l methods
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace VirtualMethods
{
public class Control
{
// Ovi lanovi su zatieni i zbog toga vidljivi
// metodama izvedene klase. 0 tome emo
// detaljnije govoriti kasnije u poglavlju,
protected int top;
protected int left;

// Konstruktor uzima dvije cjelobrojne vrijednosti


// za fiksiranje lokacije na konzoli
public Control( int top, int left )
{
this.top = top;
this.left = left;
}
// si mul ira iscrt avanje prozora
public Vir tual vo id DrawWindow()

{
Console.WriteLine( "Control: drawing Control at {o}, {l}",
top, left );
}
}

// ListBox izvodi iz Control


public class ListBox : Control

104 | Programiranje C#
r 5 -1 . Koritenje virtualnih metoda (nastavak)

private string listBoxContents; // nova varijabla lanica

// konstruktor dodaje parametar


public ListBox(
int top,
int left,
string contents ):
base(top, left) // poziva osnovni konstruktor

{
listBoxContents = contents;

* )

B 7 premoena inaica (pogledajte kljunu rije) jer u


// izvedenoj metodi mijenjamo ponaanje
t)ri public override void DrawWindow()
{
base.DrawWindow(); // poziva osnovnu metodu
Console.WriteLine( "Writing string to the listbox: {o}",
listBoxContents );
}
}
1public class Button Control

public Button(
int top,
int left ):
base(top, left)
{
}

i Prelnoena inaica (pogledajte kljunu rije) jer u


f // izvedenoj metodi mijenjamo ponaanje
public override void DrawWindow()
{
Console.WriteLine( Drawing a button at {0}, (l}\n",
top, left );
}
ii l \
i public class Tester
S
i{ i
li static void Main()
{
Control win = new Control( i, 2 );
ListBox lb = new ListBox( B, 4 , "tand alone list box" );
Button b = new Button( 5 , 6 );
win.DrawWindow();
lb.DrawWindow();
b.DrawWindow();

Poglavlje 5: Nasljeivanje i polimorfizam | 105


Primjer 5-1. Koritenje virtualnih metoda (nastavak)
C o nt ro l [] winArr ay = n ew Cont rol[ 3l ;
winArray[0] = ne w Control( 1 , 2 );
winArray[l] = new ListBox( 3, 4, "List box in array )
winAiray[2] = new Button( 5, 6 );

for ( int i = 0; i < 3; i++ )

winArray [i]. Draw Wind ow();

}
}
}
}
U cijelom su primjeru nove premoene metode oznaene kljunom rijeci overnde:
public override void DrawWindow()
P revoditelj sada zna k o ristiti p re m o e n u m eto d u k o d p o lim o rfn e o b ra d e ov ih obje-
k a ta . P revoditelj je odgovoran za p ra e n je stv a rn o g tip a o b je k ta i za ru k o v a n je kasnim
povezivanjem 11 ta ko da se ListBox.DrawWindow() poziva kad referen ca C o n tro l zapravo
p o ka z u je na o b je k t ListBox.

N a p o m e n a z a C++ p r o g r a m e r e : deklaraciju svake metode koja pre-


mouje virtualnu metodu morate eksplicitno oznaiti kljunom rijei
override.

Pozivanje konstruktora osnovne klase


U prim jeru 5-1 nova k lasa ListBo x izvo di iz C on trol i im a sv og k o n stru k to ra ko ji uzima
tri p ara m etra. K o n stru k to r L istB o x poziva k o n stru k to ra svo g ro d ite lja (C ontrol) sta-
v ljan jem dvotoke (0 iza p o p isa p a ra m e ta ra i z a tim po ziva o sn o v n u k la su s pomou

k lju n e rije i base:


public ListBox(
int theTop,
int theLett,
string theC on te nt s):
base(theTop, theLeft) // Poziva osnovnog konstruktora

B u d u i da k la se ne m ogu n a slje iv a ti k o n s tru k to r e , izv eden a k la sa m ora implem en-


tira ti svoj k o n stru k to r, a k o n s tru k to r o sn o v n e k la se m oe u p o trije b iti sa m o ako ga
ek sp licitn o pozove.
Izvedeni k o n stru k to r ne m ora e k sp lic itn o p o zv ati o sn o v n i k o n stru k to r a k o u osnovnoj
k la si p o sto ji dostu p an p o d ra z u m ije v a n i k o n stru k to r - p o d ra z u m ije v a n i konstruktor
se poziva im p licitn o. M e u tim , a k o u o sn o v n o j kla si n e p o sto ji p o d ra z u m ije v a m kon-
stru k to r, svaki izvedeni k o n stru k to r m o m e k sp licitn o p o zv ati je d n o g od konstruktora
o sn o vn e klase ko ristei k lju cn u rije base.

106 1 Programiranje C#
Jf *
Kao to je objanjeno u poglavlju 4, ako ne deklarirate nikakav kon-
struktor, prevoditelj e za vas stvoriti podrazumijevani konstruktor.
Bilo da ga sami napiete, ili koristite jedan od konstruktora koje je
pruio prevoditelj, podrazumijevani konstruktor je onaj koji ne uzima
parametre. Meutim, kada stvorite bilo kakav konstruktor (sa ili bez
parametara), prevoditelj nee umjesto vas stvoriti podrazumijevam
konstruktor.

Kontrola pristupa
Vidljivost klase i njenih lanova moe se ograniiti koritenjem modifikatora pristupa
kao to su public, private, protected, internal i protected internal (objanjenje modi-
fikatora pristupa potraite u poglavlju 4).
Kao to ste ve vidjeli, public doputa da lanu pristupe metode lanice drugih klasa,
dok private oznaava da je lan vidljiv samo metodama lanicama svoje klase. Kljuna
rije protected vidljivost proiruje ne metode izvedenih klasa, dok je internal proi-
ruje na metode svih klasa iz istog sklopa.'
Par kljunih rijei internal protected doputa pristup lanovima istog sklop (inter-
nal) ili izvedenih klasa (protected). Ovu oznaku moete shvatiti i kao internal ili
protected.

Klase se, isto kao i njihovi lanovi, mogu oznaiti bilo kojim od navedenih modifika-
tora pristupa. Ako lan klase ima drugaiji modifikator pristupa od klase, primjenjuje
se modifikator koji namee vee ogranienje. Stoga, ako klasu myClass definirate na
sljedei nain:
public class myClass
{
U ...
protected int myValue;
} .
pristup myValue je ogranien iako je sama klasa oznaena kao javna .Javna klasa je vidljiva
svim ostalim klasama koje joj ele pristupiti. esto se stvaraju klase ija je jedina svrha
pomoi drugim klasama u sklopu, a te je klase bolje oznaiti s internal nego s public.

Praenje inaica s pomou kljunih rijei new i override


U C # odluka programera da premosti virtualnu metodu eksplicitno se provodi s
pomou kljune rijei override. To pomae pri stvaranju novih inaica koda. Pro-
mjene osnovne klase nee pokvariti postojei kod u izvedenim klasama. Zahtjev za
koritenjem kljune rijei override pomae u sprjeavanju tog problema.

Sklop (kao to je objanjeno u Poglavlju 1) je jedinica dijeljenja i ponovnog koritenja koda u CLR-u
(logiki DLL). Sklop se obino stvara od skupa fizikih datoteka koje se nalaze u jednoj mapi koja sadri
sve resurse (bit mape, .g if datoteke itd.) potrebne za izvedbenu datoteku, zajedno slL-om i metapodacima
za taj program.

Poglavlje 5: Nasljeivanje i polimorfaam | 107


Evo kako se to odvija: pretpostavimo da je osnovnu klasu Control iz prethodnog ptp i
mjera napisala Tvrtka A. Pretpostavimo i da su klase ListBox i RadioButton napisali
programeri iz Tvrtke B koristei kupljenu kopiju klase Control koju je napisala Tvrtka 1
A kao osnovu. Programeri Tvrtke B mogu u maloj ili nikakvoj mjeri kontrolirati dizajn 1
klase Control, ukljuujui budue promjene koje e Tvrtka A napraviti. J
Pretpostavimo zatim da jedan od programera iz Tvrtke B odlui u ListBox dodati 1
metodu S o rt(): |
public class ListBox : Control !

{ i
public Virtual void Sort() {...} 1
} i
To ne predstavlja nikakav problem dok Tvrtka A, autor klase Control, ne izda inaicu j
2 svoje klase Control i ispostavi se da su i programeri Tvrtke A dodali metodu Sort() j
svojoj javnoj klasi Control:
public class Control
{
// ...
public Virtual void Sort() {...}
}
U drugim objektno orijentiranim jezicima (npr. u C++) nova virtualna metoda Sort() -
funkcionirala bi kao osnovna metoda za virtualnu metodu So rt() u ListBox. Prevodi-
telj bi pozvao metodu S ort() iz ListBox kad ste zapravo namjeravali pozvati metodu
S o rt() iz Control. U Java jeziku, ako So rt() iz Control ima drugi povratni tip,program
za uitavanje klase bi smatrao da je S o rt() iz ListBox nepravilno premoivanje i ui-
tavanje ne bi uspjelo.
C # sprjeava ovaj problem. Virtualna metoda se u C # uvijek smatra za korijen vir-
tualnog otpremanja tj. kad C # pronae virtualnu metodu on prestaje s traenjem uz
hijerarhiju nasljeivanja. Ako je u Control uvedena nova virtualna metoda Sort(),
ponaanje ListBox tijeko izvedbe ostaje nepromijenjeno.
Kada se ListBox ponovno prevede prevoditelj generira upozorenje:
...\classl.cs(54,24): warning CS0114: 'ListBox.Sort()' hides
inherited member 'Control.Sort() .
To make the current member override that implementation,
add the override keyword. O t h e m i s e add the new keyword.

Za uklanjanje upozorenja programer mora naznaiti svoju namjeru. On moe metodu


Sort() iz ListBox oznaiti s new kako bi konkretizirao da se ne radi o premoivanju
virtualne metode u Control:
public class ListBox : Control
{
public new Virtual void Sort() {...}

108 | Programiranje C#
-><Ova akcija uklanja upozorenje. Ako, s druge strane, programer eli premostiti metodu
tontrol, treba samo s pomou kljune rijei override naznaiti kako se radi o eks-
nam)en;
I public class ListBox : Contiol
i . ^ {
I' f public override void So rt() { . . . }

Da biste izbjegli ovo upozorenje moda ete htjeti svim svojim virtu-
alnim metodama dodati kljunu rije new. To nije dobra ideja. Kad se
kljuna rije new pojavi u kodu ona bi trebala dokumentirati inaice
koda. Ona potencijalnom klijentu pokazuje osnovnu klasu kako bi
vidio to ne premoujete. esto koritenje kljune rijei new podriva
takvu dokumentaciju. Nadalje, upozorenje postoji kako bi vam pomo-
glo da prepoznate stvarni problem.

Apstraktne klase
Svakapodklasa klase Control trebala bi implementirati vlastitu metodu DrawWindow()
-a li nita je na to ne obavezuje. Kako biste klase obavezali na implementaciju metode
^voje osnovne klase, trebate tu metodu oznaiti kao apstraktnu.

Apstraktna metoda nema implementaciju. Ona stvara naziv i potpis metode koji se
moraju implementirati u svim izvedenim klasama. Nadalje, oznaavanje jedne ili vie
metoda neke klase apstraktnima e i klasu uiniti apstraktnom.
Apstraktne klase tvore osnovu za izvedene klase, ali instancijacija objekta apstraktne
klase nije doputena. Deklariranjem apstraktne metode zabranjuje se stvaranje bilo
>kakvih instanci te klase.
J Stoga, ako DrawWindow() oznaite s abstract u klasi Control, moete izvoziti iz Control,
ali ne moete stvoriti objekte Control. Svaka izvedena klasa morala bi implementirati v
DrawWindow(). Ako izvedena klasa ne uspije implementirati apstraktnu metodu, klasa
Jjje isto bila apstraktna te instanciranje ponovno ne bi bilo mogue.
-^Metoda se s abstract oznaava tako da se kljuna rije abstract napie na poetku
spdefinicije metode:
r abstract public void DrawWindow();

(Kako metoda ne moe imati implementaciju, na stavljaju se vitiaste zagrade ve


iSamo toka zarez.)

Ako postoji jedna ili vie apstraktnih metoda, definicija klase se takoer mora oznaiti
- sabstract, kao u sljedeem primjeru:
abstract public class Control

U primjeru 5-2 prikazano je stvaranje apstraktne klase Control i apstraktne metode


DrawWindow().

Poglavlje 5: Nasljeivanje i polimorfizam | 109


Primjer 5-2. Upotreba apstraktne metode i klase
ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace abstractmethods
{
using System;

abstract pubiic class Control

{
protected int top;
protected int left;

I I Konstruktor uzima dvije cjelobrojne vrijednosti


// za fiksiranje lokacije na konzoli
pubiic Control( int top, int left )
{
this.top = top;
this.left = left;
}
I I Simulira iscrtavanje prozora
// napomena: nema implementacije

abstract pubiic void DrawWindow();

// ListBox izvodi iz Control


pubiic class ListBox : Control
{
private string listBoxContents; // Nova varijabla lanica

// Konstruktor dodaje parametar


pubiic ListBox(
int top,
int left,
string contents ):
base(top, left) // poziva konstruktora base
{

listBoxContents = contents;
}

// Premoena inaica implementira


I I apstraktnu metodu

pubiic override void DrawWindow()


{

110 | Programiranje C#
| fimjer5-2. Upotreba apstraktne metode i klase (nastavak)
, Console.WriteLine( "Mriting string to the listbox: {o}",
listBoxContents );
}

P
- - j g p p public class Button : Control

public Button(
int top,
int left ):
base(top, left)
{
}
// implementira apstraktnu metodu

public override void DrawWindow()


{
Console.WriteLine( "Drawing a button at {oj, {i)\n"
to p , l e f t ) ;
}

public class Tester


{
static void Main()
{
Control[] winArray = new Control[3 ];
winArray[0 ] = new ListBox( 1 , 2 , "First List Box" )
winArray[lJ = new ListBox( 3, 4, "Second List Box" );
winArray[2 ] = new Button( 5, 6 );

for ( int i = 0; i < 3; i++ )


| f
winAiray[i ].DzaMindow();

}
}
* }

, V Pnm^ ru 5 -2 k,asa Control je deklarirana kao apstraktna i stoga se ne moe instan-


' ; cirati. Ako zamijenite prvi lan polja:
winArray[oJ = new ListBox(l,2 ,"First List Box");

sa sljedeim kodom:
winArray[o] = new Control(l,2 );

'program e generirati sljedeu pogreku:


<Tannot create a" instance of the abstract class or interface
abstractmethods.Control 1

Poglavlje 5: Nasljeivanje i polimorfizam | m


Moete instancirati objekte ListBox i Button jer te klase premouju apstraktnu metodi
to ih ini konkretnima (tj. ne-apstraktnima). J
3
Ogranienja apstraktnih klasa
Iako oznaavan'je DrawWindow-() apstraktnom prisil l jjava sve
izve--d--e-n
--e
kla-s-eiinaun
lm
pie,^
mentaciju te metode, to je vrlo ogranieno rjeenje problema. Ako klasu izvedemo '1
) ?is<t;tBRonxy i(nrptrr. Dm
L roripHDo/
nuwnnl Li iqsttBBnoxxV
), tu
tu izizv
vededenu
nu klasu n ita ng prisiljava na
implemeittac ' ! vlastite metode DrawWindow().

Napomena za C++ programere: u jeziku C # Control.DrawWindow() ne


moe pruiti implementaciju pa se ne mogu iskoristiti prednosti zajed-
f,' nikih rutina DrawWindow() koje inae mogu koristiti izvedene klase.

Naposljetku, apstraktne klase ne bi smjele biti samo implementacijski trik. One bi t


trebale predstavljati ideju apstrakcije koja uspostavlja ugovor" za sve izvedene klase 1
Drugim rijeima, apstraktne klase opisuju javne metode klasa koje e implementirati!
apstrakciju. 1

Ideja apstraktne klase Control je u postavljanju zajednikih karakteristika i p o n aan ja!


svih kontrola, ak i ako nikad nije planirano instanciranje same apstrakcije Control. I
Ideja apstraktne klase implicirana je u rijei abstract" (apstraktno). Ona slui zal
implementaciju apstrakcije kontrole" koja e se manifestirati u raznim konkretnim!
instancama klase Control, poput prozora preglednika, okvira, gumba, padajueg!
popisa ili izbornika, Apstraktna klasa odreuje to Control jest, iako namjera nikad!
nije stvaranje kontrole per se. Alternativa koritenju kljune rijei abstract je definira-J
nje suelja, kao to je opisano u poglavlju 8. a

Zapeaena klasa
U projektiranju programa, suprotno od apstraktnog je zapeaeno (engl. sealed). Iakol
je namjena apstraktne klase da se iz nje izvodi i da prui predloak koji trebaju slijediti!
njene podklase, zapeaena klasa uope ne doputa da se iz nje izvode klase. Ako sef
ispred deklaracije klase napie kljuna rije sealed, ona sprjeava izvoenje. Klase sef
sa sealed najee oznaavaju kako bi se sprijeilo sluajno nasljeivanje.
a\
r** Napomena za Java programere: zapeaena klasa u C # odgovara final-
V.N 4 noi k*as' uJavalezku.

Ako se deklaracija klase Control u primjeru 5-2 promjeni iz abstract u sealed (i elitni-'
nira kljuna rije abstract iz deklaracije DrawWindow()), program se nee prevesti. Ako|
pokuate izgraditi ovaj projekt, prevoditelj e vratiti sljedeu poruku o pogreci:

112 | Programiranje C#
,, 'LiitB::x' cannot inherit from sealed class 'Control'

ne druge primjedbe (na primjer, da ne moete stvoriti novog zatienog lana u


jeaenoj klasi).

|j|rijen svih klasa: Object


C# klase, bilo kojeg tipa, tretiraju se kao da izvode iz System.Object. To, zani-
^ k jjivo, ukljuuje i vrijednosne tipove.

^Olovna klasa je neposredan roditelj" izvedenoj klasi. Izvedena klasa moe biti
; osnova daljnjim izvedenim klasama stvarajui tako stablo" nasljeivanja ili hijerar-
^hl|U. Korijenska klasa je najvia klasa u hijerarhiji nasljeivanja. Korijenska klasa u
ob3ect- Nomenklatura je pomalo zbunjujua dok ne zamislite stablo okrenuto
'^naopako, s korijenom na vrhu i izvedenim klasama ispod njega. Osnovna klasa je
11ffakle, iznad" izvedene klase.

Napomena za C++programere: C# koristi jednostruko nasljeivanje s


^ monolitnom hijerarhijom klase: svaka klasa nasljeuje iz osnovne klase
5 ? ; Object, a viestruko nasljeivanje nije mogue. Meutim, C # suelja
i pruaju brojne pogodnosti viestrukog nasljeivanja. Vie informacija
o tome potraite u poglavlju 8.

, Object prua razne virtualne metode koje podklase mogu premoivati. Meu njima je
^lEgualsO s kojom se utvruje jesu li dva objekta ista, GetType() koja vraa tip objekta
. (detaljnije je objanjena u poglavlju 8) i ToString() koja vraa niz za predstavljanje
p l i e g objekta (objanjeno u poglavlju 10). U tablici 5-1 moete pronai saetak
|metoaa klase Object.

fablica 5-1. Metode klase Object


i;
Storadi
f|juals() Procjenjuje jesu li dva objekta jednaka
etllashCode()
Doputa objektima da prue vlastitu metodu rasprivanja za upotrebu u kolekcijama
(pogledajte poglavlje 9 za vie informacija)
petType()
Omoguava pristup objektu tipa (pogledajte poglavlje 18 za vie informacija)
|~7oString()
Omoguava predstavljanje objekta nizom
|Finalize()
Cisti resurse koji nisu memorijski; implementiraju destruktor (pogledajte poalavlie A
za vie informacija)
^ f le m b e rv v is e C lo n e O Stvara kopije objekta; va tip je nikad ne bi smio implementirati
4 t 5 ! l eI e!lceE(l ual s ( ) Procjenjuje pokazuju li dva objekta na istu instancu
u ...............................................................................................................

^ 5 ' 3 pnkazana Je uPoeba metode ToStringO naslijeene iz Object, kao i


4 0bW e pnmitlVni tlpovi podataka PPt int mogu tretirati kao da nasljeuju
J a u P3ZnjU 113 einJenicu da metoda DisplayValue oekuje objekt, ali
0 dobro funkcionira i ako proslijedite cjelobrojnu vrijednost.

Poglavlje 5: nasljeivanje i p ofm orfu m / m


Primjer 5-3. Nasljeivanje iz O bjed
#region Using directives

using System;
using Sy st em . C ollecti ons.Ge neric;
using Sy stem.Text;

#endregion

names pace Inh eri ti ng Fr om Ob je c t


{
Public class SomeClass
{
private int val;

public SomeClass( int someVal )


{
val = someVal;
}
public override string ToSt ring O
{
return val.ToStringO;
}
}

pu b l i c class Tester

static v oid Disp la y V al ue ( obje ct o )

{
Console.WriteLine(
"The value of the object passed in is {0} , o.ToStringO );

static void Main()


{
Console.WriteLine( "The value of i is: {o}", i.ToStringO );
DisplayValue( i );

SomeClass s = new SomeClass( 7 );


Console.WriteLine( "The value of s is {o}", s.ToStringO );
DisplayValue( s );
}
}
}
Dokumentacija za Object.ToStringO otkriva njezin potpis:

public Virtual string To StringO;

To je javna virtualna metoda koja vraa niz i ne uzima parametre. Svi ugraeni tipovi,
poput tipa in t, izvedeni su iz Object i stoga mogu pozivati njene metode.

114 | Programiranje C#
fi^ er 5-3 premouje virtualnu metodu za SomeClass, to je uobiajen sluaj, tako da
metoda ToStringO moe vratiti smislenu vrijednost. Ako smjestite u komentar
iena
S noenu metodu, bit e pozvana osnovna metoda koja e izlaz promijeniti u:
The value of s is SomeClass

fik je podrazumijvano ponaanje vraanje niza s nazivom klase.


'lase ne trebaju eksplicitno deklarirati da izvode izObject. Nasljeivanje je implicitno.

Pakiranje i raspakiravanje tipova


' pakiranje (engl. boxing) i raspakiravanje (engl. unboxing) su procesi koji omoguavaju
% uetiranje vrijednosnih tipova (npr. cjelobrojnih vrijednosti) kao da su referentni tipovi
V objekti). Vrijednost se pakira* unutar Object i zatim se ,,raspakirava u vrijednosni tip.

Napomena zaJava programere: ujeziku Java pakiranje osnovnih tipova


u objekte zahtjeva eksplicitnu upotrebu tipova omotaa poput Integer i
- -3? Float. U C# mehanizam pakiranja sve to radi automatski. Tipovi omo-
taa nisu potrebni.

Pakiranje je im p lic itn o


f?
4flP|kiranje je implicitna pretvorba vrijednosnog tipa u tip Object. Pakiranje vrijednosti
j'T dodjeljuje instancu tipa boxed i kopira vrijednost u novu instancu objekta, kao to je
prikazano na slici 5-4.

Na stogu Na gom ili

123
inti= 123 ;

123
object o=i;

lika 5-4. Pakiranje referentnih tipova

Pakiranje je implicitno kad pruite vrijednosni tip na mjestu na kojem se oekuje


Ako, na primjer, primitivni tip dodijelite varijabli tipa Object (to je dopus-
tiUjenti o jer int izvodi iz Object), vrijednost se pakira, kao sto je prikazano u sljedeem
Jlpnmjeru:
ag/pf using System;
class Boxing

public static void Main()

Poglavlje 5: Nasljeivanje i polimorfizam | 115


{
int i = 123;
Co ns ol e. Wr it eL in e( "T he object value = { o }", i);

}
}
Console.WriteLine() oekuje objekt, a ne cjelobrojnu vrijednost. Kako bi zadovo-
ljila metodu, CLR automatski pakira tip cjelobrojne vrijednosti, a na rezultirajuem
objektu se poziva ToString(). Ova znaajka omoguava stvaranje metoda koje kao
parametar uzimaju objekt. Metoda e funkcionirati bez obzira na to to joj se prosli-
jedi (referenca ili vrijednosni tip).

Raspakiravanje mora biti eksplicitno


Kako biste zapakirani objekt vratili u vrijednosni tip, morate ga eksplicitno raspaki -
ran. To trebate uiniti u sljedea dva koraka:
1. Provjerite je li instanca objekta zapakirana vrijednost odgovarajueg vrijedno-
snog tipa.
2 . Kopirajte vrijednost iz instance u varijablu vrijednosnog tipa.

To je prikazano na slici 5-5.


Za uspjeno raspakiravanje objekt koji se raspakirava mora biti tipa koji odgovara vari -
jabli kojoj ga dodjeljujete. Pakiranje i raspakiravanje su prikazani u primjeru 5-4.

Na stogu Na gomili

inti= 123;

object o=i;

int j(int) o;

Slika 5-5. Pakiranje i zatim raspakiravanje

Primjer 5-4. Pakiranje i raspakiravanje


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

116 | Programiranje C#
p r i m j e r 5 - 4 . P a k ir a n je i r a s p a k i r a v a n j e ( n a s t a v a k )

:#endregion

namespace boxing

^ public class UnboxingTest

^ public static void Main()


{
int i = 123;

//pakiranje
object o = i;

// raspakiravanje (mora biti eksplicitno)


int j = ( int ) o;
Console.WriteLine( "j: {0}, j );
}
}
}
Primjer 5-4 stvara cjelobrojnu vrijednost i i implicitno je pakira prilikom dodjele
objektu o. Ta se vrijednost zatim eksplicitno raspakirava i dodjeljuje novoj cjelobroj-
noj vrijednosti int ija se vrijednost prikazuje.
Uveini ete sluajeva operaciju raspakiravanja smjestiti u bloku try, kao stoje obja-
njeno u poglavlju 11. Ako je objekt koji se raspakirava nuli ili referenca objekta nekog
drugog tipa, izbacuje se InvalidCastException.

Ugnjeivanje klasa
Klase imaju lanove, a lan klase moe biti drugi korisniki definiran tip. Klasa Button,
dakle, moe imati lana tipa Location, a klasa Location moe sadravati lanove tipa
Point. Naposljetku, Point moe sadravati lanove tipa int.

Sadrana klasa ponekad moe postojati samo kako bi sluila vanjskoj klasi i ne mora
postojati razlog da bude vidljiva (ukratko, sadrana klasa slui kao pomona klasa).
Pomonu klasu moete definirati unutar definicije vanjske klase. Sadrana klasa se
naziva ugnijeena klasa (engl. nested class), a klasa koja ju sadri se jednostavno zove
vanjska klasa.
Ugnijeene klase mogu pristupati svim lanovima vanjske klase. Meotda ugnijeene
klase moe pristupiti privatnim lanovima vanjske klase.

Ugnijeena klasa moe biti skrivena od svih ostalih klasa - to jest, ona
moe biti privatna za vanjsku klasu.

Poglavlje 5: Nasljeivanje i polimorfizam | 117


Naposljetku, javnoj ugnijeenoj klasi pristupa se unutar dosega vanjske klase. Ako je
Outer vanjska klasa, a N e s t e d je (javna) unutarnja klasa, N e s t e d referirajte kao Outer.
Nested, gdje vanjska klasa funkcionira (vie-manje) kao imenski prostor ili doseg.

N a p o m e n a z a j a v a p r o g r a m e r e : ugnijeene klase otprilike odgovaraju


statinim unut ar nj im klasama. U C # ne postoje klase koje odgovaraju
nestatikim u nu tarnjim k la s a m a jezika Java.

U primjeru 5-5 prikazana je klasa Fraction A rtist koja je ugnijeena u klasi Fraction.
Metoda klase FunctionArtist je prikaz razlomka na konzoli. U ovom primjeru prikazi-
vanje izvodi par jednostavnih iskaza WriteLine().

P r im je r 5 - 5 . U p o t r e b a u g n i j e e n e k l a s e

#region Usirtg directives

using System;
using System.Collections.Generic;
using System.Text;

ttendregion

namespace NestedClasses
{
public class Fraction
{
private int numerator;
private int denominator;

public Fraction( int numerator, int denominator )


{
this.numerator = numerator;
this.denominator = denominator;
}
public override string To St ri ng O
{
return String.Format( "{0}/{l}",
numerator, denominator );
}
internal class FractionArtist
{
public void Draw( Fraction t )
{
Console.WriteLine( "Drauing the numerator: {o}'1,
f.numerator );
Console.WriteLine( ''Drauing the denominator: {o}",
f.denominator );
}
}
}

118 | Programiranje C#
Primjer 5-5. U p o t r eb a u g n i je e n e k l a s e ( n a s t a v a k )

public class Tester

static void Main()


{
Fraction fl = new Fraction( 3, 4 );
Console.WriteLine( "fl: {0}", fl.ToString() );

Fraction.FractionArtist fa = new Fraction.FractionArtist();


fa.Draw( fl );
}
}
}
Ugnijeena klasa je prikazana podebljanim slovima. Klasa FractionArtist prua
samo jednog lana, metodu Draw( ). Ono to je posebno zanimljivo jest da Draw( ) ima
pristup privatnim lanovima podacima f.numerator i f.denominator kojima ne bi imala
pristup da nije ugnijeena klasa.

U Main() moete primijetiti kako za deklariranje instance ove ugnijeene klase morate
zadati naziv tipa vanjske klase:
Fraction.FractionArtist fa = new Fraction.FractionArtist();

Doseg klase FractionArtist je ogranien na klasu Fraction.

Poglavlje S: Nasljeivanje i polimorfizam I 119


POGLAVLJE 6
Preoptereivanje operatora

Projektanti jezika C # imali su za cilj da omogue da korisniki definirane klase imaju


sve funkcionalnosti ugraenih tipova. Pretpostavimo da ste definirali tip koji predsta-
vlja razlomke. Osiguravanje da ova klasa ima sve funkcionalnosti koje imaju ugraeni
tipovi znai da na instancama razlomaka morate moi provesti aritmetike operacije
(npr. zbrojiti dva razlomka, pomnoiti ih itd.) i pretvarati razlomke u i iz ugraenih
tipova. Moete, naravno, za svaku operaciju implementirati metode i pozvati ih pisa-
njem iskaza poput:
Fraction theSum = firstFraction.Add(secondFraction);

Iako e ovaj iskaz funkcionirati, on je nespretan i to nije nain na koji se koriste ugra-
eni tipovi. Mnogo bi bolje bilo napisati:
Fraction theSum = firstFraction + secondFraction;

Ovakvi su iskazi intuitivni i u skladu su s nainom dodavanja ugraenih tipova, poput


int.
U ovom ete poglavlju nauiti tehnike za dodavanje standardnih operatora tipovima
koje ste sami definirali. Nauit ete i kako se dodaju operatori pretvorbe kako bi se
korisniki definirani tipovi mogli implicitno i eksplicitno pretvarati u druge tipove.

Koritenje kljune rijei operator


U C # se operatori implementiraju stvaranjem statikih metoda ije povratne vrijed-
nosti predstavljaju rezultat operacije i iji su parametri operandi. Prilikom stvaranja
operatora za klasu naznaujete kako ste preopteretili" taj operator, na isti nain na
koji bistepreopteretili bilo koju drugu metodu lanicu. Stoga biste za preoptereivanje
operatora zbrajanja (+) napisali:
public static Fraction operator+(Fraction lhs, Fraction rhs)

Ja za parametre obino koristim nazive lhs i rhs. Naziv lhs znai lijeva strana (engl. left
handside) i podsjea me da prvi parametar predstavlja lijevu stranu operacije. U skladu
s tim, rhs znai desna strana (engl. right hand side).

120
Sintaksa je z ik a C# za p re o p te reiv a n je o p e ra to ra je da se ispred op erato ra k o ji se
treba p reop teretiti n a p ie rije o p era to r. K lju n a rije op era to r je m o d ifika to r m eto de.
Stoga, za p re o p tereiv a n je op eratora zbrajan ja (+) treb ate n ap isati operator+.

Kada nap iete:


Fraction theSum = firstFraction + secondFraction;

poziva se preoptereeni operator +, pri emu se fir s t F ra c t io n prosljeuje kao prvi argu-
ment, a secon d Fraction kao drugi argument. Kada prevoditelj vidi izraz:
firstFraction + secondFraction

on ga prevodi u:
Fraction.operator+(firstFraction, secondFraction)

Rezultat je v ra a n je novog F ra c tio n ko ji se u o v o m slu aju d o d je lju je F ra c tio n o b je k tu


pod im enom theSum.

Napomena za C++ programere: stvaranje nestatikih operatora nije


^ mogue, stoga binarni operatori moraju uzimati dva operanda.

Podrka ostalim .NET jezicima


C# prua mogunost preoptereivanja operatora za vae klase. Ostali .N ET jezici,
poput VB.NET-a, moda nee podravati preoptereivanje operatora i stoga je vano
osigurati da vaa klasa podrava alternativne metode koje bi drugi jezici mogli pozvati
za postizanje istog efekta.

Stoga, ako preoptereujete operator zbrajanja (+), dobro bi bilo dodati i metodu add()
koja ima istu funkciju. Preoptereivanje operatora treba biti sintaktiki preac, a ne
jedini nain na koji objekti izvode odreeni zadatak.

Stvaranje korisnih operatora


Preoptereivanje operatora moe kod uiniti jasnijim i da funkcionira poput ugrae-
nih tipova. Ono kod moe uiniti i neupravljivim, sloenim i nefunkcionalnim ako
prekrite uobiajeni idiom za koritenje operatora. Oduprite se iskuenju koritenja
operatora na nov i individualan nain.

Na primjer, iako je ideja preoptereivanja operatora uveavanja (++) na klasi zaposle-


nika kako bi se pozvala metoda za uveavanje razine zaposlenikove plae moda pri-
mamljiva, to moe stvoriti veliku zbrku za klijente klase. Preoptereivanje operatora
je najbolje koristiti tedljivo i samo onda kada je znaenje jasno i u skladu s nainom
na koji funkcioniraju ugraene klase.

P oglavlje 6: P reo p tereivan je op erato ra | 121


Logiki parovi
P re o p te re iv a n je o p era to ra je d n a k o s ti (==) radi p ro v jere je s u li dva o b je k ta jednaka
(b e z ob z ira na n a in na k oji je d e fin ira n a je d n a k o s t o b je k a ta ) p rili n o je uobiajeno. A
ko u C # p re o p te re u je te o p e ra to r je d n a k o s ti, ob av ezn o m o ra te p re o p te re titi i opera- |
tor n e je d n a k o sti (! =). S li n o to m e , m o ra ju se u p a riti i o p e ra to ri m a n je o d (<) i vee i
o d (>), k a o i op erato ri m an je od ili je d n a k o 11 (<=) i vee od ili je d n a k o (>=).

Napomena za C i C++ programere: m noge C+ + b ib lio teke zahtijevaju


im plem entaciju sam o o peratora < ili =, a da se ostale operacije izvode
iz njih. C # zahtijev a im plem entaciju o b je polovine ovih uparem h
oneratora.

Operator jednakosti
A ko p re o p te re u je te o p era to r je d n a k o s ti (==), p re p o ru ljiv o je p re m o stiti i virtualnu j j
m eto du E q u a ls() koju p ru a o b je c t i n je n u fu n k c io n a ln o st u sm je riti n a z a d do opera- |
tora je d n a k o s ti. T o k la si o m o g u a v a p o lim o rfn o st i k o m p a tib iln o s t s o sta lim .NET 1
je z icim a k o ji ne p re o p te re u ju o p e ra to re (ali p o d r a v a ju p re o p te re iv a n je metoda). F |
C L k la se n e e k o ris titi p re o p te re e n e o p era to re , ali e o e k iv a ti da k la se implemen- q
tira ju tem e ljn e m eto d e. K la sa o b je c t im p le m e n tira m e to d u E q u a ls () sa sljedeim |

p o tp iso m : 1
public Virtual bool Equals(object o)

P re m o iv a n je m ove m eto d e d o p u ta te k la si F ra c tio n d a sa sv im d ru g im objektim a j

d jelu je p o lim o rfn o . U n u ta r tije la m e to d e E q u a ls () tre b a t e te o sigu ra ti d a usporeu-


je te s d ru g im F ra c tio n te u tom s lu a ju m o e te p ro d u iti im p lem e n ta ciju do definicije i
o p era to ra je d n a k o sti:
public override bool Equals(object o)

i-f ( ! (o is Fractio n ) )
{
return false;

retu rn t h is == (F ra ctio n ) o;
}
O p e ra to r i s k o risti se k a k o bi se p ro v je rilo je li tip o b je k ta tije k o m iz v o en ja kompati-
b ila n s o p e ra n d o m (u o v om s lu a ju , s F r a c tio n ). Sto ga e o i s F ra c tio n b iti tru e ako
je o u stv a ri tip k o ji je k o m p a tib ila n s F r a c tio n .

P revoditelj e oekiv ati da prem ostite i GetHashCode, kao to je o b ja -


njeno dalje u tekstu .

122 | P ro g ra m ira n je C#
Operatori pretvaranja
C# int implicitno pretvara u long i omoguava eksplicitnu pretvorbu long u int. Pre-
' tvaranje int u long je implicitno (za njeno izvoenje nije potrebna nikakva sintaksa) i
| jj sigurno jer znate da e bilo koji int stati u memorijsku reprezentaciju long. Obrnuta
^ operacija, pretvaranje iz long u int mora biti eksplicitno (koritenjem operatora pretva-
> ranja tipa) jer se pri takvom pretvaranju mogu izgubiti informacije:
-** int mylnt = 5;
long myLong;
myLong = mylnt; // implicitno
mylnt = (int) myLong; // eksplicitno

Za svoje razlomke morate imati istu funkcionalnost. Ako upotrijebite in t, moete


podrati implicitno pretvaranje u razlomak jer je cijela vrijednost jednaka toj vrijed-
nosti kroz 1 (npr. 15==15/1).
1 Kad imate razlomak, moda ete ga poeljeti eksplicitno pretvoriti natrag u cjelobro-
jnu vrijednost iako znate da e neke informacije moda biti izgubljene. Tako moete
9 / 4 pretvoriti u 2 .

Prilikom implementacije vlastitog pretvaranja kljuna rije im plicit se koristi kad je


sigurno da e pretvorba uspjeti i da nee doi do gubitka informacija. U ostalim se
sluajevima koristi kljuna rije ex p licit.

K lju nu rije im p lic it obavezno koristite u vijek kad ne koristite


e x p lic it .
>'' 4
k1

U primjeru 6-1 prikazana je mogua implementacija implicitnog i eksplicitnog pretva-


ranja te neki od operatora iz klase Fraction. (lako sam koristio ConsoleWriteLine()
r za ispis poruka koje govore koja se metoda unosi, bolji nain ovakvog praenja jest s
pomou alata za ispravljanje pogreaka. Toku prekida moete postaviti u svaki iskaz
'% koji se provjerava, zatim ui u kod promatrajui pozivanje konstruktora onim redosli-
^fijedom kojim se pojavljuju.) Dok prevodite ovaj primjer generirat e se upozorenja jer
pmetoda GetHashCode() nije implementirana (pogledajte poglavlje 9).

Primjer 6-1. Definiranje pretvorbi i operatora za operatore iz klase razlomaka


~'h
' public class Fraction
{
private int numerator;
private int denominator;

public Fraction(int numerator, int denominator)


{
Console.WriteLine("In Fraction Constructor(int, int)");
this.numerator=numerator;
V this.denominator=denominator;
}

Poglavlje 6: Preoptereivanje operatora | 123


Primjer 6-1. Definiranje pretvorbi i operatora za operatore iz klase razlom aka (nastavak)

public Fraction(int wholeNumber)

Console.UriteLinef'In Fraction Constructor(int)");


numerator = wholeNumber;
denominator = l;
}
public static implicit operator Fraction(int thelnt)

^ Console.WriteLine("In implicit conversion to Fraction'1);


return new Fraction(thelnt);
}
public static explicit operator int(Fraction theFraction)

Console.WriteLine("In explicit conversion to int");


return theFraction.numerator /
theFraction.denominator;

public static bool operator==(Fraction lhs, Fraction rhs)

Console.WriteLine("In operator ==);


if (lhs.denominator == rhs.denominator &&
lhs.numerator == rhs.numerator)
{
return true;

// Upiite kod za obradu nejednakih razlomaka


return false;
}
public static bool operator !=(Fraction lhs, Fraction rhs)

Console.WriteLine("In operator !=");

return !(lhs==rhs);
}
public override bool Equals(object o)

Console.WriteLine(''In method Equals");


if (! (o is Fraction) )
{
return false;
}
return this == (Fraction) o;
}
public static Fraction operator+(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator+ );

124 | Programiranje C#
^ }^ p ritjtjer 6-1. Definiranje pretvorbi i operatora za operatore iz klase razlomaka (nastavak)
if (lhs.denominator == rhs.denominator)
{
return new Fraction(lhs.numerator+rhs.numerator,
lhs.denominator);
}

// Jednostavno rjeenje za nejednake razlomke


// 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8
int firstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction(
firstProduct + secondProduct,
lhs.denominator * rhs.denominator
);
}
i
public override string ToStrin g O
{
String s = numerator.ToStringO + +
denominator.ToString();
return s;

r public class Tester

static void Main()


{
Fraction fl = new Fraction(3 ,4 );
Console.WriteLine("fl: {o}", fl.ToStringO);

Fraction f2 = new Fraction(2 ,4 );


Console.WriteLine("f2 : {o}", f2 .ToStringO);

' Fraction f3 = fl + f 2 ;
Console.WriteLine("fl + f2 = f3: {o}", f3.ToStringO);

Fraction f4 = f3 + 5 ;

t Console. HriteLine("-f3 + 5 = f4: {o}", f4.ToStringO);

Fraction f 5 = new Fraction(2,4);


if (f5 == f2)
{
Console.WriteLine("F5: {0 } == F2 : {l}",
f5.ToStringO,
^ f2.ToStringO);

Klasa Fraction poinje s dva konstruktora. Jedan ima brojnik i nazivnik, a drugi cijeli
broj. Iza konstruktora slijedi deklaracija dva operatora pretvaranja. Prvi operator cje-
rojnu vrijednost mijenja u Fraction:

Poglavlje 6: Preoptereivale operatora | 125


public static implicit operator Fraction(int thelnt)
{
return new Fraction(thelnt);
}
Ova je pretvorba oznaena s implicit jer se bilo koji cijeli broj (int) moe pretvoriti
u Fraction tako da se int postavi za brojnik, a 1 za nazivnik. Delegirajte ovaj posao'
konstruktoru koji uzima int.
Drugi operator pretvaranja slui za eksplicitnu pretvorbu razlomaka u cjelobrojne
vrijednosti:
public static explicit operator int(Fraction theFraction)
{
return theFraction.numerator /
theFraction.denominator;
}
Budui da se u ovom primjeru koristi dijeljenje cjelobrojnih vrijednosti, vrijednost e
se skratiti. Stoga, ako je razlomak 15/16, rezultirajua cjelobrojna vrijednost bit ceo.
Upotrebom sofisticiranijeg operatora za pretvaranje moe se postii zaokruivanje.
Iza operatora pretvaranja nalazi se operator jednakosti (==), a ne operator nejednakosti
(!=). Upamtite da, ako implementirate jedan od ova dva operatora jednakosti, morate
implementirati i drugi.
Jednakost za Fraction ste definirali tako da brojnici i nazivnici moraju biti jednaki. U
ovoj se vjebi 3 / 4 i 6 / 8 ne smatraju jednakim a. Ponovo, upotreba sofisticiranije imple-
mentacije skratila bi ove razlomke i primijetila jednakost.
Ukljuite preoptereivanje metode Equals() iz klase objekta kako bi se Fraction objekti
tretirali polimorfno sa svim ostalim objektima. Vaa implementacija je da se procjena
jednakosti delegira operatoru jednakosti.
Klasa Fraction bi, bez sumnje, implementirala sve aritmetike operatore (zbrajanje,
oduzimanje, mnoenje, dijeljenje). Kako bi primjer bio to jednostavniji, implementi-
rajte samo zbrajanje i to maksimalno pojednostavite. Provjerite jesu li nazivnici jed-
naki; ako jesu, dodajte sljedee brojnike:
public static Fraction operator+(Fraction lhs, Fraction rhs)
{
if (lhs.denominator == rhs.denominator)
{
return new Fraction(lhs.numerator+rhs.numerator,
lhs.denominator);
}
Ako nazivnici nisu jednaki, upotrijebite unakrsno mnoenje:
int tirstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction(
tirstProduct + secondProduct,
lhs.denominator * rhs.denominator
);

126 | Programiranje C#
. kod lake razumjeti s pomou primjera. Ako zbrajate 1 / 2 i 3/4, moete prvi
p j e)
l
fjngiik (l) pomnoiti drugim nazivnikom (4) i rezultat spremiti u firstProduct. Moete
ikbrojnik (3) pomnoiti prvim nazivnikom (2 ) i rezultat spremiti u secontProduct.
rugi
ve rezultate zbrojite (6+4), pri emu dobijete zbroj 1 0 , to je brojnik rezultata. Zatim
%noite dva nazivnika (2*4) kako biste generirali novi nazivnik (8 ). Toan odgovor
c razk>mak 1 0 /8 .
JgBnano, premostite ToStringO kako bi Fraction mogao svoju vrijednost vratiti u
"^.iJt>ku numerator/denominator:
,, V publlc override string T o StringO

^ String s = numerator.ToStringO + "/ +


denominator.ToStringO;
jV return s;
r i
i?" . . . .
^Jdasa Fraction vam je pri ruci i spremni ste za provjeru. U prvim se provjerama stva-
rajujednostavni razlomci, 3/4 i 2 /4 :
Fraction fl = new Fraction(3,4);
Console.WriteLine(''fl: {0 } " , fl.ToString(j);

Fraction f2 = new Fraction(2,4);


> Console.WriteLine("f2: {0 }", f2.ToString());

tOvim se postupkom dobiva oekivani rezultat - pozivanje konstruktora i vrijednost


,fispisana u WriteLine():
In Fraction Constructor(int, int)
fl: 3/4
t^ in Fraction Constructor(int, int)
!? f2: 2/4

^Sljedei red u Main() poziva statiki operator+. Svrha ovog operatora je zbrajanje dva
ikr^zlomka i vraanje zbroja u obliku novog razlomka:
Fraction f3 = fl + f2;
Console.UriteLine("f 1 + f2 = f3: { 0 } " , f3.ToStringO);

pogled na rezultat otkriva nain na koji operator+ funkcionira:


In operator+
In Fraction Constructor(int, int)
< fl + f2 = f3: 5/4

^Pozivae operatori, zatim konstruktor za f 3 koji uzima dvije int vrijednosti koje pred-
stavljaju brojnik i nazivnik rezultirajueg razlomka.

''Nova provjera u Main() zbraja int i Fraction f 3 i rezultirajuu vrijednost dodaje novom
razlomku f 4 :

1 Ponovimo jo jednom: 1/24/8, 3/4-6/8, 4/8+6/8=10/8. U primjeru razlomak nije skraen kako bi kod
> ostao to jednostavniji.

m- Pog/avlje 6: Preopterevanje operatora | 127


Fraction f4 = fB + 5;
Console.WriteLine("f3 + 5: {0}", f4.ToString());

U izlazu se mogu vidjeti koraci raznih pretvorbi:


In implicit conversion to Fraction
In Fraction Constructor(int)
In operatora- |
In Fraction Constructor(int, int) I
f3 + 5 = U : 25/4

Primijetit ete kako je operator implicitnog pretvaranja pozvan kako bi se 5 pretvorilo!


u razlomak. U povratnom iskazu operatora implicitnog pretvaranja pozvan je kon-i
struktor Fraction ime je stvoren razlomak 5 /1 . Ovaj je novi razlomak zatim proslije-1
en zajedno s Fraction f3 do operatora-, a zbroj je proslijeen do konstruktora zaf 4 .|
U konanoj provjeri stvara se novi razlomak (f 5). Provjerite je li on jednak razlomku'!
f 2 . Ako jest, ispiite njihove vrijednosti: |
Fraction f5 = new Fraction(2,4);
if (f5 == f2)
{
Console.WriteLine("F5: {0} == F2 : {l}",
f5.ToString(),
f2.ToString());
1

U izlazu se vidi nain stvaranja f 5 , a zatim pozivanje preoptereenog operatora!


jednakosti:
In Fraction Constructor(int, int)
In operator ==
F5: 2/4 == F 2 : 2/4

128 j Programiranje C#
POGLAVLJE 7

Strukture

Struktura (engl. struct) je jednostavan korisniki definiran tip, laka alternativa klasi.
Strukture su sline klasama u toliko to mogu sadrati konstruktore, svojstva, metode,
.f^polja, operatore, ugnijeene tipove i indekse (pogledajte poglavlje 9).
, Postoje i znaajne razlike izmeu klasa i struktura. Na primjer, strukture ne podravaju
ifnasljeivanje niti destruktore. to je jo vanije, iako je klasa referentni tip, struktura je
^'Vrijednosni tip (vie informacija o klasama i tipovima potraite u poglavlju 3). Strukture
Jflti stoga korisne za predstavljanje objekata koji ne zahtijevaju semantiku referenci.
f Opeprihvaeno stajalite je da se strukture trebaju koristiti samo za tipove koji su
jmali, jednostavni i po ponaanju i karakteristikama slini ugraenim tipovima.

Napomena za C++ programere: znaenje konstrukcije strukture u C #


* J uvelike je drugaije od onog u C++. Struktura je u C++ potpuno jed-
' ni*.1 naka klasi, osim to je podrazumijevana postavka vidljivosti (javna
nasuprot privatnoj) drugaija. U C # strukture su vrijednosni tipovi,
dok su klase referentni tipovi, a strukture u C# imaju i druga ogranie-
nja koja su opisana u ovom poglavlju.

frakture su neto uinkovitije po upotrebi memorije u poljima (za vie informacija


gledajte poglavlje 9). One, meutim, mogu biti manje uinkovite kada se koriste
nekim kolekcijama. Kolekcije koje uzimaju objekte oekuju reference, a strukture
oraju biti zapakirane. Pakiranje i raspakiravanje usporava izvedbu pa klase mogu
ti uinkovitije u veim kolekcijama.
|H ovom ete poglavlju nauiti kako se strukture definiraju i kako se s njima radi te
||ako se za inicijalizaciju njihovih vrijednosti mogu koristiti konstruktori.

Definiranje struktura
jffjntaksa za deklariranje strukture gotovo je identina onoj za definiranje klase:
[atributi] [modifikator pristupa] struct identifikator [:popis suelja]
{ lanovi }

129
U primjeru
menzionah
to bi se di
primijetiti
ih lanova-----
svojstva.

P r im je r 7-1. S t v a r a n je s t r u k t u r e

#region Using directives

using System;
using S y s t e m . C o l le ct io ns . G en eri c ;
using System.Text;

(fendregion

namespace Cr eating AStru ct

{
public struct Location

private int xVal;


private int yVal;

public Location( int xCoordinate, int yCoordinate )

xVal = xCoordinate;
yVal = yCoord inat e;

public int x
{
get
{
return xVal;
}
set
{
xVal = value;
}
}
public int y
1
. get
{
return yVal;
}
set
{
yVal = value;
}

130 ( Programiranje C#
'prim jer 7-1. S t v a r a n je s t r u k t u r e ( n a s ta v a k )

public override string ToStr ing O

return ( String.Format( {0}, {l}, xVal, yVal ) );

public class Tester


{
public void myFunc( Location loc )
{
loc.x = 5 0 ;
loc.y = 1 0 0 ;
Console.WriteLine( "In MyFunc loc: {0}", loc );
}
-static void Main()
{
Location locl = new Location( 200, 300 );
Console.WriteLine( "Locl location: {o}", locl );
Tester t = new Tester();
t.myFiinc( locl );
Console.WriteLine( Locl location: {o}", locl );
}
}
}
Za razliku od klasa, strukture ne podravaju nasljeivanje. One su implicitno izve-
dene iz object (isto kao i svi tipovi u C# , ukljuujui ugraene tipove), ali ne mogu
?'-.nasljeivati od drugih klasa niti struktura. Strukture su takoer implicitno zapeaene
v

Ai*(tojest, nijedna klasa niti struktura se ne moe izvesti iz strukture). Strukture, meu-
j; tim, poput klasa mogu implementirati vie suelja. Ostale razlike ukljuuju:
~v Nema destruktora niti prilagoenog podrazumijevanog konstruktora
Strukture ne mogu sadrati destruktore niti prilagoene konstruktore bez para-
metara (podrazumijevane). Ako konstruktor ne postoji, CLR e inicijalizirati
strukturu i sva polja postaviti na nulu. Ako pak navedete konstruktor koji nije
zadan, CLR inicijalizacija se nee pokrenuti te morate eksplicitno inicijalizirati
sva polja.
'L Nema inicijalizacije
U strukturi ne moete inicijalizirati polje instance. Stoga, nije doputeno napisati:
private int xVal = 50;
private int yVal = 100;

iako bi to bilo u redu da se radi o klasi.

.^Strukture su projektirane da budu jednostavne i lake za koritenje. Dok privatni


d a i lana promoviraju sakrivanje i uahurivanje podataka, neki programeri dre
ako je to prevelik napor za strukture. Oni podatke lana oznaavaju kao javne i tako

Poglavlje 7: Strukture | 131


pojednostavnjuju implementaciju strukture. Drugi programeri misle kako svojstva
omoguavaju jasno i jednostavno suelje te da dobra programerska praksa nalae ]
sakrivanje podataka ak i u jednostavnim objektima. Zahvaljujui novoj mogunosti j
refaktoriranja u Visual Studiju javne varijable moete jednostavno pretvoriti u privatne i
sa pridruenim javnim svojstvima. Varijablu jednostavno pritisnete desnom tipkom
mia i odaberete Refactor -* Encapsulate Field. Visual Studio e javnu varijablu pretvo- j
riti u privatnu i stvoriti svojstvo s pristupnicima get i set. 1

Stvaranje struktura
Instancu strukture moete stvoriti koristei kljunu rije new u iskazu dodjeljivanja,
isto kao da se radi o klasi. U primjeru 7-1 klasa Tester stvara instancu Location na
sljedei nain:
Location locl = new Location(200,300);

Ovdje je nova instanca nazvana lo cl i prosljeuju joj se dvije vrijednosti, 200 i 300.

Strukture kao vrijednosni tipovi


Definicija klase Tester u primjeru 7-1 sadri strukturu objekta Location (locl) koja
je stvorena s vrijednostima 2 0 0 i 3 0 0 . Sljedei red koda poziva konstruktora Location:
Location locl = new Location(200,300);

Zatim se poziva W riteLine():


Console.WriteLine("Locl location: {0}", locl);

WriteLine() oekuje objekt, ali, naravno, Location je struktura (vrijednosni tip). Pre-
voditelj automatski pakira strukturu (kao sto bi to uinio s bilo kojim drugim vrijed-
nosnim tipom) i taj se zapakirani objekt prosljeuje do W riteLine(). Za zapakirani
objekt poziva se ToStringO a kako struktura (implicitno) nasljeuje od object, ona
moe odgovoriti polimorfno, premoujui metodu na isti nain na koji bi to uinio
bilo koji drugi objekt:
Locl location: 200, 300

P akiran je m oete izb jei tak o da p reth od n i od jeljak prom ijenite u:


C on sole.W riteLine("Locl lo c a tio n : { 0 } " ,
lo c l.T o S t r in g O ) ; 3!
P akiranje se izbjegava izrav nim pozivanjem m etode ToString za v arija-
blu v rijed nosno g tipa gdje v rijed nosn i tip pru a prem oiv anje m etode
ToString.

U ovoj knjizi termin objekt koristim i za referentne tipove i za vrijednosne tipove. U svijetu objektno ori- .
jentiranog programiranja postoji neslaganje oko takve prakse, ali ja se tjeim injenicom da je Microso
vrijednosne tipove implementirao kao da su naslijedili od korijenske klase Object (i stoga za bdo koji vri-
jednosni tip, ukljuujui ugraene cipove poput int, moete pozivati sve metode Object).

132 | Programiranje C#
Vl;i__ SU meutim, vrijednosni objekti i kad se prosljeuju do metode oni se pro-
jeuju po vrijednosti - kao to moete vidjeti u sljedeem redu koda u kojem se objekt
tici prosljeuje metodi myFunc():
t.myFunc(locl);

metodi myFunc() nove se vrijednosti dodjeljuju x i y i te se nove vrijednosti ispisuju:


Locl location: 50, 100

Kad s e vratite na pozivajuu metodu (Main()) i ponovno pozovete WriteLine() vrijed-


nosti ostaju nepromijenjene:
Locl location: 200, 30 0

Itruktura je proslijeena kao vrijednosni objekt te je u myFunc() stvorena kopija. Poku-


cajte deklaraciju promijeniti u class:
'0-
public class Location

iponovno pokrenite provjeru. Generira se sljedei izlaz:


Locl location: 200, 300
tn MyFunc loc: 50, 100
Locl location: 5 0 , 10 0

,Ovaj put objekt Location ima semantiku reference. Stoga, kad se vrijednosti promijene
mmyFunc(), one se promijene u stvarnom objektu u Main().-

Stvaranje struktura bez kljune rijei new


Budui da je lo c l struktura (a ne klasa), stvorena je na stogu. Stoga, kad je u primjeru
M 7 4 pozvan operator new:
Location locl = new Location(200,300);

"'l rezultirajui objekt Location je stvoren na stogu.


Operator newpoziva konstruktor Location. Meutim, za razliku od klase, struktura se
moe stvoriti bez koritenja operatora new. To je u sklad s nainom definiranja varijabli
ugraenih tipova (poput int), kao to je prikazano u primjeru 7 -2 .

Upozorenje: u ovom primjeruje prikazan nain stvaranja strukture bez


koritenja operatora newjer se C# i C++ po tome razlikuju. Stvaranje
struktura bez kljune rijei newdonosi malo prednosti i na taj se nain
mogu stvoriti programi koji su manje razumljivi, vie podloni pogre-
kama i tei za odravanje. Nastavite na vlastitu odgovornost.

Drugi nain za rjeavanje ovogproblema je upotreba kljune rijei ref (kao stoje objanjeno upoglavlju
v koja doputa da vrijednosni tip proslijedite po referenci.

Poglavlje 7: Strukture | 133


P r im je r 1 -2 . S tv aran je s t r u k t u r e b e z k l j u n e r i je i new

fcregion Using directives

using System;
using System.Collections.Oeneric;
using System.Text;

ftendregion

namespace StructWithoutNew
{
public struct Location
{
public int xVal;
public int yVal;

public Location( int xCoordinate, int yCoordinate )


{
xVal = xCoordinate;
yVal = yCoordinate;
}
public int x
{
get
{
return xVal;
}
set
{
xVal = value;
}
}
public int y
{
get
{
return yVal;
}
set
{
yVal = value;
}
}

public override string ToStri ng O


{'
return ( String.Format( "{0}, {!}", xVal, yVal ) );
}
}

public class Tester


{
static void Main()

134 | Programiranje C#
7-2. S t v ara n je s t r u k t u r e b e z k l ju n e r i je i new ( n a s t a v a k )
ritnjer

{
Location locl; / / Nema poziva konstruktora

locl.xVal = 75; U Inicijalizira lanove


locl.yVal 225;
m Console.WriteLine( locl );

fa.- }
' }
, /}
H U primjeru 7-2 lokalne varijable se inicijaliziraju izravno prije pozivanja metode lo cl
j prije prosljeivanja objekta do WriteLine():
7$R, locl.xVal = 75;
4 r - iocl.yVal = 2 2 5 ;

^ Kad bi jednu od dodjela smjestili u komentar i ponovno preveli kod:


static void Main()
{
Location l o c l ;
locl.xVal = 75;
U locl.yVal = 2 2 5 ;
Console.WriteLine(locl);
}
dolo bi do pogreke prevoditelja:
Use of unassigned local variable 'locl'

" Kad dodijelite sve vrijednosti, moete im pristupiti preko svojstava x i y:


static void Main()
II; {
Location locl;
locl.xVal = 7 5 ; // Dodjeljuje varijablu lanicu
vr . locl.yVal = 225; // Dodjeljuje varijablu lanicu
>.y locl.x = 300; // Koristi svojstvo
locl.y = 4 0 0 ; // Koristi svojstvo
Console.WriteLine(locl);
I#" }

Budite oprezni prilikom koritenja svojstava. Iako ona daju podrku za uahurivanje
tako to stvarne vrijednosti ine privatnima, sama su svojstva zapravo metode lanice,
a metodu lanicu ne moete pozvati dok ne inicijalizirate sve varijable lanice.

Poglavlje 7: Strukture | 135


POGLAVLJE 8
Suelja

Suelje (engl. interface) je ugovor koji klijentu jam i kako e se klasa ili struktura
ponaati. Kad klasa (ili struktura) implementira suelje ona svakom potencijalnom
klijentu kae ja jam im da u podrati metode, svojstva, dogaaje i indekse ovog
suelja" (informacije o metodama i svojstvima potraite u poglavlju 4, informacije o
dogaajima u poglavlju 12, a vie o indeksima u poglavlju 9).
Suelje nudi alternativu apstraktnoj klasi za stvaranje ugovora izmeu klasa i njihovih I
klijenata. Ti se ugovori oituju koritenjem kljune rijei interface koja deklarira refe- 1
rentni tip koji uahuruje ugovor. j
Prilikom definiranja suelja moete definirati metode, svojstva, indeksere i/ili doga-
aje koje e implementirati klasa koja implementira suelje.
Suelja se esto usporeuju s apstraktnim klasama. Apstraktna klasa slui kao osnovna
klasa za obitelj izvedenih klasa, dok bi se suelja trebala mijeati s ostalim stablima
nasljeivanja.

Ono to je u ovom poglavlju navedeno za klasu vrijedi i za strukturu,


osim ako nije drugaije navedeno.

Kad klasa implementira suelje, ona mora implementirati sve dijelove tog suelja
(metode, svojstva itd.). Klasa, u stvari, kae pristajem na ispunjavanje ugovora koji
je definiran ovim sueljem".

* . Napomena za Java programere: C # ne podrava upotrebu polja kon-


4*, stanti (konstanti lanica) u suelju. Najblia alternativa je koritenje
f jf; nabrojanih konstanti (enumeracija).

U poglavlju 5 saznali smo kako nasljeivanje iz apstraktne klase implementira odnos


to-je. Implementacija suelja, s druge strane, definira odnos drugaiji od ovog, a koji
se naziva (nimalo iznenaujue) odnos implementacije. Postoji fina razlika izmeu ova
dva odnosa. Auto je vozilo, ali bi mogao implementirati sposobnost MoeSeKupitiAkoU
zmeVelikiKredit (kao to to, na primjer, moe i kua).

136
Mjeavine
USomerv*Neu<drava Massachusetts, postojala je nekad slastiarnica u kojoj bi vam
u omiljeni sladoled umijeali bombone i ostale slatkie. To se pionirima objektno
orijentiranog programiranja s oblinjeg sveuilita MIT koji su radili na programskom
jeziku SCOOPS inilo kao dobra metafora. Oni su termin mjeavina" primijenili i
na klase u koje su se mogle umijeati dodatne sposobnosti. C++ sadri brojne takve
klase. Takve klase imaju otprilike istu ulogu kao i suelja u C# .
-IvM-'

' \J ovom ete poglavlju nauiti kako se suelja stvaraju, implementiraju i koriste. Nau-
jit ete kako se implementiraju viestruka suelja, kako se suelja mogu kombinirati
"J d proiriti te kako se moe provjeriti je li klasa implementirala suelje.
i
Definiranje i implementiranje suelja
Sintaksa za definiranje suelja je sljedea:
[atributi] [modifikator pristupa] interface ime suelja[:popis osnovnih suelja]
{tijelo suelja}

Zasad nemojte obraati panju na atribute; oni su obraeni u poglavlju 18.


Modifikatori pristupa (public, private, protected, internal i protected interna!.)
objanjeni su u poglavlju 4.

< Iza kljune rijei interface navodi se naziv suelja. Uobiajeno je (ali ne i obavezno) da
fnaziv suelja poinje slovom I (dakle, IStorable, ICloneable, IClaudius itd.).

.. Popis osnovnih suelja daje popis svih suelja koje ovo suelje proiruje (kao to je
opisano u sljedeem odjeljku).

, Tijelo suelja opisuje metode, svojstva i druge elemente koje implementirajua klasa
^treba implementirati.

Pretpostavimo da elite stvoriti suelje koje opisuje metode i svojstva koja su klasi
^potrebna za spremanje i uzimanje podataka iz baze podataka ili drugog spremita, na
^primjer datoteke. To suelje odluili ste nazvati IStorable.

U ovom suelju moete zadati dvije metode: Read() i Write() koja se pojavljuju u tijelu
i suelja.
interface IStorable
{
void Read();
void Write(object);

Svrha suelja je definiranje sposobnosti za koje elite da budu dostupne u klasi.


Moete, na primjer, stvoriti klasu Document. Ispostavlja se kako se tipovi Document
m o g u spremiti u bazu podataka, stoga odluujete da Document implementira suelje
# 'IStorable.

Poglavlje 8: Suelja | 137


Kako biste to uinili, koristite istu sintaksu koju biste koristili da klasa Document nasl'
uje iz IStorable - dvotoku (:) iza koje slijedi naziv suelja:
public class Document : IStorable
{
public void Read() {...}
public void Write(object obj) {...}
H ...
}
Dalje je vaa odgovornost, kao autora klase Document, osigurati smislenu implemen-1
taciju Istorable metoda. Budui da ste naveli da Document implementira IStorable
morate implementirati sve metode iz isto rab le, u suprotnom e se prilikom prevode- ?
nja dogoditi pogreka. To je prikazano u primjeru 8-1 u kojem klasa Document imple- :
mentira suelje istorable.

P r im je r 8 - 1 . K o r i t e n j e j e d n o s t a v n o g s u e l ja

ifregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Simplelnterface
{
// Deklarira suelje

interface istorable
{
// Nema modifikatora pristupa, metode su javne
// Nema implementacije
void Read();
void Write( object obj );
int Status { get; set; }

// Pravi klasu koja implementira suelje istorable


public class Document : istorable
{

// Sprema vrijednost za svojstvo


private int status = 0;

public Document( string s )


{
Console.WriteLine( "Creating document with: {0 }", s );

// Implementira metodu Read


public void Read()

138 | Programiranje C#
8-1 K o r i t e n je j e d n o s t a v n o g s u e l ja [n a s t a v a k )
Wf
1 console.WriteLine(
"Impleroenting the Read Method tor IStorable" );

}
// implementira metodu Write
public void Write( object o )

{
Console.WriteLine(
"Implementing the Write Method tor IStorable );

}
// Implementira svojstvo
public int Status
{
get
{
return status;
}

status = value;

}
'I I Koristi suelje
public class Tester
{

static void Main()


{
// Pristupa metodama u objektu Document
Document doc = new Document( "Test Document );
doc.Status = -l;
doc.ReadO;
Console.Writeline( "Document Status: {o}", doc.Status );
}

U primjeru 8-1 definirano je jednostavno suelje IStorable koje sadri dvije metode
(Read() i W rite()) i svojstvo (Status) tipa integer. Obratite panju da deklaracija svoj-
stva ne prua implementaciju za g et() i se t(), ve jednostavno oznaava kako postoje
get() i s e t():
int Status { get; set; }

Obratite panju i da deklaracija metode IStorable ne ukljuuju modifikatore pristupa


(public, protected, internal ili private). Navoenje modifikatora pristupa u stvari
generira pogreku u prevoenju. Metode suelja su implicitno public jer je suelje

Poglavlje 8: Suelja | 139


ugovor koji trebaju koristiti druge klase. Ne moete stvoriti instancu suelja; umjesto,
toga moete instancirati klasu koja implementira suelje.
Klasa koja implementira suelje mora tono i u potpunosti ispuniti ugovor. Documenti
mora pruiti i metodu Read() i metodu Write() te svojstvo Status. Nain na koji e i
ispuniti zahtjeve ovisi iskljuivo o klasi Document. lako IStorable odreuje da Document t
mora imati svojstvo Status, to suelje ne zna niti mu je bitno je li klasa Document stvarni ?
status spremila kao varijablu lanicu ili ga uzima iz baze podataka. Pojedinosti su na 1
implementacijskoj klasi.

Implementiranje vie od jednog suelja


Klase mogu implementirati vie od jednog suelja. Ako se, na primjer, klasa Document
moe spremiti, a moe se takoer i komprimirati, moete implementirati i suelje
IStorable i suelje ICompressible. Kako biste to uinili, promijenite deklaraciju (popis
osnovnih suelja) kako biste naveli da su implementirana oba suelja. Suelja odvojite
zarezom:
public class Document : IStorable, ICompressible

Kad je to uinjeno, klasa Document mora implementirati i metode koje zadaje suelje
ICompressible (koje je deklarirano u primjeru 8-2):

public void Compress()

Console.Writeline("Implementing the Compress Metbod");

}
public void Decompress()

Console.WriteLine("Implementing the Decompress Method");

Proirivanje suelja
Postojee suelje moe se proiriti kako bi se dodale nove metode ili lanovi, odnosno
kako bi se promijenio nain funkcioniranja postojeih lanova. ICompressible moete,
na primjer, proiriti novim sueljem ILoggedCompressible koje poetno suelje proi-
ruje metodama koje evidentiraju pohranjene bajtove.
interface ILoggedCompressible : ICompressible

void LogSavedBytes();
}
Ako ICom pressible proirite na ovaj n ain , zapravo n aznau jete da sve
on o to im plem entira ILoggedCompressible m ora im plem entirati i ICom-
p re ssib le .

140 | Programiranje C#
sada slobodno mogu implementirati ili ICompressible ili ILoggedCompressible,
ja e

0 tome treba li im dodatna funkcionalnost. Ako klasa implementira ILoggedCom-

|essible, ona mora implementirati sve metode iz ILoggedCompressible i ICompressi-


|e. Objekti tog tipa mogu se pretvoriti u ILoggedCompressible ili u ICompressible.

|nbiniranje suelja
^ lino tome, nova suelja moete stvoriti kombiniranjem postojeih suelja i, opcio-
%ialno, dodajui nove metode ili svojstva. Na primjer, moete stvoriti IStorableCompres-
-;sible. To bi suelje kombiniralo metode oba suelja, ali bi dodalo i novu metodu za
^spremanje izvorne veliine unaprijed komprimirane stavke:
interface IStorableCompressible : IStorable, ILoggedCompressible
-i {
void LogOriginalSize();
; i
Prim jer 8 - 2 . P r o ir iv a n je i k o m b i n i r a n je s u e l ja

fc-egion Using directives

jising System;
using System.Collect ions.Generic;
using System.Text;

jfendregion

namespace ExtendAndCombineInterface
{
interface IStorable
f
void Read();
void Write( object obj );
int Status { get; set; }

.'// Evo novog suelja


i interface ICompressible
" {
void Compress();
void Decompress();
}

U Proirenje suelja
interface ILoggedCompressible : ICompressible

void LogSavedBytes();

H Kombiniranje suelja
interface IStorableCompressible : IStorable, ILoggedCompressible

Poglavlje 8: Suelja | 141


Primjer 8-2. Proirivanje i kombiniranje suelja (nastavak)
void LogOriginalSize();
}
// lo jedno suelje
interface IEncryptable
{
void Encrypt();
void Decrypt();
}
public class Document : IStorableCompressible, IEnciyptable

{
// Dri podatke za svojstvo Status suelja IStorable
private int status = o;

// Konstruktor dokumenta
public Document( string s )

Console.WriteLine( Creating document with: {0}, s )

}
// Implementacija IStorable
public void Read()
{
Console.WriteLine(
"Implementing the Read Method for IStorable" );

}
public void Write( object o )
{
Console.WriteLine(
"Implementing the Write Method for IStorable" );

}
public int Status
{
get
{
return status;
}

set
{
status = value;
}
}
// Implementacija ICompressible
public void Compress()

Console.WriteLine( "Implementing Compress" );

142 | Programiranje (#
J f r f n i j e r 8 - 2 . P r o ir iv a n je i k o m b in ir a n je suelja (nastavak)

}
5i> public void Decompress()
{
Console.WriteLine( "Implementing Decompress" );
}

// Implementiranje ILoggedCompressible
public void LogSavedBytes()
{
Console.WriteLine( "Implementing LogSavedBytes" );
}

// Implementiranje IStorableCompressible
public void LogOriginalSize()
{
Console.WriteLine( "Implementing LogOriginalSize" );

// Implementiranje IEncryptable
public void Encrypt()
{
Console.WriteLine( "Implementing Encrypt" );

public void Decrypt()


{
Console.WriteLine( "Implementing Oecrypt" );

}
}

public class Tester


{ '

static void Main()


{
// Pravi objekt dokumenta
Document doc = new Document( "Test Document" );

// Pretvara dokument za razliita suelja


IStorable isDoc = doc as IStorable;
if ( isDoc != nuli )
{
isDoc.Read();
}
else
Console.WriteLine( "IStorable not supported" );

ICompressible icDoc - doc as ICompressible;


if ( icDoc != nuli )
{

Poglavlje 8: Suelja | 143


Primjer 8-2. Proirivanje i kombiniranje suelja (nastavak)
icDoc.Compress();
}
else
Console.WriteLine( "Compressible not supported" );

ILoggedCompressible ilcDoc = doc as ILoggedCompressible;


if ( ilcDoc != nuli )
{
ilcDoc.LogSavedBytes();
ilcDoc. CompressO;
// ilcDoc.Read();
}
else
Console.WriteLine( "LoggedCompressible not supported" );

IStorableCompressible isc = doc as IStorableCompressible;


if ( isc != nuli )
{
isc.LogOriginalSize(); // IStorableCompressible
isc.LogSavedBytes(); // ILoggedCompressible
isc.Compress(); // ICompressible
isc.Read(); // IStorable

}
else

Console.WriteLine( "StorableCompressible not supported );


}
IEncryptable ie = doc as IEncryptable;
if ( ie U nuli )
{
ie.Encrypt();
}
else
Console.WriteLine( "Encryptable not supported" );

Primjer 8-2 poinje implementacijom suelja IStorable i suelja ICompressible. Ovo


drugo se proiruje u ILoggedCompressible i zatim se ta dva suelja kombiniraju u ISto-
rableCompressible. Naposljetku je u primjer dodano novo suelje IEncryptable.

Program Tester stvara novi objekt Document i zatim ga koristi kao instancu razliitih
suelja. Slobodno moete pretvarati:
ICompressible icDoc = doc as ICompressible;

No to nije potrebno. Prevoditelj zna da doc implementira ICompressible i moe ovo


pretvaranje uiniti implicitno:
ICompressible icDoc = doc;

144 | Programiranje C#
ICompressible icDoc = doc as ICompressible;
if ( icDoc \- nuli )

icDoc.Compress();

else
Console.WriteLine( "Compressible not supported" );
S
Pretvaranje u proirena suelja
^ Kad je objekt pretvoren u ILoggedCompressible moete koristiti suelje za pozvanje
metoda na ICompressible jer ILoggedCompressible proiruje (i stoga obuhvaa) metode
iz osnovnog suelja:
ILoggedCompressible ilcDoc = doc as ILoggedCompressible;
if (ilcDoc != nuli)
{
ilcDoc.LogSavedBytes();
ilcDoc.Compress();
// ilcDoc.Read();
}
Ne moete, meutim, pozvati Read() jer se radi o metodi suelja IStorable koje nije
povezano. Ako poziv metode Read() izdvojite u komentar, primit ete pogreku pre-
' voditelja.

Ako pretvorite u IStorableCompressible (koje proireno suelje kombinira sa sueljem


IStorable), onda moete pozivati metode suelja IStorableCompressible, ICompressi-
: ble i IStorable:
IStprableCompressible isc = doc as IStorableCompressible
if (isc != nuli)

? {
isc-LogOriginalSize(); // IStorableCompressible
isc.LogSavedBytes(); // ILoggedCompressible
isc.Compress(); // ICompressible
isc.Read(); // IStorable
>

Pristupanje metodama suelja


lanovima suelja IStorable moete pristupiti kao da pristupate lanovima klase
Document:

Document doc = new Document(''Test Document);


doc.status = -l;
doc.Read();

Poglavlje 8: Suelja | 145


Moete i stvoriti instancu suelja' tako da dokument pretvorite u tip suelja i zatim to 5$,; An exception of type System. InvalidCastException was thrown.
suelje koristite za pristup metodama:
; itnimke su iscrPniie objanjene u poglavlju 11.
IStorable isDoc = doc;
isDoc.status = 0;
isDoc.Read(); Operator is
U tom sluaju u Main() znate kako je Document zapravo IStorable te moete iskoristiti '.'^Korisno bi bilo pitati objekt podrava li on suelje, a zatim pozvati odgovarajue
to znanje i ne izvesti eksplicitno promjenu tipa ili provjeru promjene tipa. metode. To se u C # moe postii na dva naina. Prvi je koritenje operatora is. Oblik

Kao to je ranije navedeno, suelja se ne mogu izravno instancirati. T o jest, ne moete *i operatora i s iest:
napisati: ;; izraz is tip
IStorable isDoc = new IStorable(); Operator is ima vrijednost true ako se izraz (koji mora biti referentni tip) moe sigurno
Meutim, moete stvoriti instancu implementirajue klase, kao u sljedeem odlomku pretvoriti u tip bez izbacivanja iznimke.' U Primjeru 8-3 prikazana je upotreba opera-
koda: tora is za provjeru implementira li Document suelja IStorable i ICompressible.

Document doc = new Document("Test Document");


N a p o m e n a z a J a v a p r o g r a m e r e : operator is jezika C # od govar a opera-
Moete zatim stvoriti instancu suelja tako da tip implementacijskog objekta pretvo- toru instanceof u jeziku java.
rite u tip suelja, a to je u ovom sluaju IStorable:
IStorable isDoc = doc; m
\ P rim jer8 -3 . K o r i t e n je o p e r a t o r a is
Ove korake moete kombinirati tako da napiete:
dregion Using directives
IStorable isDoc = new Document("Test Document");
.v-t'jising System;
Pristup kroz suelje omoguava da suelje tretirate polimorfno. Drugim rijeima, dvije
using System.Collections.Generic;
ili vie klasa mogu implementirati suelje, a ako tim klasama zatim pristupate isklju- using System.Text;
ivo putem suelja, moete zanemariti njihov stvarni tip tijekom izvedbe i tretirati ih
naizmjenino. Vie informacija o polimorfizmu potraite u poglavlju 5. . #endregion

namespace IsOperator
Pretvaranje u suelje ;<
r. interface IStorable
U veini sluajeva se ne zna unaprijed podrava li objekt neko suelje. U kolekciji {
dokumenata moda neete znati podrava li odreeni objekt suelje IStorable ili ICom- void Read();
void Write( object obj );
pressible ili oba. Moete samo pretvarati u suelja:
int Status { get; set; }
Document doc = myCollection[0];

IStorable isDoc = (IStorable) doc;


isDoc.Read();
Jfj i// Novo s u e lje

ICompressible icDoc = (ICompressible) doc;


A {interface ICompressible

icDoc.Compress(); _ void Compress();


void DecompressO;
Ako se ispostavi kako Document implementira samo suelje IStorable: T )
public class Document : IStorable
rll Document implementira IStorable
pretvaranje u ICompressible e se ipak prevesti jer je ICompressible valjano suelje. Public class Document : IStorable
Meutim, zbog nedoputenog pretvaranja e se prilikom pokretanja programa izba-
citi iznimka:
i I operator is i operator as (opisan u nastavku poglavlja) mogu se, osim za provjeru implementacije suelja,
koristiti i za procjenu tipova kroz nasljeivanje. Stoga is moete koristiti i kako biste provjerili je li pas
* Ili, preciznije, ispravno premjestiti referencu u objekt koji implementira suelje. ifit/Sisavac.

146 | Programiranje C#
Poglavlje 8: Suelja | 147
P r im j e r 8 - 3 . K o r i t e n j e o p e r a t o r a is (n a s t a v a k )

private int status = 0;

public Document( string s )


{
Console.WriteLine(
"Creating document with: {0}", s );

// IStorable.Read
public void Read()
{
Console.WriteLine( "Reading..
}

// IStorable.Write
public void Write( object o )
{
Console.WriteLine( "Writing..
}
// IStorable.Status
public int Status
{
get
{
return status;
}

set
{
status = value;
}
}
}
// Izvodi iz Document i implementira ICompressible
public class CompressibleDocument : Document, ICompressible
{
public CompressibleDocument(String s) :
base(s)
{ }
public void Compress()
{
Console.WriteLine("Compressing..
}
public void Decompress()
{
Console.WriteLine("Decompressing...
}

148 | Programiranje C#
frimjer 8-3- Koritenje operatora is (nastavak)

}
public class Tester

{
i static void Main()

: {
// Kolekcija Documents
Document[] docArray = new Document[2];

// Prvi unos je Document


docArray[0 ] = new Document( "Test Document" );

// Drugi unos je CompressibleDocument (u redu je


// jer je CompressibleDocument Document)
docArray[l] =
new CompressibleDocument("Test CompressibleDocument");

// Ne znamo to emo izvui iz eira


foreach (Document doc in docArray)
{
// Ispisuje ime
Console.WriteLine("Got: {o}, doc);

// Obje prolaze test


if (doc is IStorable)
{
IStorable isDoc = (IStorable)doc;
isDoc.Read();
}

// Ne uspjeva za Document
// Prolazi za CompressibleDocument
it (doc is ICompressible)
{
ICompressible icDoc = (ICompressible)doc;
icDoc.Compress();
}

}
* )

Primjer 8-3 se od primjera 8-2 razlikuje po tome to Document vie ne implementira


suelje ICompressible, ve to ini klasa koja je izvedena iz Document i zove se Compres-
sibleDocument.

Hain() provjerava je li svako pretvaranje doputeno (ponekad se naziva i sate) provje-


ri: ravajui sljedei uvjet i f :
if (doc is IStorable)

) Taj je postupak jasan i gotovo da sam sebe objanjava. Iskaz i f govori da e se pretva-
3. ranje dogoditi samo ako objekt pripada ispravnom tipu suelja.

Poglavlje 8: Suelja | 149


Klasa Document uspjeno prolazi tu provjeru, ali ne zadovoljava sljedeu:
if (doc is ICompressible)

CompressibleDocument zadovoljava obje provjere.

Oba tipa dokumenta stavljamo u polje (moete zamisliti predavanje takvog polja
metodi koja ne moe znati kakav je sadraj polja). Prije nego to pokuate pozvati
metode iz ICompressible, morate biti sigurni da tip kojem pripada Document implemen-
tira ICompressible. To e umjesto vas provjeriti operator is.
Na alost, ovakva upotreba operatora is nije uinkovita. Kako biste razumjeli zato,
morate pogledati MSIL kod koji se generira. Evo malog odlomka (primijetit ete kako
su brojevi redaka u heksadecimalnom zapisu).
IL0023: isinst ICompressible
IL_0028: brfalse.s IL_0039
IL_002a: ldloc.o
IL_002b: castclass ICompressible
IL_0030: Stl0C.2
IL_003l: ldloc . 2
IL_0032: callvirt instance void ICompressible::Compress()

Najvanija je provjera ICompressible u redu 23. Kljuna rije is in s t u MSIL kodu


oznaava operator is. Ona provjerava je li objekt (doc) doista pravog tipa. Kad je ova
provjera zadovoljena, nastavljamo na red 2b u kojem se poziva ca stcla ss. Naalost,
ca stcla ss takoer provjerava tip objekta. Provjera se, dakle, izvodi dvaput. Uinkovi-
tije rjeenje je upotreba operatora as

Operator as
Operator as kombinira operator is s operacijama pretvaranja tako to prvo provjerava
je li pretvaranje valjano (to jest, bi li provjera s is vratila true), a zatim, ako pretvaranje
jest valjano, zavrava pretvaranje. Ako pretvaranje nije valjano (to jest, ako provjera s
is vrati vrijednost false), operator as vraa vrijednost nuli.
ari
Kljuna rije nuli predstavlja praznu referencu - onu koja ne pokazuje
ni na jedan objekt.
i
Koritenje operatora as eliminira potrebu za obradom iznimki pretvaranja tipa. U isto
vrijeme izbjegavate i dvostruku provjeru pretvaranja. Iz tih je razloga za pretvaranje
tipa suelja najbolje koristiti operator as.

O blik operatora as je:


izraz as tip

Kod iz primjera 8-3 je u sljedeem odlomku koda prilagoen tako da se koristi opera-
tor as i provjerava se hoe li povratna vrijednost biti nuli:
static void Main()

150 j Programiranje C#
// Kolekcija Documents
Document[] docArray = new Document[2 ];

// Prvi unos je Document


docArray[0] = new Document( "Test Document" );

// Drugi unos je CompressibleDocument (to je u redu jer


// je CompressibleDocument Document)
docArray[l] = new CompressibleDocument("Test CompressibleDocument");

// Ne znamo to emo izvui iz eira


foreach (Document doc in docArray)
{
// Ispisuje ime
Console.WriteLine("Got: {0}", doc);

// Obje prolaze test


IStorable isDoc = doc as IStorable;
if (isDoc != nuli)
{
isDoc.Read();
}

// Ne prolazi za Document
// Prolazi za CompressibleDocument
ICompressible icDoc = doc as ICompressible;
if (icDoc != nuli)
{
icDoc.Compress();

}
}
}
'Ako pogledate odgovarajui MS1L kod, primijetit ete kako je ova inaica daleko
^uinkovitija:
IL_0023: isinst ICompressible
IL_0028: stloc.2
IL_0029: ldloc.2
IL_002a: brfalse.s IL_0034
IL_002c: ldloc.2
m .
:le*V
IL 002d: callvirt instance void ICompressible::Compress()

Usporedba operatora is i operatora as


s
.Ako elite provjeriti je li objekt odgovarajueg tipa, te ako je to tako, odmah pretvoriti
:pbjekt u drugi tip, operator as je mnogo uinkovitiji. Ponekad ete, meutim, moda
1 iktjeti provjeriti tip operatora, ali ga neete odmah pretvoriti. Moda ga elite samo
(provjeriti, a pretvaranje vam nije potrebno - jednostavno ga elite dodati na popis onih
kji zadovoljavaju odgovarajue suelje. U tom je sluaju bolje koristiti operator is.

Poglavlje S; Suelja | 151


U sp o redb a suelja s a p s tra k tn o m klaso m
Suelja su vrlo slina apstraktnim klasama. Deklaraciju suelja IStorable zapravo
moete promijeniti tako da bude apstraktna klasa:
abstract class Storable

abstract public void Read();


abstract public void Write();
}
Document sada moe nasljeivati iz Storable i ovakva se upotreba ne bi mnogo razliko-
vala od koritenja suelja.
Pretpostavimo kako ste klasu List kupili od nekog proizvoaa te da njene sposob-
nosti elite kombinirati s onima definiranim u Storable. U C++ biste mogli stvoriti
klasu StorableList i nasljeivati i iz List i iz Storable. U C# , meutim, nema takve
mogunosti. Ne moete nasljeivati i iz apstraktne klase Storable i iz klase List jer
C # ne podrava viestruko nasljeivanje kod klasa.
C # vam, meutim, doputa implementiranje neogranienog broja suelja i izvoenje
iz jedne osnovne klase. Stoga, ako od Storable nainite suelje, moete nasljeivati i iz
klase List i iz suelja IStorable, kao to to u sljedeem primjeru ini StorableList:
public class StorableList : List, IStorable
{
// Ovdje popiit metode ...
public void Read() {...}
public void Write(object obj) {...}
II . . .
}

Neki Microsoft razvojni inenjeri ne preporuuju upotrebu suelja, ve pre-


feriraju apstraktne osnovne klase zbog jednostavnijeg praenja inaica.
Pretpostavimo kako ste projektirali suelje i programeri u vaoj tvrtci
ga ponu koristiti. Sada tom suelju elite dodati novi lan. Na ras-
polaganju su vam dvije loe m ogunosti: moete promijeniti suelje i
prekinuti postojei kod ili suelje moete tretirati kao nepromjenjivo i
stvoriti, na primjer, IStore2 ili IStorageExtended. Ako to esto inite,
ubrzo ete imati desetke usko povezanih suelja i kod e biti u kaoti-
nom stanju.
Ako koristite apstraktnu osnovnu klasu, novu virtualnu metodu
moete dodati sa podrazumijevanom implementacijom. Postojei kod
e i dalje funkcionirati, ali se u imenski prostor ne uvodi nova klasa.
Ako stvarate biblioteku klasa koju e ponovno koristiti velik broj ljudi
(osobito ako se radi o ljudima izvan vae tvrtke), vjerojatno je najbolje
koristiti apstraktne osnovne klase. Ako, meutim, stvarate klase za
jedan projekt, koritenjem suelja kod e biti fleksibilniji i jednostavniji
za razumijevanje.

152 | Programiranje C#
premoivanje implementacija suelja
rjinplementirajua klasa moe neke, ili sve, metode koje implementiraju suelje slo-
b dn0 oznaiti kao virtualne. Izvedene klase mogu premostiti (override) te implemen-
;%||I|taCIJe kako bi postigle polimorfizam. Na primjer, klasa Document moe implementirati
^Jlfl'suelj IStorable i metode Read() i Write() oznaiti s Virtual. Document moe svoj
'Iflsadraj proitati (Read()) i zapisati (Write()) u tip File. Razvojni inenjer moe kasnije
izDocument izvoditi nove tipove, na primjer Note ili EmailMessage te moe odluiti kako
Jl'.e Note itati iz baze podataka ili zapisivati u nju, a ne u datoteku.
-

% U primjeru 8-4 pojednostavljen je sloeni primjer 8-3 i prikazano je premoivanje


*,implementacije suelja. Metoda Read() je oznaena s Virtual i implementira je Docu-
ment. Read() se zatim premouje u tipu Note koji izvodi iz Document.

P rim jer 8 - 4 . P r e m o i v a n j e i m p l e m e n t a c i j e s u e l ja

(tregion Using directives

. using System;
using System.Collections.Oeneric;
using System.Text;

#endregion

v-namespace overridinglnterface

interface IStorable
1
void Read();
void Write();
}

v H Pojednostavnjuje Document da implementira samo IStorable


public class Document : IStorable
i *
// konstruktor dokumenta
public Docuntent( string s )
% {
; Console.WriteLine(
"Creating document with: {o}", s );

U ini itanje virtualnim

public Virtual void Read()


{
Console.WriteLine(
"Document Read Method for IStorable" );

II NB: Nije virtualna!

Poglavlje 8: Suelja | 153


Primjer 8-4. Premoivanje implementacije suelja (nastavak)

public void Write()


{
Console.WriteLine(
"Document Write Method for IStorable" );
}
}
I I Izvodi iz Document
public class Note : Document
{
public Note( string s ):
base(s)
{
Console.WriteLine(
"Creating note with: {o}, s );
}
I I Premouje metodu Read

public override void Read()


{
Console.WriteLine(
"Overriding the Read method for Note!" );
}
// Implementiram moju vlastitu metodu Write
public new void Write()
{
Console.WriteLine(
"Implementing the Write method for Note!" );
}
}
public class Tester
{

static void Main()


{
I I Stvara referencu dokumenta do objekta Note
Document theNote = new Note( Test Note" );
IStorable isNote = theNote as IStorable;
if ( isNote != nuli )
{
isNote.Read();
isNote.Write();
_ }
Console.WriteLine( "\n" );

I I Izravni poziv metoda


theNote.Read();
theNote.Write();

Console.WriteLine( "\n" );

154 | Programiranje C#
t*Primjer Premoivanje implementacije suelja (nastavak)
I I Pravi objekt Note
Note rote2 = new Note( "Second Test" );
IStorable isNote2 = note2 as IStorable;
if ( isNote2 != nuli )
{
isNote2.Read();
isNote2.Write();
}
Console.WriteLine( "\n" );

I I Izravno poziva metode


note2.Read();
note2.Write();

}
}
Uovom primjeru Document implementira pojednostavljeno suelje IStorable (pojedno-
stavljeno kako bi primjer bio to jasniji):
interface IStorable
{
void Read();
void Write();

Razvojni inenjer koji je stvorio Document je metodu Read() oznaio kao virtualnu, ali
to nije uinio s metodom W rite():
public Virtual void Read()

U stvarnim aplikacijama, kad jednu metodu oznaite kao virtualnu, najvjerojatnije


biste i drugu oznaili s Virtual, ali ovdje je izmeu metoda napravljena razlika kako
biste vidjeli da razvojni inenjer moe slobodno izabrati koje e metode biti virtu-
alne. '

Klasa Note izvodi iz Document:


public class Note : Document

Note ne mora premostiti metodu Read(), no moe to slobodno uiniti, kao stoje sluaj
u primjeru:
public override void Read()

U Tester se metode Read() i Write() pozivaju na etiri naina:


Preko reference osnovne klase do izvedenog objekta
Preko suelja koje je stvoreno iz reference osnovne klase do izvedenog objekta
Preko izvedenog objekta
Preko suelja koje je stvoreno iz izvedenog objekta

Poglavlje 8: Suelja I 155


Za postizanje prva dva poziva stvara se referenca Document (osnovna klasa) i adresa
novog objekta Note (izvedenog) koji je stvoren na gomili dodjeljuje se referenci
Document:
Document theNote = new Note("Test Note");

Stvara se referenca suelja i za pretvaranje Document u referencu IStorable koristi se


operator as:
IStorable isNote = theNote as IStorable;

Zatim se metode Read() i W rite() pozivaju kroz to suelje. Izlaz otkriva kako se na
metodu Read() odgovara polimorfno, to, prema oekivanju, nije sluaj s metodom
W rite():
Overriding the Read pethod for Note!
Document Write Method for IStorable

Metode Read() i W rite() se zatim pozivaju izravno na samom objektu:


theNote.Read();
theNote.Write();

i ponovno moete vidjeti kako je radila polimorfna implementacija:


Overriding the Read method for Note!
Document Write Method for IStorable

U oba sluaja poziva se metoda Read() iz Note i metoda W rite() iz Document.


Kako biste se uvjerili da se radi o rezultatu premoivanja metode, stvorite drugi
objekt Note i njegovu adresu ovog puta dodijelite referenci za Note. To se koristi za
prikaz konanih sluajeva (tj. pozivanje kroz izvedeni objekta i pozivanje kroz suelje
stvoreno iz izvedenog objekta):
Note note2 = new Note("Second Test");

Ponovno se prilikom pretvaranja u referencu poziva premoena metoda Read(). Meu-


tim , kad se metode pozivaju izravno na objektu Note:
note2 .Read();
note2 .Write();

izlaz pokazuje kako ste pozvali Note, a ne premoenu Document:


Overriding the Read method for Note!
Implementing the Write method for Note!

Eksplicitna implementacija suelja


U implementaciji koja je prikazana, implementirajua klasa (u ovom sluaju Document)
stvara metodu lanicu koja ima isti potpis i povratni tip kao metoda iz suelja. Ne
treba se eksplicitno oznaiti kako se radi o implementaciji suelja - prevoditelj to impli-
citno razumije.

156 | Programiranje C#
to e se dogoditi ako klasa implementira dva suelja i oba suelja sadre metodu
s istim potpisom? U primjeru 8-5 stvorena su dva suelja: IStorable i ITalk. Drugo
suelje implementira metodu Read() koja knjigu ita na glas. Ona se, na alost, suko-
bljava s metodom Read() iz suelja IStorable.

Budui da i IStorable i ITalk sadre metodu Read(), implementirajua klasa Docu-


ment mora barem za jednu metodu koristiti eksplicitnu implementaciju. U eksplicitnoj
implementaciji implementirajua klasa (Document) eksplicitno identificira suelje za
metodu:
void ITalk.Read()

To rjeava su k o b , ali stvara i niz z a n im ljiv ih nuspojava.

Prvo, druga m etoda T a lk () se ne tre b a e k sp licitn o im plem entirati;


public void Talk()

Budui da n em a su k o b a , o n a se m o e d e kla rira ti na u o b iajen n ain .

Jo vanije, metoda s eksplicitnom implementacijom ne moe imati modifikator


pristupa:
void ITalk.Read()

Ta je metoda implicitno javna.

Metoda koja se deklarira putem eksplicitne implementacije se, u stvari, ne moe dekla-
rirati s modifikatorima abstract, Virtual, override i new.

Nadalje, eksplicitno implementiranoj metodi ne moete pristupiti kroz sam objekt


Kad napiete:
theDoc.Readj);

prevoditelj e pretpostaviti kako se to odnosi na implicitno implementirano suelje


za Istorable. Jedim nain pristupa eksplicitno implementiranom suelju je preko pre-
tvaranja u suelje:
f
ITalk itDoc = theDoc;
itDoc.Read();

Eksplicitna je implementacija prikazana u primjeru 8-5.

P r im jer 8 - 5 . E k s p l ic it n a i m p l e m e n t a c i j a

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

(fendregion

namespace ExplicitImplementation

Poglavlje 8: Suelja | 157


Primjer 8-5. Eksplicitna implementacija (nastavak)

interf ace IStorable


{
void R e a d ();
void Write();

interface ITalk
{
vo id Talk();
void Read();

}
// Modi fi ka ci ja Document da imple m entira ISto rable i ITalk
public class Do c ument : IS torable, ITalk

{
// Ko n strukt or do ku me nt a
public Document( string s )

Consol e. Wr it e L in e ( "Crea ting do c um ent with: {o}", s )

}
// i ni Read() vi rt u a l n o m
public Virt ua l void Read()

Co ns ol e. Wr it el ine( "I mp le me nt in g IStorabl e.Re ad" );

}
public vo id Write()

^ Consol e . Wr it e L in e( " Impl ementi n g IStorab le. Write " );

}
void ITalk.Read()

Consol e. Wr it et in e( "I mp le me nt in g ITalk.Re ad" );

}
public vo id Talk()

Co ns ol e. Wr it eL ine( " Implem enting I Talk. Talk );

}
}
public class Tester
{
static vo id Main()
{
// Pr av i ob je k t d okumen ta
Document t heDoc = new Document( "Test Do cu me nt" );

158 | Programiranje C#
primjer 8-5. Eksplicitna implementacija (nastavak)
IStorable isDoc = theDoc;
isDoc.Read();

ITalk itDoc = theDoc;


itDoc.Read();

theDoc.Read();
theDoc.Talk();

}
}

Selektivno izlaganje metoda suelja


Razvojni inenjer koji je stvorio klasu moe iskoristiti injenicu da, kada je suelje
implement>ran0 kroz eksplicitnu implementaciju, suelje nije vidljivo klijentima imple-
mentirajue klase, osim ako se koristi pretvaranje tipa.
Pretpostavimo kako semantika objekta Document zahtjeva da on implementira sue-
lje IStorable, ali ne elite da metode Read() i Write() budu dio javnog suelja Docu-
ment. Moete koristiti eksplicitnu implementaciju kako biste osigurali da su te metode
dostupne samo kroz pretvaranje tipa. To vam omoguava ouvanje javnog API-ja Docu-
ment i istovremenu implementaciju suelja IStorable. Ako vaem klijentu treba objekt
koji implementira suelje IStorable, on moe izvesti pretvaranje, ali kad dokument
koristi kao Document API nee ukljuivati Read() i W rite().
Eksplicitnom implementacijom, u stvari, moete izabrati koje e metode biti vidljive
kako biste neke implementacijske metode izloili kao dio Document, a druge ne. U
primjeru 8-5 objekt Document izlae metodu Talk() kao metodu koja je dio Document,
no metodi ITalk. Read() moe se pristupiti samo kroz pretvaranje tipa. ak i kad sue-
lje IStorable ne bi sadravalo metodu Read() mogli biste eksplicitno implementirati
Read() kako metodu Read() ne biste izloili kao metodu iz Document.
Zbog toga to eksplicitna implementacija suelja sprjeava koritenje kljune rijei
Virtual, izvedena klasa morala bi ponovno implementirati metodu. Stoga, ako je Note
izvodila iz Document, ona bi morala ponovno implementirati metodu ITalk.Read() jer
Document implementacija ITalk.Read() ne moe biti virtualna.

Sakrivanje lana
lan suelja moe biti skriven. Pretpostavimo da elite da suelje IBase ima svojstvo P;
interface IBase
{
int P { get; set; }
}
Pretpostavimo i da iz tog suelja izvodite novo suelje IDerived koje svojstvo P skriva
novom metodom P():

Poglavlje 8; Suelja | 159


interface IDerived : IBase
{
new int P();
}
Zanemarimo li pitanje je li ovo dobra zamisao, svojstvo Psada je skriveno u osnovnom
suelju. Implementacija ovog izvedenog suelja zahtijevat e najmanje jednog eksplicit-
nog lana suelja. Moete koristiti eksplicitnu implementaciju ili za osnovno sueljei/j
za izvedenu metodu, ili ju moete koristiti i za jedno i za drugo. Stoga, sve tri sljedee
inaice bit e doputene:
class myClass : IDerived
{
// Eksplicitna implementacija za osnovno svojstvo
int IBase.P { get {...} }

// Implicitna implementacija izvedene metode


public int P() {...}

class myClass : IDerived


{
// Implicitna implementacija za osnovno svojstvo
public int P { get {...} }

I I Eksplicitna implementacija izvedene metode


int IDerived.P() {...}

class myClass : IDerived


{
// Eksplicitna implementacija za osnovno svojstvo
int IBase.P { get {...} }

// Eksplicitna implementacija izvedene metode


int IDerived.P() {...}

Pristupanje zapeaenim klasama i vrijednosnim tipovima


Metodama suelja uglavnom je bolje pristupati kroz pretvaranje suelja. Iznimka su
vrijednosni tipovi (npr. strukture) i zapeaene klase. U tom je sluaju metodu suelja
bolje pozvati kroz objekt.
Kada suelje implementirate u strukturi, zapravo ga implementirate u vrijednosnom
tipu. Prilikom pretvaranja tipa u referencu suelja dolazi do implicitnog pakiranja
objekta: Na alost, kad to suelje koristite za modifikaciju objekta, modificirate zapa-
kiran objekt, a ne izvorni objekt vrijednosti. Nadalje, ako vrijednost strukture promi-
jenite iz metode, zapakirani e tip ostati nepromijenjen. U primjeru 8 -6 stvorena je
struktura koja implementira suelje IStorable i prikazane su posljedice implicitnog
pakiranja prilikom pretvaranja tipa strukture u referencu suelja.

160 | Programiranje C#
'fim jer 8 - 6 . R e f e r e n c e n a v r i je d n o s n e t ip o v e

b'sing System;

Uegion Using directives


S''
^using System;
gfjjsing System.Collections.Generic;
f using System.Text;

. #endregion

pamespace ReferencesOnValueTypes
{
7 / Deklarira jednostavno suelje
intertace IStorable
{
void Read();
int Status { get;set;}

// Implementira kroz strukturu


public struct myStruct : IStorable
{

public void Read()


{
Console.WriteLine(
"Implementing IStorable.Read" );

public int Status


{
get
{
return status;
}
set
{
status = value;
}
}

private int status;


}

public class Tester


{

static void Main()


{
// Pravi objekt myStruct
myStruct theStruct = new myStruct();
theStruct.Status = -i; // Inicijalizira

Poglavlje 8
Primjer 8-6. Reference na vrijednosne tipove (nastavak)
Console.WriteLine(
theStmct.Status: {0}", theStruct.Status );

// Mijenja vrijednost
theStruct.Status = 2;
Console.WriteLine( "Changed object. );
Console.WriteLine(
"theStruct.Status: {o}", theStruct.Status );

// Pretvara tip u IStorable


// implicitno pakira u referentni tip
IStorable isTemp = ( IStorable ) theStruct;

// Postavlja vrijednost kroz referencu suelja


isTemp.Status = 4;
Console.WriteLine( "Changed interface." );
Console.WriteLine( "theStruct.Status: {0}, isTemp: {l}",
theStruct.Status, isTemp.Status );

// Ponovno mijenja vrijednost


theStruct.Status = 6;
Console.WriteLine( "Changed object." );
Console.WriteLine( "theStruct.Status: {0}, isTemp: {l}",
theStruct.Status, isTemp.Status );
}
}
}
U primjeru 8-6 suelje IStorable sadri metodu (Read) i svojstvo (Status).

Ovo suelje je implementirala struktura myStruct:


public struct myStruct : IStorable

Zanimljiv se kod nalazi unutar Tester. Poinje stvaranjem instance strukture i inicija-
liziranjem njenog svojstva na -1. Vrijednost statusa se zatim ispisuje:
myStruct theStruct = new myStruct();
theStruct.status = -l; // Inicijaliziranje
Console.WriteLine(
"theStruct.Status: {0}", theStruct.status);

Izlaz pokazuje kako je svojstvo status ispravno postavljen:


theStruct.Status: -l

Zatim se pristupa svojstvu radi promjene statusa, ponovno putem samog objekta vri-
jednosti;
// Mijenja vrijednost
theStruct.status = 2;
Console.WriteLine("Changed object.");
Console.WriteLine(
"theStruct.Status: {0}", theStruct.status);

162 | Programiranje C#
^ 'Promjena se moe vidjeti u izlazu:
Changed object.
theStruct.Status: 2

po sad se nije dogodilo nita neobino. Sada se stvara referenca do suelja IStorable.
To e uzrokovati implicitno pakiranje objekta vrijednosti theStruct. Zatim se suelje
koristi za promjenu vrijednosti statusa na 4:
// Pretvara tip u IStorable
// implicitno pakira u referentni tip
IStorable isTemp = ( IStorable ) theStruct;

// Postavlja vrijednost kroz referencu suelja


isTemp.Status = 4 ;
Console.WriteLine( "Changed interface." );
Console.WriteLine( "theStruct.Status: {o}, isTemp: {i}",
theStruct.Status, isTemp.Status );

Izlaz sada moe biti malo neobian:


Changed interface.
theStruct.Status: 2 , isTemp: 4

Aha! Objekt na kojeg pokazuje referenca suelja je promijenjen na vrijednost statusa


4, ali je objekt vrijednosti strukture ostao nepromijenjen. Jo zanimljivije, kad metodi
pristupate preko samog objekta:
// Ponovno mijenja vrijednost
theStruct.Status = 6 ;
Console.WriteLine( "Changed object." );
Console.WriteLine( "theStruct.Status: {o}, isTemp: {i}",
theStruct.Status, isTemp.Status );

izlaz pokazuje kako je objekt vrijednosti promijenjen, no nije promijenjena zapakirana


referentna vrijednost za referencu suelja:
Changed object.
theStruct.Status: 6 , isTemp: 4

Ako pogledamo MSIL kod (primjer 8-7) otkrit emo to se zapravo dogaa u pozadini.

Primjer 8-7. MSIL kod za primjer 8-6


method private hidebysig static void Main() cil managed

entrypoint
// Code sie 1 9 4 (0 xc 2 )
.maxstack 3
locals init ([0 ] valuetype ReferencesOnValueTypes.myStruct theStruct,
[1 ] class ReferencesOnValueTypes.IStorable isTemp)
IL_0 0 0 0 : ldloca.s theStruct
IL_0 0 0 2 : initobj ReferencesOnValueTypes.myStruct
IL_0 0 0 8 : ldloca.s theStruct
IL_0 0 0 a: ldc.i4 .ml
ILo oo b: call instance void ReferencesOnValueTypes.myStruct::set_Status(int3 2 )

Poglavlje 8: Suelja | 163


Primjer 8-7. MSIL kod za primjer 8-6 (nastavak)
IL_0010: ldstr ''theStruct. Status: {o}'1
IL 0 0 1 5 : ldloca.s theStruct
IL 0017: call instance int32 ReferencesOnValueTypes.myStiuct::get_Status()
IL 001c: box [mscorlib]System.Int32
IL~002l: call void [mscorlib] System.Console::WriteLine(string,
object)

IL_0 0 2 6 : nop
IL OO2 7 : ldloca.s theStruct
IL 0029: ldc.i4.2
IL 002a: call instance void ReferencesOnValueTypes.myStruct::set_Status(int32)
IL~002f: ldstr "Changed object."
IL 0034: call void [mscorlib]System.Console::WriteLine(string)

IL_0039: nop
IL_003a: ldstr "theStruct.Status: {0}"
IL 003f: ldloca.s theStruct
IL 0041: call instance int32 ReferencesOnValueTypes.myStruct::get_Status()
IL 0046: box [mscorlib]System.Int32
IL_004b- call void [mscorlib]System.Console::WriteLine(string,
object)

IL_0050: nop
IL_0051: ldloc.O
IL_0052: box ReferencesOnValueTypes.myStruct
IL_0057: stloc.l
IL_0058: ldloc.l
IL 00 59 : ldc.i4.4

IL_005a: callvirt instance void ReferencesOnValueTypes.IStorable::set_Status(int32)


IL_005f: ldstr "Changed interface."
call void [mscorlib]System.Console::WriteLine(string)
IL_0064:
IL_0069: nop
IL_006a: ldstr "theStruct.Status: {0}, isTemp: (l)"
IL_006f: ldloca.s theStruct
call instance int32 ReferencesOnValueTypes.myStruct::get_Status()
IL_0071:
IL_0076: box [mscorlib]System.Int32
IL_007b: ldloc.l

callvirt instance int32 ReferencesOnValueTypes.IStorable::get_Status()


IL_007c:
IL_0081: box [mscorlib]System.Int32
IL 0086: call void [mscorlib]System.Console::WriteLine(string,
object,
object)

IL_008b: nop
IL_008c: ldloca.s theStruct
IL_0 0 8 e: ldc.i4.6
instance void ReferencesOnValueTypes.myStruct::set_Status(int32)
IL_008f: call
IL_0094: ldstr "Changed object."

IL_0099: call void [mscorlib]System.Console::WriteLine(string)


IL_0 0 9 e: nop
IL_009f: ldstr "theStruct.Status: {0}, isTemp: {l}"
IL_OOa4: ldloca.s theStruct

IL_00a6: call instance int32 ReferencesOnValueTypes.myStruct::get_Status()


IL_0 0 ab: box [mscorlib]System.Int32
IL_OObO: ldloc.l

callvirt instance int32 RefeiencesOnValueTypes.IStorable::get_Status()


IL_OObl:
IL~00b6: box [mscorlib]System.Int32

164 | Programiranje C#
Jprimjer 8-7. MSIL kod za primjer 8-6 (nastavak)

1L oobb: call void [mscorlib]System.Console::Wiiteline(string,


object,
object)
JL_00C0: nop
iC o o c i- ret
J I ? i // end of method Tester: :Main
u J
Uredu IL_0 0 0 b metoda set_Status() je pozvana na objektu vrijednosti. Drugi se poziv
vidi u redu IL_0017. Primijetit ete kako su pozivi metode WriteLine() uzrokovali paki-
ranje cjelobrojne vrijednosti statusa kako bi se mogla pozvati metoda GetString().
J Kljuan je red IL_001c (podebljan) u kojem dolazi do pakiranja same strukture. To
1 pakiranje zapravo stvara referentni tip za referencu suelja. Obratite panju da se u
redu IL_005a ovaj put umjesto myStruct: :set_Status poziva IStorable: :se t Status.

V Ako implementirate suelje sa vrijednosnim tipom, lanovima suelja svakako pristu-


- ' pajte kroz objekt, a ne preko reference suelja.

3*
!

Poglavlje 8: Suelja | 165


POGLAVLJE 9
Polja, indekseri i kolekcije

.N ET kostur prua mnotvo klasa kolekcija. Od pojave generika u inaici 2.0 veina
ovih klasa kolekcija sada su sigurne za tipove, ime je samo programiranje uvelike
olakano. U te se klase ubrajaju Array, List, Dictionary, Sorted Dictionary, Oueue i
Stack.
Najjednostavnija kolekcija jest Array, jedini tip kolekcije za koju C # prua ugraenu
podrku. U ovom poglavlju ete nauiti kako se radi s jednostrukim, viedimenzio-
nalnim i nejednakim poljima. Polja imaju ugraene indeksere, to vam omoguava da
zatraite n-ti lan polja. U ovom je poglavlju objanjeno i stvaranje vlastitog indeksera
to vam omoguava da svojstvima klase pristupite kao da je klasa indeksirana poput
polja.
.NET kostur prua razna suelja, poput IEnumerable i ICollection ija vam implemen-
tacija omoguava standardne naine interakcije s kolekcijama. U ovom ete poglavlju
vidjeti kako se radi s najosnovnijim. Poglavlje zavrava pregledom .N ET kolekcija koje
se najee koriste, a u koje se ubrajaju L ist, Dictionary, Oueue i Stack.

U prethodnim inaicam a jezika C# objekti kolekcija nisu bili sigurno


za tipove (u Dictionary ste, na primjer, mogli nizove pomijeati s cje -
lobrojnim vrijednostima). Inaice L ist (ArrayList), Dictionary, Oueue i
Stack koje nisu sigurne za tipove i dalje su dostupne radi kom patibilno-
sti sa starijim inaicam a, ali one nee biti obraene u ovoj knjizi jer je
njihova upotreba slina upotrebi inaica koje se temelje na genericima
a i zbog toga to su te inaice zastarjele i izgubile su na vrijednosti.

Polja
Polje (engl. array) je indeksirana kolekcija objekata koji su svi istog tipa. Polja u jeziku
C # se pomalo razlikuju od polja u C++ jer su objekti. To im prua razne korisne
metode i svojstva.

166
?# prua izvornu sintaksu za deklariranje polja. Ono to se zapravo stvara jest objekt
ijpa System.Array. Polja jezika C # stoga vam pruaju najbolje od oba svijeta: jedno-
tavnu sintaksu u C stilu koju podupire stvarna definicija klase tako da instance polja
jrriaju pristup metodama i svojstvima iz System.Array. Te su metode i svojstva opisani
utablici 9-1.

Tablicu 9 -1 . M e t o d e i s v o js t v a S y s t e m .A r r a y

gttodailisvojstvo Svrha
BinaiySearch() Preoptereena javna statika metoda koja trai jednodimenzionalno sortirano polje.

Clear() Javna statika metoda koja raspon elemenata u polju postavlja na 0 ili na referencu nuli.

Copy() Preoptereena javna statika metoda koja dio jednog polja kopira u drugo polje.
Createlnstance() Preoptereena javna statika metoda koja stvara novu instancu polja.

IndexOf() Preoptereena javna statika metoda koja vraa indeks (pomak) prve instance vrijednosti
ujednodimenzionalnom polju.

Last IrdexOff () Preoptereena javna statika metoda koja vraa indeks posljednje instance vrijednosti
u jednodimenzionalnom polju.

Reverse() Preoptereena javna statika metoda koja elemente jednodimenzionalnog polja postavlja
u obrnuti redoslijed.
Sort() Preoptereena javna statika metoda koja sortira vrijednosti u jednodimenzionalnom polju.

IsFixedSize Obavezno svojstvo jer A r ra y implementira I C o l le c t io n . Spoljima e uvijek vratiti true


(sva su polja fiksne veliine).
IsReadOnly Javno svojstvo (obavezno jer A r ra y implementira I L i s t ) koje vraa Boolean vrijednost koja
pokazuje je li polje samo za itanje.
IsSynchronized Javno svojstvo (obavezno jer A r r a y implementira I C o l le c t io n ) koje vraa Boolean vrijed-
nost koja pokazuje je ii polje sigurno za dretve.
Length Javno svojstvo koje vraa duljinu polja.
Rank Javno svojstvo koje vraa broj dimenzija polja.

SyncRoot Javno svojstvo koje vraa objekt koji se moe koristiti za sinkronizaciju pristupa polju.
GetEnumerator() Javna metoda koja vraa IE n u m e ra to r.
v CetLength() Javna metoda koja vraa duljinu zadane dimenzije polja.

GetLowerBound() Javna metoda koja vraa donju granicu zadane dimenzije polja.
GetUpperBound() Javna metoda koja vraa gornju granicu zadane dimenzije polja.

Initialize() Inicijalizira sve vrijednosti u polju vrijednosnog tipa pozivanjem podrazumijevanog konstruktora
za svaku vrijednost. U referentnim poljima svi su elementi postavljeni na nuli.
SetValue() Preoptereena javna metoda koja zadane elemente polja postavlja na neku vrijednost.

Naravno, prilikom stvaranja polja s pomou in t [] myArray - new in t[s] u I L kodu zapravo stvarate
instancu System.int32[ ], no budui da izvodi iz apstraktne osnovne klase System.Array, moe se rei i da
ste stvorili instancu System.Array.

Poglavlje 9: Polja, indekseri i kolekcije | 167


Deklariranje polja I
Polje se u jeziku C # deklarira sljedeom sintaksom: 1
tip[] ime polja; m
Na primjer |
'jj
int[] myIntArray; |

4
Zapravo se ne deklarira polje. Tehniki deklarirate varijablu (myIntAr- j
ray) koja e sadrati referencu polja cjelobrojnih vrijednosti. Kao i j
obino, koristit emo preac i myIntArray nazivati poljem, znajui da se
' zapravo radi o varijabli koja sadri referencu (neimenovanog) polja.

Uglate zagrade ([]) C # prevoditelju govore da se radi o deklaraciji polja, a tip odreuje ;
tip elemenata koje e polje sadrati. U prethodnom primjeru myIntArray je polje cjelo- :
brojnih vrijednosti.
Instancirajte polje koristei kljunu rije new. Na primjer:
myIntArray = new int[5];
Ovom se deklaracijom stvara i inicijalizira polje od pet cjelobrojnih vrijednosti koje su
sve inicijalizirane na vrijednost 0.

Napomena za VB6 programere: U C # broj elemenata u polju odreuje


vrijednost veliine polja, a ne gornja granica. U stvari, ne postoji nain
za postavljanje gornje i donje granice (uz iznimku donje granice viedi-
' menzionalnih polja koju moete postaviti (objanjeno kasnije), ali ak
ni to nije podrano u biblioteci klasa .N ET kostura).
Stoga, prvi element polja je 0. Sljedei C # iskaz deklarira polje od 10
elemenata s indeksima od 0 do 9:
string myArray[lo];

Gornja je granica 9, ne 10 i veliina polja se ne moe promijeniti (to jest,


ne postoji metoda koja odgovara metodi Redim u VB 6).

Vano je polje (koje je kolekcija elemenata) razlikovati od elemenata polja. myIntArray


je polje (ili, preciznije, varijabla koja sadri referencu polja). Elementi polja su pet cje-
lobrojnih vrijednosti koje ono sadri.
C # polja su referentni tipovi koji se stvaraju na gomili. Stoga je polje na koje myIntAr-
ray referira alocirano na gomilu. Elementi polja su alocirani prema svom tipu. Budui
da cjelobrojne vrijednosti pripadaju vrijednosnom tipu, elementi polja myIntArray bit
e vrijednosni tipovi, a ne zapakirane cjelobrojne vrijednosti te e se svi elementi stvo-
riti unutar bloka memorije koji je dodijeljen polju.
Blok memorije dodijeljen polju referentnih tipova sadrat e reference stvarnih eleme-
nata koji se stvaraju na gomili u memoriji koja je odvojena od memorije dodijeljene
polju.

168 | Programiranje C#
Razum ijevanje podrazumijevanih vrijednosti
IfeijCada stvorite polje vrijednosnih tipova, svaki element na poetku sadri podrazumij e-
' vanu vrijednost za tip spremljen u polju (pogledajte tablicu 4-2). Iskaz:
myIntArray = new i n t [ 5 ] ;

S* ^stvara polje od pet cjelobrojnih vrijednosti, a vrijednost svakog elementa postavljena


- je na 0, to jest na podrazumijevanu vrijednost za tip cjelobrojne vrijednosti.
- ' Za razliku od polja vrijednosnih tipova, referentni tipovi u polju se ne inicijalizi raj u
V 4 na svoju podrazumijevanu vrijednost. Umjesto toga se reference koje polje sadri inici-
. jaliziraju na nuli. Ako elementu polja referentnih tipova pokuate pristupiti prije nego
/f to ste inicijalizirali elemente, izbacit e se iznimka.

* 4 Pretpostavimo kako ste stvorili klasu Button. Deklarirajte polje Button objekata slje-
^.deim iskazom:
Button[] myButtonArray;

i mstancirajte polje na sljedei nain:


myButtonArray = new Button[3j;

To moete skratiti na:


Button[] myButtonArray = new Button[3 ];

. , Ovim se iskazom ne stvara polje s referencama tri Button objekata. Umjesto toga se
stvara polje myButtonArray s tri reference nuli. Za koritenje ovog polja prvo morate
konstruirati i dodijeliti Button objekte svakoj referenci u polju. Objekte moete kon-
struirati u petlji koja ih jedan po jedan dodaje u polje.

Pristupanje elementima polja


Elementima polja moete pristupiti s pomou indeksnog operatora ([]). Polja imaju
osnovu nula, to znai da je indeks prvog elementa uvijek 0 - u ovom sluaju, myAr-
4 ra y [o ],

. i Kao to je ranije objanjeno, polja su objekti i stoga imaju svojstva. Jedno od korisni-
ji jih svojstava je Length koje govori koliko elemenata polje sadri. Objekti polja mogu
se indeksirati od 0 do Length-l. To jest, ako u polju postoji pet elemenata, njihovi su
indeksi 0 ,1 , 2, 3, 4.

: Dosad opisani koncepti polja prikazani su u primjeru 9-1. U ovom primjeru klasa
Testei stvara polje Employees i polje cjelobrojnih vrijednosti, ispunjava polje Employee
: i zatim ispisuje vrijednosti oba polja.

Primjer 9-1. Rad s poljem


namespace Programming_CSharp

// Jednostavna klasa koja e pohranjivati u polje

Poglavlje 9: Polja, indekseri i kolekcije | 169


Primjer 9-1. Rad s poljem (nastavak)
public class Employee
{

public Employee(int empID)


{
this.empID = empID;
}
public override string T o Str ing O
{
return empID.ToStringO;
}
private int empID;
}
public class Tester
{
static void Main()
{
int[] intArray;
Employee[] empArray;
intArray = new int[5];
empArray = new Employee[3];

// Ispunjava polje
for (int i = 0;i<empArray.Length;i++)
{
empArray[i] = new Employee(i+5);
}

for (int i = 0;i<intArray,Length;i++)


{
Console.WriteLine(intArray[i] .ToStringO);
}

for (int i = 0;i<empArray,Length;i++)


{
Console.WriteLine(empArray[i]. ToStringO);
}
}
}
}
Primjer poinje definicijom klase Employee koja implementira konstruktora koji uzima
jedan cjelobrojni parametar. Metoda ToStringO naslijeena od Object preoptereena
je kako bi ispisala vrijednost identifikatora zaposlenika iz klase Employee.
Metoda za provjeru deklarira i zatim instancira par polja. Polje cjelobrojnih vrijednosti
se automatski ispunjava cjelobrojnim vrijednostima koje su postavljene na 0. Sadraj
polja Employee mora se runo konstruirati.
Na kraju se sadraj oba polja ispisuje kako bi se provjerilo je li u skladu s namjerom.
Prvo se ispisuje pet cjelobrojnih vrijednosti, a zatim tri elementa polja Employee.

170 | Programiranje C#
Iskaz foreach
Iskaz petlje foreach nov je u obitelji C jezika, iako je ve dobro poznat VB programe
rima. On omoguava iteraciju kroz elemente polja ili druge kolekcije, pri emu se svaki
element provjerava. Sintaksa iskaza foreach je sljedea:
foreach (tip identifikator in izraz) iskaz

Primjer 9-1 stoga moete aurirati i iskaze for koji iteriraju kroz sadraj ispunjenih
polja zamijeniti iskazima foreach, kao stoje prikazano u primjeru 9-2

P rim jer 9 - 2 . K o r i t e n j e i s k a z a foreach

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace UsingForEach
{
// Jednostavna klasa koja e pohranjivati u polje
public class Employee
{
// Jednostavna klasa koja e pohranjivati u polje
public Employee( int empID )

this.empI = empID;
}
public override string ToStringO

r e t u r n em p ID .ToStringO;

' private int empID;


}
public class Tester
{
static void Main()
{
int[] intArray;
Employee[] empArray;
intArray = new int[5 ];
empArray = new Employee[3 j;

// Ispunjava polje
for ( int i = 0 ; i < empArray.Length; i++ )

empArray[i] = new Employee( i + 5 );

foreach ( int i in intArray )

Console.WriteLine( i.ToStringO );

Poglavlje 9: Polja, indekseri i kolekcije | 171


Primjer 9-2. Koritenje iskaza fo re a c h (nastavak)

}
foreach ( Employee e in empArray )
{
Console.WriteLine( e.To Strin g O );
}
}
}
}
}
Izlaz primjera 9-2 identian je onom iz primjera 9-1. Meutim, umjesto stvaranja
iskaza for koji mjeri veliinu polja i kao indeks koristi privremenu varijablu za broja-
nje, pokuali smo s drugim pristupom:
for (int i = 0; i < empArray.Length; i++)
{
Console.WriteLine(empArray[i] .ToString());
}
Iteracija kroz polje se izvodi s pomou petlje foreach koja automatski iz polja izdvaja slje-
dei element i dodjeljuje ga privremenom objektu koji je stvoren u zaglavlju iskaza:
foreach (Employee e in empArray)
{
Console. UriteLine (e.ToStringO);
}
Objekt koji je izdvojen iz polja pripada odgovarajuem pa za njega moete pozvati bilo
koju javna metodu.

Inicijaliziranje elemenata polja


Sadraj polja moe se inicijalizirati u vrijeme instanciranja tako da se u vitiastim
zagradama ({}) navede popis vrijednosti. C # omoguava duu i krau sintaksu:
int[] myIntArray = new int[5] { 2, 4, 6, 8, 10 }
int[] myIntArray = { 2, 4, 6, 8, 10 }

Nema funkcionalne razlike izmeu ova dva iskaza i veina e programera koristiti
krau sintaksu, no pogledajte sljedeu napomenu.
a 4
Dvije sintakse postoje jer se u nekim situacijam a mora koristiti dua
sintaksa - na primjer, ako C # prevoditelj ne moe zakljuiti ispravan
!*,' tip polja.

Kljuna rije params


Moete stvoriti metodu koja na konzoli prikazuje neogranien broj cjelobrojnih vri-
jednosti tako to prosljeuje polje cjelobrojnih vrijednosti i zatim prolazi kroz polje s
pomou petlje foreach. Kljuna rije params omoguava prosljeivanje promjenjivog
broja parametara bez eksplicitnog stvaranja polja.

172 | Programiranje C#
Sljedeem se primjeru stvara metoda DisplayVals() koja uzima promjenjiv broj cje-
Ri$jjjbrojnih argumenata.
public void DisplayVals(params int[] intVals)

metoda moe polje tretirati kao da je polje cjelobrojnih vrijednosti eksplicitno


'jvoreno i proslijeeno kao parametar. Iteraciju kroz polje moete izvesti kao da se
Sidi o bilo kojem drugom polju cjelobrojnih vrijednosti:

ST
,;
foreach (int i in intVals)
{
Console.WriteLine("DisplayVals {0}",i);

}
^Pozivajua metoda, meutim, ne mora eksplicitno stvoriti polje. Ona jednostavno
vimoe proslijediti cjelobrojne vrijednosti i prevoditelj e parametre sakupiti u polje za
f^jpetodu DisplayVals():
X' t.DisplayVals(5,6,7,8);

Ako elite, moete radije proslijediti polje:


int [] explicitArray = new int[5] {1,2,3,4,5};
t.DisplayVals(explicitArray);

U primjeru 9-3 naveden je cjelokupan izvorni kod koji prikazuje upotrebu kljune
rijei params.

P rim jer 9 - 3 . K o r i t e n j e k l ju n e r i je i params

Kregion Using directives

i;using System;
using System.Collections.Generic;
using System.Text;

>#endregion

narespace UsingParams
#{
public class Tester
{
static void Main()
{
Tester t = new Tester();
t.DisplayVals(5,6,7,8);
int [] explicitArray = new int[5] {1,2,3,4,5};
t.DisplayVals(explicitArray);
}
public void DisplayVals(params int[] intVals)
{
foreach (int i in intVals)
{
Console.WriteLine(DisplayVals {0}",i);
}
}
1
}

Poglavlje 9: Polja, indekseri i kolekcije | 173


Viedimenzionalna polja
Polja se mogu shvatiti kao dugi nizovi pregrada u koje se mogu postaviti vrijednosti 1
Zamislite prvo red pregrada, a zatim zamislite deset takvih redova postavljenih jcdati
iznad drugog. Redovi idu vodoravno kroz polje, a stupci okomito.
Mogua je i trea dimenzija, no neto ju je tee zamisliti. Svoja polja uinite trodimenzi-
onalnima postavljanjem novih redova iznad starog dvodimenzionalnog polja. U redu
sad zamislite etiri dimenzije. Pa zatim deset.

itatelji koji nisu fiziari do sad su vjerojatno odustali, ba kao i ja. Viedimenzio-
nalna polja su, meutim, korisna ak i ako ne moete zamisliti kako bi ona trebala
izgledati.
C# podrava dva tipa viedimenzionalnih polja: pravokutna (engl. rectangular) i nejed-
naka (engl. jagged). U pravokutnom polju svaki je red iste duljine. Nejednaka se polja
sastoje od polja, a svako polje moe biti razliite duljine.

Pravokutna polja
Pravokutno polje ima dvije (ili vie) dimenzije. Prva dimenzija klasinog dvodimenzio-
nalnog polja je broj redova, a druga je broj stupaca.

N a p o m e n a z a J a v a p r o g r a m e r e : pravok ut na polja ne postoje u jeziku


Java.

Za deklariranje dvodimenzionalnog polja koristite sljedeu sintaksu:


tip [,] naziv polja

Na primjer, za deklariranje i instanciranje dvodimenzionalnog pravokutnog polja


myRectangularArray koje se sastoji od dva reda i tri stupca cjelobrojnih vrijednosti,
napisali biste:
int [,] myRectangularArray = new int[2,3];

U primjeru 9-7 se deklarira, instancira i inicijalizira polje a zatim se ispisuje njegov


sadraj. U ovom se primjeru petlja fo r koristi za inicijaliziranje elemenata polja.

P r im je r 9 - 4 . P r a v o k u t n o p o l j e

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace RectangularArray
{

174 | Programiranje C#
p rim jer 9-1. P r a v o k u t n o p o l j e ( n a s t a v a k )

public class Tester

{
static void Main()

const int rows = 4;


const int columns = 3;

// Deklarira cjelobrojno polje 4x3


int[,] rectangularArray = new int[rows, columns];

// Ispunjava polje
for ( int i = 0 ; i < rows; i++ )
{
for ( int j = o; j < columns; j++ )
{
rectangularArray[i, j] = i + j;
}
}
// Ispisuje sadraj polja
for ( int i = o; i < rows; i++ )
{
for ( int j = 0; j < columns; j++ )
{
Console.WriteLine( "rectangularArray[{o},{l}] = {2},
i, j, rectangularArray[i, j] );
}
}
}
}
}
U ovom se primjeru deklarira par konstantnih vrijednosti:
const int rows = 4;
codst int columns = 3;

koje se zatim koriste za odreivanje dimenzija polja:


int[,] rectangularArray = new int[rows, columns];

Obratite panju na sintaksu. Zagrade u deklaraciji int[,] oznaavaju da je tip polje


cjelobrojnih vrijednosti, a zarez oznaava kako polje ima dvije dimenzije (dva bi zareza
oznaavala tri dimenzije i tako dalje). Stvarna instancijacija polja rectangularArray s
new int [rows, columns] postavlja veliinu svake dimenzije. U ovom se primjeru dekla-
racija i instancijacija kombiniraju.
Program ispunjava pravokutnik parom for petlji, iteriranjem kroz svaki stupac u sva-
kom redu. Stoga se prvo unosi element rectangularArray[o,0], iza njega rectangula-
rArray[o,l] i rectangularArray[0,2]. Kad je to uinjeno, program prelazi na sljedee
redove: rectangularArray[i,o],rectangularArray[l,l], rectangularArray[l,2] i tako
dalje, sve dok se u svim redovima ne popune svi stupci.

Poglavlje 9: Polja, indekseri i kolekcije | 175


Kao to se jednodimenzionalno polje moe inicijalizirati koristei popis vrijednosti uniji
tar zagrada, dvodimenzionalno se polje moe inicijalizirati slinom sintaksom. U
mjeru 9-5 deklarirano je dvodimenzionalno polje (rectangularArray), njegovi elementi si
inicijaliziraju s pomou popisa vrijednosti unutar zagrada, a zatim se sadraj ispisnj/i

Primjer 9-5. Inicijaliziranje viedimenzionalnog polja


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace InitializingMultiDimensionalArray
{
public class Tester
{
static void Main()
{
const int rows = 4;
const int columns = 3 ;

// Zakljuuje da je rije o polju 4 x 3


int[,] rectangularArray =
{
{0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}
};

tor ( int i = 0 ; i < rows; i++ )


{
tor ( int j = 0; j < columns; j++ )
{
Console.WriteLine( ''rectangularArray[{o},{i}] = {2}",
i, j, rectangularArray[i, j] );
}
}
}
}
}
Ovaj je primjer slian primjeru 9 -4, no ovaj se put tone dimenzije polja odreuju nai-
nom inicijalizacije:
int[,] rectangularArrayrectangularArray =
{
{0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}
};
Dodjela vrijednosti u etiri popisa unutar zagrada, od kojih se svaki sastoji od tri ele-
menta, naznauje polje dimenzija 4x3. Da ste umjesto toga napisali;
int[,] rectangularArrayrectangularArray =
{
{0,1,2,3}, {4,5,6,7}, {8,9,10,11}
};

176 | Programiranje C#
i biste polje dimenzija 3x4.

Iete uoiti da C# prevoditelj razumije vae naznake jer moe pristupiti objektima
%govarajuim pomacima, kao to se vidi u izlazu.
o?da ete pomisliti kako, budui da se radi o polju od 12 elemenata, jednako lako
blete pristupiti elementu na rectangularArray[0,3] (etvrtom elementu u prvom
;|u) kao ionom na rectangularArray[l,0] (prvomelementu udrugom redu). To funk-
||nira u C++, ali ako to pokuate u C# , generirat e se sljedea iznimka:
5 j Exception occurred: System.IndexOutOfRangeException:
W f : Index was outside the bounds of the array.
at programming_CSharp.Tester.Main() in
cshaip\programming csharp\listing0703.cs:line 23

-Silolja ujeziku C # su pametna i paze na svoja ogranienja. Ako naznaite polje dimen-
x@ ] a 4*3, morate ga i tretirati kao takvo.

^Nejednaka polja
Nejednako polje (engl. jagged array) je polje sastavljeno od polja. Naziva se nejednako"
jjer ne moraju svi redovi biti iste veliine pa grafiki prikaz polja ne bi bio kvadratan.
iprilikom stvaranja nejednakog polja deklarirate broj redova u polju. Svaki e red sadr-
' sati polje koje moe biti bilo koje duljine. Svako se od ovih polja mora zasebno dekla-
mirati. Zatim moete ispuniti vrijednosti elemenata u tim unutarnjim" poljima.
Svaku dimenziju nejednakog polja ini jednodimenzionalno polje. Za deklariranje
mfeednakog polja koristite sljedeu sintaksu u kojoj broj zagrada zadaje broj dimen-
z i j a polja:
tip [] []...

J|N a primjer, dvodimenzionalno nejednako polje cjelobrojnih vrijednosti myJaggedArray


ipioete deklarirati na sljedei nain:
;7||t int [] [] my3aggedArray;

fetom elementu treeg polja moete pristupiti tako da napiete my3aggedArray[2][ 4 ]


J I t J primjeru 9 -6 stvara se nejednako polje myJaggedArray, inicijaliziraju se njegovi
' srelementi i zatim se ispisuje njihov sadraj. Da bismo utedjeli na prostoru, program
yfj|ltoristi injenicu da se cjelobrojni elementi polja automatski inicijaliziraju na 0 i inici-
Ijijalizira samo neke elemente.

3 [Primjer 9-6. Rad s nejednakim poljima


%,:
Sfelregion Using directives
'St'
using System;
:):using System.Collections.Generic;
using System.Text;

;jendregion

Poglavlje 9: Polja, Indekseri i kolekcije | 177


Primjer 9-6. Rad s nejednakim poljima (nastavak)
namespace laggedArray
{
public class Tester
{
static void Main()
{
const int rows = 4;

// Deklarira nejednako polje visoko etiri reda


int[][] jaggedArray = new int[rows][];

// Prvi red ima pet elemenata


jaggedArray[0] = new int[5];

// Red s dva elementa


jaggedArray[l] = new int[2];

// Red s tri elementa


jaggedArray[2] = new int[3];

// Zadnji red ima pet elemenata


jaggedArray[3] = new int[5];

// Popunjava neke (ali ne sve) elemente redova


jaggedArray[o][3] = 15;
jaggedArray[l][l] = 12;
jaggedArray[2][l] = 9;
jaggedArray[2 ][2 ] = 99;
jaggedArray[3][0] = 10;
jaggedArray[3][l] = li;
jaggedArray[3][2] = 12;
jaggedArray[3][3] = 13;
jaggedArray[3][4] = 14;

for ( int i = 0; i < 5; i++ )


{
Console.WriteLine( "jaggedArray[o][{o}] = {l}
i, jaggedArray[o][i] );
)

for ( int i = 0; i < 2; i++ )


{
Console.WriteLine( "jaggedArray[l]({o}] = {l}'
i, jaggedArray[l][i] );
_)

for ( int i = 0; i < 3; i++ )


{
Console.WriteLine( "jaggedArray[2][{o}] = {l}'
i, jaggedArray[2][i] );
)
for ( int i = 0; i < 5; i++ )
{

178 | Programiranje C#
9 -6 . R ads nejednakim poljima (nastavak)
Console.WriteLine( "jaggedArray[3][{0}] = {1}",
i, jaggedArray[3][i] );
}

'% Uovom se primjeru stvara nejednako polje s etiri reda:


int[][] jaggedArray = new int[rows][];

' Moete primijetiti kako druga dimenzija nije zadana. To je odreeno stvaranjem novog
polja za svaki red. Svako polje moe biti razliite veliine:
// Prvi red ima pet elemenata
jaggedArray[o] = new int[5];

// Red s dva elementa


jaggedArray[l] = new int[2];

// Red s tri elementa


jaggedArray[2] = new int[3];

// Zadnji red ima pet elemenata


jaggedArray[3] = new int[5];

Kad je polje zadano za svaki red trebate samo popuniti razliite lanove svakog polja
i zatim ispisati njihov sadraj kako biste provjerili je li sve u redu.
Obratite pozornost da, kada pristupate lanovima pravokutnog polja, svi se indeksi
stavljaju unutar jednog skupa uglatih zagrada:
rectangularArrayrectangularArray[i,j]

dok je kod nejednakih polja potreban dodatni par zagrada:


jaggedArray[3][i]

Prvu varijantu moete shvatiti kao jedno polje s vie dimenzija, a drugu kao polje
sastavljeno od polja.

Granice polja
Klasa Array moe se stvoriti i s pomou preoptereene metode Createlnstance. Jedno
od preoptereenja omoguava zadavanje donje granice (poetnog indeksa) svake
dimenzije viedimenzionalnog polja. Ova je mogunost prilino nejasna i ne koristi
se esto.
Ukratko, evo kako to moete uiniti: pozovete statiku metodu Createlnstance koja
vraa Array i prihvaa tri parametra: objekt tipa Type (oznaava tip objekata koji e
se nalaziti u polju), polje cjelobrojnih vrijednosti koje oznaava veliinu svake dimen-
zije u polju i drugo polje cjelobrojnih vrijednosti koje oznaava donju granicu svake
dimenzije. Dva polja cjelobrojnih vrijednosti moraju imati isti broj elemenata tj. za
svaku dimenziju morate zadati donju granicu:

Poglavlje 9: Polja, indekseri i kolekcije | 179


ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace SettingArrayBounds
{

public class SettingAriayBounds


{
public static void CreateArrayWithBounds() I
{
// Pravi i inicijalizira viedimenzionalno
// polje tipa String.
int[] lengthsArray = new int[2 ] { 3, 5 };
int[] boundsArray = new int[2] { 2, 3 };
Array multiDimensionalAri'ay = Array.CreateInstance(
typeof( String ),
lengthsArray,
boundsArray );

// Prikazuje donju granicu i


U gornju granicu svake dimenzije.
Console.WriteLine( "Bounds:\tLower\tUpper );
tor ( int i = 0; i < multiDimensionalArray.Rank; i++ )
Console.WriteLine(
"{0}:Yt{l}Yt{2}\
i,
multiDimensionalArray.GetLowerBound( i ),
multiDimensionalArray.GetUpperBound( i ) );
}
static void Main()
{
SettingArrayBounds.CreateArrayWithBounds();
>
}
}

Pretvaranje polja
Polja se mogu meusobno pretvarati ako su njihove dimenzije jednake i ako je mogue
pretvaranje izmeu tipova elemenata. Implicitna se pretvorba moe provesti ako se ele-
menti mogu implicitno pretvarati; u suprotnom pretvaranje mora biti eksplicitno.
Polje izvedenih objekata se, naravno, moe pretvoriti u polje osnovnih objekata. U
primjeru 9-7 prikazana je pretvorba polja korisniki definiranih tipova Employee u
polje objekata.

180 | Programiranje C#
primjer 9-7. Pretvaranje polja
Ijegion Using directives

using System;
using System.Collections.Ceneric;
using System.Text;

(fendregion

namespace ConvertingArrays

// Stvara objekt koji


// moemo uvati u polju
public class Employee
{
// Jednostavna klasa za uvanje u polju
public Employee( int empID )
{
this.empID = empID;
}
public override string T o StringO
{
return empID.ToStringO;
}
private int empID;
}

public class Tester


{
I I Ova metoda uzima polje objekata.
I I Proslijedit emo polje Employee
I I i zatim polje nizova.
// Pretvorba je implicitna jer i Employee
I I i nizovi izvode iz objekta,
public static void PrintArray( object[] theArray )
{
Console.WriteLine( "Contents of the Array {0}'',
theArray.ToStringO );

I I Prolazi kroz polje i


// ispisuje vrijednosti,
foreach ( object obj in theArray )
{
Console.WriteLine( "Value: {0 }", obj );
}
}

static void Main()


{
// Pravi polje Employee objekata
Employee[] myEmployeeArray = new Employee[3];

I I Inicijalizira vrijednost svakog Employee


for ( int i = o; i < 3 ; i++ )

Poglavlje 9: Polja, indekseri i kolekcije | 181


Primjer 9-7. Pretvaranje polja (nastavak)

{
myEmployeeArray[i] = new Employee( i + 5 );
}

// Prikazuje vrijednosti
PrintArray( myEmployeeArray );

// Pravi polje od dva niza


string[] array =
{
"hello", "world"
};

// Ispisuje vrijednost nizova


PrintArray( array );
}
}
}
Primjer 9-7 poinje stvaranjem jednostavne klase Employee, kao i ranije u poglavlju.
Klasa Tester sada sadri novu statiku metodu PrintArray() koja kao parametar pri-
hvaa jednodimenzionalno polje Object:
public static void PrintArray(object[] theArray)

Object je implicitna osnovna klasa svakog objekta u .N ET kosturu te je tako i osnovna


klasa za String i Employee.
Metoda PrintArray() izvodi dvije akcije. Prvo poziva metodu To St r i n g O za samo
polje:
Console.WriteLine("Contents of the Array {0}",
theArray.ToString());

System.Array premouje metodu ToString O i ispisuje identifikacijski naziv polja:


Contents of the Array Programming_CSharp. Employee []
Contents of the Array System.String[]

PrintArray() zatim poziva metodu ToStringO za svaki element polja koji uzima kao
parametar. Budui da je ToStringO virtualna metoda u osnovnoj klasi Object, ona e
sigurno biti dostupna u svakoj izvedenoj klasi. Tu ste metodu preopteretili u Employee
te kod ispravno funkcionira. Pozivanje metode ToStringO za objekt String moda nije
potrebno, ali ne moe biti tetno i omoguava vam polimorfno tretiranje tih objekata.

Sortiranje polja
U Array postoje dvije korisne metode Sort() i Reversef). One su potpuno podrane za
polja ugraenih C # tipova poput String. Primjena tih metoda na klase koje ste sami
stvorili neto je kompliciranija jer morate implementirati suelje IComparable (pogle-
dajte odjeljak Implementacija suelja IComparable1' u nastavku ovog poglavlja). U
primjeru 9-8 prikazana je upotreba te dvije metode za rad s objektima String.

182 | Programiranje C#
ii primjer 9-8. Koritenje metoda Array.Sort i Array.Reverse
1 ffregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

Oendregion

namespace ArraySortAndReverse
{
public class Tester
{
public static void PrintMyArray( objectf] theArray )

foreach ( object obj in theArray )

Console.WriteLine( "Value: {o}", obj );

Consoie.UriteLine( "\n" );

static void Main()


{
Stringf] myArray =
{
"Uho", "is", "John", "Galt"
};

PrintMyArray( myArray ) ;
Array.Reverse( myArray );
PrintMyArray( myArray );

String[] myOtherArray =

"We", "Hold", "These", "Truths",


To', "Be", "Self","Evident",

PrintMyArray( myOtherArray );
Array.Sort( myOtherArray );
PrintMyArray( myOtherArray );

}
}

Primjer poinje stvaranjem polja myArray koje sadri nizove s rijeima:


"Uho", "is", "John", "Galt

Polje se ispisuje, a zatim se prosljeuje do metode Array.Reverse() gdje se ponovno


ispisuje kako bi se vidjelo je li redoslijed polja obrnut:

Poglavlje 9: Polja, indekseri i kolekcije | 183


Value: Galt
Value: John
Value: is
Value: Who

Na slian nain primjer stvara drugo polje myOtherArray koje sadri sljedee rijei:
"We", "Hold", "These", "Truths",
"To", "Be", "Self", "Evident",

Ti se elementi prosljeuju do metode Array. Sort(). Zatim ih Array.Sort() sortira po


abecedi:
Value: Be
Value: Evident
Value: Hold
Value: Self
Value: These
Value: To
Value: Truths
Value: We

Indekseri
Ponekad je kolekciji unutar klase bolje pristupiti kao da je klasa polje. Pretpostavimo,
na primjer, da ste stvorili kontrolu padajueg popisa myListBox koja sadri popis nizova
koji su spremljeni u jednodimenzionalnom polju, privatnoj varijabli lanici myStrings.
Kontrola padajueg popisa sadri svojstva i metode lanove te svoje polje nizova. Meu-
tim, prikladno bi bilo polju padajueg popisa pristupiti s pomou indeksa, kao da se
radi o polju.' Na primjer, takvo bi svojstvo doputalo sljedee iskaze:
string theFirstString = my!istBox[o];
string theLastString = myListBox[!ength-l];

Indekser (engl. indexer) je konstrukcija jezika C # koja doputa da kolekcijama unutar


klase pristupite koristei poznatu sintaksu [ ] za polja. Indekser je posebna vrsta svoj-
stva i sadri pristupnike get i set koji odreuju njegovo ponaanje.
Svojstvo indeksera se unutar klase odreuje sljedeom sintaksom:
tip this [argument tipa]{get; set;}

Povratni tip odreuje tip objekta koji e indekser vratiti, dok argument tipa odreuje
kakav e se argument koristiti za indeksiranje kolekcije koja sadri ciljne objekte.
Iako se za vrijednosti indeksa obino koriste cjelobrojne vrijednosti, kolekciju moete
indeksirati i s drugim tipovima, ukljuujui nizove. Moete ak i puiti indekser s vie
parametafa da biste stvorili viedimenzionalno polje!
Kljuna rije th is slui kao referenca objekta u kojem se indekser pojavljuje. Kao i za
ostala svojstva, morate definirati pristupnike get i set koji odreuju kako e se zatra-
eni objekt uzeti iz kolekcije ili se dodati u nju.

Stvarna kontrola ListBox, dostupna u Windows Forms i ASP.NET, ima kolekciju Items koja implementira
indekser.

184 | Programiranje C#
u primjeru 9-9 deklarirana je kontrola padajueg popisa (ListBoxTest) koja sadri jed-
nostavno polje (myStrings) i jednostavni indekser za pristupanje sadraju.

Napomena za C++ programere: indekser ima otprilike istu svrhu kao i


. PrePterec,vanje indeksnog operatora u C++ ([]). Indeksni operator se
u C # ne moe preopteretiti, zbog ega je uveden indekser

Primjer 9-9. Koritenje jednostavnog indeksera


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Simplelndexer
{
/ / Pojednostavnjena kontrola ListBox
public class ListBoxTest
{
private stringl] strings;
private int ctr = 0 ;

// Inicijalizira popis s nizovima


public ListBoxTest( params stringl] initialStrings )

// Dodjeljuje prostor za nizove


strings = new Stringf 256];

// Kopira nizove prosljeene konstruktoru


foreach ( string s in initialStrings )

strings[ctr++] = s;
}
}

// Dodaje niz na kraj popisa


public void Add( string theString )

if ( ctr >= strings.Length )

// Obrauje lo indeks
}
else
strings[ctr++] = theString;

// Dozvoljava pristup kao kod polja

public string this[int index]

Poglavlje 9: Polja, indekseri i kolekcije | 185


Primjer 9-9. Koritenje jednostavnog indeksera (nastavak)

{
get

if ( index < 0 || index >= strings.Length )

{
U Obrauje lo indeks

return strings[index];

}
set

// Dodavanje samo kroz metodu za dodavanje


i-f ( index >= ctr )
{
// Obrauje pogreke
}
else
strings[index] = value;

}
}
// Objavljuje koliko ima nizova
public int GetNumEntries()
{
return ctr;
}

public class Tester


{
static void Main()

// Pravi novi popis i inicijalizira ga


ListBoxTest lbt =
new ListBoxTest( "Hello", "World" ) ;

// Dodaje nekoliko nizova


lbt.Add( "Who" );
lbt.Add( "Is" );
lbt.Add( "lohn" );
lbt.Add( "Galt" );

// Testira pristup
string subst = "Universe";
lbt[l] = subst;

// Pristupa svim nizovima


-for ( int i = o; i < lbt.GetNumEntries(); i++ )

Console.WriteLine( "lbt[{0}]: {l}, i, lbt[i] );

}
}
}
}

186 | Programiranje C#
Kako bi primjer 9-9 bio to jednostavniji, kontrolu padajueg popisa sveli smo na
nekoliko znaajki koje su nam bitne. Popis zanemaruje sve to ima veze s korisnikom
kontrolom i fokusira se samo na popis nizova koje padajui popis odrava i na metode
za rad s njima. U stvarnoj aplikaciji ovo je, naravno, samo mali dio metoda padajueg
popisa iji je glavni zadatak prikaz nizova i omoguavanje izbora.
prvo treba obratiti panju na dva privatna lana:
private string[] strings;
private int ctr = 0;

Padajui popis u ovom programu odrava jednostavno polje nizova: strings. Ponovno,
u pravom biste padajuem popisu vjerojatno koristili sloeniji i dinaminiji spremnik,
na primjer he-tablicu (engl. hash table). Varijabla lanica c tr prati koliko je nizova
dodano ovom polju.
Polje u konstruktoru incijalizirajte iskazom:
strings = new String[256];

Ostatak konstruktora polju dodaje parametre. Novi se nizovi, ponovno radi jednostav-
nosti, polju dodaju redoslijedom primanja.

| Kako ne moete znati koliko e se nizova dodati, koristite kljunu rije


u '.' J Params kao to je opisano ranije u ovom poglavlju.
_ _ _ 3&
Metoda Add() iz ListBoxTest samo pridruuje novi niz unutarnjem polju.
Kljuna metoda iz ListBoxTest je indekser. Indekser nema naziva, stoga koristite
kljunu rije this:
public string this[int index]

Sintaksa indeksera vrlo je slina sintaksi svojstava. Postoji metoda get (), metoda se t()
ili obje. U prikazanom primjeru metoda g et() implementira osnovnu provjeru granica
te, pod pretpostavkom da je zatraeni indeks prihvatljiv, vraa zatraenu vrijednost:
get
{
if (index < 0 [| index >= strings.Length)
{
// Obrauje lo Indeks
}
return strings[index];
}
Metoda s e t() provjerava da li indeks koji postavljate ve ima vrijednost u padajuem
popisu. Ako nema, ona postavku tretira kao pogreku (novi se elementi u ovom pri-
stupu mogu dodati samo s pomou Add). Pristupnik set iskoritava prednost implicit-
nog parametra value koji predstavlja sve ono to je dodijeljeno s pomou indeksnog
operatora:

Poglavlje 9: Polja, indekseri i kolekcije | 187


set
{
if (index >= ctr )
{
// Obrauje pogreke
}
else
strings[index] = value;
}
Stoga, ako napiete:
T?
lbt[5] = "Hello World" '4 :
prevoditelj e za objekt pozvati metodu indeksera s e t() i niz Hellokiorld proslijediti l :
kao implicitni parametar value.
4 v

In d ekseri i d o d je ljiv a n je
U primjeru 9-9 ne moete dodijeliti indeksu koji nema vrijednost. Stoga, ako
napiete:
lbtjlO] = "wow! ;

pokrenut ete obradu pogreaka u metodi se t() koja e primijetiti d a je proslijeeni


indeks (10) vei od brojaa (6).
Naravno, za dodjeljivanje moete koristiti metodu s e t (); samo morate upravljati indek-
sima koje primite. Kako biste to uinili, metodu set () moete promijeniti tako da umje-
sto trenutne vrijednosti brojaa (counter) provjerava vrijednost Length bafera. Ako je
unesena vrijednost za indeks koji jo uvijek nema vrijednost, trebate aurirati c tr:

{
// Dodavanje samo kroz metodu za dodavanje
if (index >= strings.Length )
{
// Obrauje pogreke
}
else
{
strings[index] = value;
if (ctr < index+l)
ctr = index+l;
}
}

Ovaj kod je pojednostavljen i stoga nije robustan. Postoje razne druge


provjere koje trebate izvesti na proslijeenim vrijednostima (npr. pro-
vjera je li proslijeen negativan indeks i premauje li indeks veliinu
temeljnog polja strings[ ]).

To vam omoguava stvaranje rijetkog" polja u kojem moete dodijeliti na poziciju 10


bez dodjeljivanja na poziciju 9. Ako napiete:

188 | Programiranje C#
jzlaz e biti:

lbt[0] Hello
lbt[1] Universe
lbt[2] Who
lbt[3] Is
lbt[4] lohn
lbt[5] Galt
lbt[6]
lbt[7]
lbt[8]
lbt[9]
lbt[10 : wow!

u Main() se stvara instanca klase ListBoxTest pod nazivom lbt i dva se niza proslje-
uju kao parametri:
ListBoxTest lb t = new ListBoxTest("Hello", "World");

Zatim se poziva metoda Add() kako bi se dodala jo etiri niza:


// Dodavanje nekoliko nizova
lbt.Add("Who");
l b t . Add( " I s " ) ;
lbt.Add(''lohn");
lbt.A dd("Galt");

Prije p rov jere v rije d n o sti m od ificira se druga v rijedn ost (s indeksom 1):
string subst = Universe";
l b t [i ] = subst;

K onano se svaka v rije d n o st p rik a z u je u petlji:


for (in t i = 0;i<lbt.GetNumEntries();i-H-)
{
Console.W riteLine("lbt[{o}]: { l } " , i , l b t [ i ] ) ;
} .

Indeksiranje na druge vrijednosti


U C # se za in deks k o le k cije n e m oraju u v ijek k o ristiti cje lo b ro jn e vrije d n o sti. P rilik o m
stvaran ja p rila go en e k la se k o le k cije i v lastito g in deksera m oete slo b o d n o stv o riti
in dekser k o je za in d e ks k o risti nizove i dru ge tipove. U stv ari, v rijed n o st se in d e ksa
m oe p re o p te re titi pa se k o le k cija m o e in d e k sira ti, n a prim jer, cje lo b ro jn im v rije d n o -
stim a ili v rije d n o stim a n izo va, ov isn o o p o treb a m a klijen ta .

U slu a ju n a eg p a d a ju eg p o p isa m od a n a m je p o treb n a m o g u n o st in d e k sira n ja


p ad aju eg p o p isa na tem elju n iz a . U p rim jeru 9 -1 0 p rika z an o je in d eksira n je n iz o m .
In dekser prvo poziva m eto du fin d S trin g (), p o m o n u m eto du ko ja v ra a zap is k o ji se
tem elji na v rije d n o sti p ru en o g n iza. P rim ije tit e te ka ko se p re op tereen i in d e k se r
m oe k o r is titi z aje d n o s in d eksero m iz p rim jera 9 -9 .

Poglavlje 9: Polja, indekseri i kolekcije | 189


P r im je r 9 - 1 0 . P r e o p t e r e i v a l e i n d e k s a

ftregion Using directives

using 5ystem;
using 5ystem.Collections.Ceneric;
using System.Text;

Kendregion

namespace Overloadedlndexer
{
// Pojednostavnjena ListBox kontrola
public class ListBoxTest
{
private string[] strings;
private int ctr = 0;

// Inicijalizira popis s nizovima


public ListBoxTest( params string[] initialStrings )
{
// Dodjeljuje prostor za nizove
strings = new String[256];

// Kopira nizove proslijeene konstruktoru


foreach ( string s in initialStrings )
{
strings[ctr++] = s;
}
}
// Dodaje jedan niz na kraj popisa
public void Add( string theString )
{
strings[ctr] = theString;
ctr++;
}
// Dozvoljava pristup kao kod polja
public string this[int index]
{
get
{
if ( index < 0 || index >= strings.Length )
{
// Obrauje lo indeks
}
return strings[index];
}
set
{
strings[index] = value;
}
}

190 j Programiranje C#
primjer 9-10. Preoptereimnje indeksa (nastavak)
private int findString( string searchString )
{
for ( int i = 0; i < strings.Length; i++ )
{
if ( strings[i].StartsWith( searchString ) )
{
return i;
}
}
return -i;

// Indeksiranje nizom
public string this[string index]
{
get
{
if ( index.Length == o )
{
// Obrauje lo indeks
}

return this[findString( index )];


}
set
{
strings[findstring( index )] = value;
}

// Objavljuje koliko nizova ima


public int GetNumEntries()
{
return ctr;
}'

public class Tester


{
static void Main()
{
// Pravi novi popis i inicijalizira ga
ListBoxTest lbt =
new ListBoxTest( "Hello", "World" );

// Dodaje nekoliko nizova


lbt.Add( "Who" );
lbt.Add( "Is" );
lbt.Add( "lohn" );
lbt.Add( "Galt" );

Poglavlje 9: Polja, indekseri i kolekcije | 191


Primjer 9-10. Preoptereivanje indeksa (nastavak)

I I Testira pristup
string subst = "Universe";
lbt[l] = subst;
lbt["Hel"j = "GoodBye";
U lbt["xyz"] = "oops";

I I Pristupa svim nizovima


-for ( int i = 0; i < lbt.GetNumEntries(); i++ )
{
Console.WriteLine( "lbt[{0}]: {i}", i, lbt[i] );
} H Kraj for
} II Kraj main
} II Kraj tester
}
Primjer 9-10 je gotovo identian primjeru 9-9, dodano je jedino preoptereeno indeksi-
ranje koje moe odgovarati nizu i metoda fmdString koja podrava taj indeks.
Metoda fmdString jednostavno iterira kroz nizove unutar myStrings dok ne pronae
niz koji poinje ciljnim nizom koji se koristi u indeksu. Kada pronae takav niz, vraa
indeks tog niza; u suprotnom vraa vrijednost - l .
U metodi Main() moemo vidjeti kako korisnik u indeks prosljeuje segment niza, kao
da se radi o cjelobrojnoj vrijednosti:
lbt["Hel"] = GoodBye";

Time se poziva preoptereeni indeks koji provodi osnovnu provjeru pogreaka (u


ovom sluaju provjerava sadri li proslijeeni niz barem jedno slovo) i zatim vrijed-
nost (Hel) prosljeuje do fmdString. Zatim vraa indeks koji se koristi za indeksiranje
polja myStrings:
return this[findString(index)];

Vrijednost set funkcionira na isti nain:


myStrings[findString(index)] = value;
a\

moete vidjeti uklanjanjem komentara iz sljedeeg reda u metodi


flain():
lbt["xyz"] = "oops";

Ispravno rjeavanje problema kad dobar niz nije pronaen ostavljeno


je , kako se kae, kao vjeba za itatelja. Poruku o pogreci moete pri-
kazati ili korisniku moete omoguiti oporavak od pogreke.

192 | Programiranje C#
Suelja kolekcija
NET kostur prua dva skupa standardnih suelja za enumeriranje i usporeivanje
kolekcija: tradicionalne (nesigurne za tipove) i nove, sigurne za tipove, generike kolek-
cije. U ovoj knjizi usredotoit emo se na nova suelja kolekcija sigurna za tipove jer
su takva suelja mnogo bolja.

Suelje ICollection bilo kojeg specifinog tipa moete deklarirati tako da opi tip u
deklaraciji suelja (<T>) zamijenite stvarnim tipom (na primjer, int ili string).
#>
N a p o m e n a z a C++ p r o g r a m e r e : generici jezika C # su po sintaksi i upo-
trebi slini predlocima u jeziku C++. Meutim, budui da se gene-
riki tipovi proiruju u specifian tip tijekom izvoenja, J IT prevoditelj
moe dijeliti kod izmeu vie instanci, te je kod znatno krai od onog
koji se generira upotrebom C++ predloaka.

Kljuna generika suelja kolekcija navedena su u tablici 9-2.'

T a b lica 9 - 2 . S u e l ja k o l e k c i j a

Suelje Svrha
ICollection<T> Osnovno suelje za generike kolekdje.

IEnumerator<T> Enumerira kolekdju s pomou iskaza foreach.


IEnumerable<T>

ICollection<T> Implementiraju ga sve kolekdje jer prua metodu CopyTo( ), kao i svojstva Count,
IsSynchronized i SyncRoot.

IComparer<T> Usporeuje dva objekta unutar kolekdje kako bi se kolekcija mogla sortirati.
lComparable<T>

lList<T> Koriste ga kolekdje koje se mogu indeksirati po poljima.

IDictionary<K,V> Koristi se za kolekdje temeljene na parovima klju/vrijednost kakva je Dict ionary.

Suelje IEnumerable<T>
Iskaz foreach u ListBoxTest moete podrati implementacijom suelja IEnumerable<T>
(pogledajte primjer 9-11). IEnumerable sadri samo jednu metodu, GetEnumerator()
koja vraa implementaciju suelja IEnumerator<T>. Jezik C# prua posebnu pomo pri
stvaranju enumeratora, koritenjem nove kljune rijei yield.

P r im j e r 9 - 1 1 . ListBox k a o k l a s a k o j a s e m o e e n u m e r i r a t i

ftregion Using directives

using System;
us ing System.Collections.Generic;

Radi kompatibilnosti s prethodnim inaicama u C # su dostupna inegenerika suelja (npr. ICollection,


IEnumerator), ali ona nisu obraena u ovoj knjizi jer su zastarjela.

Poglavlje 9: Polja, indekseri i kolekdje | 193


Primjer 9-11. List B ox kao klasa koja se moe enumerirati (nastavak)
using System.Text;

#endregion

namespace Enumerable
{
public class ListBoxTest : IEnumerable<String>
{
private string[] strings;
private int ctr = 0;
// Klase koje se mogu nenumerirati mogu vratiti enumerator
public IEnumerator<string> GetEnumerator()
{
foreach ( string s in strings )
{
yield return s;
}
}

// Inicijalizira popis s nizovima


public ListBoxTest( params stringf] initialStrings )
{
// Dodjeljuje prostor za nizove
strings = new String[8];

// Kopira nizove proslijeene konstruktoru


foreach ( string s in initialStrings )
{
strings[ctr++] = s;
}

// Dodaje niz na kraj popisa


public void Add( string theString )
{
strings[ctr] = theString;
ctr++;
}

// Dozvoljava pristup kao kod polja


public string this[int index]
{
get
{
if ( index < o |[ index >= strings.Length )
{
// Obrauje lo indeks
}
return strings[index];
}
set
{

194 | Programiranje C#
frimj e r ^ ~ ' - ' s* Box k ao klasa koja se moe enumerirati (nastavak)
strings[index] = value;

}
}
// Objavljuje koliko nizova ima
public int GetNumEntries()
{
return ctr;
}

public class Tester


{
static void Main()
{
// Pravi novi popis i inicijalizira
ListBoxTest lbt =
new ListBoxTest( "Hello", "World" );

// Dodaje nekoliko nizova


lbt.Add( "Who" );
lbt.Add( "Is" );
lbt.Add( "lohn" );
lbt.Add( "Galt" );

// Testira pristup
string subst = "Universe";
lbt[1 j = subst;

// Pristupa svim nizovima


foreach ( string s in lbt )
{
Console.WriteLine( Value: {o}, s );
}
}
}
}
Program poinje u metodi Main() stvaranjem novog objekta ListBoxTest i prosljei-
vanjem dva niza do konstruktora. Nakon stvaranja objekta stvara se polje Strings u
kojem ima prostora za osam nizova. etiri dodatna niza se dodaju s pomou metode
Add i drugi se niz aurira, ba kao u prethodnom primjeru.

Velika promjena u ovoj inaici programa je pozivanje petlje foreach koja uzima svaki
niz iz padajueg popisa. Petlja foreach automatski koristi suelje IEnumerable<T> pozi-
vajui metodu GetEnumerator().

Metoda GetEnumerator je deklarirana tako da vraa lEnumerator niza:


public IEnumerator<string> GetEnumerator()

Implementacija iterira kroz polje nizova, pruajui svaki niz:

Poglavlje 9: Polja, indekseri i kolekcije | 195


-foreach ( string s in strings )
{
yield return s;
}
Sve sto je potrebno za praenje redoslijeda elemenata, ponovno postavljanje iteratora
i tako dalje, prua kostur.

Ogranienja
Ponekad morate osigurati da su elementi koje dodajete generikom popisu u skladu s
odreenim ogranienjima (npr. da su izvedeni iz odreene osnovne klase ili da imple-
mentiraju odreeno suelje). U sljedeem primjeru implementirat emo pojednosta-
vljen jednostruko povezan popis koji se moe sortirati. Popis se sastoji od vorova
(Nodes) i svaki Node mora zadovoljavati uvjet da tipovi koji mu se dodaju implementi-
raju suelje IComparer. To moete uiniti sljedeim iskazom:
public class Node<T> :
IComparable<Node<T>> where T : IComparable<T>

Ovaj iskaz definira generiki Node koji sadri tip T. Node T implementira suelje ICompa-
rable<T>, to znai da se dva vora T mogu usporediti. Klasa Node je ograniena (where
T : IComparable<T>) na samo one tipove koji implementiraju suelje IComparable. Tip T,
stoga, moete zamijeniti bilo kojim tipom koji implementira suelje IComparable.
U primjeru 9-12 prikazana je cjelokupna implementacija koja je analizirana u sljede-
im odlomcima.

Primjer 9-12. Upotreba ogranienja


using System;
using System.Collections.Ceneric;

namespace UsingConstraints
{
public class Employee : IComparable<Employee>
{
private string name;
public Employee(string name)
{
this.name = name;
}
public override string To String O
{
return this.name;
}

U Implementira suelje
public int CompareTo(Employee rhs)
{
return this.name.CompareTo(rhs.name);
}

196 | Programiranje C#
primjer 9-12. Upotreba ogranienja (nastavak)

public bool Equals(Employee rhs)


{
return this.name ~ rhs.name;
}

I I vor mora implementirati IComparable od Node od T.


I I Ograniava Nodes da uzimaju samo stavke koje implementiraju IComparable
I I upotrebom kljune rijei where.
public class Node<T> :
IComparable<Node<T>> where T : IComparable<T>
{
// lanovi polja
private T data;
private Node<T> next = nuli;
private Node<T> prev = nuli;

// Konstruktor
public Node(T data)
{
this.data = data;
}

I I Svojstva
public T Data { get { return this.data; } }

public Node<T> Next


{
get { return this.next; }
}

public int CompareTo(Node<T> rhs)


{
// Funkcionira zbog ogranienja
return data.CompareTo(rhs.data);
}

public bool Equals(Node<T> rhs)


{
return this.data.Equals(rhs.data);
}

I I Metode
public Node<T> Add(Node<T> newNode)
{
if (this.CompareTo(newNode) > 0) I I ide prije mene
{
newNode.next = this; I I novi vor pokazuje na mene

// ako imam prethodno, postavi ih da pokazuju na


// novi vor kao svoj sljedei
i-f (this.prev != nuli)
{

Poglavlje 9; Polja, indekseri i kolekcije |


Primjer 9-12. Koritenje ogranienja (nastavak)
this.prev.next = newNode;
newNode.prev = this.prev;
}
// Postavlja prev u tekuem voru da pokazuje na novi vor
this.prev = newNode;

// Vraa newNode u sluaju da je to novo zaglavlje


return newNode;
}
else // Ide nakon mene
{ . . . .
// ako imam sljedei, prosljeuje novi vor
// na usporeivanje
if (this.next '.= nuli)
{
this.next.Add(newNode);
}
// Nemam sljedei pa postavi novi vor
// da bude moj sljedei i postavi njegov prev da pokazuje na mene.
else
{
this.next = newNode;
newNode.prev = this;
}

return this;
}
}
public override string To St ri ng O

string output = data.ToStringO;

if (next != nuli)

output += ", " + next.ToStringO;


}

return output;
}
} // Kraj klase

public class Linkedlist<T> where T : IComparable<T>


{
// Polja lanovi
private Node<T> headNode = nuli;

// Svojstva

// Indekser
public T this[int index]

198 | Programiranje C#
Primjer 9-12. Koritenje ogranienja (nastavak)
{
get
{
int ctr = o;
Node<T> node = headNode;

while (node != nuli && ctr <= index)

if (ctr == index)
{
return node.Data;
}
else
{
node = node.Next;
}

++ctr;
} // Kraj while
throw new ArgumentOutOfRangeException();
} // Kraj get
} // Kraj indeksera

// Konstruktor
public LinkedList()
{
}

// Metode
public void Add(T data)
{
if (headNode == nuli)
{
headNode = new Node<T>(data);

else
{
headNode = headNode.Add(new Node<T>(data));

}
public override string ToStringO

if (this.headNode != nuli)
{
return this.headNode.ToStringO;

else
{
return string.Empty;
}
}
}

Poglavlje 9: Polja, indekseri i kolekcije | 199


Primjer 9-12. Koritenje ogranienja (nastavak)
// Testiranje
class Test
{
// Ulazna toka
static void Main(string[] args)
{
// Pravi instancu, pokree metodu
Test t = new Test();
t.Run();
}
public void Run()

LinkedList<int> myLinkedList = new LinkedList<int>();


Random rand = new Random();
Console.Write("Adding: ");

for (int i = 0 ; i < 1 0 ; i++)


{
int nextlnt = rand.Next(lo);
Console.Write("{o} ", nextlnt);
myLinkedList.Add(nextInt);
}
Linkedlist<Employee> employees = new LinkedList<Employee>();
employees.Add(new Employee("iohn"));
employees.Add(new Employee("Paul"));
employees.Add(new Employee(''Ceorge"));
employees.Add(new Employee("Ringo"));

Console.Writeline("\nRetrieving collections..

Console.WriteLine(Integers: " + myLinkedList);


Console.WriteLine("Employees: " + employees);
}
}
}
Ovaj primjer poinje deklariranjem klase koja se moe staviti u povezani popis;
public class Employee : IComparable<Employee>

Ova deklaracija govori kako se objekti Employee mogu usporediti i vidimo da klasa
Employee implementira potrebne metode (CompareTo i Equals). Ove metode su sigurne
za tipove (one znaju da e proslijeeni parametar pripadati tipu Employee). U dekla-
raciji za, LinkedList odreeno je da taj popis sadri samo tipove koji implementiraju
suelje IComparable:
public class LinkedList<T> where T : IComparable<T>

te je mogunost sortiranja popisa zajamena. LinkedList sadri objekt tipa Node. Node
takoer implementira suelje IComparable i zahtijeva da svi objekti koje sadri kao
podatke i sami implementiraju suelje IComparable;

200 | Programiranje O
public class Node<T> :
lComparable<Node<T>> where T : IComparable<T>

...-Ova ogranienja implementaciju metode CompareTo iz Node ine sigurnom i jednostav-


, nom jer Node zna da e usporeivati druge vorove (Nodes) iji se podaci mogu uspo-
reivati:
public int CompareTo(Node<T> rhs)
{
// Funkcionira zbog ogranienja
return data.CompareTo(rhs.data);
}
rhs se ne mora testirati kako bi se vidjelo implementira li IComparable. Node smo ve
ograniili na takav nain da sadri samo podatke koji implementiraju suelje ICompa-
rable.

List<T>
Klasini problem s tipom Array je njegova fiksna veliina. Ako unaprijed ne znate
koliko e objekata polje sadrati, postoji rizik deklariranja premalog polja (pa e pone-
stati prostora) ili prevelikog polja (pa e se potroiti previe memorije).

Program koji piete e od korisnika moda zatraiti unos ili e uzimati podatke s Web
stranice. Kada pronae objekte (nizove, vrijednost itd.) dodat e ih u polje, ali vi neete
znati koliko ete objekata prikupiti u odreenoj sesiji. Klasino polje fiksne veliine
nije dobar izbor jer ne moete predvidjeti veliinu polja koje e vam biti potrebno.
Klasa L ist je polje ija se veliina prema potrebi dinamiki poveava. Poljima List se
moe upravljati s pomou raznih metoda i svojstava koja ona sadre. U tablici 9-3 pri-
kazane su najvanije metode i svojstva.

T a b lica 9 - 3 . M e t o d e i s v o j s t v a p o l j a L is t
p:.'
f Metoda ili svojstvo Svrha

Capacity Svojstvo za postavljanje ili uzim anje broja elem enata koje polje L i s t m oe sadrati. Ova se
vrijednost autom atski poveava ako broj elem enata premauje kapacitet. Ovu vrijednost m oete
postaviti kako biste sm anjili broj ponovnih alokacija te m oete pozvati m etodu T r i m ( ) za sma-
njivanje ove vrijednosti na stvarni broj elem enata C o u n t.
Count Svojstvo za uzim anje broja elem enata koje polje trenutno sadri.

Item() U zim a ili postavlja e le m e n t na odreeni indeks. To je zapravo indekser za klasu L i s t . 3


Add() Javna m etod a za dodavanje objekta u L i s t .

AddRange() Javna m etod a koja dodaje objekte iz suelja I C o l l e c t io n na kraj L i s t .

BinarySearch() Preoptereena javna m eto d a koja koristi binarno pretraivanje za pronalaenje odreenog
e lem en ta u sortiranom L i s t .

Clear() U klanja sve elem ente iz L i s t .

Contains() U tvr uje pripada li e le m e n t L i s t .

Poglavlje 9: Polja, indekseri i kolekcije | 201


Tablica 9-3. Metode i svojstva polja List (nastavak)

Metoda ili svojstvo Svrha

C o p y T o () Preoptereena javna metoda koja L i s t kopira ujednodimenzionalno polje.


E x is t s ( ) Utvruje pripada li element L i s t .
F in d ( ) Vraa prvu pojavu elementa u L i s t . r
i& s&

F in d A ll( ) Vraa sve navedene elemente u L i s t . t sf
-
G e tE n u m e ra to r ( ) Preoptereena javna metoda koja vraa enumeraciju za iteraciju kroz L i s t .

G e tR a n g e () Kopira raspon elemenata u novo polje L i s t .


In d e x O f ( ) Preoptereena javna metoda koja vraa indeks prve pojave vrijednosti.
In s e r t( ) Umee element u L i s t .
In s e r t R a n g e ( ) Elemente kolekcije umee u polje L i s t .
L a s t In d e x O f ( ) Preoptereena javna metoda koja vraa indeks posljednje pojave vrijednosti u L i s t .

R em ove() Uklanja prvu pojavu odreenog objekta.

R em o ve A t() Uklanja element na odreenom indeksu.

Rem oveR ange() Uklanja raspon elemenata.


R e v e rs e ( ) Elemente u L i s t postavlja obrnutim redoslijedom.
S o r t() Sortira L i s t .

T o A r ra y ( ) Kopira elemente L i s t u novo polje.


T r im T o S iz e ( ) Postavlja kapacitet stvarnog broja elemenata u L i s t .

1 Idiom u FCL-u je da prui element I tem za klase kolekcija koji je u Cd implementiran kao indekser.

Prilikom stvaranja List ne definira se broj elemenata koje e ono sadrati. Elementi se
u List dodaju s pomou metode Add(), a polje samo vodi brigu o broju svojih eleme-
nata, kao to je prikazano u primjeru 9-13.

P r i m j e r 9 - 1 3 . R a d s p o l j e m L is t

ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ListCollection
{
// Jednostavna klasa koja e uvati u List
public class Employee
{
private int empID;

public Employee( int empID )


{
this.empID = empID;

202 | Programiranje C#
i p rim jer 9 - 1 3 . R a d s p o l j e m L is t ( n a s ta v a k )

}
public ovferride string ToStringO
{
return empID.ToStringO;
}
public int EmpID
{
get
{
return empID;
}
set
{
empID = value;
}
}
}
public class Tester
{
static void Main()
{

List<Employee> empList,= new List<Employee>();


List<int> intList = new List<int>();

// Popunjava List
for ( int i = 0; i < 5; i++ )
{
empList.Add( new Employee( i + 1 0 0 ) );
intList.Add( i * 5 );
}

// Ispisuje sav sadraj


for ( int i = 0; i < intList.Count; i++ )
{
Console.Write( "{0} ", intList[i],ToStringO );
}

Console.WriteLine( "\n" );

// Ispisuje sav sadraj Employee _List


for ( int i = 0; i < empList.Count; i++ )
{
Console.Write( ''{0} ", empList[i].ToStringO );
}

Console.WriteLine( "\n" );
Console.WriteLine( "empList.Capacity: {0}",
empList.Capacity );
}
}
}

Poglavlje 9: Polja, indekseri i kolekcije | 203


S klasom Array definirate koliko e objekata polje sadravati. Ako pokuati dodati vie | i
objekata od tog broja, klasa Array e izbaciti iznimku. Ako koristite List ne morate W
deklarirati broj objekata koje e sadravati. L ist ima svojstvo Capacity koje predstavlja f|
broj elemenata koje L ist moe spremiti: I
public int Capacity { get; set; } J

Podrazumijevani kapacitet je 16. Ako dodate sedamnaesti element, kapacitet se auto- f


matski udvostruuje na 32. Ako petlju for promijenite na: |
for (int i = 0;i<l7;i++)

izlaz e izgledati ovako:


0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
empArray.Capacity: 32

Kapacitet moete runo postaviti na bilo koji broj jednak ili vei od broja elemenata.
Ako ga postavite na broj manji od broja elemenata, program e izbaciti iznimku tipa
Argument0ut0fRangeException.

Implementiranje suelja IComparable


Poput svih ostalih kolekcija List implementira metodu Sort() koja omoguava sortira-
nje svih objekata koji implementiraju suelje IComparable. U sljedeem emo primjeru
modificirati objekt Employee kako bi implementirao suelje IComparable:
public class Employee : IComparable<Employee>

Za implementaciju suelja IComparable<Employee> objekt Employee mora pruati


metodu CompareTo():
public int CompareTo(Employee rhs)

return t his.emp ID . C om pa re To ( r . em p I D) ;

Metoda CompareToO uzima Employee kao parametar. Znamo da se radi o objektu


Employee jer je to kolekcija sigurna za tipove. Tekui objekt Employee mora se uspo-
rediti s objektom Employee koji je proslijeen kao parametar i vratiti vrijednost -l
ako je tekui objekt manji od parametra, 1 ako je vei od parametra i 0 ako je jednak
parametru. Objekt Employee mora sam zadati to znai smaller than, greater than i
equal U ovom primjeru se usporedba delegira lanu empld. lan empld je int i koristi
podrazumijevanu metodu CompareToO za cjelobrojne tipove koja ce usporediti dvije
vrijednosti.

Klasa System. Int32 implementira IComparable<Int32> tako d a o dg o v o r -


nost za u s p o r e d b u m o e t e delegirati cjelobrojnim vrijednostima.

204 ( Programiranje C#
f^da ste spremni za sortiranje popisa zaposlenika empList. Kako biste provjerili funk-
cionira 1' sortiranje, trebate cjelobrojne vrijednosti i instance Employee dodati odgo-
varajuim poljima s nasuminim vrijednostima. Za stvaranje nasuminih vrijedno- s
t i instancirajte objekt klase Random. Za generiranje nasuminih vrijednosti pozovite
metodu Next() na objektu Randomkoja e vratiti pseudonasumini broj. Metoda Next()
% se preoptereuje, jedna inaica omoguava prosljeivanje cjelobrojne vrijednosti koja
t predstavlja najvei eljeni nasumini broj. U ovom se primjeru prosljeuje vrijednost
1 10 za generiranje nasuminog broja izmeu 0 i 10:
Random r = new Random();
r.Next(l0);

U primjeru 9-14 stvoreno je polje cjelobrojnih vrijednosti i polje Employee, oba se ispu-
njavaju nasuminim brojevima i njihove se vrijednosti ispisuju. Oba se polja zatim
sortiraju i ispisuju se nove vrijednosti.

P rim jer 9 - 1 4 . S o r t ir a n je p o l j a c j e l o b r o jn i h v r i je d n o s t i i p o lja E m p lo y e e

#region Using directives

using System;
using Sy5tem.Collections.Generic;
using System.Text;

.#endregion

namespace IComparable
{
// Jednostavna klasa koja e uvati u polju
public class Employee : IComparable<Employee>
{
private int empID;

public Employee( int empID )


('
this.empID = empID;
}

public override string To Strin g O


{
return empID.ToStringO;
}
public bool Equals( Employee other )
{
if ( this.empID == other.empID )
{
return true;
}
else
{
return false;
}

Poglavlje 9: Polja, indekseri i kolekcije | 205


Primjer 9-14. Sortiranje polja cjelobrojnih vrijednosti i polja Employee (nastavak)

// Comparer delegira natrag do Employee


// Employee koristi podrazumijevanu metodu
// CompareTo cjelobrojnih vrijednosti

public int CompareTo( Employee rhs )


{
return this.empID.CompareTo( rhs.empID );
}
}
public class Tester
{
static void Main()

List<Employee> empArray = new List<Employee>();


List<Int32> intArray = new List<Int32>();

// Generira sluajne brojeve za


II cjelobrojne vrijednosti
// i identifikatore zaposlenika

Random r = new Random();

// Popunjava polje
tor ( int i = 0; i < 5; i++ )
{
// Dodaje sluajni identifikator zaposlenika
empArray.Add( new Employee( r.Next( 10 ) + 100 ) );

// Dodaje sluajnu cjelobrojnu vrijednost


intArray.Add( r.Next( 10 ) );
}
// Prikazuje sav sadraj polja cjelobrojnih vrijednosti
for ( int i = 0; i < intArray.Count; i++ )
{
Console.Write( "{0} , intArray[i].To5tring() );
}
Console.WriteLine( "\n );

// Prikazuje sav sadraj polja Employee


for ( int i = 0; i < empArray.Count; i++ )
{
Console.Virite( "{0} ", empArray[i].To5tring() );
}
Console.WriteLine( "\n" );

// Sortira i prikazuje polje cjelobrojnih vrijednosti


intArray.Sort();
for ( int i = o; i < intArray.Count; i++ )
{
Console.Write( "{0} ", intArray[i].ToString() );

206 | Programiranje Q
p r im jer 9 -1 4 . S o r t ir a n je p o l j a c j e lo b r o jn ih v r ijed n o s t i i p o l ja E m p lo y e e ( n a s ta v a k )

}
Console.WriteLine( "\n" );

// Sortira i prikazuje polje zaposlenika


Employee.EmployeeComparer c = Employee.GetComparer();
empArray.Sort(c);

empArray.Sort();

// Prikazuje sav sadraj polja Employee


-for ( int i = 0; i < empArray.Count; i++ )
{
Console.Write( "{0 } ", empArray[i].ToString() );
}
Console.WriteLine( "\n" );

U izlazu se moe vidjeti kako su polje cjelobrojnih vrijednosti i polje Employee generi-
rani s nasuminim brojevima. Nakon sortiranja u izlazu se vidi kako su vrijednosti
ispravno poredane.

Implementacija suelja IComparer


Kada na List pozovete metodu Sort() pozivae podrazumijevana implementacija sue-
lja IComparer koja koristi OuickSort za pozivanje IComparable implementacije metode
CompareTo() za svaki element u List.

Moete slobodno stvoriti svoju implementaciju IComparer to je preporuljivo ako


elite kontrolirati nain na koji se definira redoslijed sortiranja. U sljedeem se pri-
mjeru polju Employee dodaje drugo polje yearsOfSvc. Potrebna je mogunost sortiranja
objekata Employee u List po oba polja, empID i yearsOfSvc.

Kako bismo to postigli, stvorit emo prilagoenu implementaciju suelja IComparer


koju emo zatim proslijediti do metode Sort() od List. Klasa EmployeeComparer suelja
IComparer zna za Employee objekte i kako ih sortirati.
EmployeeComparer ima svojstvo WhichComparison tipa Employee.EmployeeComparer.Com-
parisonType:
public Employee.EmployeeComparer.ComparisonType
WhichComparison
{
getfreturn whichComparison;}
set{whichComparison = value;}
}
ComparisonType je enumeracija s dvije vrijednosti, empID ili yearsOfSvc (oznaava kako
elite sortirati po identifikatoru zaposlenika ili prema godinama staa):

Poglavlje 9: Polja, indekseri i kolekcije | 207


public enum ComparisonType
{
EmpID,
VearsOfService
};
Prije pozivanja metode S o rt() stvara se instanca klase EmployeeComparer i postavlja se 3
njeno svojstvo ComparisonType:
Employee.EmployeeComparer c = Employee.GetComparer();
c.WhichComparison=Employee.EmployeeComparer.ComparisonType.EmpID;
empAiray.Sort(c);

Kada pozovete metodu S o rt() L ist na EmployeeComparer poziva metodu Compare


to delegira usporeivanje metodi Employee.ComapareTo() i prosljeuje svojstvo
WhichComparison:
public int Compare( Eniployee lhs, Employee rhs )
{
return lhs.CompareTo( rhs, WhichComparison );
>
Objekt Employee mora implementirati prilagoenu inaicu metode CompareTo() koja
preuzima usporedbu i usporeuje objekte:
public int CompareTo(
Employee rhs,
Employee.EmployeeComparer.ComparisonType which)
{
switch (which)
{
case Employee.EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo(rhs.empID);
case Employee.EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo(rhs,yearsOfSvc);
}
return 0;
}
Potpuni izvor ovog primjera naveden je u primjeru 9-15. Polje cjelobrojnih vrijednosti
je uklonjeno kako bi primjer bio to jednostavniji, a izlaz metode ToStringO je unapri-
jeen kako biste mogli vidjeti rezultat sortiranja.

P r im j e r 9 - 1 5. S o r t i r a n j e p o l j a p r e m a I D - o v i m a z a p o s l e n i k a i g o d i n a m a s t a a

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

ttendregion

namespace IComparer
{
public class Employee : IComparable<Employee>

208 1 Programiranje C#
ifritnjer 9-15- Sortiranje polja prema ID-ovima zaposlenika godinama staa (nastavak)

private int empID;

private int yearsOfSvc = 1;

public Employee( int empID )

this.empID = empID;

public Employee( int empID, int yearsOfSvc )


{
this.empID = empID;
this.yearsOfSvc = yearsOfSvc;
}

public override string ToStringO


{
return "ID: " + empID.ToStringO +
Vears of Sve: " + yearsOfSvc.ToStringO;
}

public bool Equals( Employee other )


{
if ( this.empID == other.empID )
{
return true;
}
else
{
return false;
}
}

/'/ Statika metoda za uzimanje objekta Comparer


public static EmployeeComparer GetComparerO
{
return new Employee.EmployeeComparer();
}

// Comparer delegira natrag Employee.


// Employee koristi podrazumijevanu
// metodu CompareTo cjelobrojnih vrijednosti
public int CompareTo( Employee rhs )
{
return this.empID.CompareTo( rhs.empID );
}

// Posebna implementacija koju e pozvati prilagoeni usporeivao


public int CompareTo(
Employee rhs,
Employee.EmployeeComparer.ComparisonType which )

Poglavlje 9; Polja, indekseri i kolekcije | 209


Primjer 9-15. Sortiranje polja prema ID-ovima zaposlenika i godinama staa (nastavak)
switch ( which )
{
case Employee.EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo( rhs.empID );
case Employee.EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo( rhs.yearsOfSvc );
}
return 0;

// Ugnijeena klasa koja implementira IComparer


public class EmployeeComparer : IComparer<Employee>
{

// Privatna varijabla stanja


private Employee.EmployeeComparer.ComparisonType
whichComparison;

// Enumeracija tipova za usporeivanje


public enum ComparisonType
{
EmpID,
Yrs
};

public bool Equals( Employee lhs, Employee rhs )


{
return this.Compare( lhs, rhs ) == 0 ;
}

public int GetHashCode(Employee e)


{
return e.GetHashCode();
}

// Govori objektima Employee da se usporede


.public int Compare( Employee lhs, Employee rhs )
{
return lhs.CompareTo( rhs, WhichComparison );
}

public Employee.EmployeeComparer.ComparisonType
WhichComparison
{
get{return whichComparison;}
set{whichComparison = value;}
}
}
}
public class Tester
{

210 | Programiranje C#
Primjer 9-15. Sortiranje polja prema ID-ovima zaposlenika i godinama staa (nastavak)
static void Main()
{
List<Employee> empArray = new List<Employee>();

// Generira sluajne brojeve za


// za cjelobrojne vrijednosti i
// identifikatore zaposlenika
Random r = new Random();

// Popunjava polje
for ( int i = 0; i < 5; i++ )
{
// add a random employee id

empArray.Add(
new Employee(
r.Next( 10 ) + ioo, r.Next( 20 )

);
}

// Prikazuje sav sadraj polja Employee


for ( int i = 0; i < empArray.Count; i++ )

^ Console.Write( "\n{o} ", empArray[i].ToStringO );

Console.WriteLine( "\n" );

// Sortira i prikazuje polje zaposlenika


Employee.EmployeeComparer c = Employee.GetComparer();
c.WhichComparison =
Employee.EmployeeComparer.ComparisonType.EmpID;
empArray.Sort( c );

// Prikazuje sav sadraj polja Employee


for ( int i = 0; i < empArray.Count; i++ )

Console.Write( "\n{o} ", empArray[i].ToStringO );

Console.WriteLine( "\n" );

c.WhichComparison = Employee.EmployeeComparer.ComparisonType.Yrs;
empArray.Sort( c );

for ( int i = 0; i < empArray.Count; i++ )

^ Console.Write( "\n{o} ", empArray[i].ToStringO );

Console.WriteLine( "\n" );

}'
}

Poglavlje 9; Polja, indekseri i kolekcije | 211


U prvom bloku izlaza moete vidjeti Employee objekte redoslijedom kojim su dodani u H
L ist. Vrijednosti identifikatora zaposlenika i godine staa poredane su nasumino U II
drugom se bloku vide rezultati sortiranja prefha identifikatoru zaposlenika, a u treem '%
se vide rezultati sortiranja prema godinama staa. l|

Ako stvarate kolekcije kao u primjeru 9-11 i elite implementirati sue-


lje IComparer, vjerojatno ete koritenjem ranije opisanih ogranienja
morati osigurati da svi tipovi smjeteni u polje implementiraju suelje
IComparer (kako bi se mogli sortirati).

Redovi
Red (engl. queue ) predstavlja kolekciju koja funkcionira na naelu prvi-unutra, prvi-
van (engl. first in, first out, FIFO). Obino se usporeuje s redom osoba koje ekaju
na blagajni kako bi kupili kartu. Prva osoba u redu trebala bi biti i prva osoba koja
kupuje kartu i izlazi iz reda.
Red je kolekcija koju je zgodno koristiti ako upravljate ogranienim resursom. Na
primjer, moda ete trebati poslati poruke resursu koji istovremeno moe obraditi
samo jednu poruku. U tom biste sluaju stvorili red poruka kako biste svojim klijen-
tima mogli rei: Vae su nam poruke bitne i zato se obrauju redoslijedom kojim su
primljene."
Klasa Oueue ima razne metode i svojstva koja su prikazana u tablici 9-4.

Tablica 9-4. M etode i svojstva klase Queue

Metoda ili svojstvo Svrha

Count Javno svojstvo koje uzima broj elemenata u Oueue.

Clear() Uklanja sve objekte iz Oueue.

Contains() Odreuje pripada li red Oueue.

CopyTo() Kopira elemente Oueue u postojee jednodimenzionalno polje.

Dequeu e() Brie i vraa objekt na poetku Oueue.

Enqueue() Dodaje objekt na kraj Oueue.

GetEnumerato r O Vraa enumerator Oueue.

Peek() Vraa objekt na poetku Oueue, ali ga ne brie.

ToArray() Kopira element u novo polje.

Elemente moete dodati u red naredbom Enqueue, a iz reda ih moete ukloniti nared-
bom Dequeue ili s pomou enumeratora. Te su operacije prikazane u primjeru 9-16.

212 | Programiranje C#
P rim jer 9-16. R ads redovima
(tregion Using direct ives

using $ystem;
using System.Collections.Ceneric;
using System.Text;

#endregion

namespace Oueue
{
public class Tester
{
static void Main ()
{

Oueue<Int32> intOueue = new Oueue<Int3 2 >();

U Popunjava polje
for ( int i = O; i < 5 ; i++ )

intOueue.Enqueue( i * 5 );

// Prikazuje red
Console.Write( "intOueue values:\t" );
PrintValues( intOueue );

// Brie element iz reda.


Console.WriteLine(
"\n(Dequeue)\t{0 }", intOueuee.Dequeue() );

// Prikazuje red.
Console.Write( "intOueue values:\t" );
PrintValues( intOueue );

// Brie jo jedan element iz reda.


Console.WriteLine(
"\n(Dequeue)\t{o}", intOueuee.Dequeue() );

// Prikazuje red.
Console.Write( "intOueue values:\t" ) ;
PrintValues( intQueue );

I I Pregleda prvi element u redu


I I ali ga ne uklanja.
Console.WriteLine(
"\n(Peek) \t{o}", intOueuee.Peek() );

I I Prikazuje red.
Console.Write( "intQueue values:\t" );

Poglavlje 9: Polja, indekseri i kolekcije | 213


Primjer 9-16. Rad s redovima (nastavak)
P r in t V a lu e s ( in tO u e u e ) ;

public static void PrintValues(IEnumerable<Int32> myCollection)


{ :
IEnumerator<Int32> myEnumerator *
myCollection.CetEnumerator();
while ( myEnumerator.MoveNext() )
Console.Write( "{0} , myEnumerator.Current );
Console.WriteLine();
}
}
}
U ovom primjeru je L ist zamijenjeno s Oueue. Iz primjera je uklonjena klasa Employee
kako bismo utedjeli na prostoru, no naredbu Enqueue moete koristiti i za korisniki
definirane objekte.
U izlazu moete vidjeti kako redanje objekte dodaje u Oueue, a Dequeue se poziva kako
bi se objekt vratio i obrisao iz Oueue. Klasa Oueue ima i metodu Peek() koja omoguava
da prvi element vidite bez uklanjanja.
Kako klasa Oueue podrava enumeriranje, moete ju proslijediti do metode PrintVa-
lues koja je pruena kao suelje IEnumerable. Ova pretvorba je implicitna. U metodi
PrintValues pozivate GetEnumerator, jedina metoda svih IEnumerable klasa. To vraa
IEnumerator koje zatim moete koristiti za enumeraciju svih objekata u kolekciji.

Stogovi
Stog (engl. stack ) je kolekcija koja funkcionira na naelu posljednji-unutra, prvi-van
(engl. last in, first out, LIFO), poput hrpe tanjura na vedskom stolu ili hrpe novia
na vaem stolu. S gomile ete najprije uzeti tanjur koji stoji na vrhu (a on je posljednji
dodan na hrpu).
Osnovne metode za dodavanje na stog i uklanjanje sa stoga su Pushf) i Pop(). Stack,
kao i Oueue, nudi i metodu Peek(). Najvanije metode i svojstva klase Stack prikazani
su u tablici 9-5.

Tablica 9-5. Metode i svojstva stoga

Metoda ili svojstvo Svrha

C ount Javno svojstvo koje uzima broj elemenata u S t ac k.


C le a r ( ) Uklanja sve objekte iz S ta c k .

C lo n e ( ) Stvara plitku kopiju.

C o n ta in s ( ) Utvruje pripada li element S ta c k .

214 | Programiranje C#
tablica 9 -5 . Metode i svojstva stoga (nastavak)

Sfioda ili svojstvo Svrha

copyT () Kopira elemente S ta c k u postojee jednodimenzionalno polje.


CetEnumerator() Vraa enumerator S tack.

PeekO Vraa objekt svrha S ta c k , ali ga ne uklanja.

PopO Vraa i brie objekt svrha S tack .


Push() Dodaje objektna vrh S tack.
ToArray() Kopira elemente u novo polje.

Tipovi L ist, Oueue i Stack sadre preoptereene metode CopyTo() i ToArray() za kopira-
nje elemenata u polje. U sluaju Stack metoda CopyTo() e kopirati elemente u postojee
jednodimenzionalno polje, prepisujui sadraj polja poevi od indeksa koji zadate.
Metoda ToArray() vraa novo polje u kojem se nalazi sadraj elemenata stoga. To je
prikazano u primjeru 9-17.

P rim jer 9 -1 7 . R a d s a s t o g o m

ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Stack
{
public class Tester
{
static void Main()
{
Stack<Int3 2 > intStack = new Stack<Int32>();

// Popunjava polje

for ( int i = 0; i < 8; i++ )


{
intStack.Push( i * 5 );
}

// Prikazuje stog.
Console.Write( "intStack values:\t" );
PrintValues( intStack );

// Brie element sa stoga.


Console.WriteLine( "\n(Pop)\t{o}",
intStack.Pop() );

// Prikazuje stog.

Poglavlje 9: Polja, indekseri i kolekcije | 215


Primjer 9-17. Rad sa stogom (nastavak)
Co ns ol e. Wr it e( "intStack values:Yt" );
PrintValuesj intStack );

// Brie jo jedan element sa stoga.


Console.WriteLine( "\n(Pop)\t{0>",
intStack.Pop() );

// Prikazuje stog.
Console.Write( "intStack valuesrVt" );
PrintValues( intStack );

// Prikazuje prvi element na stogu


// ali ga ne brie.
Console.WriteLine( "\n(Peek) Vt{0} ,
intStack.Peek() );

// Prikazuje stog.
Console.Write( "intStack values'.Vt" );
1 PrintValuesj intStack );

// Deklarira objekt polja koji e sadravati


// 12 cjelobrojnih vrijednosti
int[] targetArray = new int[i2];

for (int i = 0; i < targetArray.Lengthj i++)

targetArray[i] = i * 100 + loo;

// Prikazuje vrijednosti odredine instance Array.


Console.WriteLine( "\nTarget array: );
PrintValues( targetArray );

// Kopira cjelokupni izvorni Stack u odredinu


// instancu Array s poetkom na indeksu 6.
intStack.CopyTo( targetArray, 6 );

// Prikazuje vrijednosti odredine instance Array.


Console.WriteLine( "\nTarget array after copy: );
PrintValues( targetArray );

}
public static void PrintValues(
IEnumerable<IntB2> myCollection )

^ IEnumerator<Int32> enumerator =
myCollection.GetEnumerator();
while ( enumerator.MoveNext() )
Console.Write( "{0} ", enumerator.Current );
Console.WriteLine();

216 ) Programiranje C#
Izlazu se vidi kako se elementi dodani stogu uklanjaju obrnutim redoslijedom.
||nakCopyTo() moe se vidjeti pregledom ciljnog polja prije i nakon pozivanja metode
|jjyTo()- Elementi polja se prepisuju poevi od zadanog indeksa (6).

.jenici
? R j e n i k (engl. dictionary) je kolekcija koja klju povezuje s vrijednosti. U rjeniku odre-

iSltJenogjezika se rije (klju) povezuje s odgovarajuom definicijom (vrijednost).


Vrijednost rjenika moete vidjeti ako zamislite kako vam je potreban popis glavnih
f t gradova amerikih saveznih drava. To moete postii tako da ih stavite u polje:
stringf] stateCapitals = new string[50];

i P o lje stateCapitals e sadrati pedeset glavnih gradova. Svakom se glavnom gradu


pristupa kao pomaku u polju. Na primjer, ako elite pristupiti glavnom gradu drave
Arkansas, potrebno je znati kako je Arkansas etvrta savezna drava prema abeced-
nom redu:
string capitalOfArkansas = 5tateCapitals[3];

Koritenje polja za pristup glavnim gradovima prilino je nepraktino. Naposljetku,


ako mi je potreban glavni grad drave Massachusetts, ne mogu jednostavno odrediti
kako je Massachusetts 21. drava po abecedi.
Mnogo bi praktinije bilo glavni gradi povezati s nazivom drave. Rjenik vam omo-
guava da vrijednost (u ovom sluaju, glavni grad) spremite pod kljuem (u ovom
sluaju, naziv drave).
Rjenik koji nudi .N ET kostur moe bilo kakav klju (niz, cjelobrojnu vrijednost,
objekt itd.) povezati s bilo kojom drugom vrijednosti (nizom, cjelobrojnom vrijedno-
sti, objektom itd.). Obino je klju, razumljivo, prilino kratak, dok je vrijednost pri-
lino sloena.

Dobar se rjenik mora odlikovati jednostavnim dodavanjem i brzim vraanjem vrijed-


nosti (pogledajte tablicu 9-6).

Tablica 9 - 6 . M etode i svojstva rje n ika

( Metoda ili svojstvo Svrha

Count Javno svojstvo koje uzima broj elemenata u Dictionaxy.

It:em() lndekserzaDictionary.

Keys Javno svojstvo koje uzima kolekciju s kljuevima za Dictionary (pogledajte i svojstvo Values).

Values Javno svojstvo koje uzima kolekciju koja sadri vrijednosti u Dict ionary (pogledajte i svojstvo
Keys).

Add ( ) Dodaje unos s odreenim kljuem (Key) i vrijednosti (Value).

Clear( ) Uklanja sve objekte izDictionary.

Poglavlje 9: Polja, indekseri i kolekcije | 217


Tablica 9-6. Metode i svojstva rjenika (nastavak)

Metoda ili svojstvo Svrha f


ContainsKey() Utvruje pripada li odreeni klju D ic t io n a r y .

ContainsValue() Utvruje pripada li odreena v r ije d n o s tO ic tio n a ry .


GetEnumerator() Vraa enumerator za D ic t io n a r y .

GetObjectData() Implementira suelje I S e r i a l i z a b l e i vraa podatke potrebne za serijalizaciju D ic t io n a r y .


Remove() Uklanja unos s odreenim kljuem.

Klju u klasi Dictionary moe biti primitivni tip ili instanca korisniki definiranog
tipa (objekt). Objekti koji se koriste kao kljuevi u Dictionary moraju implementirati
metode GetHashCode() i Equals. U veini sluajeva moete jednostavno koristiti nasli-
jeenu implementaciju izO bject.

IDictionary<K,V>
Rjenici implementiraju suelje IDictionary<K,V> (K oznaava tip kljua, a V oznaava
tip vrijednosti). IDictionary prua javno svojstvo Item. Ono vraa vrijednost sa zada-
nim kljuem. Deklaracija svojstva Item u C# glasi:
V[K key]
{get; set;}

U C# svojstvo Item je implementirano s pomou indeksnog operatora ([]). Stoga ele-


mentima svih Dictionary objekata moete pristupiti koristei sintaksu pomaka, kao
da se radi o polju.
U primjeru 9-18 prikazano je dodavanje elemenata u Dictionary koji se zatim vraaju
s pomou svojstva Item.

P rim je r 9-18 . Svojstvo Ite m kao opera tor pom aka


namespace Dictionary
{
public class Tester
{
static void Main()
{
// Pravi i inicijalizira novi rjenik.
Dictionary<string,string> Dictionary =
new Dictionaiy<string,string>();
Dictionary.Add("000440312", "3esse Liberty");
Dictionary.Add("000123933", "Stacey Liberty");
Dictionary.Ad d("000145938", "3ohn Galt");
Dictionary.Add("000773394", "Ayn Rand");

// Pristupa zadanoj stavci


Console.WriteLine("myDictionary[\"000145938\"]: {0}",
Oictionary["000145938"]);
}
}
}

218 | Programiranje C#
ijjrimjer 9-18 poinje instanciranjem novog Dictionary. Tip kljua i vrijednosti dekla-
gjranisu kao string.

jJDictionary se dodaju etiri para klju-vrijednost. U ovom se primjeru broj zdravstvenog


osiguranika dodaje imenu osobe (namjerno su koriteni lani brojevi osiguranika).

iNakon dodavanja elemenata odreenom se unosu u rjeniku pristupa koritenjem


broja osiguranika kao kljua.

Ako kao klju koristite referentni tip i taj tip moe mutirati (nizovi ne
mogu mutirati), ne smijete mijenjati vrijednost objekta kljua nakon
to ga ponete koristiti u rjeniku.

Ako, na primjer, kao klju koristite objekt Employee i promijenite iden-


tifikator zaposlenika, doi e do problema ako metode Equals i Get-
ElashCode, koje rjenik konzultira, koriste to svojstvo.

Poglavlje 9: Polja, indekseri i kolekcije | 219


POGLAVLJE 10
Nizovi i regularni izrazi

Nekad su ljudi kupovali raunala iskljuivo zbog obrade brojanih vrijednosti. Prva
raunala su se najprije koristila za izraun putanje projektila (iako neki nedavno obja-
vljeni dokumenti otkrivaju kako su se koristila i za deifriranje). U svakom sluaju,
neko se programiranje uilo na katedrama za matematiku velikih sveuilita, a infor-
matika se smatralo matematikom disciplinom.
Danas se veina programa bavi nizovima slova, a ne nizovima brojeva. Ti se nizovi
obino koriste za obradu rijei, rad s dokumentima i stvaranje Web-stranica.
C # prua ugraenu podrku za potpuno funkcionalan tipa string. to je jo vanije,
C # nizove tretira kao objekte koji uahuruju sve metode za manipulaciju, sortiranje i
pretraivanje koje se obino primjenjuju na niz slova.

Napomena za C++ programere: u C # niz je tip prve klase, a ne polje


znakova.
_LflS*
Sloene operacije s nizovima i usporeivanje uzoraka potpomognuti su regularnim
izrazima (engl. regular expressions ). C # kombinira snagu i sloenost sintakse regular-
nih izraza koja je izvorno postojala samo u jezicima za rad s nizovima, kao to su awk
i Perl, s potpuno objektno orijentiranim dizajnom.
U ovom ete poglavlju nauiti kako se u C # radi s tipom string i klasom System. String
.N ET kostura. Saznat ete i kako se izdvajaju podnizovi, kako se manipulira nizovima
i kako se oni ulanavaju te kako se novi nizovi mogu izraditi s pomou klase String-
Builder. Uz to ete nauiti kako se klasa RegEx moe koristiti za usporeivanje nizova
na temelju sloenih regularnih izraza
Nizovi
c # nizove tretira kao tipove prve klase koji su fleksibilni, moni i jednostavni za
koritenje.

U programiranju u jeziku C # za tip kostura (npr. int ili Int32) obino


se koristi C # alias, ali moete slobodno koristiti i temeljni tip. C # pro-
grameri stoga strin g (malo poetno slovo) i temeljni tip kostura String
(veliko poetno slovo) koriste naizmjenino.

Deklaracija klase String glasi:


public sealed class String :
IComparable<T>, ICloneable, IConvertible, IEnumerable<T>

Ova deklaracija otkriva kako se radi o zapeaenoj klasi, to znai da izvoenje iz


klase String nije mogue. Klasa takoer implementira etiri sistemska suelja - Icom-
parable<T>, ICloneable, IConvertible i IEnumerable<T> - koja odreuju funkcionalnosti
koje klasa String dijeli s ostalim klasama u .NET kosturu.

Svaki objekt strin g je nepromjenjivi slijed Unicode znakova. inje-


nica da je String nepromjenjiva klasa znai da metode koje naizgled
mijenjaju niz zapravo vraaju promijenjenu kopiju. Izvorni niz ostaje
netaknut u memoriji dok se ne sakupi kao otpad. To moe utjecati na
izvedbu; ako planirate koristiti veliki broj nizova koji se ponavljaju,
radije koristite klasu StringBuilder koja je opisana kasnije u ovom
poglavlju.

Kao to smo vidjeli u poglavlju 8, suelje IComparable<T> implementiraju tipovi ije


se vrijednosti mogu poredati. Nizovi se, na primjer, mogu poredati po abecedi. Svaki
se niz moe usporediti s nekim drugim nizom kako bi se odredio njihov redoslijed
u poredanom popisu. Klase IComparable implementiraju metodu CompareTo. Suelje
IEnumerable takoer je opisano u poglavlju 9, a omoguava vam koritenje konstrukta
foreach za enumeraju niza string kao kolekcije chars.

Objekti ICloneable mogu stvoriti nove instance s istom vrijednosti koju imaju izvorne
instance. U ovom je sluaju mogue klonirati niz kako bi se proizveo novi niz koji sadri
iste vrijednosti kao izvorni niz. Klase ICloneable implementiraju metodu Clone().

Redanje niza je jedna od brojnih leksikih operacija koje djeluju na vrijednost niza i u obzir uzimaju kul-
turoloke informacije koje se temelje na eksplicitno deklariranoj kulturi ili na implicitnoj tekuoj kulturi..
Stoga, ako je trenutna kultura American English (kao to je pretpostavljeno u cijeloj ovoj knjizi), metoda
Compare smatra kako je a manje od ,,A. Metoda CompareOrdinal usporeuje redoslijed i stoga je, bez
obzira na kulturu, a vee od ,,A.

Poglavlje 10: Nizovi i regularni izrazi | 221


Bud u i d a su nizovi nepromjenjivi, m e t o d a Clone() za String vraa
s a m o referencu izvornog niza. N o v i String se stvara a k o promijenite
klonirani niz:

string si = "One Two Three Four;


string sx = (string)sl.Clone();
Console.WriteLine(
Object.ReferenceEquals(sl,sx));
sx += " Five";
Console.WriteLine(
Object.ReferenceEquals(sl,. sx));
Console.WriteLine(sx);

U o v o m sluaju sx je stvoren k a o klon o d si. Prvi iskaz FJriteLine ispi-


sat e rije true. Dvije varijable niza po ka zu ju na isti niz u memoriji.
K a d promijenite sx vi zapravo stvarate novi niz iz izvornog niza, a ka d
m e t o d a ReferenceEquals vrati false, posljednji iskaz Writel_ine vraa
sadraj izvornog niza k o j e m je d o d a n a rije ,,Five.

Klase I C o n v e r t i b l e pruaju metode koje olakavaju pretvaranje u druge primitivne


tipove poput To Int 3 2 ( ), T o D ou bl e() , T o D e c i m a l ( ) itd.

S tv aran je n izova
Nizovi se najee stvaraju tako da se niz znakova pod navodnicima, to se naziva
literalom niza., dodijeli varijabli tipa string koju je korisnik deklarirao:
string newString = "This is a string literat ;

Nizovi pod navodnicima mogu sadrati i kontrolne znakove (engl. escape characters),
kao to su \n i \t, koji poinju obrnutom kosom crtom (\). Dva ranije navedena znaka
slue za oznaavanje prijelaza u novi red, odnosno novi tabulator.

Bud u i da je obrnut a kosa crta kontrolni znak, a k o u niz elite umet-


*, nuti ob rn utu k o s u crtu (npr. za nav oenje putanje), tu ob rn ut u k os u
crtu m or at e oznaiti d r u g o m o b r n u t o m k o s o m c r t o m (\\).

Nizovi se mogu stvoriti i s pomou doslovnih literala nizova (engl. verbatim string
literals), koji poinju simbolom @ . To konstruktoru String govori kako se niz moe
koristiti doslovno, ak i ako zauzima vie redova ili sadrava kontrolne znakove. U
doslovnom literalu niza obrnute kose crte i znakovi iza njih smatraju se samo dodat-
nim znakovima u nizu. Stoga su sljedee dvije definicije jednake.
string literalOne = " NNNNf^ ste m N V^Directo^NNProgrammingCit.cs";
string verbatimliteralOne =
[S)"\\MySystem\MyDirectory\ProgrammingC# cs";

U prvom redu koristi se literal niza koji nije doslovan te se obrnuta kosa crta mora
izdvojiti11. To jest, ispred nje se mora napisati druga obrnuta kosa crta. U drugom se
redu koristi doslovan literal niza pa dodatna obrnuta kosa crta nije potrebna. Drugi
primjer prikazuje doslovne nizove u vie redova:

222 | Programiranje C#
string literalTwo = "Line OneNnLine Two";
string verbatimLiteralTwo = @"Line One
Line Two";
(TJ,
I Ako unutar doslovnog niza imate dvostrukenavodnike, morateihizdvo-
uV kako prevoditelj znao na kojem mjestu se doslovni niz zavrava

Ove su deklaracije ponovno istoznane. Moete koristiti onu koja vam se ini priklad-
nija ili jednostavnija.

Metoda ToStringO
Jo jedan nain stvaranja nizova jest pozivanje metode ToStringO na objekt i dodje-
ljivanje rezultata varijabli niza. Svi ugraeni tipovi premouju ovu metodu kako bi
se pojednostavila pretvorba vrijednosti (esto se radi o brojanoj vrijednosti) u njenu
nizovnu reprezentaciju. U sljedeem se primjeru metoda ToStringO tipa cjelobrojne
vrijednosti poziva za spremanje svoje vrijednosti u niz:
int mylnteger = 5 ;
string integerString = mylnteger.ToStringO;

Poziv metode mylnteger. ToStringO vraa objekt String koji se zatim dodjeljuje inte-
gerString.

Klasa String prua brojne preoptereene konstruktore koji podravaju razliite teh-
nike za dodjelu vrijednosti niza tipovima string. Neki od tih konstruktora omogua-
vaju vam stvaranje niza prosljeivanjem polja znakova ili pokazivaa na znak. Proslje-
ivanje polja znakova kao parametra konstruktoru String stvara novu instancu niza
kompatibilnu s CLR-om. Za prosljeivanje pokazivaa na znak potreban je marker
unsafe koji je objanjen u poglavlju 22.

Rad s nizovima
Klasa string prua razne metode za usporedbu, pretraivanje i rad s nizovima, a naj-
vanije su prikazane u tablici 10-1.

T a b l i c a 1 0 -1 . M e t o d e i p o l j a z a k l a s u strin g

| Metoda ili polje Svrha

Empty Javno statiko polje koje predstavlja prazan niz.


Compare() Preoptereena javna statika metoda koja usporeuje dva niza.
CompareOrdinal() Preoptereena javna statika metoda koja usporeuje dva niza bez obzira na lokacijske ili
kulturoloke postavke.
Concat() Preoptereena javna statika metoda koja novi niz stvara iz jednog ili vie nizova.
Copy() Javna statika metoda koja novi niz stvara kopiranjem drugog niza.
Equals() Preoptereena javna statika metoda i metoda instance koja utvruje imaju li dva niza istu
vrijednost.

Poglavlje 10: Nizovi i regularni izrazi | 223


Tablica 10-1 Metode i polja za klasu string (nastavak)

Metoda ilipolje Svrha

Format() Preoptereena javna statika metoda kojaformatira nizspomou specifikacijeformata.

Join() Preoptereena javna statika metoda koja ulanava zadani nizizmeu svakog elementa polja
nizova.

Chars Indekser niza.

Length Brojznakova u instanci.

Com pare To O Niz usporeuje sdrugim nizom.

CopyTo() Kopira zadani broj znakova u polje Unicode znakova.

EndsWith() Oznaava odgovara lizadani nizzavretku ovog niza.

Equals() Odreuje imaju lidva niza istuvrijednost.

Insert() Vraa novi nizsumetnutim zadanim nizom.

Lastlndex0f() Daje indeks posljednje pojave odreenog znaka iliniza unutar niza.

PadLeft() Znakove u nizu poravnava desno, dok lijevustranu popunjava razmacima ilizadanim znakom.

PadRight() Znakove u nizu poravnava lijevo,dok desnu stranu popunjava razmacima ilizadanim znakom.

Remove() Brie zadani brojznakova.

Split() Vraa podnizove odvojene odreenim znakovima u polju niza.


StartsWith() Oznaava poinje iinizzadanim znakom.

Subst ri ng O Dohvaa podniz.

ToCharArray() Kopira znakove izniza u poljeznakova.

ToLower() Vraa kopiju niza napisanu malim slovima.

ToUpper() Vraa kopiju niza napisanu velikim slovima.

Trim() Uklanja sve pojave skupa zadanih znakova spoetka izavretka niza.

TrimEnd() Ponaa se kao Tr im (), alina zavretku niza.

TrimStart() Ponaa se kao Trim(),ali na poetku niza.

U primjeru 10-1 prikazana je upotreba nekih od navedenih metoda, ukljuujui


Compare(), Concat() (i preoptereeni operator +), Copy() (i operator =), In sert(),
EndsWith() iIn d ex 0 f().

P r im je r 20-2. R a d s n i z o v im a

#region Using directives

using System;
using Syst
e m.Collections.Generic;
using System.Text;

#endregion

namespace WorkingWithStrings
{

224 | Programiranje C#
p r im jer 1 0 -1 . R a d s n iz o v im a ( n a s t a v a k )

public class StringTester


{
static void Main()
{
// Nizovi skojima emo raditi
string si ="abcd";
string s2 ="ABCD";
string s3 =@"LibertyAssociates, Ine.
provides custom .NET development,
on-site Training and Consulting";

int result; // uva rezultat usporeivanja

// Usporeuje dva niza i pritom razlikuje velika i mala slova


result = string.Compare( si, s2 );
Console.WriteLine(
compare si: {0}, S2: {l}, result: {2}\n",
si, S2, result );

// Preoptereeno usporeivanje, uzima Boolean


//parametar (true = ignorira razliku izmeu velikih i malih slova)
result = string.Compare( si, s2, true );
Console.WriteLine( "compare insensitiveNn" );
Console.WriteLine( "s4: {0}, s2: {l}, result: {2}\n",
si, s2, result );

// Metoda za nastavljanje nizova


string s6 = string.Concat( si, s2 );
Console.WriteLine(
"s6 concatenated trom si and s2: {0}", s6 );

// Koristi preoptereeni operator


string s7 = si + s2;
Console.WriteLine(
"s7 concatenated trom si + s2: {0}", s7 );

// Metoda za kopiranje niza


string s8 = string.Copy( s7 );
Console.WriteLine(
"s8 copied trom s7: {0}", s8 );

// Koristi preoptereeni operator


string s9 = s8;
Console.WriteLine( "s9 = s8: {0}", s9 );

// Tri naina za usporeivanje.


Console.WriteLine(
"\nDoes s9 .Equals(s8 )?: {0 }",
s9-Equals( s8 ) );
Console.WriteLine(
"Does Equals(s9,s8)?: {0 }",
string.Equals( s9, s8 ) );
Console.WriteLine(

Poglavlje 10: Nizovi i regularni izrazi | 225


Primjer 10-1. R ads nizovima (nastavak)
"Does s9 ==s 8 ?: {0}", s9 == s8 );

// Dva korisna svojstva: index i duina


Console.WriteLine(
"\nString s9 is {0} characters long. ",
s9.Length );
Console.Writeline(
"The 5th character is {l}\n",
S9-Length, s9[4] );

// Testira da li niz zavrava sa skupom znakova


Console.WriteLine( "s3 :{0 }\nEnds with Training?: (l}\n",
SB,
s3-EndsWith( "Training" ) );
Console.WriteLine(
"Ends with Consulting?: {0}",
s3-EndsWith( "Consulting" ) );

// Vraa indeks podniza


Console.WriteLine(
"\nThe first occurrence of Training " );
Console.WriteLine( "in s3 is {0}\n",
S3.1nd ex0f( "Training" ) );

// Umee rije excellent prije training


string slO = s3.Insert( 101, "excellent " );
Console.WriteLine( "slO: {0}\n", slO );

// Moete kombinirati dva niza na sljedei nain:


string sli = s3 .Insert( s3.Index0f( "Training" ),
"excellent " );
Console.WriteLine( "sli: {0 }\n", sli );
}
}
}
Primjer 10-1 poinje deklaracijom tri niza:
string si = "abcd";
string s2 = "ABCD";
string s3 = |S"Liberty Associates, Ine.
provides custom .NET development,
on-site Training and Consulting";

Prva dva niza su literali nizova, a trei je doslovni literal niza. Prvo se s i usporeuje
sa s2. Metoda Compare() je javna statika metoda klase string i preoptereena je. Prva
preoptereena inaica uzima dva niza i usporeuje ih:
// Usporeuje dva niza i pritom razlikuje velika i mala slova
result = string.Compare(sl, s2);
Console.WriteLine("compare si: {0}, s2: {1}, result: {2}\n",
si, s2, result);

226 | P ro g ra m ira n je C#
Ova usporedba razlikuje mala i velika slova te moe vratiti razliite vrijednosti, ovisno
0 rezultatima usporedbe:
Negativan cijeli broj ako je prvi niz manji od drugog niza
Nulu ako su nizovi jednaki
Pozitivan cijeli broj ako je prvi niz vei od drugog niza

U ovom sluaju izlaz pokazuje kako je si manji od s2. U Unicodeu (kao i u ASCII-ju)
malo slovo ima manju vrijednost od velikog slova:
compare si: abcd, s2: ABCD, result: -1

Druga usporedba koristi preoptereenu inaicu metode Compare() koja uzima trei,
Boolean, parametar ija vrijednost odreuje treba li se u usporedbi zanemariti razlika
izmeu velikih i malih slova. Ako je vrijednost ovog parametra zanemari razliku"
true, usporedba se izvodi bez obzira na razliku izmeu velikih i malih slova, kao u
sljedeem primjeru:
result = string.Compare(sl,s2, true);
Console.WriteLine("compare insensitive\n");
Console.WriteLine(s4: {0 }, s2 : {l}, result: {2 }\n",
si, s2, result);

---- 1
' Rezultat je ispisan sava iskaza WriteLine() kako bi redovi bili dovoljno
^ kratki za tiskanje u knjizi.

Ovog se puta razlika izmeu velikih i malih slova zanemaruje i rezultat je nula, to
oznaava kako su dva niza identina (bez obzira na razliku izmeu velikih i malih
slova):
compare insensitive

s4: abcd, s2: ABCD, result: 0

U primjeru 10-1 zatim se nastavljaju nizovi. Postoji nekoliko naina da se to postigne.


Moete koristiti metodu ConcatO koja je statika javna metoda klase string:
string s6 = string.Concat(sl,s2);

ili jednostavno moete koristiti preoptereeni operator ulanavanja (+):


string s7 = si + s2;

U oba se sluaja u izlazu vidi kako je ulanavanje bilo uspjeno:


s6 concatenated from si and s2: abcdABCD
s7 concatenated from si + s2: abcdABCD

Slino tome se i kopiranje niza moe izvesti na dva naina. Prvi je s pomou statike
metode Copy():
string s8 = string.Copy(s7);

Poglavlje 10: Nizovi i regularni izrazi | 227


Time se zapravo stvaraju dva zasebna niza s istim vrijednostima. Budui da se nizovi
ne mogu mijenjati, ovaj je nain beskoristan. Bolje je koristiti preoptereeni operator
dodjeljivanja ili metodu Clone (spomenuta je ranije u poglavlju) koji daju dvije varijable
koje pokazuju na isti niz u memoriji:
string s9 = s8;

Klasa String prua tri naina za provjeru jednakosti dva niza. Prvo, moete koristiti
preoptereenu metodu Equals() i izravno upitati s9 ima li s8 istu vrijednost:
Console.WriteLine("\nDoes s9.Equals(s8)?: {o}",
s9.Equals(s8));

Drugi je nain prosljeivanje oba niza do statike metode Equals() klase String:
Console.WriteLine(''Does Equals(s9,s8)?: {0}",
string.Equals(s9,s8));

Konano, moete koristiti i operator jednakosti (==) klase String:


Console.WriteLine("Does s9==s8?: {0}", s9 == s8);

Svi e naini vratiti Boolean vrijednost, kao to je prikazano u izlazu:


Does 59.Equals(s8)?: True
Does Equals(s9,s8)?: True
Does s9==s8?: True

Sljedeih par redova u primjeru 10-1 koristi indeksni operator ([]) za traenje zadanog
znaka unutar niza, a svojstvo Length koriste za vraanje ukupne duljine niza:
Console.WriteLine('\nString s9 is {0} characters long.",
s9.Length);
Console.WriteLine("The 5th character is {l}\n",
s9.Length, s9[4]);

Rezultat je sljedei:
String s9 is 8 characters long.
The 5 th character is A

Metoda EndsWith() pita niz da li se na zavretku niza nalazi podniz. Stoga, s3 moete
prvo pitati zavrava li s Training (to nije sluaj), a zatim zavrava li s Consulting (to
je istinito):
I I Testira da li niz zavrava sa skupom znakova
Console.WriteLine( "s3:{o}\nEnds with Training?: {l}\n",
s3,
s3.EndsWith( "Training" ) );
Console.WriteLine(
"Ends with Consulting?: {o}",
s3.EndsWith( "Consulting" ) );

U izlazu se moe vidjeti kako prva provjera nije uspjela, a druga jest:
s3:Liberty Associates, Ine.
provides custom .NET development,
on-site Training and Consulting
Ends with Training?: False
Ends with Consulting?: True

228 | Programiranje C#
M etoda IndexOf() u n u ta r niza locira podn iz,
u kopiju izv orn og niza.
a metoda In sert() umee novi podniz

Sljedei kod p ro n a la z i prvo p ojavljivanje Training u s 3 :


Console.WriteLine("\nThe first occurrence of Training ")
Console.WriteLine ("in s3 is {o}\n",
s3.Index0f("Training"));

(J izlazu se vidi kako je pomak 101:


The first occurrence of Training
in S3 is 10 1

Tu vrijednost zatim moete koristiti za umetanje rijei excellent i razmaka u tai niz
Rije se, zapravo, umee u kopiju niza koju vraa metoda In serti) i zatim se dodie
ljuje nizu slO: aoaje-

string slO = s3.Insert(ioi,"excellent");


Console.WriteLine("siO: {o}\n,slo);
Izlaz je sljedei:
slO: Liberty Associates, Ine.
provides custom .NET development,
on-site excellent Training and Consulting

Naposljetku, te operacije moete i kombinirati:


string sli = s3.Insert(s3.IndexOf("Training"),"excellent ")
Console.WriteLine("su: {0}\n",sll);

da biste dobili identian izlaz:


sli: Liberty Associates, Ine.
provides custom .NET development,
on-site excellent Training and Consulting

Traenje podnizova
Tip String prua preoptereenu metodu Substring() kojom se iznizova izdvajaju pod-
mzovi. Obje inacice uzimaju indeks koji oznaava poetak izdvajanja, a jedna od dvije
mac.ce uzima i drugi indeks koji oznaava gdje zavriti operaciju. Metoda Substring )
ilustrirana je u primjeru 1 0 -2 . angu

Primjer 10-2. Koritenje metode SubstringO


#region Using direetives

using System;
using System.Collections.Generic;
using System.Text;

ftendregion

namespace SubString

Poglavlje 10: Nizovi i regularni izrazi | 229


Primjer 10-2. K o r i t e n je m e t o d e S u b st rin g O ( n a s t a v a k )

public class StringTester

static void Main()

* // Plavi nekoliko nizova s kojima emo raditi


string si = "One Two Three Four ;

int ix;

// Uzima indeks posljednjeg razmaka


ix = sl.Last!ndexOf( " " );

// Uzima zadnju rije


string s2 = sl.Substring( ix + 1 )>

// Postavlja si na podniz koji poinje na o


// i zavrava na ix (poetak sljedee rijeci
// tako da si ima one two three

// Pronalazi zadnji razmak u si


ix = sl.Last!ndexOf( " " )!

// Postavlja s3 na podniz zapoinjui na


// ix, razmak nakon "two" plus jos jedan
// thus s3 = "three"
string s3 = sl.Substring( ix + 1 );

// Vraa si na podniz koji zapoinje na 0

// i zavrava na ix, tako da je niz "one two"


si = sl.Substring( 0, ix );

// Vraa ix na razmak izmeu


// "one" i "two"
ix = sl.Last!ndexOf( " " )>

// Postavlja s4 na podniz kojim zapoinje jedan


// razmak nakon ix, tako da je podniz wo
. - . .U/- 4- r i n n { 1y + 1 Ii

// vraa si na podniz koji z a p o i n j e m o


// i zavrava na ix, tako da je one
si = sl.Substring( 0, ix );

// Postavlja ix na zadnji razmak, ali njega


// nema pa je ix sada -1
ix = sl.Last!ndexOf( " " );

// Postavlja s5 na podniz na one i


// zadnji razmak. Nema zadnjeg razmaka
// pa s5 postavlja na podniz koji poinje na

U nuli ,
string s5 = s l. Su bs tn ng ( ix + l J,

230 | Programiranje C#
:t,;mjer 10-2. Koritenje metode SubstringO (nastavak)
Console. Writ eLin e( "s2: {0}\ns3: {l}", s2, s3 );
Cons ole. Writ eLin e( "s4: {0}\ns5: {l}\n", s4, s5 );
Cons ole. Writ eLin e( "si: { 0 }\ n, si );

% }
i}

rimjer 10-2 zapravo ne predstavlja elegantno rjeenje problema izdvajanja rijei iz


&za, a\i je dobra prva aproksimacija. Primjer poinje stvaranjem niza si:
string si = 0ne Two Three Four";

tim se ix pridruuje vrijednost zadnjeg razmaka u nizu:


ix=sl.LastIndexOf(" ");

e se ix+l izdvaja na zavretak reda, a nizu s2 se dodjeljuje vrijednost Four:


dei je korak uklanjanje rijei Four iz niza si. To se moe uiniti tako da se nizu si
dodijeli podniz koji poinje s 0, a zavrava s ix.
si = si.Substring(o,ix);

^fijednost ix se ponovno dodjeljuje posljednjem (preostalom) razmaku koji pokazuje


poetak rijei Three koja se zatim izdvaja u niz s3. Takav se postupak nastavlja dok
ne popune nizovi s4 i s5. Na kraju se ispisuju rezultati:
p s2: Four
s3: Three
| s4: Two
s5: One

si: One

|o rjeenje nije elegantno, ali funkcionira i ilustrira upotrebu Substring. Ona nalikuje
'otrebi aritmetike pokazivaa u C++, ali nema pokazivaa niti nesigurnog koda.

Seljenje nizova
inkovitije rjeenje problema prikazanog u primjeru 10-2 jest koritenje metode
i t ( ) klase String koja slui za razlaganje nizova na podnizove. Za koritenje
|ode S p lit() treba proslijediti polje graninika (znakovi koji oznaavaju mjesto
:le rijei), a metoda vraa polje podnizova. Upotreba ove metode prikazana je u
lmjeru 10-3

p t je r 1 0 -3 . K o r i t e n j e m e t o d e S p lit()

|gion Using directives

)hg System;
ng System.Collections.Generic;
(g. System.Text;

Tegion

Poglavlje 10: Nizovi i regularni izrazi | 231


Primjer 10-3. Koritenje metode Split() (nastavak)

namespaceStringSplit sj
{ 1
public class StringTester

static voidMain() *
{ )
// Pravi nizove 5 kojima emo raditi
string si = "One,Two,Three Liberty Associates, Ine.1;

// Konstante za znakove razmak i zarez


const ehar Space = ' ';
const ehar Comma = ',' >

// Polje graninika za dijeljenje reenice


char[] delimiters = new char[]
{
Space,
Comma
1;
string output =
int etr = l;

// Dijeli niz i zatim prolazi kroz rezultirajue


// Polje nizova
foreach ( string subString in sl.Split( delimiters ) )
{
output += ctr++;
output +=
output += subString;
output += "\n";
}
Console.WriteLine( output );
}
}
}
Primjer poinje stvaranjem niza za analizu;
string si = "One,Two,Three Liberty Associates, Ine. ;

Graninici se postavljaju na znakove razmaka i zareza. Zatim se za ovaj niz poziva


metoda S p lit( ), a rezultati se prosljeuju do petlje foreach:
foreach (string subString in sl.Split(delimiters))

M e t o d a Split koristi kljunu rije params pa se k o d m o e skratiti na:

foreach (string subString in sl . SplitC ',

U potpunosti je izostavljena deklaracija polja.

Ponite s inicijalizacijom izlaza na prazan niz, a zatim izlazni niz sagradite u etin
koraka. Ulanajte vrijednost c tr. Zatim dodajte dvotoku, zatim podniz koji je vratilo

232 | Programiranje C#
dijeljenje, a zatim novi red. Prilikom svakog se ulanavanja stvara nova kopija niza,
, i sva se etiri koraka ponavljaju za svaki podniz koji metoda StringO pronae. Ovo
; ponavljanje kopiranja niza je vrlo neuinkovito.
i
problem je u tome to tip niza nije predvien za ovakvu operaciju. Vi zapravo elite
$ stvoriti novi niz dodavanjem formatiranog niza kroz petlju. Potrebna vam je klasa
% StringBuilder.

' Rad s dinamikim nizovima


'i--:
Klasa System.Text.StringBuilder koristi se za stvaranje i modifikaciju nizova. Najva-
r mji lanovi klase StringBuilder navedeni su u tablici 10-2.

Tablica 10-2. Metode klase StringBuilder

|p|pda Objanjenje
Chars (ndekser.

Length Uzima ili postavlja duljinu S t r in g B u ild e r .

A ppendj) Preoptereena javna m etoda koja niz znakova dodaje na zavretak trenutnog S t r in g B u ild e r .

AppendFormat ( ) Preoptereena javna m etod a koja specifikatore form ata zam jenjuj form atiranom vrijednosti
objekta.

In s e r t ( ) Preoptereena javna metoda koja na zadani poloaj umee niz znakova.


5,
Remove() Uklanja zadane znakove.

- R e p la c e O Preoptereena javna m etoda koja sve instance zadanih znakova zam jenjuje novim znakovima.

Za razliku od String, StringBuilder se moe promijeniti. Prilikom modifikacije String-


Builder zapravo modificirate stvarni niz, a ne njegovu kopiju. U primjeru 10-4 objekt
String iz primjera 10-3 zamijenjen je objektom StringBuilder.

Primjer 10-4. Koritenje klase StringBuilder


tfregion Using directives

Using System;
-Jising System.Collections.Generic;
tusing System,Text;

dfendregion

flamespace UsingStringBuilder

public class StringTester

static void Main()


{
U Pravi nizove s kojima e se raditi
string si = "One,Two,Three Liberty Associates, Ine.

Poglavlje 10: Nizovi i regularni Izrazi | 233


Primjer 1 0 -4 . Koritenje klase StringBuilder (nastavak)
// Ko ns tante za razmak i zarez
const char Space = ' ';
const char Cotnma = ',';

// Polje grani nika s ko jima e se reenica po dij eliti


char[] de limi ters = new char[]

{
Space,
Comma

};
// Upotreba klase St ri ng Bu il de r za izgradn ju
// iz lazn og niza .
StringBuilder output = new StringBuilder();
int ctr = 1;

// Dije li niz i za ti m pr olazi kroz


// rezult iraj uCe po lje nizo va . ,
foreach ( string sub String in sl.S plit( delim iters ) )

// Append F ormat dodaj e f ormat ir an i niz


outpu t.Appe ndForm at( "{0}: {a}\n", ctr++, sub Strin g );

Cons ole. Write Line( out put );

M o d ific ira n je sa m o p o slje d n ji dio p ro g ra m a . Z a m o d ifik a ciju n iza se u ^ eSt p m :


tora u la n a v a n ja k o t L i m eto d a A p p e .d F o ) k la se S t r in g B .z ld m k a k o b , se
(o rm a tira n i nizovi d od av ali im se stv o re. O v a j je p o stu p a k m n o go o c m k o v m ,!. Izlaz

ie isti k a o o n a j iz p rim jera 1 0 -3 :

1: One
2: Two
3: Thr ee
4: Libe rty
5: As soc iates
6:
7 : Ine.

Ogranienja graninika
Budui da ste proslijedili graninike i za razm ake i za zareze, razm ak izm eu Assocu
ateS i Ine se vraa kao rije i num eriran je brojem 6 , kao sto je p rikazano. To n ]
ono to elite. K ako biste elim inirali ovu pojavu trebate m etodi S p lit reci da zarez
(kao onaj izmeu One, Two i Three), razm ak (kao onaj izm eu Liberty i Associates)
zarez iza kojeg slijedi razm ak tretira na isti nain. O va posljednja tnatca je najproble-
m atinija i za nju trebate koristiti regularni izraz.

234 | Programiranje C#
$ Takav je niz esto cijeli tekstualni dokument.
f Regularni izrazi se primjenjuju na nizove da bi se provjerilo odgovara li niz regular-
nom izrazu, da bi se vratio podniz ili novi niz koji predstavlja modifikaciju jednog
3- dijela izvornog niza (Ne zaboravite da su nizovi nepromjenjivi i stoga ih ni regularni
* izrazi ne mogu promijeniti).
* Primjenom ispravno sastavljenog regularnog izraza na sljedei niz:
One,Two,Three Liberty Associates, Ine.

? vraa se bilo koji ili svi pripadajui podnizovi (npr. Liberty ili One) ili modificirane ina-
ie pripadajuih podnizova (npr. LIBerTV ili OnE). Uinak regularnog izraza odreen
je sintaksom samog regularnog izraza.
Regularni se izraz sastoji od dva tipa znakova: literata i metaznakova. Literal je znak
* koji elite pronai u ciljnom nizu. Metaznak je poseban simbol koji slui kao naredba
za analizator (engl. parser) regularnih izraza. Analizator je stroj odgovoran za razumi-
jevanje regularnog izraza. Ako, na primjer, napiete regularni izraz:
,'(From|To| Subject |Date):

njemu e odgovarati svi podnizovi koji sadre rijei ,,Froin, ,,To, Subject" ili Date"
. i ako poinju novim redom (A) i zavravaju dvotokom (:).
Znak Au ovom sluaju analizatoru regularnih izraza govori kako traeni niz mora
poinjati novim redom. Rijei Froin" i ,,To su literali, a metaznakovi - lijeve i desne
zagrade (()) te okomite crte (|) - se koriste za grupiranje literala u skupove i oznaava-
nje kako se treba poklopiti bilo koja od opcija (A je takoer metaznak koji se koristi
za oznaavanje poetka reda).
r Stoga bi se sljedei red:
v A(From|To|Subject|Date):

5 mogao proitati kao: Pronai sve nizove koji poinju novim redom iza kojeg slijedi
bilo koji od etiri literala, From, To, Subject ili Date iza kojih slijedi dvotoka.

Potpuno objanjenje regularnih izraza suvie je sloeno za ovu knjigu,


41 no sv* regularr|i izrazi koji se koriste u primjerima su objanjeni. Ako
TV elite u potpunosti razumjeti regularne izraze, preporuam vam knjigu
Mastering Regular Expressions u izdanju 0 Reilly Media, Ine.

Upotreba regularnih izraza: Regex


NET kostur prua objektno orijentiran pristup usklaivanju i zamjeni regularnih
izraza.

Poglavlje 10: Nizovi i regularni izrazi | 235


49 \
~ *& T Regularni izrazi se u C # temelje n a r e g e x p iz jezika Perl 5, ukljuujui
4- kvalifikatore (??, *?, + ?, {n,m}), pozitivno inegativno traenje te uvjetnu
M?' J
__J <4.* procjenu.

Svi objekti .N ET kostura koji su povezani s regularnim izrazima potiu iz imenskog


prostora System.Text.ReuglarExpression. Sredinja klasa za podrku regu armm izra-
zima je Regex koja predstavlja nepromjenjiv i preveden regularni izraz Iako se mogu
stvoriti instance klase Regex, klasa prua i niz korisnih statikih metoda. Koritenje
klase Regex prikazano je u primjeru 10-5.

P r im je r 1 0 - 5 . K o r i t e n j e k l a s e Regex z a r e g u l a r n e iz r a z e

ttregion Using direetives

using System;
using System.Collections.Ceneric;
using System.Text;
using System.Text.RegularExpressions;

flendregion

namespace UsingRegEx
{
public class Tester

static void Main()


{
string si =
"One,Two,Three Liberty Associates, Ine. ;
Regex theRegex = new Regex( I. Ii )>
StringBuilder sBuilder = new StringBuilder();
int id = 1;

foreach ( string subString in theRegex.Split( si ) )

sBuilder.AppendFormat(
"{0}: {l}\n", id++, subString );

Console.WriteLine( "{0}", sBuilder );

}
}
}
Primjer 10-5 poinje stvaranjem niza s i koji je identian nizu iz primjera 10-4:
string si = "One,Two,Three Liberty Associates, Inc.;

Stvara se i regularni izraz koji e se koristiti za pretraivanje tog niza:


Regex theRegex = new Regex(" t,l, ");
Tedan od preoptereenih konstruktora klase Regex uzima niz regularnog izraza kat)
p t m . J T o * pomalo d m n ju jo . ,o ,,
izraz? Je li to tekst proslijeen konstruktoru ili sam Regex objekt. Istin J

236 | Programiranje C#
tekstualni niz proslijeen konstruktoru regularan izraz u tradicionalnom smislu. Sa
ilita objektno orijentiranog jezika argument proslijeen konstruktoru je samo niz
%akova; theRegex je stvarni objekt regularnog izraza.
- .^.itak programa slian je ranijem primjeru 10-4, osim to se za niz s i umjesto
^ ^ e to d e S p lit() poziva metoda S p lit() klase Regex. Metoda R egex.Split() djeluje na
tfiti nain kao i metoda S trin g .S p lit() - vraa polje nizova kao rezultat usklaivanja
Iregularnog izraza unutar objekta theRegex.
-

^lletoda Regex. S p lit() je preoptereena. Najjednostavnija inaica se poziva za instancu


' lllase Regex, kao stoje prikazano u primjeru 10-5. Postoji i statika inaica ove metode
Ikoja uzima niz za pretraivanje i uzorak s pomou kojeg se pretrauje, kao to je pri-
k a z a n o u primjeru 10-6.

||>Wmjer 10-6. Koritenje statike metode Regex.Split()

llregion Using direct ives

'$ using System;


Sliing System.Collections.Generic;
lising System.Text;
%sing SystanText.RegularExpressions;
s
ijtendregion

:?faamespace RegExSplit

s public class Tester


i {
x static void Hain()

string si =
jf| "One,Two,Three Liberty Associates, Ine.";
Jj: StringBuilder sBuilder = new StringBuilder();
int id = l;
toreach ( string subStr in Regex.Split( si, " |, |," ) )
{
sBuilder.AppendFormat( "{0}: {l}\n , id++, subStr );
}
Console.WriteLine( "{o}", sBuilder );
}

^Primjer 10-6 gotovo je identian primjeru 10-5, jedino to se u drugom primjeru ne


Hjpstancira objekt tipa Regex. U primjeru 10-6 se umjesto toga koristi statika inaica
fmetode Sp lit () koja prima dva argumenta; niz koji se trai i niz regularnog izraza koji
Sjredstavlja uzorak koji treba pronai.
fetoda instance S p lit () je takoer preoptereena s inaicama koje ograniavaju broj
vljivanja podjela a odreuju i poloaj unutar ciljnog niza na kojem e pretraivanje
.ipoeti.

Poglavlje 10: Nizovi i regularni izrazi | 237


Koritenje kolekcija za traenje klase Regex
Jo dvije klase iz .N ET imenskog prostora RegularExpressions omoguavaju pono. :
vljeno traenje niza i vraanje rezultata u kolekciji. Kolekcija koja se vraa je tipa
MatchCollection koja se sastoji od nula ili vie Match objekata. Dva bitna svojstvaMatch
objekta su njegova duljina i vrijednost, a svako od tih svojstava moe se proitati na
nain prikazan u primjeru 10-7.

Primjer 10-7. Koritenje tipa MatchCollection i Match objekata

(tregion Using directives

using System;
using Sy stem .Col lec tions. Generic;
usin g System.Text;
using Sys tem.Te xt. Regul arExpre ssion s;

#endregion

names pace Us in g M atch Col lection

{
class Test

public static void Main()

string stringl = 1This is a test string ,

// Trai bilo koji znak iza kojeg slijedi bijeli prostor


Regex theReg = new Regex( @"(\S+)\s" );

// Uzima kolekciju pronaenih poklapanja


MatchCollection theMatches =
theReg.Matches( stringl );

// Prolazi kroz kolekciju


foreach ( Match theMatch in theMatches )
{
Console.WriteLine(
"theMatch.Length: {o}'\ theMatch.Length );

if ( theMatch.Length != 0 )

Console.WriteLine( "theMatch: {0}",


theMatch.ToStringO );

}
}
}
}
}
U primjeru 10-7 stvoren je jednostavan niz koji treba pronai:
string stringl = "This is a test string ;

i jednako jednostavan regularni izraz s pomou kojeg se pretrauje:

2B8 | Programiranje C#
Regex theReg = new Regex(@"(\S+)\s");

1? (i^iz \S pronalazi znak koji nije bjelina, a znak plus oznaava jedan ili vie. Niz\s (napi-
san malim slovom) oznaava bjelinu. Stoga se ovim nizom trae bilo kakvi znakovi
^oji nisu bjeline, a iza kojih slijedi bjelina.

Upamtite kako simbol (@) ispred niza stvara doslovni niz ime se izbje-
gava upotreba obrnute kose crte (\).

' U izlazu se moe vidjeti kako su pronaene prve etiri rijei. Posljednja rije nije pro-
'? naena jer iza nje ne slijedi bjelina. Ako umetnete razmak izmeu rijei string i ispred
ft zavrnih navodnika, program e pronai i tu rije.
i
i Svojstvo length je duljina pronaenog podniza i to je svojstvo objanjeno kasnije u
'> ovom poglavlju.

Koritenje R egex g ru p a
' Podnizove koji su pronaeni esto je korisno grupirati radi analize odgovarajueg
niza. Na primjer, moda ete htjeti pronai i grupirati sve IP adrese pronaene bilo

IP adrese se koriste za obiljeavanje raunala u mrei i obino imaju


oblik x .x .x.x., pri emu x moe biti bilo koja znamenka od 0 do 255
(npr. 192.168.0.1.).

, Klasa Group omoguava stvaranje grupa rezultata na temelju sintakse regularnog izraza
' i predstavlja rezultate iz jednog izraza za grupiranje.
Izraz za grupiranje oznaava grupu i prua regularni izraz: u grupu e se dodati bilo
koji poniz koji odgovara regularnom izrazu. Na primjer, za stvaranje grupe ip moete
napisati:
-3? @"(?<ip>(\d|\.)+)\s"

ip Klasa Match izvodi iz Group i sadri kolekciju Groups u kojoj se nalaze sve grupe prona-
. ene s pomou Match.
Stvaranje i upotreba kolekcije Groups i klasa Group prikazana je u primjeru 10-8.

. Primjer 10-8. Koritenje klase Group

itregion Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
v/h

4V Poglavlje 10: Nizovi i regularni izrazi | 239


Primjer 10-8. Koritenje klase Group (nastavak)
flendregion

namespace RegExGroup
{
class Test

public static void Main()

^ st ri ng st ri ngl = "04 : 03 :27 127.0.0.0 Li bert yAssoci ates. com ";

l l Grupa time = jedna ili vie znamenki ili dvotoki iza kojih slijedi bjelina
Regex theReg = new Regex( @"(?<time>(\d|\:)+)\s" +
// ip address = jedna ili vie znamenki iza kojih slijedi toka
@"(?<ip>(\d|\.)+)\s" +
// site = jedan ili vie znakova
(8"(?<site>\S+)" );

// Uzima kolekciju pronaenih nizova


MatchCollection theMatches = theReg.Matches( stringl );

I I Prolazi kroz kolekciju


foreach ( Match theMatch in theMatches )
{
if ( theMatch.Length != 0 )

Console.WriteLine( "\ntheMatch: {0 }",


theMatch.ToStringO );
Console.WriteLine( time: {0}",
theMatch.Groups["time"] );
Console.WriteLine( "ip: {0 }",
theMatch.Groups["ip"] );
Console.WriteLine( "site: {0}",
theMatch.Groups["site"] );

}
}
}
}
}
Primjer 10-8 ponovno poinje stvaranjem niza za traenje:
string st ringl = " 04:03:27 127.0.0 .0 Liber tyAsso ciates . com";

Ovaj niz moe biti jedan od mnogih koji su zabiljeeni u dnevniku Web posluitelja
ili koji su dobiveni kao rezultat pretraivanja baze podataka. U ovom jednostavnom
primjeru postoje tri stupca odvojena razmacima: jedan za vrijeme unosa zapisa, jedan
za IP adresu i jedan za stranicu. Naravno, kad rjeavate stvarni problem, moda ete
morati provesti sloenije upite i koristiti druge graninike i sloenija pretraivanja.
U primjeru 10-8 eljeli smo stvoriti jedan Regex objekt za pretraivanje nizova ovog
tipa koji bi se onda podijelili u tri grupe: time, ip address i s ite . Niz regularnog izraza
je prilino jednostavan te je primjer lako razumjeti. Meutim, upamtite kako biste u

240 | Programiranje C#
Istvarnom pretraivanju vjerojatno koristiti samo dio izvornog niza, a ne cijeli izvorni
Spiz kako je ovdje prikazano.
// grupa time = jedna ili vie znamenki ili dvotoki
// iza kojih slijedi bjelina
Regex theReg = new Regex((8"(?<time>(\d|\:)+)\s" +
// ip address = jedna ili vie znamenki ili toka
// iza kojih slijedi razmak
@"(?<ip>(\d|\.)+)\s" +
// site = jedan ili vie znakova
@"(?<site>\S+)");

^ Usredotoimo se na znakove koji stvaraju grupu:


,si (?<time>(\d]\:)
' > , J 1 v

g Zagrade slue za stvaranje grupe. Sve izmeu otvorene zagrade (neposredno ispred
upitnika) t zatvorene zagrade (u ovom sluaju nakon znaka +) je jedna neimenovana
gruPa-

Niz ?<time> toj grupi daje naziv time i grupa se povezuje odgovarajuim tekstom to
, jest regularnim izrazom ( \d|\: )+) \x'''. O vaj se regularni izraz moe interpretirati kao
T/ Je na **1vise znamenki, odnosno dvotoki iza kojih slijedi razmak.
Na slian nain niz ?<site> imenuje grupu site . Kao i primjer 10-7, i primjer 10-8 trai
'pkolekciju svih rezultata:
MatchCollection theMatches = theReg.Matches(stringi);

^ Primjer 10-8 prolazi kroz kolekciju Matches i pronalazi sve Match objekte.

1 Akie duljina (Length) objekta Match vea od nula, Match je pronaen; ispisuje se cijeli
I rezultat: 1
> Console.WriteLine(''\ntheMatch: {o}",
j theMatch.ToStringO);

Izlaz glasi:
*#
|i theMatch: 04:03:27 127.0.0,0 LibertyAssociates.com

|Zatim se uzima grupa time iz kolekcije theMatch.Group i ta se vrijednost ispisuje:


-|L Console.WriteLine("time: {o}'',
theMatch.Groups[time"]);

f Time se dobiva izlaz:


time: 0 4 :0 3 : 2 7

Kod zatim uzima grupe ip i site:


, Console.WriteLine("ip: {o}",
theMatch.Groups[ip"]);
Console.WxiteLine("site: {0},
, theMatch.Groups[site"]);
' Time se dobiva izlaz:
1 ip: 1 2 7 .0 .0 . 0
site: LibertyAssociates.com

Poglavlje 10: Nizovi i regularni izrazi | 241


U primjeru 1 0 - 8 kolekcija Matches ima samo jedan Match. Mogue je, meutim, pronai 1
vie od jednog izraza unutar niza. To moete uiniti tako da strin g l u primjeru 10-8 j
modificirate na sljedei nain kako bi umjesto jednog pruio vie unosa logFile:
string stri ngl = "04:03:27 127. 0.0.0 Liberty Ass oc ia te s. co m " +
04:03:28 127.0.0.0 to o. co m
" " +
"04:03:29 127.O.O.O bar. co m " ;

Time se u MatchCollection stvaraju tri rezultata pod nazivom theMatches. Generira se


sljedei izlaz:
theMatch: 04 :03: 27 127.0.0.0 Liber tyAssoci ates. co m
time: 04 :03: 27
ip: 127
.O.O.O
site: Li be rt y A ssociat es. com

theMatch: 04 :03: 28 12 7. 0. 0. 0 foo. c om


time: 04:03:28
ip: 127. 0.0.0
site: foo. co m

theMatch: 04:03: 29 12 7. 0. 0. 0 bar. com


time: 04 : 03: 29
ip: 12 7. 0. 0. 0
site: ba r. com

U ovom primjeru theMatches sadri tri Match objekta. Svaki put se kroz vanjsku petlju
foreach pronalazi sljedei Match u kolekciji i prikazuje se njegov sadraj,
foreach (Match theMatch in theMatches)

Za svaki pronaeni Match objekt moete ispisati cijeli rezultat, razne grupe ili oboje.

Koritenje kolekcije CaptureColIection


Svaki put kad objekt Regex pronae podizraz, stvara se instanca Capture koja se dodaje
kolekciji CaptureColIection. Svaki objekt Capture predstavlja jedan rezultat. Svaka grupa
ima svoju kolekciju rezultata prikupljenih za podizraz koji je povezan s grupom.
Kljuno svojstvo objekta Capture jest length koje oznaava duljinu pronaenog pod-
niza. Kada Match upitate za duljinu, uzet ete Capture.Length jer Match izvodi iz Group
koja zauzvrat izvodi iz Capture.

' She ma nasljeivanja regularnih izraza u .NET-u doputa Match da u


*, svoje suelje ukljui metode i svojstva ovih roditeljskih klasa. Group
-f je, u odreenom smislu, dohvaanje: dohvaanje koje uahuruje zami-
'' sao grupiranja podizraza. Match je, zauzvrat, Group: Match je uahurenje
svih grupa podizraza koji ine cijeli rezultat regularnog izraza (vie o
odnosu to je i drugim odnosima potraite u poglavlju 5).

U CaptureColIection se obino nalazi samo jedan Capture, ali to ne mora obavezno


biti tako. Moe se na primjer dogoditi da analizirate niz u kojem se naziv tvrtke moe

242 | Programiranje C#
Snalaziti na jednom od dva poloaja. Kako biste te poloaje grupirali ujedan rezultat,
na dva mjesta u uzorku regularnog izraza stvorite grupu ?<company>:
Regex theReg = new Regex(@"(?<tirae>(\d|\:)+)\s" +

< @"(?<company>\S+)\s" +
@"(?<ip>(\dl\.)+)\s" +
M- @ (?<company>\S+)\s");

| Ova grupa regularnog izraza uzima sve odgovarajue nizove znakova koji se nalaze
iza time, ali i sve odgovarajue nizove koji se nalaze iza ip. Pomou ovog regularnog
i izraza moete analizirati sljedei niz:
string stringl = "04:03:27 lesse 0.0.0.127 Liberty

U nizu se na oba zadana poloaja nalaze nazivi. Rezultat je sljedei:


theMatch: 04:03:27 Jesse 0.0.0.127 Liberty
time: 04:03:27
ip: 0.0.0.127
Company: Liberty

to se dogodilo? Zato grupa Company prikazuje Liberty? Gdje je prvi termin koji je
takoer pronaen? To se dogodilo jer je drugi termin prepisao prvi. Grupa je, meu-
tim, uhvatila oba termina. To se moe vidjeti u kolekciji Captures, kao to je prikazano
u primjeru 10-9.

P rim jer 1 0 -9 . I s p it iv a n j e k o l e k c i j e C a p t u r e s

(tregion Using directives

using System;
using System.Collections.Generie;
using System.Text;
using System.Text.RegularExpressions;
%
(tendregion

namespace CaptureCollection
{
class Test

i*. public static void Main()

t {
// Niz za analiziranje
H obratite pozornost da se ime
H H pojavljuje na dva mjesta
string stringl =
04:03:27 3esse 0,0.0.127 Liberty

U Regularni izraz koji grupira tvrtku dvaput


H Regex theReg . new Regex( (?<time>(\d| V.)+)\s" +
# @"(?:ompany>\S+)\s" +
H #"(?<ip>(\d|\.)+)\s" +
^ @''(?<company>\S+)\s" );

// Uzima kolekciju rezultata

Poglavlje 10: Nizovi i regularni izrazi I 243


P r im j e r 1 0 -9 . I s p i t i v a n j e k o l e k c i j e Captures ( n a s t a v a k )

MatchCollection theMatches =
theReg.Matches( stringl );

// Prolazi kroz kolekciju


foreach ( Match theMatch in theMatches )
{
if ( theMatch.Length != 0 )
{
Console.WriteLine( "theMatch: {0}",
theMatch.ToStringO );
Console.WriteLine( "time: {0}",
theMatch.Groups["time"] );
Console.WriteLine( "ip: {o}",
theMatch.Groups["ip"] );
Console.WriteLine( "Company: {0}",
theMatch.Groups["company"] );

// Prolazi kroz kolekciju rezultata


/ / u grupi company group unutar kolekcije
// groups rezultata

foreach ( Capture cap in


theMatch.Groups["company"].Captures )

Console.WriteLine( "cap: { 0 } \ cap.ToStringO );


}
}
}
}
}
}
Kod napisan podebljanim slovima izvodi iteraciju kroz kolekciju Captures za grupu
Company:
foreach (Capture cap in
theMatch.Groups["company"].Captures)

Pogledajmo kako je ovaj red analiziran. Prevoditelj poinje s traenjem kolekcije krozkoju
e iterirati. theMatch je objekt koji sadri kolekciju Groups. Kolekcija Groups ima indekser
koji uzima niz i vraa jedan objekt Group. Stoga sljedei red vraa jedan objekt Group:
theMatch.Groups["company"]

Objekt Group ima kolekciju Captures. Stoga sljedei red vraa kolekciju Group za objekt
Group spremljen u Groups["company''] unutar objekta theMatch:
theMatch.Groups["company"].Captures

Petlja foreach prolazi kolekciju Captures i izdvaja svaki element te ga dodjeljuje lokal-
noj varijabli cap koja je tipa Capture. U izlazu moete vidjeti kako postoje dva elementa
za uzimanje: Desse i Liberty. Drugi element prepisuje prvi u grupi, te se prikazuje
samo vrijednost Liberty. Ako, meutim, pogledate kolekciju Captures, moete vidjeti
kako su uzete obje vrijednosti.

244 | Programiranje C#
Obrada i;

C# , kao i mnogi drugi objektno orijentirani jezici, pogrekama i neuobiajenim uvje-


tima upravlja s pomou iznimki. Iznimka (engl. exception) je objekt u kojem su uahu-

'0i rene informacije o neobinoj pojavi u programu.


Vano je napraviti razliku izmeu programerskih pogreaka, korisnikih pogreaka
i iznimki. Programersku pogreku (engl. bug) ini programer i trebalo bi ju ispraviti
prije isporuke koda. Iznimke ne predstavljaju zatitu od programerskih pogreaka.
i; Iako programerska pogreka moe prouzroiti izbacivanje iznimke, u njihovoj obradi
i ne smijete se osloniti na iznimke. Programerske pogreke trebate ispraviti.
m
Pogreku moe izazvati i akcija korisnika. On, na primjer, moe unijeti broj na mjestu

$i na kojem se oekuje slovo. Korisnika pogreka takoer moe izbaciti iznimku, ali
to moete sprijeiti hvatanjem pogreaka s pomou koda za provjeru valjanosti. Kori-
m snike pogreke bi se trebale predvidjeti i sprijeiti kad god je to mogue.
8s

ak i ako uklonite sve programerske pogreke i predvidite sve korisnike pogreke


moe doi do predvidljivih problema koji se ne mogu sprijeiti. Moe, na primjer, pone-
stati memorije ili moete pokuati otvoriti datoteku koja vie ne postoji. Iznimke se ne
mogu sprijeiti, ali se mogu obraditi kako ne bi blokirale program.
Kad program naie na neoekivanu situaciju, na primjer kad ponestane memorije, on izba-
cuje (ili pokree ) iznimku. Kad se iznimka izbaci, zaustavlja se izvedba trenutne metode
i stog se odmotava dok se ne pronae odgovarajuu metodu za obradu iznimki.
To znai da e se metoda koja se trenutno izvodi prekinuti ako ne moe obraditi
iznimku, a pozivajua metoda e dobiti priliku za obradu iznimke. Ako nijedna od
pozivajuih metoda ne moe obraditi iznimku, to e uiniti CLR, a program e se
odmah ugasiti.

Metoda za obradu iznimki (engl. exception handler) je blok koda projektiran za obradu
izbaene iznimke. Metode za obradu iznimki implementiraju se kao iskazi catch. U ide-
alnim uvjetima, ako je iznimka uhvaena i obraena, program moe ispraviti problem
i nastaviti s radom. ak i ako program ne moe nastaviti s radom, hvatanjem iznimke
omoguava se ispis smislene poruke o pogreci i prikladno zatvaranje programa.
Ako u metodi postoji kod koji se mora izvoditi bez obzira na izbacivanje iznimke (npr
radi oslobaanja dodijeljenih resursa), taj kod moete staviti u blok finally unutar '
kojeg e on sigurno biti pokrenut, ak i ako doe do iznimke.

Izbacivanje i hvatanje iznimki


U C# se mogu izbacivati samo objekti tipa System. Exception ili objekti koji su izvedeni
iz tog tipa. Imenski prostor System sadri razne tipove iznimki koje program moe
koristiti. U te se tipove ubrajaju ArgumentNullException, InvalidCastException i Over-
flowException te mnogi drugi.

Napomena za C++ programere: u C # se ne moe izbaciti bilo koji


objekt - on mora biti izveden iz System. Exception.

Iskaz throw
Kako bi se u C # klasi objavila neuobiajena situacija, izbacuje se iznimka. Za to se
koristi kljuna rije throw. Sljedei red koda stvara novu instancu System.Exception i
zatim je izbacuje:
throw new System.Exception();

Izbacivanje iznimke odmah zaustavlja izvedbu, a CLR trai metodu za obradu iznimki.
Ako se metoda za obradu iznimki ne moe pronai u tekuoj metodi, izvedbeni okoli
odmotava stog i provjerava pozivajue metode sve dok ne pronae metodu za obradu
iznimki. Ako se stog odmota sve do Main(), a metoda za obradu iznimki nije prona-
ena, program se zatvara. To je prikazano u primjeru 11-1.

Primjer 11-1. Izbacivanje iznimke


namespace Programming_CSharp
{
using System;

public class Test


{
public static void Main()
{
Console.WriteLine("Enter Main..
Test t = new Test();
.t.Funci();
Console.WriteLine("Exit Main...");

public void Funcl()


{
Console.WriteLine("Enter Funcl..

246 | Programiranje C#
primjer 11-1- Izbacivanje iznimke (nastavak)
Func2();
Console,WxiteLine("Exit Funcl...")
>?! }

5^fe public void Func2()


{
Console.WxiteLine("Enter Func2 ...
throw new System.Exception();
Console.WriteLine("Exit Func2
}

Kada ovaj program pokrenete u reimu za ispravljanje pogreaka, pojavit e se okvir


s porukom Exception was unhandled" (Iznimka nije obraena") kao to je prika-
zano na slici 11-1. r

Exception of type 'Svstem.&fception' was throrn


Troubteshooting T#ps
Get general help fbr this exceptiorij
iii
_ cij
Search far more Help Online...
Actlons
View Detail...

^ Slika 11-1. Iznimka koja nije obraena

-I ^k pridsnete View Detail prikazat e se pojedinosti iznimke koja nije obraena (slika

I f Vaj iednostavan Primjer na konzoli ispisuje svaki ulaz i izlaz iz metode. Metoda
t Main(J) StVara mstancu tipa Test i poziva Funci(). Nakon ispisa poruke Enter Funcl
metoda Funcl odm ah poziva Func2 (). Func2 ispisuje prvu poruku i izbacuje objekt tipa
j System.Exception. v

Izvoenje se smjesta zaustavlja, a CLR u Func2 trai metodu za obradu izntmk.


Metoda ne postoj, te izvedbeni okoli odmotava stog (iskaz ex it se ne ispisuje) do
Funci(). Kako metoda za obradu iznimki ne postoji ni u toj metodi, poziva se zadana
. metoda za obradu koja otvara okvir s porukom o iznimci.

Poglavlje 11: Obrada iznimki | 247


' View Detail

Exceptlon Snapshot:

a i
BiExceptton {"Exception of type 'System.Exception' was thrown."} ~~
s.?0 Data i{System.Collections.UstDictionaryInternal}
HelpLink nuli
S lnnerException nuli
Message Exception of type 'System.Exception' vvas thrown.
Non-Public members
' Source i ThrowingAnException

\ StackTrace ," at ThrowingAnExceptlon.Test.Func20 in c:\ldocuments and setthgs


ajffl Static members
| (Vold Func2()>

OK

S l i k a 11-2. P o j e d i n o s t i i z n i m k e

Iskaz catch
Metoda za obradu iznimki se u C # naziva blok za hvatanje i stvara se s pomou
kljune rijei catch. :|
U primjeru 11-2 iskaz throw se izvodi unutar bloka try , a blok catch obavjetava kako J
je iznimka obraena. |

P r im j e r 1 1 -2 . H v a t a n j e i z n im k e

ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CatchingAnException
{
public class Test
{
public static void Main()
{
Console.WriteLine( "Enter Main.. ) ;
Test t = new Test();
t.Funcl();
Console.WriteLine( "Exit Main..." );

243 | Programiranje C#
i_imj e r U -2 . H vatanje iznim ke (nastavak)

}
public void Funcl()

Console.WriteLire( "Enter Funcl..." );


Func2 ();
Console.WriteLine( "Exit Funcl..." );
}
public void Func2 ()
{
Console.WriteLine( "Enter Func2 ..." );

try
{
Console.WriteLine( "Entering try block..." );
throw new 5ystem.Exception();
Console.Writeline( "Exiting try block..." );
}
catch
{
Console.WriteLine(
4( "Exception caught and handled." );
vi' ; }
i Console.WriteLine( "Exit Func2 ..." );

}
I'
primjer 11-2 gotovo je identian primjeru 11-1, razlikuje se samo po tome to sadri i
blok try/catch. Blok try se obino stavlja oko potencijalno opasnog" iskaza, na pri-
fjnjer onog za pristup datoteci, dodjelu velikog prostora u memoriji i slino.
f za iskaza try nalazi se generiki iskaz catch. Iskaz catch iz primjera 11-2 je generiki
er nije odreeno kakve tipove iznimki treba hvatati. U ovom e sluaju iskaz hvatati
;vc izbaene iznimke. Koritenje iskaza catch za hvatanje odreenih tipova iznimki
objanjeno je kasnije u ovom poglavlju.

poduzimanje korektivnih akcija


U primjeru 11-2 iskaz catch samo izvjetava o hvatanju i obradi iznimke. U primjeru
Xi stvarnog ivota vjerojatno e se poduzeti neka akcija za ispravljanje problema koji je
m uzrokovao izbacivanje iznimke. Ako, na primjer, korisnik pokuava otvoriti datoteku
V koja je samo za itanje, moe se pozvati metoda koja korisniku omoguava promjenu
atributa te datoteke. Ako je programu ponestalo memorije, korisniku se moe pruiti
mogunost zatvaranja drugih aplikacija. Ako nema drugog izlaza, blok catch moe
[Ispisati poruku o pogreci kako bi korisnik znao to nije u redu.

Poglavlje 1 1 : O brada izn im ki | 249


Odmotavanje stoga poziva
Paljivo pogledajte izlaz primjera 11-2. Primijetit ete kako je kod uao u Main()i
Funcl(), Func2() i blok try. Nema naznake d aje kod izaao iz bloka try , iako se vidi
da je izaao iz Funcl(), Func2() i Main(). to se dogodilo?
Izvoenje se zaustavlja odmah nakon izbacivanja iznimke i predaje se bloku catch.
Ono se uope nee vratiti izvornoj stazi koda. Nikad nee doi do reda koji ispisuje
iskaz ex it za blok try . Blok catch upravlja pogrekom, a izvoenje se zatim vraa kroz
kod koji slijedi iza catch.
Kada ne bi bilo bloka catch, stog bi se zbog iznimke odmotao, ali to se ovdje ne
dogaa jer postoji blok catch. Iznimka se sada obrauje. Problemi vie ne postoje i
program nastavlja s radom. Ovo e biti neto jasnije ako blokove try/catch premjestite
do metodu Funcl(), kao to je prikazano u primjeru 11-3.

P r im je r 1 1 -3 . H v a t a n je u p o z i v n o j m e t o d i

ftregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CatchingExceptionInCallingFunc
{
public class Test
{
public static void Main()

Console.Writeline( "Enter Main..." );


Test t = new Test();
t.Funcl();
Console.WriteLine( "Exit Main..." );

public void Funcl()

Console.WriteLine( "Enter Funcl..." );

try

Console.WriteLine( "Entering try block..." );


Func2();
Console.WriteLine( "Exiting try block..." );
}
catch
{
Console.Writetine(
"Exception caught and handled." );

250 | P ro g ra m ira n je C#
! p rim jer 11-3. H v a t a n je u p o z i v n o j m e t o d i ( n a s t a v a k )

Console.WriteLine( "Exit Funcl..." );


}
public void Func2()
{
Console.WriteLine( "Enter Func2 ..." );
throw new System.Exception();
Console.WriteLine( "Exit Func2 ..." );
}
}
; }

. Iznimka se ovog puta ne obrauje u metodi Func2(), ve u Funcl(). Kad je pozvana


metoda Func2() ispisuje se iskaz Enter i zatim se izbacuje iznimka. Izvoenje se zau-
stavlja, a izvedbeni okoli trai metodu za obradu iznimki koja, meutim, ne postoji.
Stog se odmotava i izvedbeni okoli metodu za obradu pronalazi u Funcl(). Poziva se
iskaz catch, a izvoenje se nastavlja odmah nakon iskaza catch, ispisujui iskaz Exit
za Funcl() i zatim za Main().

Bitno je da razumijete zato se iskaz Exiting Try Block i Exit Func2 ne ispisuju. Ovo
je klasian ptimjer u kojem smjetanje koda u program za ispravljanje pogreaka i pro-
lazak kroz njega mogu pojasniti stvari.

Stvaranje namjenskih iskaza catch


Dosad smo koristili samo generike iskaze catch. Mogu se napisati i namjenski iskazi
catch koji obrauju samo neke iznimke, na temelju njihovog tipa. Primjer 11-4 prika-
zuje kako se oznaava tip iznimke koju iskaz treba obraditi.

P rim jer 1 1 -4 . Z a d a v a n j e iz n im k e z a h v a t a n je

#region Using directives

using System;
. using System.Collections.Generic;
: using System.Text;

(tendregion

namespace SpecifyingCaughtException

public class Test


{
Public static void Main()
{
Test t = new Test();
t.TestFunc();
, }

Poglavlje 11: Obrada iznimki | 251


P r im je r 1 1 -4 . Z a d a v a n j e i z n i m k e z a h v a t a n j e ( n a s t a v a k )

U Pokuaj dijeljenja dva broja


I l i obrade mogue iznimke
public void TestFunc()
{
try
{
double a = 5;
double b = 0;
Console.WriteLine( "{0} / {1} - \2f >

a, b, DoDivide( a, b ) );

}
// Prvo ide tip najee izvoene iznimke
catch ( System.DivideByZeroException )

Console.Writetine(
"DivideByZeroException caught! );

catch ( 5ystem.ArithmeticException )

Console.Writetine(
"ArithmeticException caught!' );

}
// Generiki tip iznimke je zadnji
catch

Console.WriteLine(
Unknown exception caught" );
}
}
// Dijeli ako je dozvoljeno I
public double DoDivide( double a, double b ) i
3
{ I
if ( b == 0 )
throw new System.DivideByZeroException(),

lf throw new System.ArithmeticException();


return a / b;

U ovom primjeru metoda DoOivide() ne doputa da se nula podijeli nekirndjugim


"I

ne postoji odgovarajua iznimka; dijeljenje nule drugim brojem je doputena


m a S k a o p e r L ja i ne treba se izbaciti iznimka. Pretpostavtmo meunm d o j
primjera kako ne elite da se nula dijeli drug.m brojem, vec da se izbaci
ArithmeticExceDtion.

252 | Programiranje C#

i
aj se iznimka izbaci, izvedbeni okoli redom provjerava sve metode za obradu
nki i odabire prvu odgovarajuu. Kad program pokrenete s a=5 i b=7, generirat
Je se sljedei izlaz:
5 / 7 = 0.7142857142857143

j.ema oekivanju, iznimka nije izbaena. Meutim, ako vrijednost a postavite na


fiulu, generira se ovaj izlaz:
ArithmeticException caught!

Izbaena je iznimka i izvedbeni okoli provjerava prvu iznimku DivideByZeroExcep-


liort. Budui da to nije odgovarajua iznimka on prelazi na sljedeu metodu za obradu
iznimki ArithmeticException koja odgovara.
pretpostavimo, na kraju, da a postavite na sedam, a b na nulu. To e izbaciti iznimku
OivideByZeroException.

Prilikom zadavanja redoslijeda iskaza catch morate biti vrlo paljivi


jer je DivideByZeroException izveden iz ArithmeticException. Ako
i*,' iskaze catch navedete obrnutim redoslijedom, DivideByZeroException
uskladit e se poklopiti s metodom za ArithmeticException te iznimka
nee doi do metode za obradu DivideByZeroException. Zapravo, u slu-
aju obrnutog redoslijeda nijedna iznimka nee doprijeti do metode
DivideByZeroException. Prevoditelj e prepoznati kako metodi Divi-
deByZeroException nije mogue pristupiti i generirat e pogreku u pre-
voenju!

Iskazi try/catch se mogu rasporediti tako da se neke odreenije iznimke hvataju u


jednoj metodi, a da se druge iznimke, koje su vie generike, hvataju u metodama koje
$6 nalaze vie u pozivnom stogu. Toan raspored bit e odreen vaim specifinim
Tojektom.
pretpostavimo kako metoda A poziva drugu metodu B koja zatim poziva metodu C.
etoda C poziva metodu D koja zatim poziva metodu E. Metoda E se nalazi duboko '
kodu, dok se metode B i A nalaze neto vie. Pretpostavite li da bi metoda E mogla
baciti iznimku, blok try/catch trebate smjestiti duboko u kodu kako bi se ta iznimka
Jtvatila to blie mjestu na kojem je problem nastao. Korisno je i stvoriti openitije
etode za obradu iznimki na viim mjestima u kodu kako vam ne bi promakle nepred-
viene iznimke.

skazfinally
nekim instancama izbacivanje iznimke i odmotavanje stoga moe uzrokovati pro-
em. Ako ste, na primjer, otvorili datoteku ili na drugi nain zauzimate resurs, moe
m zatrebati mogunost zatvaranja datoteke ili pranjenja privremene memorije.
*ko postoji akcija koju morate izvesti bez obzira na izbacivanje iznimke (na pri-
mjer, ako trebate zatvoriti datoteku), moete izabrati izmeu dvije strategije. Moete

Poglavlje 11: Obrada iznimki | 253


zatvoriti opasnu akciju u blok try i zatim zatvoriti datoteku i u bloku catch i u bloku
try. Meutim, time se kod duplicira i poveava se opasnost od pogreaka. U C#
dostupna je i prikladnija alternativa u obliku bloka finally.
Izvoenje koda u bloku finally je zajameno bez obzira na izbacivanje iznimke/
Metoda TestFunc() u primjeru 11-5 prvo simulira otvaranje datoteke. Metoda izvodi

odreene matematike operacije i datoteka se zatvara. Izmeu otvaranja i zatvaranja


datoteke moe se izbaciti iznimka. Ako se to dogodi, datoteka moe ostati otvorena.
I
Razvojni inenjer zna kako e se na kraju metode, bez obzira na sve, datoteka zatvo-
riti pa se pozivanje metode za zatvaranje datoteke premjeta u blok finally gdje se ono
moe izvesti bez obzira na izbacivanje iznimke.

I
Primjer 11-5. Koritenje bloka finally
(tregion Us in g d ire ctiv es

using System;
using S y s t e m .C o l l e c t i o n s .Ge n e r i c ;
using System .Tex t;

Se ndregion

names pace Usin g F in al l y


{
public class Test

public static vo id M ain ()

{
Test t = n ew Test();
t.TestFuncO;

}
// Poku ava po dij eliti dva broja i
// ob r auje mogue iznimke
public void Test Func ()

{
try

Console.Writeline( "Open file here"


double a = 5;
double b = 0;
Console.WriteLine( "{0} / {1} = {2},
a, b, DoDivide( a, b ) );
Console.WriteLine(
"This lin e may or may not print" );

}
H Prvo ide tip najee izvoene iznimke

Ako iznimku izbacite iz bloka fmally, nije sigurno da e blok fmally dovriti izvoenje.

254 | Programiranje C#
primjer 11 5. Koritenje bloka finally (nastavak)
c a tc h ( S y s t e m . D i v id e B y Z e r o E x c e p t io n )

Console.WriteLine(
"DivideByZeroException caught!" );

i catch
{
Console.WriteLine( "Unknown exception caught" );

finally
{
Console.WriteLine( "Close file here." );

l. }

// Dijeli ako je dozvoljeno


public double DoDivide( double a, double b )
# {
if ( b == o )
throw new System.DivideByZeroException()-
if ( a == o )
throw new System.ArithmeticException();
return a / b;
}

U primjeru je izostavljen jedan blok catch kako bi se utedjelo na prostoru, a dodan


# je bok flnally- Blok finally se izvodi bez obzira na izbacivanje iznimke i u oba izlaza
MRmozete vidjeti poruku Close file here.

Blok ftnally se m o e stvoriti sa ili bez blokova catch, n o za izvoenje


bloka finally potreban je blok try. Pogreno je izai iz bloka finally s
p o m o u break, continue, return ili goto.

pObjekti Exception
Jg o sa d smo iznimke koristili kao osiguranje- to jest, postojanje iznimke oznaavalo
! h k PT c 3 1 3 S nlSm pnstuPlh lznlmci niti pregledali sam objekt Exception
g b je k t System. Exception prua razne korisne metode i svojstva. Svojstvo Message daje
iJ E
l t
aC1,e 1ZJ ,
nimC1 "aPnmjerraz,g nJeng izbacivanja. Svojstvo Message moe se
" kod koB Je Izbacl iznimku moe svojstvo Message postaviti kao argument
sl l f 0nstrUlktora iznimke.

E r HelpLink prua vezu do datoteke sustava pomoi povezane s iznimkom. U


mvvo se svojstvo moe i zapisivati.

Poglavlje 11: Obrada iznimki | 255


r** Napomena za VB6 programere: u C # trebate biti paljivi prilikom dekla-
riranja i distanciranja varijabli objekata u istom redu koda. Postoji li
Tkv mogunost izbacivanja pogreke u metodi konstruktora, moda dekla-
raciju i instancijaciju varijable trebate staviti unutar bloka try. To,
meutim, moe uzrokovati ogranienje dosega varijable na blok try te
se ona nee m oi referencirati unutar blokova catch i finally. Najbolje
je varijablu objekta deklarirati ispred bloka try i instancirati je unutar
bloka try.

Svojstvo StackTrace se moe samo itati, a postavlja ga izvedbeni okoli. U primjeru


11-6 se svojstvo Exception.HelpLink postavlja i dohvaa kako bi korisnik dobio infor-
macije o DivideByZeroException. Svojstvo iznimke StackTrace moe dati trag u stogu
za iskaz pogreke. Trag u stogu prikazuje stog poziva: niz poziva metoda koje prethode
metodi u kojoj je izbaena iznimka.

Primjer 11-6. Rad s objektom Exception


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

Sendregion

namespace ExceptionObject
{
public class Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// Pokuava podijeliti dva broja
// i obrauje mogue iznimke
public void TestFunc()
{
try
{
Console.WriteLine( "Open file here" );
double a = 1 2 ;
. double b = 0;
Console.WriteLine( "{} / {l} = {2}'',
a, b, OoOividej a, b ) );
Console.WriteLine(
"This line may or may not print" );
}

// Najee izvoene iznimke idu prve

256 | Programiranje C#
rimjer 11-6. Rad s objektom Exception (nastavak)
catch ( System.DivideByZeroException e )

Console.WriteLine(
"\nDivideByZeroException! Msg: {o}".
e.Message );
Console. WriteLine(
"knHelpLink: {o}", e.HelpLink );
Console.WriteLine(
"inHere's a stack trae: {0}\n",
e.StackTrace );
}
catch (System.Exception e)

Console.WriteLine(
"Unknown exception caught + e.Message );

finally
{
Console.WriteLine(
"Close file here." );

// Dijeli ako je dozvoljeno


public double DoDivide( double a, double b )

if ( b == o )
{
Divide8yZexoException e =
new DivideByZeroException();
e.HelpLink =
"http://www.libertyassociates.com";
throw e;
}
if ( a == o )
throw new ArithmeticException();
return a / b;

f e a g u stogu koji se vidi u izlazu popisuje sve metode redoslijedom obrnutim od redo-
1 ; e 3 P zlvanja tj. u tragu se vidi kako se pogreka pojavila u metodi DoDivide() koju
^ je pozvala metoda TestFunc(). Kad su metode duboko ugnijeene, trag u stogu vam
||moze pomoi u razumijevanju redoslijeda pozivanja metoda.

U primjeru se umjesto jednostavnog izbacivanja iznimke DivideByZeroException stvara


jp o v a instanca iznimke:
^ DivideByZeroException e = new DivideByZeroException();

j j p prosljeuje se prilagoena poruka pa se ispisuje zadana poruka:


DivideByZeroException! Msg: Attempted to divide by zero.

Poglavlje 11: Obrada iznimki | 257


Ovaj red koda se moe modificirati kako bi se proslijedila podrazumijevana poruka-
new DivideByZeroException(
"You tried to divide by zero which is not meaning-ful");

U ovom sluaju, izlazna poruka oslikavat e prilagoenu poruku:


DivideByZeroException! Msg:
You tried to divide by zero which is not
meaningful

Prije izbacivanja iznimke postavlja se svojstvo HelpLink:


e.HelpLink = "http://www.libertyassociates.com";

Kad se iznimka uhvati program ispisuje poruku i HelpLink:


catch (System.DivideByZeroException e)
{
Console.WriteLine("\nDivideByZeroException! Msg: {o}",
e.Message);
Console.WriteLine("\nHelpLink: {o}", e.HelpLink);

To vam omoguava da korisniku pruite korisne informacije. Uz to, ispisuje se i Stac-


kTrace uzimanjem svojstva StackTrace objekta iznimke:
Console.WriteLine("\nHere's a stack trae: {o}\n",
e.StackTrace);

U izlazu ovog poziva vidi se potpuni StackTrace do trenutka izbacivanja iznimke:


Here's a stack trae:
at Programming_CSharp.Test.DoDivide(Double a, Double b)
in c:\...exceptiono6.cs:line 56
at Programming_CSharp.Test.TestFunc()
in...exceptiono6.cs:line 22

Nazivi staza su skraeni pa va izlaz moe izgledati drugaije.

Prilagoene iznimke
Ugraeni tipovi iznimki koje CLR prua, zajedno s prilagoenim porukama prika-
zanim u prethodnom primjeru, bit e vam dovoljni za pruanje dovoljno detaljnih
informacija catch bloku.
Ponekad e vam, meutim, biti potrebne zasebne metode za obradu iznimki ovisno o
uzroku iznimke. Za to ete morati stvoriti vlastite prilagoene tipove iznimki (stoga
moete stvoriti i zasebne metode za obradu iznimki). Prilagoeni tipovi iznimki mogu
dodati vi? informacija ili sposobnosti, no glavni je razlog postojanje drugaijeg tipa
kojeg blok catch moe razlikovati.

Microsoft preporua da se u potpunosti izbjegava izbacivanje osnov-


nog objekta Exception, pa ak i objekta ApplicationException. Te je
objekte najbolje tretirati kao apstraktne tipove.

258 | Programiranje C#
jednostavno je potrebno stvoriti vlastitu klasu prilagoene iznimke. Jedino je oerani-
enj da ona mora izvoditi (izravno ili neizravno) iz System.ApplicationException. U
primjeru 11-7 prikazano je stvaranje prilagoene iznimke.

P rim jer 11-7. S t v a r a n je p r i l a g o e n e iz n im k e

ftregion Using dire ctives

using System;
using S y s t e m .C o l l e c t i o n s .C e n e r i c ;
using System.Text;

tfendregion

namespace CustomExceptions
{
public class MyCustomException :
System.ApplicationException
{
public MyCustomException( string message ):
base(message)
{

}
}

public class Test


{
public static void Main()

Test t = new Test();


t.TestFunc();
}

// Pokuava podijeliti dva broja


// i obrauje moguu iznimku
public void TestFunc()

try
{
Console.WriteLine( "Open file here );
double a = 0 ;
double b = 5 ;
Console.WriteLine( "{oj / {i} = { 2 }",
a, b, DoDivie( a, b ) );
Console.WriteLine(
"This line may or may not print" );

// Najee izvoen tip iznimke ide prvi


catch ( System.DivideByZeroException e )

Console.WriteLine(
"\nDivideByZeroException! Msg: {o},

Poglavlje 11: Obrada iznimki [ 259


Primjer 11-7. Stvaranje prilagoene iznimke (nastavak)
e.Message );
Console.WriteLine(
"NnHelpLink: {o}\n, e.HelpLink );
}
catch ( MyCustomException e )
a
{ a
Console.WriteLine(
\nMyCustomException! Msg: {0}",
e.Message );
Console.WriteLine(
NnHelpLink: {0}\n", e.HelpLink );
}
catch
{
Console.WriteLine(
"Unknown exception caught" );
}
finally
{
Console.WriteLine( "Close file here." );
}

// Dijeli ako je dozvoljno


public double DoDivide( double a, double b )
{
if ( b == o )
{
DivideByZeroException e =
new DivideByZeroException();
e.HelpLink =
"http://www.libertyassociates.com'';
throw e;
}
if ( a == 0 )
{
MyCustomException e =
new MyCustomException(
"Can't have zero divisor" );
e.HelpLink =
"http ://www.libertyassociates.com/NoZeroDivisor.htm";
throw e;
}
return a / b;
}
}
}
Iznimka MyCustomException izvedena je iz System.ApplicationException i sastoji se
samo od konstruktora koji prihvaa niz message i prosljeuje ga do osnovne klase, kao
to je opisano u poglavlju 4. U ovom sluaju, prednost izrade klase prilagoene iznimke
je u tome to ona bolje odraava odreeni dizajn klase Test u kojoj djelitelj nula nije

260 | Programiranje C#
joputen. Umjesto prilagoene iznimke moe se koristiti i ArithmeticException, no
|o bi moglo zbuniti druge programere jer se djelitelj nula obino ne smatra aritmeti-
Jkom pogrekom.

Ponovno izba civa nje izn im k i


Moda ete htjeti da blok catch izvede neku poetnu korektivnu akciju i zatim ponovno
izbaci iznimku do vanjskog bloka try (u pozivajuoj metodi). On moe izbaciti istu
Sili moe izbaciti drugaiju iznimku. Ako izbaci drugaiju iznimku, moda e izvornu
iznimku ukljuiti unutar nove iznimke kako bi pozivna metoda mogla razumjeti pret-
ili hodne iznimke. Svojstvo InnerException nove iznimke uzima izvornu iznimku.

Neke iznimke imaju smisla samo u kontekstu u kojem su izbaene. To


se osobito odnosi na, primjerice, NullReferenceException koja moe biti
rezultat pogrenog unosa. Kada to ne moete predvidjeti prethodnom pro-
vjerom unosa, iznimku treba uhvatiti i zatim izbaciti ArgumentException
koja e pozivatelju dati toniju informaciju o uzroku problema.

Ponekad je na granicu sloja ili dizajna dobro postaviti metodu za hvatanje


kako bi se uhvatile neoekivane iznimke. U tom sluaju moete izbaciti
prilagoenu iznimku InternalErrorException koja klijentskom kodu sig-
nalizira kako je s komponentom neto polo u krivom smjeru.

Kakoje InnerException takoer iznimka, i ona moe imati unutarnju iznimku. Stoga se
| cijeli lanac iznimki moe ugnijezditi unutar drugog lance, kao to se figurice babuke
% stavljaju jedna u drugu. To je prikazano u primjeru 11-8.

IHiP rim jer 1 1 -8 . P o n o v n o i z b a c i v a n j e u n u t a rn jih iz n im k i

jKregion Using directives

Hj.using ystem;
using System.Collections.Generic;
P.using System.Text;
'
||'#endregion

namespace RethrowingExceptions
{
public class MyCustomException : System.ApplicationException

Public MyCustomException(
string message, Exception inner ):
base(message,inner)
{

Poglavlje 11: Obrada iznimki | 261


Primjer 1 1 -7 . Stvaranje prilagoene iznimke (nastavak)
public class Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();

public void TestFunc()


{
try
{
DangerousFunci();
}

// Ako uhvati prilagoenu iznimku


// ispisuje povijest iznimke
catch ( MyCustomException e )
{
Console.WriteLine( "\n{o}'\ e.Message );
Console.WriteLine(
"Retrieving exception history..." );
Exception inner =
e.InnerException;
while ( inner != nuli )
{
Console.WriteLine(
"{0}", inner.Message );
inner =
inner.InnerException;
}
}
}
public void DangerousFuncl()
{
try
{
DangerousFunc2();
}

// Ako ovdje ulovi iznimku


// izbacuje prilagoenu iznimku
catch ( System.Exception e )
{
MyCustomException ex =
new HyCustomException(
"E3 - Custom Exception Situation!",
throw ex;
}
}

public void DangerousFunc2()

262 | Programiranje C#
11-7. Stvaranje prilagoene iznimke (nastavak)

try
{
DangerousFunc3();
}
.i // Ako uhvati DivideByZeroException poduzima
s
is t
// korektivne akcije i izbacuje ope iznimke
catch ( System.DivideByZeroException e )
t
Exception ex =
new Exception(
"E2 - func2 caught divide by zero", e );
throw ex;
}
}

public void DangerousFunc3 ()


{
try
{
DangerousFunc4 ();
}
catch ( System.ArithmeticException )

Console.WriteLine("Arithmetic exception caught in 0 F 3 ,


and rethrown..
throw;
}

catch ( 5ystem.Exception )

Console.WriteLine(
"Exception handled here." );
' }
}

public void DangerousFunc4 ()

throw new DivideByZeroException( "El - DivideByZero Exception" );

}
}

Kakoje kod sveden na osnovne elemente, izlaz moe biti pomalo nejasan. Najbolji nain
za razumijevanje ovog koda je propustiti ga kroz alat za ispravljanje pogreaka.
Ponite pozivanjem metbde DangerousFunci() u bloku try:
try
{
DangerousFuncl();

Poglavlje 11: Obrada iznimki | 263


Metoda DangerousFuncl() poziva metodu DangerousFunc2() koja poziva metodu'
DangerousFunc3(), a ona zatim poziva metodu DangerousFunc4(). Svi ti pozivi nalaze'
se unutar vlastitih blokova try. Metoda DangerousFunc4() na kraju izbacuje iznimku F
DivideByZeroException. DivideByZeroException inae ima vlastitu poruku o pogreci ali
slobodno moete proslijediti i prilagoenu poruku. Ovdje, kako bi se slijed dogaaja to
lake identificirao, proslijeena je prilagoena poruka El - DivideByZeroException.

Iznimka izbaena u metodi DangerousFunc4() uhvaena je u bloku catch unutar metode !


DangerousFunc3(). Logika metode DangerousFunc3() je da u sluaju uhvaene iznimke
ArithmeticException (poput DivideByZeroException) ne poduzima nikakvu akciju, ve
samo ponovno izbacuje iznimku:
catch (Systein.ArithmeticException)
{
Console.WriteLine("Arithmetic exception caught in DF3,
and rethrown..
throw;
}
Sintaksa ponovnog izbacivanja iste iznimke (bez modifikacija) sastoji se samo od rijei
throw.

Iznimka se stoga ponovno izbacuje do metode DangerousFunc2() koja ju hvata, pod-


uzima akciju ispravljanja i izbacuje novu iznimku tipa Exception. U konstruktor te
nove iznimke, metoda DangerousFunc2() prosljeuje prilagoenu poruku (E2 - Func2
caught divide by zero) i izvornu iznimku. Stoga izvorna iznimka (El) postaje Inne-
rException za novu iznimku (E2). Metoda DangerousFunc2() zatim izbacuje tu novu
iznimku E2 do metode DangerousFuncl().
Metoda DangerousFuncl() hvata iznimku, izvodi odreenu akciju te stvara novu
iznimku tipa MyCustomException. Ona novi niz (E3 - Custom Exception Situation!)pro-
sljeuje do konstruktora, isto kao i upravo uhvaenu iznimku (E2). Upamtite, upravo
uhvaena iznimka je ona ija je unutarnja iznimka DivideByZeroException (El). Sada
imate iznimku tipa MyCustomException (E3) s unutarnjom iznimkom tipa Exception
(E2), koja zatim ima unutarnju iznimku tipa DivideByZeroException (El). Sve se to
zatim izbacuje do metode test koja hvata.
Kad se pokrene, metoda catch ispisuje poruku:
E3 - Custom Exception Situation!

i zatim prolazi kroz slojeve vanjskih iznimki, ispisujui njihove poruke:


while (inner != nuli)
{
Console.WriteLine("{0}",inner.Message);
inner = inner.InnerException;
}
U izlazu se vidi lanac izbaenih i uhvaenih iznimki:
Retrieving exception history...
E2 - Func2 caught divide by zero
El - DivideByZero Exception

264 1 Programiranje C#
Mjesto toga moete i za iznimku pozvati metodu ToString():
Console.Write(e.ToStringO);

I izlazu se vidi cijeli stog poruka i s njim povezani stogovi poziva:


RethrowingExceptions.MyCustomException: E3 - Custom Exception Situation!
System.Exception: E2 - Func2 caught divide by zero --->
System.DivideByZeroException: E1 - DivideByZero Exception
at RethrowingExceptions.Test.DangerousFunc4() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 114
at RethrowingExceptions.Test.DangerousFunc3() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 102
at RethrowingExceptions.Test.DangerousFunc2() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 79
End of inner exception stack trae ---
at RethrowingExceptions.Test.DangerousFunc2 () in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 89
at RethrowingExceptions.Test.DangerousFuncl() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 6l
--- End of inner exception stack trae ---
t*v at RethrowingExceptions.Test.DangerousFuncl() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 71
at RethrowingExceptions.Test.TestFunc() in c:\rethrowingexceptions\
rethrowingexceptions.cs:line 33

Poglavlje 11: Obrada iznimki | 265


POGLAVLJE 12
Delegati i dogaaji

Kada umre neki dravnik, predsjednik Sjedinjenih Amerikih Drava obino nema
dovoljno vremena da osobno ode na pogreb. On e stoga na pogreb poslati delegata.
Taj delegat je esto potpredsjednik, no ako ni potpredsjednik nije u mogunosti otii na
pogreb, predsjednik mora poslati nekog drugog, na primjer ministra vanjskih
poslova ili ak prvu damu. On ovlatenje delegata nee vrsto povezati sa samo jed-
nom osobom kako bi ga mogao dodijeliti bilo kojoj osobi koja je sposobna dobro oba- f
viti meunarodni protokol.
Predsjednik unaprijed definira koje e se ovlatenje dodijeliti (odlazak na pogreb), koji n
e se parametri prenijeti (suut, ljubazne rijei) te kojoj se povratnoj vrijednosti nada |
(dobra volja). On zatim ovlatenje dodjeljuje odreenoj osobi u prikladnom trenutku j
dok traje njegov mandat.
1
U programiranju se esto suoavamo sa situacijama u kojima je potrebno izvesti odre- >
enu akciju ali se ne zna unaprijed koja e se metoda ili objekt pozvati na izvoenje. /
Na primjer, gumb moe znati kako treba obavijestiti objekt da je pritisnut, ali moda : i
ne zna koji objekt ili objekte treba obavijestiti. Umjesto da gumb poveete s odreenim
objektom, povezat ete ga s delegatom i zatim tog delegata pri izvoenju programa j
dodijeliti odreenoj metodi. i.
U ranim, mranim i primitivnim danima programiranja, programi bi poeli izvoenje
i zatim prolazili kroz sve korake do kraja. Ako je korisnik bio ukljuen u proces, inter-
akcija je bila strogo kontrolirana i ograniena na popunjavanje polja.
Za dananji model programiranja s grafikim korisnikim sueljem potreban je druga-
iji pristup, poznatiji kao programiranje temeljeno na dogaajima (engl. event driven
programjning). Moderni program prikazuje korisniko suelje i eka na akciju kori-
snika. Korisnik moe izvesti vie razliitih akcija, poput odabira opcije iz izbornika,
pritiska na gumb, osvjeavanja tekstualnih polja, pritiska na ikonu i tako dalje. Svaka
akcija izaziva dogaaj. Drugi se dogaaji mogu izazvati bez izravne akcije korisnika.
Takvi su, na primjer, dogaaji koji odgovaraju na otkucaj unutarnjeg sata, prijem
poruke elektronike pote, dovretak operacije kopiranja datoteke itd.

266
Jp| j dogaaj je uahurena zamisao da se dogodilo neto na to program mora odgo-
v o r i t i . Dogaaji i delegati su usko povezani koncepti jer je za fleksibilno upravljanje
dogaajima potrebno da se odgovor na dogaaj otpremi do odgovarajue metode za
Slpbradu dogaaja. Metoda za obradu dogaaja se u C# najee implementira preko
feelegata.
Bpelegati se koriste i kao povratni pozivi kako bi jedna klasa drugoj mogla rei uini
'dvo i obavijesti me kad si gotova".

^ Delegati
Delegati (engl. delegates) su u C# objekti prve klase i za njih postoji potpuna podrka
u jeziku. Delegat je tehniki referentni tip koji se koristi za uahurivanje metode s
' odreenim potpisom i povratnim tipom.- U delegata se moe uahuriti bilo koja odgo-
u varajua metoda

To se C ++ i mnogim drugim jezicima moe postii preko pokazivaa


metode i pokazivaa metoda lanica.

Delegat se stvara s pomou kljune rijei delegate iza koje se navodi povratni tip i pot-
pis metoda koje mu se mogu delegirati, kao u sljedeem primjeru:
public delegate int WhichIsFirst(object objl, object obj2);

Ovom se deklaracijom definira delegat WhichIsFirst koji e uahuriti bilo koju metodu
koja kao parametre uzima dva objekta i vraa tip int.
;Nakon definiranja delegata u njega moete uahuriti metodu lanicu tako to ete ga
; instancirati prosljeivanjem metode koja odgovara navedenom povratnom tipu i pot-
pisu. Moete koristiti i anonimne metode, kao to je opisano kasnije. U oba se sluaja
delegat moe koristiti za pozivanje uahurene metode.

Koritenje delegata za zadavanje metoda tijekom izvedbe


Delegati razdvajaju klasu koja deklarira delegata od klase koja delegata koristi. Pret-
postavimo, na primjer, kako elite stvoriti jednostavno generiku kontejnersku klasu
Pair koja moe sadravati i sortirati bilo koja dva objekta koji joj se proslijede. Ne
moete unaprijed znati kakve e objekte klasa Pair sadravati, ali stvaranjem metoda
unutar objekata kojima se moe prepustiti sortiranje moete odreivanje redoslijeda
objekata delegirati samim tim objektima.

Razliiti e se objekti razliito sortirati (na primjer, par objekata Counter moe se sor-
i ttrati brojanim redoslijedom, dok se par objekata Button moe sortirati abecednim

V Ako je metoda metoda instance, delegat uahuruje i ciljni objekt.

Poglavlje 12: Delegati i dogaaji | 267


redoslijedom prema nazivima). Kao autor klase Pair elite da objekti od kojih se pa r;
i.
sastoji znaju koji treba biti prvi, a koji drugi. Kako bi ste to postigli, inzistirat ete da
objekti koji se spremaju u Pair moraju imati metodu koja govori kojim se redoslijedom <
objekti sortiraju.
Ovaj zahtjev moete definirati i s pomou suelja. Delegati su manji od suelja i finije
granulacije. Klasa Pair ne treba implementirati cijelo suelje ve samo treba definirati
potpis i povratni tip metode koju eli pozvati. Za to slue delegati - oni definiraju
povratni tip i potpis metoda koje se mogu pozvati kroz suelje.
U ovom sluaju klasa Pair e deklarirati delegata WhichIsFirst. Kad je klasi Pair
potrebna informacija o redoslijedu sortiranja objekata, ona poziva delegata i svoja
dva objekta prosljeuje kao parametre. Ovlatenje za odreivanje redoslijeda objekata
delegira se metodi koju je delegat uahurio:
public delegate Comparison
WhichIsFirst( T objl, T obj2 )

U ovoj je definiciji delegat WhichIsFirst definiran tako da uahuruje metodu koja kao
parametre prima dva objekta i vraa objekt tipa Comparison. Comparison je enumeracija
koju ete definirati kao:
public enum Comparison
{
theFirstComesFirst = 1 ,
theSecondComesFirst = 2
}
Kako bi isprobali delegata, stvorit ete dvije klase: Dog i Student. One nemaju ba
mnogo zajednikih karakteristika, osim to obje implementiraju metode koje moe
uahuriti delegat WhichIsFirst, stoga su i objekti Dog i objekti Student prihvatljivi za
spremanje unutar objekata Pair.
U probnom programu stvorit ete nekoliko objekata Student i nekoliko objekata Dog te
ih sve spremiti u Pair. Zatim ete stvoriti instance WhichIsFirst kako biste uahurili
odgovarajue metode koje e odrediti redoslijed objekata Student i Dog. Promotrimo
ovaj postupak korak po korak.
Poinjete sa stvaranjem konstruktora Pair koji uzima dva objekta i sprema ih u pri-
vatno polje:
public Pair(
T firstObject,
T secondObject )
{
thePair[o] = firstObject;
thePairfl] = secondObject;
}
Zatim preoptereujete metodu ToStringO kako biste dobili vrijednost niza dva
objekta:

268 | Programiranje C#
public override string ToString()

^ return thePair [o] .ToStringO + ", " +


thePair[l],ToStringO;

}
cada unutar objekta Pair postoje dva objekta ije vrijednosti moete ispisati. Spremni
% za sortiranje i ispis rezultata sortiranja. Ne moete unaprijed znati o kakvim se
.|bjektima radi pa ovlatenje za odreivanje redoslijeda objekata delegirate samim
Objektima.

Jpase Dog i Student implementiraju metode koje delegat MhichlsFirst moe uahu-
| lti. Tijekom izvoenja ovaj delegat moe uahuriti bilo koju metoda koja uzima dva
.objekta i vraa tip Comparison.

'a klasu Pair sada moete definirati metodu S ort():


public void Sort(WhichIsFirst theDelegatedFunc)
{
if (theDelegatedFunc(thePair[o],thePair[lj) ==
jM: Comparison.theSecondComesFirst)

3P T temp = thePairfo];
thePair[o] = thePairfl];
*=; thePair[i] = temp;
}

^Ova metoda prihvaa parametar: delegat tipa WhichIsFirst pod imenom theDelega-
jedFunc. Metoda Sort() ovlatenje za odreivanje redoslijeda objekta u Pair delegira

IJiietodi uahurenoj u delegatu. U tijelu metode Sort() poziva se delegirana metoda i


provjerava se povratna vrijednost koja e biti jedna od dvije enumerirane vrijednosti
m z Comparison.

sfAko se vrati vrijednost theSecondComesFirst, objekti unutar para zamjenjuju mjesta.


||Jsuprotnom se ne izvodi nikakva akcija.

Cje analogno funkcioniranju ostalih parametara. Ako imate metodu koja kao para-
jjhetar uzima int:
int SomeHethod (int myParam){//...}

-|fiaziv parametra je myParam, no proslijeditise moe bilo koja int vrijednost ili varijabla.
|Shno tome, parametar u primjeru delegata ima ime theDelegatedFunc, no proslijediti
|se moe bilo koja metoda ija povratna vrijednost i potpis odgovarju onima definira-
-nim u delegatu WhichIsFirst.

^Pretpostavimo da objekte Student elite sortirati prema nazivu. Ako elite da na prvom
mjestu bude ima prvog studenta, napiite metodu koja vraa vrijednost theFirstComes -
. First, a ako elite da na prvom mjestu bude ime drugog studenta, napiite metodu koja
jVraa vrijednost theSecondComesFirst. Ako proslijedite ,,Amy, Beth, metoda vraa
-|theFirstComesFirst, a ako proslijedite Beth, Amy, ona vraa theSecondComesFirst.

Poglavlje 12: Delegati i dogaaji | 269


Ako se vrati vrijednost theSecondComesFirst, metoda Sort() e elemente u svom
navesti obrnutim redoslijedom, postavljajui Amy na prvo mjesto, a Beth na drug0
Zatim dodajte jo jednu metodu, ReverseSort(), koja obre redoslijed elemenata p0lja:
public void ReverseSort(WhichIsFirst theDelegatedFunc)

if (theDelegatedFunc(thePair[0], thePairjlj) ==
Comparison.theFirstComesFirst)

{
T temp = thePairjo);
thePairjO] = thePair[l];
thePair[l] = temp;

}
Logika je ista kao kod metode S o rt(), samo to ova metoda izvodi zamjenu ako delegi-1
rana metoda kae da prvi element dolazi na prvo mjesto. Budui da delegirana metoda|
misli kako prvi element dolazi na prvo mjesto i radi se o obrnutom redoslijedu, eljeni
rezultat je da se drugi element nalazi na prvom mjestu. Ako ovaj put proslijedite ,,Amy, ;
Beth, delegirana metoda vraa theFirstComesFirst (tj. Amy se treba nalaziti na prvom i
mjestu), o budui da se radi o obrnutom redoslijedu, dolazi do zamjene vrijednosni i
na prvom se mjestu nalazi Beth. To omoguava da koristite istu delegiranu metodu
kao kod metode S o rt(),b ez potrebe da objekt podrava metodu koja vraa vrijednost
sortiranu obrnutim redoslijedom.
Sada su vam potrebni jo samo objekti za sortiranje. Stvorit ete dvije nevjerojatno jed-
nostavne klase Student i Oog. Objektima Student prilikom stvaranja dodijelite imena:
public class Student
{
public Student(string name)
{
this.name = name;
}
Klasa Student zahtjeva dvije metode: prvu koja premouje metodu ToStringO i drugu
koja e se uahuriti kao delegirana metoda.
Student mora premostiti metodu ToStringO tako da u Pair ta metoda, koja poziva
ToStringO za sadrane objekte, ispravno funkcionira. Implementacijom se samo vraa
ime studenta (koje ve jest objekt niza):
public override string To Strin g O
{
return name;
} '
Student mora implementirati i metodu kojoj Pair. Sort() moe delegirati ovlatenje za
odreivanje redoslijeda objekata:
return (String.Compare(sl.name, s2.name) < 0 ?
Comparison.theFirstComesFirst :
Comparison.theSecondComesFirst);

270 | Programiranje C#
feletoda String.Compare() je metoda .NET kostura iz klase String koja usporeuje
|gjva niza i vraa vrijednost manje od nule ako je prvi manji, vee od nule ako je drugi
Jljiiz manji i nulu ako su nizovi jednaki. Ta je metoda detaljnije obraena u poglavlju
JpO. Obratite pozornost da primijenjena logika vraa theFirstComesFirst samo ako je
||prvi niz manji. Ako su nizovi jednaki ili je drugi niz vei, metoda vraa theSecond-
KtornesFirst.

Jtjetoda WhichStudentComesFirst() kao parametre uzima dva objekta i vraa tip Com-
Hjparison. Time je ona prikladna da bude Pair.WhichIsFirst delegirana metoda ijem
jgpotpisu i povratnoj vrijednost odgovara.
'Druga je klasa Dog. Objekte klase Dog poredat emo prema teini tako da se laki psi
g f nalaze ispred teih. Potpuna deklaracija klase Dog glasi:
public class Dog
{
public Dog(int weight)
{
this.weight=weight;
}

U Psi se sortiraju po teini


public static Comparison WhichDogComesFirst(
m Object ol, Object 0 2 )
{
Dog dl = (Dog) ol;
Dog d2 = (Dog) 0 2 ;
return dl.weight > d2.weight ?
Comparison.theSecondComesFirst :
m Comparison.theFirstComesFirst;
1
public override string ToSt ring O

return weight.ToStringO;
. 1
private int weight;

Klasa dok takoer premouje metodu ToStringO i implementira statiku metodu s


!|ispravnim potpisom za delegata. Obratite pozornost da metode delagata Dog i Student
n em aju ista imena. One ne moraju imati ista imena jer e se dinamiki dodijeliti dele-
J|; gatu u vrijeme izvoenja.

Delegirane m e t o d e mo e te nazvati k a k o g o d elite, n o koritenje para-

<*4 lelnih naziva (na primjer, WhichStudentComesFirst iUhichOogComesFirst)


ini k o d jednostavnijim za itanje, razumijevanje iodravanje.

: U primjeru 12-1 prikazan je cijeli program koji ilustrira nain pozivanja delegiranih
i metoda.

Poglavlje 12: Delegati i dogaaji | 271


1 a
P r im je r 1 2 -1 . R a d s d e l e g a t i m a

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Delegates
{
public enum Comparison
{
theFirstComesFirst = 1,
theSecondComesFirst = 2
}

// Jednostavna kolekcija za dvije stavke


public class Pair<T>
{
// Privatno polje za dva objekta
private T[] thePair = new T[2];

// Deklaracija delegata
public delegate Comparison
WhichIsFirst( T objl, T obj2 );

// Proslijeeni konstruktor uzima dva objekta,


// po redu kojim su primljeni
public Pair(
T firstObject,
T secondObject )
{
thePair[o] = tirstObject;
thePair[l] = secondObject;
}
// Javna metoda koja sortira dva objekta
// po kriteriju koji zadaju objekti
public void Sort(
HhichlsFirst theDelegatedFunc )
{
if ( theDelegatedFunc( thePair[o], thePair[l] )
== Comparison.theSecondComesFirst )
{
T temp = thePair[o];
thePairfo] = thePair[l];
thePair[l] = temp;
}
}
// Javna metoda koja sortira dva objekta
// prema redoslijedu suprotnom od onog

272 | Programiranje C#
I
fimjer 12-1 Rads delegatima (nastavak)
I I koji zadaju objekti
public void ReverseSort(
KhichlsFirst theDelegatedFunc )
{
if ( theDelegatedFunc( thePair[o], thePair[l] ) ==
Comparison.theFirstComesFirst )
{
T temp = thePairjo];
thePair[o] = thePair[l];
thePair[l] = temp;
}
}

// Trai od objekata da daju svoje vrijednosti niza


public override string ToStringO
{
return thePairjo] .ToStringO + ",
+ thePairjl],ToStringO;
}
} // Kraj klase Pair

public class Dog


{
private int weight;

public Dog( int weight )


{
this.weight = weight;
}

I I Psi se sortiraju po teini


public static Comparison WhichDogComesFirst(
Dog dl, Dog d2 )
{
' return dl.weight > d2.weight ?
Comparison.theSecondComesFirst :
Comparison.theFirstComesFirst;
}
public override string ToStringO
{
return weight.ToStringO;
}
} // Kraj klase Dog

public class Student


{
private string name;

public Student( string name )


{
this.name = name;
}

f Poglavlje 12: Delegati i dogaaji | 273


a!*
Primjer 12-1. Rad s delegatima (nastavak)
I I Studenti se sortiraju abecedno
public static Comparison
WhichStudentComesF irst( Student si, Student s2 )
{
return ( String.Compare( sl.name, s2.name ) < o ?
Comparison.theFirstComesFirst :
Comparison.theSecondComesFirst );
}
public override string T o Strin g O
{
return name;
}
} // Kraj klse Student

public class Test


{
public static void Main()
{
// Pravi dva studenta i dva psa
// i dodaje ih objektima Pair
Student lesse = new Student( "Jesse" );
Student Stacey = new Student( "Stacey" );
Dog Milo = new Dog( 65 );
Dog Fred = new Dog( 12 );

Pair<Student> studentPair = new Pair<Student>( lesse, Stacey );


Pair<Dog> dogPair = new Pair<Dog>( Milo, Fred );
Console.WriteLine( "studentPair\t\t\t: joj",
studentPair.ToStringO );
Console.WriteLine( "dogPair\t\tYt\t: {0}",
dogPair.ToStringO );

// Instancira delegate
Pair<Student>.WhichIsFirst theStudentDelegate =
new Pair<Student>.WhichIsFirst(
Student.UhichStudentComesFirst );

Pair<Dog>.WhichIsFirst theDogDelegate =
new Pair<Dog>.WhichIsFirst(
Dog.WhichDogComesFirst );

// Sortira s pomou delagata


studentPair.Sort( theStudentDelegate );
Console.WriteLine( "After Sort studentPair\t\t: {o}",
studentPair.ToStringO );
studentPair.ReverseSort( theStudentDelegate );
Console.WriteLine( "After ReverseSort studentPair\t: {o}",
studentPair.ToStringO );

dogPair.Sort( theDogDelegate );
Console.WriteLine( "After Sort dogPair\t\t: {0}",
dogPair.ToStringO );

274 | Programiranje C#
r^nljer 1 2 - 1 . Rad s delegatima (nastavak)
dogPair.ReverseSort( theDogDelegate );
Console.WriteLine( "After ReverseSort dogPair\t: {o}",
dogPair.ToString() );

^ }
}

,Il0gram Test stvara dva objekta Student i dva objekta Dog koje zatim dodaje u kontej-
ierPair. Konstruktor student uzima string za ime studenta, a konstruktor dog uzima
?4int za teinu psa:
Student lesse = new Student( "lesse" );
% Student Stacey = new Student( "Stacey" );
Dog Milo = new Dog( 65 );
Dog Fred = new Dog( 12 );
4 to!
Pair<Student> studentPair = new Pair<Student>( lesse, 5tacey );
Pair<Dog> dogPair = new Pair<Dog>( Milo, Fred );
Console.WxiteLine( "studentPair\t\t\t: {o}",
studentPair.ToStringO );
Console.WriteLine( "dogPair\t\t\t\t: {o}",
dogPair.ToStringO );

Zatim se ispisuje sadraj dva kontejnera Pair kako bi se vidio redoslijed objekata. Izlaz
, je sljedei:
studentPair : Tesse, Stacey
dogPair : 65, 12

Kao to smo i oekivali, redoslijed objekata je isti kao i prilikom dodavanja u kontej-
, nere Pair. Zatim se instanciraju dva objekta delegata:
Pair<Student>.UhichlsFirst theStudentDelegate =
f- new Pair<Student>.WhichIsFirst(
Student.WhichStudentComesFirst );

Pair<Dog>.WhichIsFirst theDogDelegate =
new Pair<Dog>.WhichIsFirst(
Dog.whichDogComesFirst );

jPm se delegat, theStudentDelegate, stvara prosljeivanjem odgovarajue statike


metode iz klase Student. Drugom je delegatu theDogDelegate prosljeena statika
metoda iz klase Dog.

'/Delegati su sada objekti koji se mogu proslijediti metodama. Oni se prvo prosljeuju
^metodi Sort() objekta Pair, a zatim metodi ReverseSort(). Rezultati se ispisuju na
-konzoli:
After Sort studentPair : Tesse, Stacey
After ReverseSort studentPair : Stacey, lesse
After Sort dogPair : 12, 65
After ReverseSort dogPair : 65, 12

Poglavlje 12: Delegati i dogaaji | 275


Delegati i metode instance
Delegati iz primjera 12-1 uahuruju statike metode kao u sljedeem kodu:
public static Comparison
WhichStudentComesFirst(Student si, Student s2)

Delegat se zatim instancira s pomou klase umjesto s pomou instance:


Pair<Student>.WhichIsFirst theStudentDelegate =
new Pair.WhichIsFirst(
Student.WhichStudentComesFirst);

Uahuriti moete i metode instance:


public Comparison
WhichStudentComesFirst(Student si, Student s2)

U tom ete sluaju delegata instancirati prosljeivanjem metode instance, a ne kroz


klasu:
Pair<Student>.WnichIsFirst theStudentDelegate =
new Pair<Student>.WhichIsFirst(
lesse.WhichStudentComesFirst);

Statiki delegati
Primjeru 12-1 moe se zamjeriti to od pozivajue klase (u ovom sluaju Test) zahti-
jeva instanciranje delegata koji su potrebni za sortiranje objekata u Pair. Dobro bi bilo
kad bi se delegati mogli dobiti izravno iz klasa Student i Dog. To se moe postii tako
da se svakoj klasi da vlastiti statiki delegat. Student moete modificirati tako da joj
se doda:
public static readonly Pair<Student>.WhichIsFirst OrderStudents =
new Pair<Student>.WhichIsFirst( Student.HhichStudentComesFirst.);

Time se stvara statiko polje delegata samo za itanje pod nazivom OrderStudents.
<r,
Polje OrderStudents se kao polje samo za itanje nakon stvaranja vie
ne moe modificirati.

Slinog delegata moete stvoriti i unutar klase Dog:


public static readonly Pair<Dog>.WhichIsFirst OrderDogs =
new Pair<Dog>.WhichIsFirst( Dog. UhichDogComesFirst );

Ta dva delegata su sada statika polja unutar svojih klasa. Svaki je unutar klase pove-
zan s odgovarajuom metodom. Delegati se mogu pozvati bez deklariranja lokalne
instance delegata jednostavnim prosljeivanjem statikog delegata klase:
studentPair.Sort(Student.OrderStudents);
Console.WriteLine("After Sort studentpair\t\t: {o}",
studentPair.ToStringO);
studentPair.ReverseSort(Student.OrderStudents);
Console.WriteLine(''After ReverseSort studentPair\t: {0 }",

276 | Programiranje C#
studentPair.ToString());

dogPair.Sort(Dog.OrderDogs);
Console.WriteLine( "After Sort dogPair\t\t: fo}"
dogPair.ToString());
dogPair.ReverseSort(Dog.OrderDogs);
Console.WriteLine("After ReverseSort dogPair\f Col"
dogPair.ToStringO);

Izlaz ovog promijenjenog koda isti je kao izlaz u primjeru 12-1

Delegati kao svojstva


; Problem kod koritenja statikih delegata je to se oni moraju in sta n c iji ak i ako se
mkad nee konst.tg kao sto je to sluaj sa Stu de nt i Dog u primjeru 12-1. Ako treba e

m 8 ,i b iS K polia k ih

Z a Student, uzmite deklaraciju:

public static readonly Pair<Student>.WhichIsFirst OrderStudents -


new Pair<Student>.WhichIsFirst( Student.UhichStudentComesFirt );
i zamijenite ju sljedeom deklaracijom:
public static Pair<Student>.WhichIsFirst OrderStudents

get
{
return new Pair<Student>.WhichIsFirst( MhichStudentComesFirst );
}
}
Isto tako, statiko polje Dog zamijenite s:
public static Pair<Dog>.WhichIsFirst OrderDogs

get
{
I return new Pair<Dog>.WhichIsFirst( MhichDogComesFirst );

}
Dodjela delegata ostaje ista:
studentPair.Sort(Student.OrderStudents);
dogPair.Sort(Dog.OrderDogs);

Delegat se stvara nakon pristupanja svojstvu Order Stu de nt :

return new Pair.WhichIsFirst(WhichStudentComesFirst);

sNe T a t m i reTon S L Vr0 g PnStU Pa SaSt j l 56 t0 m e t 56 d d e gat stvara tek ko n to


dahe o m o a o m o 8 ucava ^ od red i ka da je d elegat p o tre b a n , no i
sno DogT ^ P J e d m 0 S t l m a StVaran Ja deleg ata P o v i j a klasa S tu de nt ( o d n o -

Poglavlje 12: Delegati i dogaaji | 277


Vieodredini delegati
Ponekad je korisno kroz jednog delegata pozvati dvije (ili vie) implementirane
metode. Taj je postupak iznimno vaan prilikom upravljanja dogaajima (objanjeno
u nastavku ovog poglavlja).
Cilj je imati jednog delegata koji poziva vie od jedne metode. Na primjer, moda ete
kad se pritisne gumb htjeti izvesti vie od jedne akcije.
Dva se delegata mogu kombinirati s pomou operatora zbrajanja (+). Rezultat je novi
vieodredini delegat (engl. multicast delegate ) koji poziva obje izvorne implementi-
rane metode. Pretpostavimo, na primjer, kako se radi o delegatima Writer i Logger
Oni se mogu kombinirati u novi vieodredini delegat myMulticastDelegate na sljedei
nain:
myMulticastDelegate = Writer + Logger;

Delegate moete dodati vieodredinom delegatu s pomou operatora plus-jednako


(+=). Ovaj operator delegata s desne strane dodaje vieodredinom delegatu s lijeve
strane. Pretpostavimo, na primjer, da su Transmitter i myMulticastDelegate delegati.
Transmitter se delegatu myMulticastDelegate moe dodati ovako:
myMulticastDelegate += Transmitter;

Da bismo vidjeli kako se vieodredini delegati stvaraju i koriste proi emo kroz kom-
pletan primjer. U primjeru 12-2 stvorit ete klasu MyClassWithDelegate koja definira
delegat koji kao parametar uzima niz, a vraa void:
public delegate void StringDelegate(string s);

Zatim ete definirati klasu myImplementigClass koja sadri tri metode koje vraaju void
i kao parametar uzimaju niz: W riteString, LogString i TransmitString. Prva metoda
ispisuje niz na standardni izlaz, druga simulira zapisivanje u datoteku dnevnika, a
trea simulira slanje niza putem Interneta. Delegate instancirate kako bi pozvali odgo-
varajue metode:
Writer("String passed to Writer\n");
Logger("String passed to L o g g e r V ) ;
Transmitter("String passed to TransmitterNn");

Da biste vidjeli kako se delegati kombiniraju, stvorit ete jo jednu instancu dele-
gata:
MyClassWithDelegate.StringDelegate myMulticastDelegate;

kojoj ete dodijeliti rezultat zbrajanja11dva postojea delegata.


myMuiticastDelegate = Writer + Logger;

Ovom se delegatu s pomou operatora += dodaje jo jedan delegat:


myMulticastDelegate += Transmitter;

Na kraju se delegati selektivno uklanjaju s pomou operatora -=:


myMulticastDelegate -= Logger;

278 | Programiranje C#
|f primjeru 12-2 prikazan je ovakav nain kombiniranja delegata.

Spritnjer 12-2. Kombiniranje delegata


fifregion Using directives

jjjiing System;
psing System. Collections.Generic;
jiing System.Text;

^fendregion

'ISamespace MulticastDelegates
f.
- 4 : public class MyClassWithDelegate
j {
/ / Deklaracija delegata
public delegate void StringDelegate( string s );

public class Mylmplementingdass

public static vo id WriteString( string s )

} Console.WriteLine( "Writing string {o}\ s );

public static void LogString( string s )

^ Console.WriteLine( Logging string {o}, s );

public static void TransmitString( string s )

j Console-WriteLine( "Transmitting string {o}", s );

public class Test

public static void Main()

/ / Definira tri delegata StringDelegate


MyClassWithDelegate.StringDelegate
Hriter, Logger, Transmitter;

// Pef^ raJ 0 JednoS delegata StringDelegate


U koji ce biti vieodredini delegat
MyClassWithDelegate.StringDelegate
myMulticastDelegate;

U Instancira prva tri delegata,


U prosljeivanjem metoda za uahurivanje

Poglavlje 12: Delegati i dogaaji I 279


Primj er 12-2. Kombiniranje delegata (nastavak)
Writer = new MyClassWithDelegate.StringDelegate(
MyImplementingClass.WriteString );
Logger = new MyClassWithDelegate.StringDelegate(
MyImplementingClass.LogString );
Transmitter =
new MyClassWithDelegate.StringDelegate(
MyImplementingClass.TransmitString );

// Poziva metodu delegata Writer


Writer( "String passed to Writer\n" );

II Poziva metodu delegata Logger


Logger( "String passed to LoggerNn" );

U Poziva metodu delegata Transmitter


Transmitter( "String passed to T r a n s m i t t e r V );

// Govori korisniku da ete kombinirati


// dva delegata u jedan vieodredini delegat
Console.WriteLine(
myMulticastDelegate = Writer + Logger" );

// Kombinira dva delegata a rezultat se


I I pridruuje myMulticast Delegate
myMulticastDelegate = Writer + Logger;

// Poziva delegirane metode. Sit e


I I pozvane dvije metode
myMulticastDelegate(
"First string passed to tollector" );

II Govori korisniku da ete dodati


// trei delegat vieodredinom delegatu
Console.WriteLine(
"\nmyMulticastDelegate += Transmitter" );

// Dodaje treeg delegata


myMulticastDelegate += Transmitter;

I I Poziva tri delegirane metode


myMulticastDelegate (
"Second string passed to Collector" );

I I Govori korisniku da ete ukloniti


, // delegat za zapisivanje u dnevnik
Console.WriteLine(
"\nmyMulticastDelegate -= Logger );

I I Brie delegat za zapisivanje u dnevnik


myMulticastDelegate -= Logger;

I I Poziva dvije preostale


// delegirane metode

280 | Programiranje C#
*ritnjer 22-2- Kombiniranje delegata (nastavak)
myMulticastDelegate(
"Third string passed to Collector" );

I j dijelu Test primjera 12-2 definiraju se instance delegata i pozivaju se prve tri (Idriter,
J^ogger i Transmitter). etvrtom se delegatu, myMulticastDelegate, zatim dodjeljuje
^kombinacija prva dva delegata. Taj se kombinirani delegat zatim poziva, to rezultira
|pozivanjem metoda oba delegata. Dodaje se trei delegat, a kad se pozove myMulticast-
Delegate, pozivaju se sve tri delegirane metode. Na kraju se uklanja Logger pa, kada se
ipozove myMulticastOelegate, pozivaju se samo dvije preostale metode.
Mo vieodredinih delegata moe se najbolje razumjeti na primjeru dogaaja, koji su
objanjeni u nastavku ovog poglavlja. Kad se dogodi dogaaj poput pritiska na gumba,
pridrueni vieodredini delegat moe pozvati seriju metoda za obradu dogaaja koje
e odgovoriti na taj dogaaj.

Dogaaji
Grafika korisnika suelja, kao to su operativni sustav Microsoft Windows ili pre-
glednik Weba, zahtijevaju da programi odgovaraju na dogaaje. Dogaaj (engl. event)
moe biti pritisak na gumb, odabir opcije iz izbornika, dovretak prijenosa datoteke
i tako dalje. Ukratko, dogodi se neto na to trebate odgovoriti. Redoslijed dogaaja
ne moe se predvidjeti. Sustav miruje do pojave dogaaja, a onda izvodi akciju za
obradu dogaaja.
U grafikim korisnikim sueljima bilo koji element moe pokrenuti dogaaj. Na pri-
mjer, kad pritisnete gumb moete pokrenuti dogaaj Click. Dodavanje elemenata na
padajui popis moe pokrenuti dogaaj ListChanged.
Druge klase e biti zainteresirane za odgovaranje na dogaaje. Nain njihova odgova-
ranja nije bitan klasi koja izaziva dogaaj. Gumb jednostavno kae Ja sam pritisnut",
a klase na to odgovaraju na prikladan nain.

Objavljivanje i pretplaivanje
U jeziku C# svaki objekt moe objaviti (engl. publish) skup dogaaja na koje se ostale
klase mogu pretplatiti (engl. subscribe). Kad klasa objavi dogaaj, sve pretplaene
klase se o tome obavjetavaju. Zahvaljujui ovom mehanizmu va objekt moe rei
Mogu vas obavijestiti o sljedeem", a druge se klase mogu pretplatiti i rei Da, oba-
vijestite me o najnovijim dogaanjima". Gumb prilikom pritiskanja moe obavijestiti
beskonano mnogo promatraa. Gumb se naziva izdavaem (engl. publisher) jer obja-
vljuje dogaaj Click, a druge se klase nazivaju pretplatnicima (engl. subscribers) jer su
pretplaeni na informacije o dogaaju Click.

Poglavlje 12: Delegati i dogaaji | 281


Ovaj projekt implementira predloak Publish/Subscribe koji je opisan u
utjecajnom radu Design Patterns (u izdanju Addison Wesley). Gamma
opisuje svrhu ovog predloka: Relaciju jedan prema vie izmeu obje-
kata definirajte tako da se, kada jedan objekt promjeni stanje, svi objekti
povezani s njim o tome obavjetavaju i automatski auriraju".

Obratite pozornost na to da klasa izdava ne zna, niti joj je bitno,


postoje li pretplatnici. Ona samo pokree dogaaj. Tko e na taj doga-
aj odgovoriti i na koji nain nije njena briga.

Uzmimo kao sljedei primjer klasu Clock koja pretplatnike klase moe obavijestiti
svaki put kad se vrijeme promijeni za jednu sekundu. Klasa Clock moe biti odgovorna
i za prikaz vremena u korisnikom suelju, umjesto za pokretanje dogaaja, pa zato
se onda uope zamarati s delegatima? Ako se koristi idiom objavljivanja/pretplaiva-
nja, klasa Clock ne mora znati kako e se njene informacije koristiti. Veza izmeu pra-
enja vremena i prikaza tih informacija se tako prekida. Nadalje, prilikom pokretanja
dogaaja moe se obavijestiti beskonano mnogo klasa. Pretplaene klase ne moraju
znati kako Clock funkcionira, a Clock ne mora znati na koji e nain pretplaene klase
odgovoriti na dogaaj.
Delegat, dakle, prekida vezu izmeu izdavaa i pretplatnika. To je vrlo poeljno jer
kod ini fleksibilnijim i robusnijim. Klasa Clock moe promijeniti nain detektiranja
vremena bez razbijanja pretplatnikih klasa. Pretplatnike klase mogu promijeniti
nain odgovaranja na vremenske promjene bez razbijanja klase Clock. Klase su pot-
puno neovisne jedna o drugoj, to kod ini jednostavnijim za odravanje.

Dogaaji i delegati
Dogaaji se u C # implementiraju s delegatima. Klasa izdava definira delegat, a pret-
platnika klasa ima dvije metode: prvo stvara metodu koja odgovara potpisu delegata,
a zatim stvara instancu tipa tog delegata uahurujui metodu. Prilikom pokretanja
dogaaja metode pretplatnike klase pozivaju se preko delegata.
Metoda koja obrauje dogaaj naziva se metoda za obradu dogaaja (engl. event han-
dler ). Metode za obradu dogaaja moete deklarirati na isti nain kao i ostale dele-
gate.
Metode za obradu dogaaja u .N ET kosturu obino vraaju void i uzimaju dva para-
metra. Prvi je parametar izvor" dogaaja (to jest, objekt izdava). Drugi je parame-
tar objekt izveden iz EventArgs. Metode za obradu dogaaja preporuljivo je stvarati
prema ovom uzorku.
a*
Napomena za VB6 programere: u C # ne postavlja ogranienja za imena
metoda koje obrauju dogaaje. .N ET implementacija modela objavlji-
vanja/pretplaivanja omoguava vam da imate jednu metodu koja je
pretplaena na razliite dogaaje.

282 | P ro g ra m ira n je C#
EventArgs je o sn o v n a kla sa za sve p o da tke do ga aja. K lasa EventArgs sve m eto de, osim
svog k o n stru k to ra , n aslje u je od k la se O b je ct, ali im a i ja v n o sta ti k o p olje empty koje
predstavlja doga aj bez sta n ja (k a k o bi se d oga aji b e z sta n ja u in k o v ito k o ristili).
Klasa izvedena iz EventArgs sadr i in fo rm a cije o dogaaju .

P retpostavim o da e lite stvo riti klasu Clock ko ja d elegate k o risti za o b a v je ta v a n je


poten cijaln ih p re tp la tn ik a svaki put kad se v rijem e p ro m ijen i za je d n u seku n d u . T aj
em o delegat n azv ati SecondChangeHandler.

Deklaracija delegata SecondChangeHandler glasi:


public delegate void SecondChangeHandler(
object clock,
TimelnfoEventArgs timelnformation
);
Ovaj e delegat uahuriti svaku metodu koja vraa vrijednost void i uzima dva para-
metra. Prvi je parametar objekt koji predstavlja sat (objekt koji pokree dogaaj), a
drugi je objekt tipa TimelnfoEventArgs koji e sadrati sve informacije koje bi mogle
biti korisne onima koje ovaj dogaaj zanima. Objekt TimelnfoEventArgs definira se na
sljedei nain:
public class TimelnfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)

this.hour = hour;
*' this.minute = minute;
this. second = second;
% >
'p public readonly int hour;
p public readonly int minute;
public readonly int second;
& }
I
Ii Objekt TimelnfoEventArgs sadrat e informacije o trenutnom satu, minuti i sekundi.
H On definira konstruktor i tri javne cjelobrojne varijable samo za itanje.

3? Klasa Clock osim delegata sadri i tri varijable lanice - hour, minute i second - te jednu
y metodu Run():
* public void Run()
{
fr(;;)
{
// Spava 1 0 milisekundi
Thread.Sleep(lo);

// Uzima trenutno vrijeme


System.Date7ime dt = System.DateTime.Wow;

! 11 Ako se sekunda promijenila


i- // obavjetava pretplatnike
' if (dt.Second != second)

ffj
I:
Poglavlje 12; Delegati i dogaaji | 283
{
// Stvara objekt TimelnfoEventArgs
// za prosljeivanje pretplatniku
TimelnfoEventArgs timelnformation =
new TimeInfoEventArgs(
dt .Hour,dt.Minute,dt.Second);

// Ako se netko pretplatio, obavjetava pretplatnike


if (OnSecondChange != nuli)
{
OnSecondChange(this,timelnformation);
}
}
// Obnavlja stanje
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}
}
Metoda Run() stvara beskonanu petlju fo r koja periodino provjerava sistemsko vri-
jeme. Ako je vrijeme razliito od trenutnog vremena objekta Clock, ona sve pretplat-
nike obavjetava o promjeni i zatim aurira svoje stanje.

Prvi je korak mirovanje u trajanju od 10 milisekundi;


Thread.Sleep(lO);

Time se koristi statika metoda klase Thread iz imenskog prostora System.Threading


koju emo detaljnije objasniti u poglavlju 20. Poziv metode Sleep() sprjeava preesto
pokretanje petlje koje bi sprijeilo izvoenje ostalih funkcija raunala.
Nakon 10 milisekundi mirovanja metoda provjerava trenutno vrijeme;
System.DateTime dt = System.DateTime.Now;

Prilikom otprilike svake stote provjere bit e dodana jedna sekunda. Metoda primje-
uje tu promjenu i obavjetava svoje pretplatnike. Kako bi to uinila, ona prvo stvara
novi objekt TimelnfoEventArgs:
if (dt.Second != second)
{
// Stvara objekt TimelnfoEventArgs
// za prosljeivanje pretplatnicima
TimelnfoEventArgs timelnformation =
new TimeInfoEventArgs(dt.Hour,dt.Minute,dt.Second);

Zatim obavjetava pretplatnike pokretanjem dogaaja OnSecondChange:


// Ako se netko pretplatio, obavjetava pretplatnike
if (OnSecondChange != nuli)
{
OnSecondChange(this,timelnformation);
}
}

284 | P ro g ra m ira n je C#
Ulomku koda moete vidjeti kako se referenca sata th is prosljeuje jer je sat izvor

Cadje dogaaj objavljen, aurira se stanje klase Clock:


E this.second = dt.Second;
Ij this.minute = dt.Minute;
4? this.hour - dt.Hour;

~~1 U ovom kodu nije pruena sigurnost dretvi. Sigurnost i sinkronizacija


I dretvi teme su u poglavlju 20.
fi-V

'M jo samo treba stvoriti klase koje se mogu pretplatiti na ovaj dogaaj. Stvaraju se dvije.
Prva je klasa DisplayClock. Njen zadatak nije praenje vremena, ve prikaz trenutnog
f | vremena na konzoli.

J Ta je klasa u primjeru pojednostavljena i ima samo dvije metode. Prva je pomona


U'metoda Subscribe() koja pretplauje na delegata OnSecondChange sata. Druga metoda
je metoda za obradu dogaajaTimeHasChanged():
public class DisplayClock
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);
. 1

object theClock, TimelnfoEventArgs ti)


{
Console.WriteLine("Current Time: {0}:{l}:{2}'\
ti.hour.ToStringO,
ti. minute. ToStringO,-
ti.second.ToStringO);

}
Kad se pozove prva metoda, Subscribe (), ona stvara novi delegat SecondChangeHandler
prosljeujui metodu za obradu dogaaja TimeHasChanged(). Zatim tog delegata regi-
strira s dogaajem OnSecondChange od Clock.
Zatim se stvara druga klasa koja takoer odgovara na ovaj dogaaj - klasa LogCurrent-
0 Time. Ona obino slui za biljeenje dogaaja u datoteku, no u ovom primjeru dogaaj
se ispisuje na standardnu konzolu:

X Poglavlje 12: Delegati i dogaaji | 285


public class LogCurrentTime
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);
}
11 Ova bi meetoda trebala zapisivati u dnevnik.
// Mi ispisujemo u konzolu kako bi ilustrirali efekt.
// ovaj objekt ne uva stanje,
public void WriteLogEntry(
object theClock, TimelnfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1 }:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToStringO);
}
}
lako su dvije klase u ovom primjeru sline, u programu se na dogaaj moe pretplatiti
beskonano mnogo razliitih klasa.
Jo treba stvoriti klasu Clock te stvoriti klasu DisplayClock i pretplatiti je na dogaaj.
Zatim ete stvoriti klasu LogCurrentTime i takoer je pretplatiti. Na kraju ete klasi
Clock rei da se pokrene. Cijeli je postupak prikazan u primjeru 12-3 (za izlaz iz apli-
kacije trebate pritisnuti Ctrl-C).

P rim je r 1 2 -3 . Im p le m e nta cija dogaaja s delegatim a


(tregion Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

#endregion

namespace EventsHithDelegates
{
// Klasa za uvanje informacija 0 dogaaju
// u ovom e sluaju uvati samo informacije
// dostupne u klasi clock, ali moe uvati i
// dodatne informacije 0 stanju
public tlass TimelnfoEventArgs : EventArgs
{
public TimelnfoEventArgs! int hour, int minute, int second )
{
this.hour = hour;
this.minute = minute;
this.second = second;
}

286 | Programiranje C#
Primjer 12-3. Implementacija dogaaja s delegatima (nastavak)
public readonly int hour;
p u b l i c i e a donly int m in u te;
public readonly int second;
}

// Na subjekt - to je klasa koju e druge klase promatrati.


// Klasa objavljuje jednog delegata:
// OnSecondChange.
public class Clock
{
private int hour;
private int minute;
private int second;

/ / Delegat kojeg pretplatnici moraju implementirati


public delegate void SecondChangeHandler

object clock,
TimelnfoEventflrgs timelnformation

// Instanca delegata
public SecondChangeHandler OnSecondChange;

/ / Pokree sat
// objavit e dogaaj za svaku sekundu
public void Run()
{

for ( ; ; )
{
// Spava 1 0 milisekundi
Thread.Sleep( 1 0 );

// Uzima trenutno vrijeme


System.DateTime dt = System.DateTime.Now;

// Ako se sekunda promijenila


// obavjetava pretplatnike
if ( dt.Second != s ec o n d )
{
// Stvara objekt TimelnfoEventArgs
// za prosljeivanje pretplatniku
TimelnfoEventArgs timelnformation =
new TimelnfoEventArgs(
dt.Hour, dt.Minute, dt.Second );

I I Ako se netko pretplatio, obavjetava pretplatnike


if ( OnSecondChange != nuli )

OnSecondChange(
this, timelnformation );

Poglavlje 12: Delegati i dogaaji | 287


Primjer 12-3. Implementacija dogaaja s delegatima (nastavak)

)
I I Aurira stanje
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}
}
}
// Promatra. DisplayClock se pretplauje
// na dogaaje sata. Posao DisplayClock je
// da prikazuje trenutno vrijeme
public class DisplayClock
{
// Pretplauje se na dogaaj
// SecondChangeHandler sata
public void Subscribe( Clock theClock )
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler( TimeHasChanged );
}

// Metoda koja implementira


// delegiranu funkcionalnost
public void TimeHasChanged(
object theClock, TimelnfoEventArgs ti )

Console.WriteLine( "Current Time: {0}:{l}:{2}",


ti.hour.ToStringO,
ti. minute. To StringO,
ti.second.ToString() );
}
}
// Drugi pretplatnik iji je posao zapisivanje u datoteku
public class LogCurrentTime

public void Subscribe( Clock theClock )


{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler( WriteLogEntry );

}
// Ova bi metoda trebala zapisivati u datoteku.
// Ispisujemo na konzolu da bismo vidjeli efekt.
I I Ovaj objekt ne uva stanje,
public void WritelogEntry(
object theClock, TimelnfoEventArgs ti )

Console.WriteLine( "Logging to file: {0}:{l}:{2},


ti.hour.ToStringO,
ti.minute. T o S tring O,

288 | Programiranje C#
'prim jer 1 2 -3 . I m p l e m e n t a c i j a d o g a a ja s d e l e g a t i m a ( n a s ta v a k )

ti.secon d.To Stri ngf) );

}
}
public class Test

public static void Main()

{
// Pravi no vi sat
Clock theC lock = new Clock();

// Pravi prikaz i govori mu da se


// pr etplati na upravo stvoren sat
Di sp layClock d c = new Ois.playCl.ockO;
dc.S ubscri be( theC lock );

// Pravi Log objekt i govori mu da


// se pr etpl ati na sat
LogCur rent Time let = new Lo g C ur re nt Ti me ();
lct.Su bscr ibe( theClo ck );

// Po kre e sat
theClock.Run();

Ovaj kod stvara dvije klase, DisplayClock i LogCurrentTime, koje su pretplaene na


dogaaj tree klase (Clock.OnSecondChange).

OnSecondChange je polje vieodredinog delegata koje na poetku ne referira na nita.


Kasnije referira na jednog delegata, a zatim na vie delegata. Kad promatrake klase
ele primiti obavijest, one stvaraju instancu delegata i zatim te delegate dodaju OnSe-
condChange. Na primjer, u metodi Subscribef) od DisplavClock moete vidjeti ovaj red
koda:
theCl ock.On Second Change +=

3 new C lock. Sec ond Chan geHa ndler(T imeH asCh anged) ;

Ispostavlja se kako i klasa LogCurrentTime eli primiti obavijest. U njenoj se metodi


Subscribef) nalazi vrlo slian kod:
public void S ubscrib efCl ock theClock)

theCloc k.OnSe condCh ange +=


ne w C l o c k .Sec ond Change H andler ( Write LogEntry);

Rjeavanje problema s delegatima s pomou dogaaja


Postoji, meutim, jedan problem u primjeru 12-3. to bi se dogodilo da klasa LogCur-
ji rentTime umjesto operatora pretplaivanja (+=) koristi operator dodjeljivanja (=), kao
- u sljedeem primjeru:

Poglavlje 12: Delegati i dogaaji | 289


public void Subscribe(Clock theClock)
{
theClock.OnSecondChange =
new Clock.SecondChangeHandler(WriteLogEntry);

}
Ako uinite tu malu promjenu, vidjet ete da se poziva metoda Logger(), ali ne i m etodi 5
DisplayClock . Operator dodjeljivanja je zamijenio delegata u vieodredinom delegatu!
OnSecondChange, a to nije dobro.

Drugi problem je da ostale metode mogu izravno pozvati SecondChangeHandler. \'a .


primjer, metodi Main() u klasi Test mogli biste dodati sljedei kod:
Console.WriteLine(''Calling the raethod directly!");
System.DateTime dt = System.DateTime.Now.AddHours(2);

TimelnfoEventArgs timelnformation =
new TimelnfoEventArgs(
d t .Hour,d t .Minute,d t .Second);

theClock.OnSecondChange(theClock, timelnformation);

Metoda Main () je ovdje stvorila vlastiti objektTimelnf oEventArgs i izravno pozvala OnSe-
condChange. Ovaj kod dobro funkcionira, iako to nije bila namjera razvojnog inenjera
klase Clock. Izlaz je sljedei:
Calling the method directly!
Current Time: 18:36:7
Logging to file: 18:36:7
Current Time: 16:36:7
Logging to file: 16:36:7

Problem se sastoji u tome to je autor klase Clock planirao da se metode uahurene u


delegatu pozivaju samo prilikom dogaaja. Ovdje je metoda Main( ) pobjegla kroz stra-
nja vrata i sama pozvala te metode. Nadalje, metoda Main() je proslijedila netone
podatke (vremensku konstrukciju postavljenu na dva sata unaprijed).
Kako da, kao autor klase Clock, osigurate da se metode delegata nee izravno pozvati?
Delegat moete uiniti privatnim, ali onda se klijenti uope nee moi registrirati s
delegatom. Potreban vam je nain kojim ete rei: Ovaj delegat je namijenjen obradi
dogaaja. Na njega se moete pretplatiti ili ponititi pretplatu, no ne moete ga pozvati
izravno".

Kljuna rije event


Ovaj se problem moe rijeiti koritenjem kljune rijei event. Ona prevoditelju ozna-
ava kako delegata moe pozvati samo klasa koja ga definira, a ostale se klase na dele-
gat mogu pretplatiti ili ponititi pretplatu s pomou operatora += i -=.
Da biste ispravili problem, promijenite definiciju OnSecondChange:
public SecondChangeHandler OnSecondChange;

u sljedeu definiciju:

290 | Programiranje C#
Jpublic event SecondChangeHandler OnSecondChange;

Idavanje kljune rijei event rijeit e oba problema. Klase se vie ne mogu na doga-
aj pretplatib preko operatora dodjele (-) kao to su to mogle ranije, niti mogu izravno
ozvati dogaaj, kao to je to uinila metoda Main() u prethodnom primjeru. Oba e
fjkuaja generirati pogreku u prevoenju:
The event 'Programming_CSharp.Clock.OnSecondChange' can only appear on
the left hand side of += or -= (except when used from within the type 'Programming_
CSharp.Clock')

kodificirani dogaaj On S e c o n d C h a n g e moemo gledati na dva naina. On je prvo samo


fjistanca delegata kojoj smo ograniili pristup koristei kljunu rije event. Drugo i
fanije shvaanje jest da j e O n S e c o n d C h a n g e dogaaj, implementiran delegatom tipa
Jec on d Chang e Handler . Ova su dva iskaza jednaka, no drugo je vie objektno orijenti-
rano i bolje odraava namjenu ove kljune rijei: stvaranje dogaaja koji objekt moe
jzazvati i na koji ostali objekti mogu odgovoriti.
potpun izvorni kod, modificiran tako da se umjesto neogranienog delegata koristi
f dogaaj, prikazan je u primjeru 12-4.

M frim jer 1 2 -4 . K o r i t e n je k l ju n e r i je i e v e n t

Hregion Using directives

Ipiising System;
Sfcusing System.Collect ions.Generic;
* using System.Text;
J$ using System.Threading;
;|j|'
||?#endregion

jinamespace EventKeyword
ll
// Klasa za uvanje informacija o dogaaju
/ / u ovom e sluaju uvati samo informacije
// dostupne u klasi clock, ali moe uvati i
// dodatne informacije o stanju
public class TimelnfoEventArgs : EventArgs
{
public readonly int hour;
public readonly int minute;
public readonly int second;
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = h o u r ;
this.minute = minute;
this.second = second;
}
}

// Na subjekt - to je klasa koju e druge klase promatrati.

Poglavlje 12: Delegati i dogaaji | 291


Primj er 12-4. Koritenje kljune rijei event (nastavak)
11 Klasa objavljuje jednog delegata:
// OnSecondChange. Promatrai se pretplauju na taj dogaaj
public class Clock
{
private int hour;
private int minute;
private int second;

// Delegat kojeg pretplatnici moraju implementirati


public delegate void SecondChangeHandler
(
object clock,
TimelnfoEventArgs timelnformation
);
// Kljuna rije event nadzire pristup delegatu
public event SecondChangeHandler OnSecondChange;

// Pokree sat
// objavit e dogaaj za svaku sekundu
public void Run()
{
for(;;)
{
// Spava 10 milisekundi
Thread.Sleep(lO);

// Uzima trenutno vrijeme


System.DateTime dt = System.DateTime.Now;

// Ako se sekunda promijenila


// obavjetava pretplatnike
if (dt.Second != second)
{
// Stvara objekt TimelnfoEventArgs
// za prosljeivanje pretplatniku
TimelnfoEventArgs timelnformation =
new TimelnfoEventArgs(
dt .Hour,dt.Minute,dt.Second);

// Ako se netko pretplatio, obavjetava pretplatnike


if (OnSecondChange != nuli)
{
OnSecondChange(
this,timelnformation);
}
}

// Aurira stanje
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;

292 | Programiranje C#
%njer 1 2 - 4 . Koritenje kljune rijei event (nastavak)

}
}
r)
// promatra. DisplayClock se pretplauje
// na dogaaje sata. Posao DisplayClock je
i // da prikazuje trenutno vrijeme
.public class DisplayClock

1^ // Kad ima sat, pretplauje se na


// dogaaj SecondChangeHandler
public void Subscribe(Clock theClock)
(
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);
}
// Metoda koja implementira
// delegiranu funkcionalnost
public void TimeHasChanged(
object theClock, TimelnfoEventflrgs ti)
{
Console.WriteLine("Current Time: {0}:{l}:{2}",
ti.hour.ToStringO,
ti. minute. ToStringO,
ti. second.ToStringO);
}
}
// Drugi pretplatnik iji je posao zapisivanje u datoteku
public class LogCurrentTime
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);

// Ova bi metoda trebala zapisivati u datoteku.


// Ispisujemo na konzolu da bismo vidjeli efekt.
// Ovaj objekt ne uva stanje,
public void WriteLogEntry(
object theClock, TimelnfoEventftrgs ti)
{
Console.WriteLine("Logging to file: {0 }:{1 }:{2 }",
ti.hour.ToStringO,
ti.minute.ToStringO,
t i. second. ToStringO);

public class Test


{

Poglavlje 12: Delegati i dogaaji | 293


Primjer 12-4. Koritenje kljune rijei event (nastavak)

public static v oid Main()

{
// Stvara novi sat
Clock theCloc k = new Clock();

// Pravi p rik az i g ovo ri mu da se


// pretpl ati na upravo stvor en sat
Di sp la yC lo ck dc = ne w D i sp la y C lo c k ();
dc.Sub scri be( theCl ock );

// Pravi Log obje kt i g ovori mu da


// se pretp lati na sat
LogCurren t Time let = new L o g C ur re n t Ti m e (),
lct.Su bscr ibe( th e Clock );

// Pokree sat
theClo ck.R un() ;

}
}
}

Koritenje anonimnih metoda


U p re th o d n o m ste se p rim je ru na do ga aj p re tp la tili p o z iv a n jem nove in sta n ce dele-
gata i p ro slje iv a n je m naziva m eto d e k o ja im p lem en tira doga aj:

th eC lo ck .O nS ec on dC ha ng e +=
new Cl oc k. Se co nd C hange Han dler(Ti me Has Changed );

Delegat moete dodijeliti i pisanjem skraene inaice:


theClock . O n S ec on d C ha ng e += Ti me Ha sC ha ng ed

U n asta v ku ko d a T i m e H a s C h a n g e d m o ra te d e fin ira ti ka o m eto d u k o ja odgova ra potpisu


delegata S e c o n d C h a n ge Ha nd le r:
public void Time HasChan ged(
object theClock, TimelnfoEventflrgs ti)

Console. Wr it e L in e( " C ur r en t Time: {0}:{l}:{2}'',


ti.hour. ToSt ring (),
ti.m inute. To Str ing(),
ti.second.ToStringO);

A n o n im n e vam m e to d e o m o g u a v a ju da u m je sto naziva m e to d e p ro slije d ite bk>


ko d a T o kod m oe u in iti u in k o v itijim i je d n o s ta v n ijim za o d r a v a n je , a anom m
m eto da im a p ristu p v a rija b la m a u n u ta r d osega u ko je m su d e fin ira n e :
cl oc k. On Sec ond Cha nge + = del egate( object th eClock, TimelnfoEventflrgs ti )

^ Co ns ol e. Wr it eL in e( "Cur rent Time: {0}: {l}: {2}",

294 | Programiranje C#
ti.hour.ToStringO,
ti.minute.ToStringO,
ti.second.ToStiing() );

Obratite pozornost da se umjesto registriranja instance delegata koristi kljuna rije


^legate, iza koje se navode parametri koji e biti proslijeeni metodi te zatim tijelo
jnetode unutar vitiastih zagrada i na kraju toka-zarez.
Ova metoda" nema naziva pa kaemo da je rije o anonimnoj metodi. Ona se moe
pozvati samo kroz delegata, no to je upravo ono to vam je potrebno.

Dohvat vrijednosti iz vieodredinih delegata


U veini situacija metode koje ete uahuriti s vieodredinim delegatom vratit e
void. Vieodredini se delegati, u stvari, najee koriste s dogaajima, a sjetit ete
,e da prema pravilu sve dogaaje implementiraju delegati koji uahuruju metode koje
vraaju void (i prihvaaju dva parametra: poiljatelja i objekt EventArgs).
Mogu se, meutim, stvoriti vieodredini delegati za metode koje ne vraaju vrijed-
nost void. U sljedeem ete primjeru stvoriti vrlo jednostavnu testnu klasu s delega-
tom koji uahuruje bilo koju metodu koja ne uzima parametre ali vraa cjelobrojnu

# ' vrijednost:
public class ClassHithDelegate
{
public delegate int DelegateThatReturnsInt();
public DelegateThatRetuinsInt theDelegate;

Kako biste to testirali, implementirajte dvije klase koje se pretplauju na delegat. Prva
uahuruje metodu koja poveava vrijednost brojaa i tu vrijednost vraa kao cjelo-
brojnu vrijednosti:
public class FirstSubscriber
{
private int myCounter = 0;

public void Subscribe(ClassWithDelegate theClassWithDelegate)


{
theClassHithDelegate.theDelegate +=
new ClassWithDelegate.DelegateThatReturnsInt(DisplayCounter);
}

public int DisplayCounter()


{
return ++myCounter;
>
}
Druga klasa takoer slui za odravanje brojaa, no njena delegirana metoda udvostru-
uje vrijednost brojaa i vraa tu udvostruenu vrijednost:

Poglavlje 12: Delegati i dogaaji | 295


public class SecondSubscriber
{
private int myCounter = 0;

public void Subscribe(ClassWithDelegate theClassWithDelegate)

{ %
theClassWithDelegate.theDelegate += ?
new ClassWithDelegate.DelegateThatReturnsInt(Doubler); }:;

} j
public int Doubler() j
{
return myCounter += 2;
}
}
Kad se ovaj delegat pokrene, naizmjenino se pozivaju obje uahurene metode i svaka
vraa vrijednost:
int result = theDelegate();
Console.WriteLine(
"Delegates fired! Returned result: {o} ,
result);

Problem je u tome to svaka metoda vraa svoju vrijednost i prepisuje vrijednost koja
je dodijeljena objektu resu lt. Izlaz izgleda ovako:
Delegates fired! Returned result: 2
Delegates fired! Returned result: 4
Delegates fired! Returned result: 6
Delegates fired! Returned result: 8
Delegates fired! Returned result: 10

Pry3 metoda DisplayCounter() (pozvana od strane FirstSubscriber) vratila je vrijed-


nosti 1, 2, 3, 4, 5, no te su vrijednosti prepisane vrijednostima koje je vratila druga
metoda.
Va je cilj naizmjenini prikaz rezultata svake metode. Za njegovo postizanje morate
preuzeti na sebe pozivanje metoda koje su uahurene u vieodredinom delegatu. To
moete uiniti uzimanjem popisa pozivanja od delegata i eksplicitnim pozivanjem
svake uahurene metode:
foreach (
DelegateThatReturnsInt del in
theDelegate.GetInvocationList() )

{
int result = del();
Console.WriteLine(
"Delegates fired! Returned result: {o}",
result);
}
Console.WriteLine();

Objektu resu lt sada se dodjeljuje vrijednost svakog pozivanja i ta se vrijednost prika-


zuje prije pozivanja sljedee metode. Promjena se vidi u izlazu:

296 | Programiranje C#
Delegates fired! Retur ned result: i
Delegates fired! R etu rned result: 2
Delegates fired! Re turn ed result: 2
Delegates fired! Returned result: 4
Delegates fired! Retur ned result: 3
Delegates fired! Retur ned result: 6
Delegates fired! Returned result: 4
Delegates fired! Retur ned result: 8
'Hi Delegates fired! Returned result: 5
_-l? Delegates fired! Returned result: 10

J|,Prva de,eglrana metoda odbr java ( . 1 2 , 3 , 4, 5), dok druga udvostruuje vrijednost (2 ,
>W 4 , 6 , 8 , 1 0 ). Cijeli je izvorni kod prikazan u primjeru 12-5.

f P rim jer 1 2 -5 . R u n o p o z i v a n j e d e le g ir a n ih m e t o d a

Ifregion Using directives

using System;
it using System.Collections.Generic;
using System.Text;
{jk using System.Threading;

#endregion

Vnamespace InvokingDelegatedMethodsManually

f public class ClassIVithDelegate

j // Vieodredini delegat koji uahuruje metodu


// koja vraa int
public delegate int DelegateThatReturnsInt();
public DelegateThatReturnsInt theDelegate;

public void Run()


{
for ( ; ; )
{
// Spava pola sekunde
Thread.Sleep( 500 );

i if ( theDelegate != nuli )
{
// Eksplicitno poziva svaku delegiranu metodu
foreach (
DelegateThatReturnsInt del in
theDelegate.GetInvocationList() )

I int result = del();

Console.WriteLine(

Poglavlje 12: Delegati i dogaaji | 297


P r im je r 1 2 -5 . R u n o p o z i v a n j e d e l e g ir a n i h m e t o d a ( n a s t a v a k ) ^

"Delegates firedl Returned result: {0}",


result );
} // kraj foreach
Console.WriteLine();
} I I kraj if
} // kraj tor ;;
} I I kraj run
\ I I kraj class

public class FirstSubscriber

private int myCounter = 0;

public void Subscribe( ClassHithDelegate theClassWithDelegate )

' ' i S S S i S S S i T . >


}
public int DisplayCounter()
{
return ++myCounter,

public class SecondSubscriber

private int myCounter = 0;

public .bid Subscribel tdeCl.ssiiitbDelegite )

public int Doubler()

return myCounter += 2;

)
}
public class Test

public static void Main()

^ ' ClassWithDelegate theClassWithDelegate =


new ClassWithOelegate();

FirstSubscriber fs = new FirstSubscriber();


-fs.Subscribe( theClassWithDelegate );

SecondSubscriber ss = new SecondSubscriber();


ss.Subscribe( theClassWitbDelegate );

298 | Programiranje C#
'Primjer 12-5. R u n o p o z i v a n je d e le g ir a n ih m e t o d a ( n a s t a v a k )

theClassWithDelegate.Run();

Asinkrono pozivanje dogaaja


Moe se ispostaviti kako metodama za obradu dogaaja treba previe vremena da
odgovore na dogaaj. U tom sluaju, obavjetavanje kasnijih metoda moe trajati vrlo
jugo, dok ekate i rezultate ranijih metoda. Pretpostavimo, na primjer, kako metoda
DisplayCunte r() u FirstSubscriber mora obaviti mnogo posla oko izrauna povrat-
nog rezultata. To bi izazvalo kanjenje u obavjeivanju SecondSubsriber o dogaaju.
To moete simulirati dodavanjem nekoliko redova koda u metodu DisplayCounter:
public int DisplayCounter()
{
Console.WriteLine("Busy in Displaytounter..
Thread.Sleep(4000);
Console.WriteLine("Done with work in DisplayCounter...
return ++myCounter;
1
sKad pokrenete program, vidjet ete kako prilikom svakog obavjetavanja metode
IfirstSubscriber dolazi do kanjenja od etiri sekunde. Umjesto pozivanja svih metoda
j kroz delegate (kao to je prikazano ranije) moete za svaki delegat pozvati metodu
||)BeginInvoke(). To e uzrokovati asinkrono pozivanje metoda i moi ete nastaviti s
f radom, bez ekanja na povratni rezultat pozvane metode.
|tzarazliku od metode Invoke(), metoda BeginInvoke() odmah vraa. Ona stvara
gposebnu dretvu u kojoj izvodi svoje akcije' (vie informacija o dretvama potraite u
/poglavlju 20).
1
To, meutim, predstavlja problem, jer ipak elite dobiti rezultate od pozvanih metoda.
IpSla raspolaganju su vam dvije mogunosti. Prva je stalno provjeravanje svake delegi-
ra n e metode kako biste vidjeli je li rezultat dostupan. To je kao da od svog pomonika
Iatraite da obavi neki zadatak, a onda ga svakih pet sekundi zovete i pitate Jesi li
||otov? (ime gubite i svoje i njegovo vrijeme). Svom pomoniku zapravo trebate rei
OJapravi ovo i nazovi me kad dobije rezultat".

^Metode povratnog poziva


pelegiranje zadatka i primanja povratnog poziva kad je zadatak obavljen moete
* postii koritenjem povratnih poziva koji se implementiraju (kakvo iznenaenje!) s
pomou delegata. .N ET kostur prua mehanizam za povratne pozive definiranjem
delegata AsyncCallBack'.

t N E T prua pon ud u dretvi, a nova dretva e najvjerojatnije biti preuzeta iz te ponude.

Poglavlje 12: Delegati i dogaaji | 299


[Serializable]
public delegate void AsyncCallback(
IAsyncResult ar
);
Atribut S e rializable je opisan u poglavlju 18. Ovdje, meutim, moete vidjeti kako
je AsyncCallBack delegat za metodu koja vraa void i uzima jedan argument, objekt-*
tipa IAsyncResult. To suelje je definirano .N ET kosturom, a CLR e metodu pozvati s
objektom koji implementira suelje pa ne morate poznavati pojedinosti suelja. Moete '
jednostavno koristiti objekt koji vam je ponuen.

Evo kako to radi. Od delegata zatraite popis pozivanja i za svaki delegat s tog popisa
pozovite Beginlnvoke s dva parametra. Prvi e biti delegat tipa AsyncCallBack, a drugi *
e biti delegat koji poziva eljenu metodu:
del.BeginInvoke(new AsyncCallback(ResultsReturned),del);

U ovom redu koda poziva se metoda uahurena u del (to jest, DisplayCounter) i kad
metoda zavri, obavijest ete primiti preko metode ResultsReturned.
Metoda kojoj treba uzvratiti poziv (ResultsReturned) mora odgovarati povratnom tipu I i
potpisu delegata AsyncCallBack te mora vraati vrijednost void i uzimati objekt tipa i
IAsycResult: %
private void ResultsReturned(IAsyncResult iar)
{ I
Kad je metodi uzvraen poziv, .N ET kostur prosljeuje objekt IAsyncResult. Drugi |
parametar za Beginlnvoke je va delegat i on se sprema u svojstvo AsyncState suelja |
IAsyncResult kao Object. Unutar metode povratnog poziva ResultsReturned taj Object
moete izdvojiti i pretvoriti ga u njegov izvorni tip: |
DelegateThatReturnsInt del = (DelegateThatReturnsInt)iar.AsyncState; |
\
Sada taj delegat moete upotrijebiti za pozivanje metode EndInvoke(), prosljeujui |
primljeni objekt IAsyncResult kao parametar: |
int result = del.Endlnvoke(iar); |

Metoda EndInvoke() vraa vrijednost pozvane (i sada dovrene) metode koju dodjelju- J
jete lokalnoj varijabli result i koju sada slobodno moete prikazati korisniku. |
I
Ukupni efekt je da u metodi Run() naizmjenino dobivate registrirane metode (prvo !
FirstSubscriber.DisplayCounter, a zatim SecondSubscriber.Doubler) i svaku pozivate
asinkrono. Izmeu pozivanja prve i druge metode nema ekanja jer ne ekate da Dis-
playCounter vrati vrijednost.
Kad DisplayCounter (ili Doubler) ima rezultate, poziva se metoda povratnog poziva
(ResultsReturned) i koristite objekt IAsyncResult proslijeen kao parametar za uzima-
nje stvarnih vrijednosti. Cijela je implementacija prikazana u primjeru 12-6.

300 | Programiranje C#
bfifn jer 12- 6. A s i n k r o n o p o z i v a n j e d e l e g a t a
legion Using directives

jsing Systern;
jsin g System.Collections.Generic;
|ing System.Text;
jsing System.Threading;

jfendregion

jamespace AsynchDelegates

P'
public class ClassWithDelegate

// Viseodredini delegat koji uahurava metodu


// koja vraa int
public delegate int DelegateThatReturnsInt()
public event DelegateThatReturnsInt theDelegate;

public void Run()


{
( ; ; )
{
H Spava pola sekunde
Thread.Sleep( 5 0 0 );

if ( theDelegate != nuli )

I I Eksplicitno poziva svaku delegiranu metodu


toreach (
DelegateThatReturnsInt del in
tbeDelegate.CetlnvocationListO )

// Poziva asinkrono i
// prosljeuje delegat kao objekt stanja

d e f )n InVke( neW AsyncCall> ^ ( ResultsReturned ),

} I I kraj foreach
} I I kraj if
U kraj for ;;
H kraj run

// P oz iv a metodu za uzimanje rezultata


private void ResultsReturned( IAsjrncResult iar )

// Pretvara objekt stanja natrag u tip delegata


DelegateThatReturnsInt del =
( DelegateThatReturnsInt ) iar.AsyncState;

// P oz iv a Endlnvoke na delegatu da uzme rezultat


int result = del.EndInvoke( iar );

// Prikazuje rezultate
Console.WriteLine( "Delegate returned result: {0 }", result );

3-
Poglavlje 12; Delegati i dogaaji | 301
P r im je r 1 2 - 6 . A s i n k r o n o p o z i v a n j e d e l e g a t a ( n a s t a v a k )

}
} // kraj class

public class FirstSubscriber


{
private int myCounter = 0;

public void Subscribe( ClassWithDelegate theClassWithDelegate )


{
theClassWithDelegate.theDelegate +=
new ClassWithDelegate.DelegateThatReturnsInt( DisplayCounter );
}

public int DisplayCounter()


{
Console.WriteLine( "Busy in DisplayCounter.. ) ;
Thread.Sleep( 10000 );
Console.WriteLine( "Done with work in DisplayCounter..." );
return ++myCounter;
}

public class SecondSubscriber


{
private int myCounter = 0 ;

public void Subscribe( ClassWithDelegate theClassWithDelegate )


{
theClassWithDelegate.theDelegate +=
new ClassWithDelegate.DelegateThatReturnsInt( Doubler );
}

public int Doubler()


{
return myCounter += 2;
}

public class Test


{
public static void Main()
{
ClassWithDelegate theClassWithDelegate =
new ClassWithDelegate();

F.irstSubscriber fs = new FirstSubscriber();


fs.Subscribe( theClassWithDelegate );

SecondSubscriber ss = new SecondSubscriber();


ss.Subscribe( theClassWithDelegate );

theClassWithDelegate.Run();
}
}
}

302 | Programiranje C#
DIO II
Programiranje na jeziku C#
POGLAVLJE 13

Programiranje Windows aplikacija

|| prethodnim poglavljima su za prikaz jezika C# i CLR-a koritene konzolne aplika-


cije, Premda se konzolne aplikacije mogu jednostavno implementirati, vrijeme je da
;se pozabavite pravim razlogom zato uope uite C# jezik, a to je izgradnja Windows
!|.Web aplikacija.
% ranim danima Windowsa, aplikacije su se izvodile na stolnim raunalima, u savr-
enoj izoliranosti. S vremenom, projektanti su nali korisnim da rasporede aplikacije
iiljem mree, sa korisnikim sueljem na jednom raunalu, a bazom podataka na
tlrugom. Ova podjela odgovornosti, odnosno distribuiranje aplikacije, naziva se dvo-
plojna arhitektura, ili pristup klijent-posluitelj. Kasnije, kada su projektanti poeli
Ijcoristiti Web posluitelje da udome poslovne objekte koji mogu upravljati pristupom
Ibazi podataka, razvili su se troslojni, odnosno vieslojni pristupi.
Kad se Web tek pojavio, postojala je jasna razlika izmeu Windows i Web aplikacija.
|Vindows aplikacije su se izvodile na stolnim raunalima ili u lokalnoj mrei, a Web
racije su se izvodile na udaljenom posluitelju i pristupano im je preko pregled-
lika. Ova razlika je postala manja kako su Windows aplikacije poele koristiti usluge
:ba. Mnoge nove usluge ukljuuju izvoenje poslovne logike na klijentu, pohranji-
vanje podataka na posluitelju baze podataka i udaljena neovisna raunala na Webu.
Klasine samostalne aplikacije, kao to su Excell ili Outlook, mogu integrirati podatke
jpnbavljene preko Weba u jednu cijelinu, a Web aplikacije mogu raspodijeliti dio posla
pnmponentama na klijentskoj strani.
Ifedina preostala razlika izmeu Windows i Web aplikacija je u tome tko posjeduje
^korisniko suelje. Da li e aplikacija svoje korisniko suelje prikazati u pregledniku
||li e korisniko suelje biti u sklopu programa koji se izvodi na stolnom raunalu?
#4

T iTi L.-v,
L
ak i razlika u tome tko posjeduje korisniko suelje je donekle proiz-
voljna, poto suelja prikazana u pregledniku mogu imati komponente
koje se izvode lokalno, a samostalne aplikacije mogu imati ugraene
Web preglednike.
Ogromne su prednosti Web aplikacija. Moe im se pristupiti s pomou bilo kojeg pre
glednika koji se moe spojiti na posluitelj. Takoer, posluitelj se moe aurirati bezi
potrebe za dijeljenjem novih DLL-ova korisnicima. I

S druge strane, ako aplikacija nema koristi od pristupa Webu, mogue je postii veu -
kontrolu nad izgledom i uvidom u aplikaciju, odnosno mogue je postii bolju izvedbu
izradom samostalnih aplikacija. |
.N ET nudi blisko povezane, ali razliite skupove alata za programiranje Windows ili 1
Web aplikacija. Oba pristupa su temeljena na pretpostavci da mnoge aplikacije imaju 1
korisnika suelja koja s korisnikom komuniciraju preko obrazaca ili kontrola kao to 1
su gumbi, padajui popisi, tekst i tako dalje. M
Alati za izradu Web aplikacija se zovu Web Forms i razmatrani su u poglavlju 15. Alati 8
za izradu Windows aplikacija se zovu Windows Forms i tema su ovog poglavlja. ii

Na sljedeim stranicama ete nauiti kako izraditi Windows aplikaciju koristei Visual i
Studio. Ova aplikacija e obuhvatiti vei broj C# metoda koje su razmatrane u pret- |
hodnim poglavljima. i|

1
Izrada jednostavnog Windows Forms obrasca |
Windows Forms je alat za izradu Windows aplikacija. .N ET kostur nudi obimnu |
podrku za razvoj Windows aplikacija, iji je sredinji dio kostur za Windows obrasce. ^
Nije iznenaujue da Windows obrasci koriste metaforu obrasca. Ideja je posuena od 1
vrlo uspjene VB okoline i podrava brzi razvoj aplikacija. Premda je to diskutabilno, |
C# je prva okolina za razvoj koja je spojila alate za brzi razvoj iz VB okoline sa kvali- |
tetnim objektno orijentiranim karakteristikama obitelji C jezika. ;j|

i
Koritenje alata Visual Studio Designer |
Iako je mogue napraviti Windows aplikaciju koristei bilo koji editor (ak i Notepad!) J
i prevoenje iz naredbene linije, nerazumno je tako raditi kad Visual Studio.NET f
toliko olakava posao. is
|
Da biste poeli raditi novu Windows aplikaciju, prvo otvorite Visual Studio i odaberite |
File - New Project. U prozoru New Project napravite novu Windows aplikaciju i i
nazovite je ProgCSharpWindowsForm, kao to je prikazano na slici 13-1. j
Visual Studio odgovara izradom Windows Forms aplikacije i to je najbolje, postavlja j
vas u okoli za razvoj, kao to je prikazano na slici 13-2.
Prozor Design prikazuje prazan obrazac Formi. Dostupan je i prozor Toolbox sa
skupom gotovih Windows programa i kontrola. Ako prozor Toolbox nije prikazan,
pokuajte pritisnuti natpis Toolbox, ili odaberite View - Toolbox u Visual Studio

306 1 Programiranje C#
New Project
Project Type:
a VfcualC* _G 3g
_ _ V f s ^ S t u t t o jn s t a I le d te n p la te s
: Office
wm dow s
y3w
Cin
tesdLobw
rsA
v*acion

f
' a SiTidrj oevte trc* l b e r y
oatabase
W ndow s Con
ttt Other Languages
) e b C ontrct U bra y
ffl O ther Projec t Types

f
ons deAppfc aboo
ndow s Service
& n p t v Project
D lC nrsU l Peports w m dow s A ccfc a to n

E
Server Project
9 t PC 2003 A p p fc a t a i
et PC 2003 CJass U r * y
P o c k e t PC 2003 C m trd Ltr*y
f flP o c k e t PC 2003 C c rede A c pfc a t r
jg lP o c k e tP C 2003 Emp ty Project
i* je t t o o e a t iig an jpp i ao on w ith a W hdow s user h t e r t o e

fftO q C S hatp W riow tf rm


Lo c atnrr [c :\ D o q m e n ts a d
g i^ ^ g g g ^ g g S ^ g ? * f ^ d O o o j ^ s g i s B O C ^ C S h a r p f~ &owse
SobtJbnName: fftogSharpWrclow sft U
------------------------- - __ I E lO a a t e D N c tia r i t o S d u t J m

Q Add to Source C ontrd

Ip ik a 13-1. Izrada aplikacije temeljene na Windou/s Forms

j; . Slika 13-2. Okoli za razvoj

' m 0 ta ' k0riSti" P" &C C" -A1,-X lk id P ' -

i.'s s s s s B s s s s s s f u liM * - . **

Poglavlje 13: Programiranje Windows aplikacija | 307


Kad je Toolbox prikazan, moete povui natpis ili gumb izravno na obrazac, kao '
je prikazano na slici 13-3. ,R

P ro g C S h a r p W ir td o w s F o rm * M ic r o s o f t D e v e lo p m e n t E n v iro n m e n t
... Me*r Projeet 6trfd Oetug Data Toolj Vfttdan Help

^forort-e*IDesijo]*\ ;^ W n E iib f f rSabtk inVregC... r j) x


>t Pohter (Sr-g
r B ActiveDoasnentHost .
Q_ w
Sduben PnJ5CSharpWSow5Fotm' ( i pro
a>( Buttan a PiooCSharpW MowsfMm
p. C h K & n &
I ; Sg|Poml.t
LaRefntnces
0 Cabrfbfeg
133 Corrtoep*
<4
9 ConttxtHenuSb^
(51 DetaCorrectof
R rl OataC rlM e*
^ OebHeAgttti
>j]i^^^i^3SSS3ga
Sofu&en EKpiorcr j3JOw*VSev* I
g) DaleTtnefteto ,.
n DamairtjpOOM) .

9 &Ttfre>vUer . b b c ll Systn.W indow j/orm iJ .abtt *


j g FfcmLavnrtPAnd
0 FoidBfrtMSrfXalg..
ip B fc B Lila,..b d^Masasifi^an^-.
: |(N8ffle) l
f f l FcnOtatog 45 05
fACC S lble t Oi:
( 3 <5r*0ox ; AcceisIbteNam
AccessIbleRole .Oefadt
jJ IW iuv kJ g (A llo ftO rop Ppbe
U FSeroCar
.Anchor Top.Loft
(S> Im sgdist
AUM
Aim*w
8U
iS^ O ulput j'| Enor lh t j ^ T > ^ lj 8 t } ^ 8 o o t a iw t e l Q c w x le |

Slika 13-3. Okolina za razvoj V/indows Forms obrazaca

Prije nego to nastavite, pogledajte okolo. Toolbox je ispunjen s kontrolama koje moete
dodati Windows Forms aplikaciji. U gornjem desnom kutu trebali biste vidjeti prozor
Solution Explorer koji prikazuje sve datoteke u projektu. U donjem desnom kutu se
nalazi prozor Properties koji prikazuje sva svojstva trenutno odabrane stavke. Na slici
13-3, odabran je natpis la b e ll i prozor Properties prikazuje njegova svojstva.
Moete koristiti prozor Properties da podesite svojstva raznih kontrola. Na primjer,
da biste dodali tekst natpisu la b e ll, moete upisati rijei Hello World u polje desno
od svojstva Text. Ako elite promijeniti pismo natpisa HelloVJorld pritisnite svojstvo
Font prikazano u donjem desnom kutu slike 13-4. (Takoer moete dodati tekst za
gumb buttonl tako to ete ga odabrati te u prozoru Properties upisati rije Cancel"
unutar njegova svojstva Text.)
Bilo koji od ovih koraka je esto laki nego mijenjanje ovih svojstava izravno u kodu
(iako je i to naravno mogue).
Kad je obrazac podeen, sve to preostaje je napraviti metodu za obradu pritiska na
gumb Cancel. Dvostruki pritisak na gumb Cancel e napraviti metodu za obradu doga-
aja, registrirati ju, i postaviti vas na stranicu koja sadri kod za obrazac, gdje moete
upisati logiku za upravljanje dogaajima kao to je prikazano na slici 13-5.

308 | Programiranje C#
*Prog CShai;p W lnd QwsFprm -M ic ro s o ft D e v e lp p m e n t E n v iro n m e n t " i ,-:v b
1F6 &Srt V froject bM Mu) CKATooHWNwtt>
1ifl*ijj*idf.kj 9 .
.A-J & *>i'*>'-> I.Cttu) *AnftfV a Ctvoftirost __

jfci slika 13-4. Promjena pisma

ProgCSharpV/indovnForm - Microsoft Dovetopmeni Environment


PV
bE <MVewRsbctcr Piojeci GuldOttug(Uta TaobW nUw
Liflt<3..\3..M ; SA-:;a ______ rgv _g} conqi.Tt
ISi&Szz
... X'.forml-a*!ftrril.atOesBi)*; i ..___ __ __ _____ *_><j ai^Edor-SoUBnRooc...
xmmasvmm up***^*^*, :'^l[av|J*Wl_^(ofci|ect serdel, 6wnjg><;4;i,Q t!,^ .^. JE 3 . : B , 3 .& ..,,
t
> - 12 ?j
fysob^ A o 5 si o V ^ w 5 flm ?
H 8 ProgC9u(pWndowtfona
* i i ftopones
<0 SlAetorencoj
3JPofliil.es

$X

.j..lT..^L..-i.Br.....S&ro'..tml;SoJfifpfjfififTOflffiiTiiffiflTIff
I

Slika 13-5. Nakon dvostrukog pritiska gumba Cancel

Kursor je ve na mjestu; samo trebate upisati jedan red koda:


Applicat ion. Exit ();

Kad pokuate upisati ovaj kod, alat Intellisense e vam pokuati pomoi. Kad upiete
A, bit e prikazano ime prvog objekta koji poinje sa A. Nastavite tipkati do Appl i

Poglavlje 13: ProgramiranjeWindows aplikacija | 309


onda pritisnite toku: klasa Application je upisana za vas' a dostupne su i m eto d e r'
postavke objekta Application. Upiite Ex i tokuzarez - red koda je napisan.

rf^r- U okoliu za razvoj ku rsor treperi, tako da je lake pratiti kod. Veini
itatelja ku rsor v jerojatn o nee treperiti u ovoj knjizi.
4'

Visual Studio generira sav kod potreban za stvaranje i inicijalizaciju komponenata.

Primjetite da datoteka s kodom (Form l.cs ) sadri samo naredbe using, konstruk-
tor i metodu za obradu dogaaja. Oni meu vama koji su programirali u prijanjim
inaicama jezika C# moda se pitaju gdje je ostatak koda za inicijaliziranje i postavlja-
nje svojstava kontrola (koje nisu navedene ovdje!). Primjetite da definicija klase sadri
kljunu rije p a rtia l. To ukazuje da se ostatak definicije klase nalazi u nekoj drugoj
datoteci. Ako pritisnete gumb Show Ali Files (zaokruen na slici 13-6), vidjet ete da
je Designer dodao drugu datoteku, Forml.Designer.es.

Solution fVe^eiharpU/indovvsForm' (1 proj


E) t|!j9 P ro g C S h a rp W in d o w s F o rm
|| j Properties
- j i References
E1 L'2U bin
&}- L l l obj
e - '13
F orm i.D esigner.]
c|jl^5rann
iL

Slika 13-6. Prikaz svih datoteka

Datoteka Forml.Designer.es sadri sav kod koji je generirao Visual Studio.

Izrada Windows Forms aplikacija 1


Da biste vidjeli kako Windows Forms mogu biti iskorteni za izradu realnijih Windows |
aplikacija u ovom odlomku ete napraviti pomonu aplikaciju FileCopier koja kopira j
sve datoteke iz odabrane skupine mapa u odredinu mapu ili na ureaj kao to je j
disketa ili mreni disk. Iako neete primjenjivati sve mogue detalje, moete zamisliti
koritenje ove aplikacije tako da oznaite desetke datoteka i kopirate ih na vie diskova, A
slaui ih tako da zauzmu to manje prostora. ak moete proiriti aplikaciju da kom- 1
primira datoteke. Pravi cilj ovog primjera je da uvjebate mogunosti C# jezika koje ste
nauili u prethodnim poglavljima te da istraite imenski prostor Windows. Form.*

* Primjetite da Intellisense pamti va zadnji izbor i poinje s njim. Obino je to velika pogodnost.

310 | P ro g ra m ira n je C#
i^a potrebe ovog primjera te da biste kod bio jednostavan, fokusirajte se na korisniko
ljieelje i korake potrebne za povezivanje njegovih kontrola. Konaan izgled korisni-
7 suelja aplikacije je prikazan na slici 13-7.

* Fite Copier

Source Files Target Hles


a D va VATemp
03 Q AbsohjtePosHionTestl
ffl 0 AdoNetAcces$Example ffl StockTickeiComplete ij
0 0 A do NelExaniple ffl StockTickeiConsumei
bin ffl StockTckeiOvwtoJtti
0AdoNelExdmpte.c$pioj ffl SlockTickefSessionState
0AdoNetExamp1e.cspioj.webirifo ffl SlockTickerSimple
0 AdoNetExample.$ln ffl SliingConcal
0 AdoNe(Exafnple.$uo i- Temp
0 AdoNetExample.v$diyco ffl Te$(CustornConttol$Ubidfy
0 G fo b a la sax ffl Tes(ft)sfflack
0 Global. asax,tesx Te$(WebApp
0 W e b.co n fig ffl UseiCortUolCode
f~] WebFotm1 ,aspx ffl U sefCorrtroCustomEventAigs
0 W ebForm 1 aspx.rea ffl UsetConhds
ffl 0 AppState ffl vaEddtion
ffl 0 archive ffl VsCodeBehind
ffl 0 A S P C o w $ e V/ebApplbalionl

ffl 0 AspBuUons
ffl 0 AspCalendat
WebApplication1 (2)
WmgDing$01 ii
i i ___________ lii a JJ r
Cleai 17] Ovemiite jf aasts

S li ka 13-7 . K o r i s n i k o s u e l j e a p l i k a c i j e F i l e C o p i e r

Korisniko suelje za FileCopier se sastoji od sljedeih kontrola:


Natpisa (Source Files i Target Files)
Gumba (Clear, Copy, Delete, i Cancel)
Polja za potvrdu Overvvrite if exists
Polje za tekst za prikaz putanje odabrane odredine mape
Dvije velike kontrole za prikaz stablaste strukture mapa, jedna za dostupne izvo-
rine mape i jedna za dostupne odredine ureaje i mape.

Cilj je omoguiti korisniku da pregleda datoteke (ili mape) u lijevoj (izvorinoj) kon-
troli za prikaz strukture mapa. Ako korisnik pritisne gumb Copy, datoteke oznaene
na lijevoj strani bit e kopirane u odredite odabrano na desnoj strani. Ako korisnik
pritisne Delete, oznaene datoteke e biti obrisane.

Ostatak ovog poglavlja opisuje nekoliko znaajki aplikacije FileCopier kako biste upo-
znali osnovne znaajke Windows Forms.

Poglavlje 13: Programiranje Windows aplikacija | 311


Izrada jednostavnog obrasca za korisniko suelje
Prvi zadatak je otvoriti novi projekt koji ete nazvati FileCopier. Razvojni okoli prika-
zuje prozor Designer, u kojem moete povlaiti gotove programe na obrazac. Mogue
je proiriti obrazac na veliinu koju elite. Povucite iz prozora Toolbox i spustite na
obrazac natpise LblSource, LblTarget, LblStatus, gumbe btnChar, btnCopy, btnDelete
btnCancel, polje za potvrdu chkOverwrite, polja za tekst txtTargetD ir i kontrole za pri-
kaz strukture mapa tvwSource, tvwTargetDir te postavite njihova svojstva Name tako da
obrazac izgleda otprilike kao na slici 13-8.

I
i
''.,5

S lika 13 -8 . I z ra d a o b ra s c a u D esig n er p roz o ru

Polja za potvrdu pored mapa i datoteka potrebna su u prozoru za odabir datoteka, j


ali ne i u odredinom prozoru (gdje je mogue odabrati samo jednu mapu). Postavite j
svojstvo Check-Boxes kontrole tvwSource na tru e, a kontrole tvwTargetDir na false. Da j
biste to napravili, odaberite kontrolu i podesite vrijednosti u prozoru Properties. j
Kada je ovo uinjeno, dvaput pritisnite Cancel gumb da biste napravili metodu za
obradu dogaaja. Kad dvaput pritisnete kontrolu, Visual Studio stvori metodu za
obradu dogaaja za taj objekt. Svaki objekt ima podrazumijevani11dogaaj koji Visual
Studio koristi kad dvaput pritisnete objekt. Za gumbe, standardan dogaaj je Click.
protected void btnCancel_Click (object sender, System.EventArgs e)
{
Application.Exit();
}
Moete obraditi mnoge razliite dogaaje za razliite kontrole. Jednostavan nain da to
napravite je pritiskanjem gumba Events u prozoru Properties. Odatle moete napraviti

312 | Programiranje C#
^ove metode za obradu jednostavnim upisivanjem novog imena ili odabirom neke od
postojeih metoda. Visual Studio registrira metodu za obradu dogaaja i otvara editor
Ikoda u kojem napravi zaglavlje i postavi kursor u prazno tijelo metode.
^Toliko to se tie jednostavnog dijela. Visual Studio generira kod za postavljanje obra-
sc a i inicijaliziranje svih kontrola, ali ne popunjava TreeView kontrole. To morate ui-
, iti runo.

Savjeti o .NET W indows Forms obrascima za VB6 program ere


Sjajno je da osnovne .NET kontrole imaju puno toga zajednikog sa svojim VB6 pret-
hodnicama. Ali postoje odreene izmjene koje biste vas mogle uhvatiti nespremnima.
Uzmite u obzir ove savjete prilikom izrade obrazaca.
U VB6 neke kontrole prikazuju tekst koristei svojstvo Text a neke koristei svojstvo
U .NET-u, sva svojstva vezana z a tekst se jednostavno zovu Text.
Caption.

Gumbi CommandButtons u VB 6 koriste svojstva Default i Cancel tako da ih korisnik


brzo moe odabrati pritiskanjem tipki Enter ili Escape. U .NET-u, ova svojstva su dio
objekta Form. Svojstva AcceptButton i CancelButton se koriste za odreivanje koji gumb
na obrascu preuzima odgovarajuu metodu.
VB6 obrasci se prikazuju pozivanjem Show() metode. Ako elite da obrazac bude pri-
kazan kao modalan dijaloki okvir, proslijedite enumerator vbModal metodi Show(). U
.NET-u ove dvije metode su razdvojene u dvije razliite metode Show() i ShowModal().

* Popunjavanje TreeVievv kontrola


TieeView kontrole rade identino, osim to lijeva kontrola, tvwSource, sadri popis
mapa i datoteka, dok desna kontrola, tvwTargetDir, sadri samo popis mapa. Chec-
|j,kBoxes svojstvo u tvviSource je postavljeno true, a u tvwTargetDir na false. Takoer,
|;iako tvwSource dozvoljava viestruki odabir, koji je standardan za TreeView kontrole,
i utvwTargetDir dozvolit ete samo jedan odabir.

I Kod zajedniki za obje TreeView kontrole faktorirat ete u zajedniku metodu F i l l -


|'DirectoryTree i proslijediti i kontrolu sa zastavicom koja ukazuje da li treba uzeti
datoteke. Tu metodu ete pozvati iz konstruktora obrasca, jedanput za svaku od
, kontrola:
FillDirectoryTree(tvwSource, true);
FillDirectoryTree(tvwTargetDir, false);

Implementacija FillDirectoryTree daje ime parametru tvw TreeView kontrole. To e


predstavljati izvorinu TreeView i odredinu TreeView kontrolu. Trebat ete neke Sys-
tem.IO klase pa dodajte deklaraciju using System.IO; na vrh obrasca Formi.cs. Zatim
dodajte deklaraciju metode obrascu Formi.cs:
private voi FillDirectoryTree(TreeView tvw, bool isSource)

Poglavlje 13: Programiranje Windows aplikacija | 313


TreeNode objekti
TreeView kontrola sadri svojstvo Nodes koje uzima TreeNodeCollection objekt. Tree-
NodeCollection je kolekcija objekata tipa Treenode od kojih svaki predstavlja vor u
stablu. Ponite s pranjenjem kolekcije:
tvw.Nodes.Clear();

Sada ste spremni napuniti kolekciju Nodes rekurzijom kroz sve mape svih diskova. Prvo
uzmite popis svih logikih diskova u sustavu. Da biste to uinili pozovite statiku
metodu GetLogicalDrives() objekta Environment. Klasa Environment prua tu informa-
ciju i pristupa trenutnoj okolini platforme. Moete koristiti Environment objekt da biste
saznali ime stroja, inaicu operativnog sustava, ime sistemske mape itd.
stringl] strDrives = Environment.GetLogicalDrives();

G etlogicalD rives() vraa polje nizova koji predstavljaju korijenske mape logikih
diskova. Iterirat ete unutar te kolekcije dodajui vorove kontroli TreeView.
foreach (string rootDirectoryName in strDrives)
{
Obradite svaki logiki disk s pomou foreach petlje.
Prvo to treba provjeriti jest da li je logiki disk spreman. Moj prijedlog za to je da
uzmete popis mapa najvie razine pozivanjem metode GetD irectories() na objektu
tipa DirectoryInfo koji sam napravio za korijensku mapu:
DirectoryInfo dir = new DirectoryInfo(rootDirectoryName);
dir.GetDirectories();

D irectoryInfo klasa izlae metode instance za izradu, pomicanje i enumeriranje


mapa, njihovih datoteka i podmapa. DirectoryInfo klasa je detaljnije objanjena u
poglavlju 21.
G etD irectories() metoda vraa popis mapa, ali ovaj kod zapravo odbacuje popis.
Ovdje ju pozivate samo da napravite iznimku kada disk nije spreman.
Smjestite poziv unutar bloka try i ne poduzimajte nikakvu akciju unutar bloka catch.
Ako to uinite disk se preskae ako se dogodi iznimka.
Kad znate da je disk spreman, napravite TreeNode objekt koji e sadravati njegovu
korijensku mapu i dodajte taj vor TreeView kontroli:
TreeNode ndRoot = new TreeNode(rootDirectoryName);
tvw.Nodes.Add(ndRoot);

Da biste dobili znakove + desno u TreeView kontroli, morate pronai barem dvije
razine map'a (tako da TreeView kontrola zna koja mapa ima podmape i moe upi-
sati znak + pokraj njih). Ne elite prolaziti kroz sve podmape jer bi to bilo previe
sporo.
Posao metode GetSubDirectoryNodes() je da proe dve razine u dubinu, prosljeujui
korijenski vor, ime korijenske mape, zastavicu koja zadaje treba li traiti datoteke i
trenutnu razinu (uvijek poinjete na prvoj razini):

314 | Programiranje C#
if ( isSource )
{
GetSubDirectoryNodes(
ndRoot, ndRoot.Text, true,l );
}
else
{
GetSubDirectoryNodes(
ndRoot, ndRoot.Text, false,l );
}
Vjerojatno se pitate zato je potrebno proslijediti ndRoot.Text ako ve prosljeujete
ndRoot? Strpite se - vidjet ete zato je to potrebno kad se vratite u metodu GetSubDi-
rectoryNodes(). Sad ste zavrili s metodom FillD irectoryTree(). Pogledajte primjer
13-1 kasnije u ovom poglavlju za kompletan ispis ove metode.

Rekurzija kroz podmape


GetSubDirectoryNodes() poinje s jo jednim pozivom metode GetDirectories(), ovaj
put odlaui dobiveno polje DirectoryInfo objekata:
private void GetSubDireoctoryNodes(
TreeNode parentNode, string fullName, bool getFileNames)
{
DirectoryInfo dir = new DirectoryInfo(fullName);
DirectoryInfo[] dirSubs = dir.GetDirectoriesj);

Primjetite da proslijeeni vor ima ime parentNode. vorovi na trenutnoj razini e biti
potomci proslijeenog vora. Na ovaj nain preslikavate strukturu mapa na hijerarhiju
treeview kontrole.

Proite kroz sve podmape, preskaui one koji su oznaene kao Hidden:
foreach (DirectoryInfo dirSub in dirSubs)
{
if ( (dirSub.Attributes &
FileAttributes.Hidden) != o )
{
continue;
1
FileAttributes je enumeracije. Njene ostale mogue vrijednosti ukljuuju Archive,
Compressed, Directory, Encrypted, Hidden, Normal, ReadOnly, itd.

Svojstvo dirSub. Attributes je predloak bitova trenutnih atributa mapa.


A k o izvedete logiku operaciju Ii z me u te vrijednosti ipredloka bitova
FileAttributes.Hidden, bit se postavlja a k o datoteka im a atribut hidden;
inae se svi bitovi briu. Postoje li skriveni bitovi moe te provjeriti tako
to ete provjeriti je li rezultirajua int vrijednost razliita o d nule.

Poglavlje 13: Programiranje Windows aplikacija I BIS


Napravite TreeNode s imenom mape i dodajte ga u kolekciju Nodes vora Pro.slijeenog
metodi (parentNode):
TreeNode subNode = new TreeNode(dirSub.Name);
parentNode.Nodes.Add(subNode);

Sad provjerite tekuu razinu (koju je proslijedila pozizvajua metoda) u odnosu i


konstantu definiranu za klasu:
p riv ate const in t MaxLevel = 2;

tako da ide samo dvije razine u dubinu:


i f ( le v e l < MaxLevel )
{
GetSubDirectoryNodes(
subNode, dirSub.FullNam e, getFileNam es, lev el+ 1 ) ;
}
Metodi kao novi roditeljski vor prosljeujete vor koji ste upravo napravili, punu puta-
nju kao puno ime roditeljskog vora, zastavicu koju ste primili, zajedno s razinom koja
je za jedan uveana u odnosu na tekuu razinu (tako da, ako krenete s prvom razinom,
u sljedeem pozivu e razina biti postavljena na dva).
* .
Poziv za TreeNode k o n stru k to r k o risti sv ojstv o Name D irecto ry In fo
o b jek ta , dok poziv m etode GetSubDirectoryNodes () koristi svojstvo Ful-
lName. A ko je o d ab ra na m apa C:\W indow s\M edia\Sounds, svojstvo
FullName vraa punu p u tanju, dok sv ojstv o Name vraa sam o Sou nds.
P roslijed ite v oru sam o im e je r je to on o to elite da bude prikazano
u t r e e view kontroli. M etodi GetSubDirectoryNodes() proslijedite puno
im e sa pu tan jo m tak o da m oe p ron ai sve pod m ape na disku . Ovo
predstavlja odgovor na ran ije postav ljeno pitanje z a s t o je potreb n o pro-
slijed iti im e korijen skog vora kad prv i put pozivate ovu m etodu. Ono
to se p rosljeu je m etod i nije im e v ora; to je puna pu tanja do m ape
koju vor predstavlja!

Uzimanje datoteka iz mape


Kad proete kroz podmape, dolazi vrijeme da uzmete datoteke iz mape ako je zasta-
vica getFileNames postavljena na true. Da biste to uinili, pozovite metodu GetFiles()
na DirectoryInfo objektu. Metoda vraa polje Fileln fo objekata:
i f (getFileNam es)
{
// Uzima datoteke za ovaj vor.
F i l e l n f o j] f i l e s = d ir .G e t F i le s ( ) ;

F ile ln fo klasa (opisana u poglavlju 21) pruae metode instance za rad s datotekama.
Sad moete proi kroz kolekciju pristupajui svojstvu Name objekta Fileln fo i prosli-
jediti ga konstruktoru TreeNode objekta, koji onda moete dodati u kolekciju Nodes
roditeljskog vora (tvorei tako vor-potomak). Ovaj put nema rekurzija jer datoteke
nemaju podmape:

316 | Programiranje C#
|!foreach (Filelnfo file in files)

TreeNode fileNode = new TreeNode(file.Name);


parentNode.Nodes.Add(fileNode) ;

' }
j je sve to je potrebno da biste popunili dva treeview objekta. Pogledajte primjer
14 za kompletan ispis ove metode.

A k o v a m je neto o d ovoga zbunjujue, p r e p o r u a m d a k od ispitate pro-


g r a m o m za otkrivanje greaka iproete korak p o korak kroz rekurziju.
T a k o m o e t e promatrati k a k o TreeView gradi svoje vorove.

Ijbrada dogaaja TreeView kontrole


ovom primjeru morate obraditi vei broj dogaaja. Prvo, korisnik moe pritisnuti
|*umb Cancel, Copy, Clear ili Delete. Drugo, korisnik moe potvrditi neko od polja u
I lijevoj TreeView kontroli, odabrati neki od vorova u desnoj TreeView kontroli ili priti-
Isnuti neki od znakova u obje kontrole.
IjRazmotrimo prvo sluajeve kad korisnik pritisne neto na TreeView kontroli, poto su
lje situacije zanimljivije i potencijalno predstavljaju vei izazov.

Iddabir opcija u izvorinoj TreeVievv kontroli


Dva su TreeView objekta, svaki sa svojom metodom za obradu dogaaja. Uzmite prvo
jiu obzir izvorini TreeView objekt. Korisnik prvo odabira datoteke i mape iz kojih eli
tkopirati. Svaki put kad korisnik pritisne polje za potvrdu odabirajui datoteku ili
; mapu, to izazove vei broj dogaaja. Dogaaj koji morate obraditi je AfterCheck.
JDa biste to postigli, implementirajte prilagoenu metodu za obradu dogaaja i nazo-
ivite ju tvwSource_AfterCheck().
tvw5ource.AfterCheck +=
new System.Windows.Forms.TreeViewEventHandler
(this.tvwSource_AfterCheck);

iImplementacija metode AfterCheck() prebacuje posao na rekurzivnu metodu Set-


Check() koju ete takoer napisati. Ona e rekurzivno postaviti oznake za potvrdu za
sve sadrane mape.

Da biste dodali AfterCheck dogaaj, odaberite kontrolu tvwSource, pritisnite ikonu


Events u prozoru Properties i zatim dvaput pritisnite AfterCheck. To e dodati doga-
aj, povezati ga i postaviti vas u editor koda gdje moete napisati tijelo metode:
private void tvwSource_AfterCheck (
object sender, Systeni.Windows.Forms.TreeViewEventArgs e)

SetCheck(e.Node,e.Node.Checked);
}

Poglavlje 13: Programiranje Windows aplikacija | 317


Metoda za obradu dogaaja prosljeuje objekt sender i objekt tipa TreeViewEventArgs--
Ispada da je vor mogue dobiti od TreeViewEventArgs objekta (e ). Pozovite metodi!
SetCheck() prosljeujui joj vor i stanje vora, odnosno informaciju je li odabran ii/
nije.
Svaki vor ima svojstvo Nodes koje uzima TreeNodeCollection sa svim podvorovima*
SetCheck() rekurzivno prolazi kroz kolekciju Nodes tekueg vora, postavljajui oznak!':
za potvrdu koje odgovaraju odabranom voru. Drugim rijeima, kad oznaite mapu
sve njene datoteke i podmape su rekurzivno oznaene, sve do dna.

Kornjae su sve do dna


Evo jedne, meni omiljene, prie o rekurziji, kako ju je ispriao Stephen Hawking. Dogo-
dilo se to jednom prilikom, kad je poznati znanstvenik priao priu o drevnim mito-
vima postanka. Neki ljudi," rekao je, vjeruju da svijet poiva na leima ogromne
kornjae. Naravo, to potie pitanje: na emu kornjaa poiva?"
Starija ena koja je stajala na kraju sobe je ustala i rekla: Jako pametno, sinko, ali
kornjae su ispod, sve do dna."

Za svaki TreeNode u kolekciji vorova provjerite da li predstavlja list (engl. leaf). vorje
list ako njegova kolekcija ne sadri nijedan vor. Ako je list, postavite njegovo svojstvo
Check na vrijednost koja je proslijeena kao parametar. Ako nije l i s t , ponovite:
private void SetCheck(TreeNode node, bool check)
{
// Trai sve vorove-potomke ovog vora
foreach (TreeNode n in node.Nodes)
{
n.Checked = check; // Odabira vor

// Ako je vor u stablu, nastavlja dalje


if (n.Nodes.Count U o)
{
SetCheck(n,check);
}
}
}
Ovaj kod prosljeuje oznake za potvrdu (ili ih brie) nadolje kroz cijelu strukturu. Na
ovaj nain korisnik moe odabrati sve datoteke u svim podmapama oznaavanjem
samojeclne mape.

irenje mape
Svaki put kad pritisnete znak + pored mape u izvorinoj (ili odredinoj) TreeView kon-
troli, elite proiriti mapu. Da biste proirili mapu trebate metodu za obradu dogaaja

318 | Programiranje C#
RforeExpand. Poto e te metode biti identine za izvorini i odredini TreeView objekt,
f c j a ete napraviti zajedniku metodu za obradu dogaaja za oba objekta:
private void tvwExpand(object sender, TreeViewCancelEventArgs e)

TreeView tvw = ( TreeView ) sender;


bool getFiles = tvw == tvw5ource;
TreeNode currentNode = e.Node;
string fullName = currentNode.FullPath;
currentNode.Nodes.Clear();
GetSubDirectoryNodes( currentNode, fullName, getFiles, l );

}
pn/i red ovog koda pretvara objekt koji je proslijedio delegat iz object u TreeView, to
|je sigurno poto znate da samo TreeView kontrola moe izazvati ovaj dogaaj.
IVa sljedei zadatak je da zadate elite li uzeti datoteke iz mape koju otvarate, a to
elite samo ako je ime objekta TreeView koji je izazvao ovaj dogaaj tvwSource.
Ispred kojeg vora je pritisnut znak + moete utvrditi uzimanjem svojstva Node od
iTreeVievtEventArgs objekta kojeg je dogaaj proslijedio:
TreeNode currentNode = e.Node;

^ Kad imate tekui vor znate njegovu punu putanju (koju ete trebati kao parametar
za GetSubDirectoryNodes) i onda morate oistiti njegovu kolekciju podvorova, poto
ete ju ponovno napuniti pozivanjem GetSubDirectoryNodes:
sSK': currentNode.Nodes.Clear () ;

J ? Zato brisati podvorove i onda ih ponovno dodavati? Jer ete ovaj put ii jo jednu
razinu u dubinu tako da podvorovi znaju ako i oni imaju podvorove te da biste posta-
vili znakove + pokraj njihovih podmapa.

Odabir opcija unutar odredine TreeVievv kontrole


?Druga metoda za obradu dogaaja za odredini TreeView objekt (osim dogaaja Befo-
reExpand) je neto sloenija. Dogaaj se zove A fterSelect. (Zapamtite da odredini
TreeVievv objekt nema polja za potvrdu.). Ovaj put elite uzeti odabranu mapu i staviti
njenu punu putanju u polje za tekst u gornjem lijevom kutu obrasca.
Da biste to uinili, morate ii prema gore kroz vorove, traei ime svake mape koja
se nalazi iznad i sklapati punu putanju:
private void tvwTargetDir_AfterSelect (
object sender, System.Windows.Forms.TreeViewEventArgs e)
{

string theFullPath = GetParentString(e.Node);

Poglavlje 13: Programiranje Windows aplikacija I 319


Uskoro emo razmotriti metodu GetParentString(). Kad imate punu putanju, moral
ukloniti obrnutu kosu crtu (ako postoji) na kraju i onda moete popuniti polje ^
tekst:
if (theFullPath.EndsWith("\\"))
1
theFullPath =
theFullPath.Substring(o,theFullPath.Length-l);
}
txtTargetDir.Text = theFullPath;

Metoda GetParentString() uzima vor i vraa niz sa punom putanjom. Da bi to izveli


kree se nagore po putanji, dodajui obrnutu kosu crtu nakon svakog vora koji nijfep
list (stablaste strukture):
private string GetParentString(TreeNode node)
{
if(node.Parent == nuli)
{
return node.Text;
}
else
1
return GetParentString(node.Parent) + node.Text +
(node.Nodes.Count == o ? : \\");
}
}

Uvjetni operator (?) je jedini ternarni operator u C # jeziku (ternarni


operator u z i m a tri argumenta). L o gik a je sljedea: provjeri da li je
* TL*!' svojstvo node.Nodes.Count je dn a k o nula. A k o je, vrati vrijednost prije
dvo toke (u o v o m sluaju p raz an niz), inae vrati vrijednost iza d v o -
toke (u o v o m sluaju ob rn ut u ko su crtu).

Rekurzija prestaje kad vie nema roditeljskih vorova tj. kad doe do korijenskej
mape.

Obrada pritiska na gumb Clear


Koritenjem metode SetCheck() koju smo ranije razradili obrada dogaaja Clicki;
gumba Clear je trivijalna:
protected void btnClear_Click (object sender, System.EventArgs e)
{
foreach (TreeNode node in tvwSource.Nodes)
C
SetCheck(node, false);
}
}
Samo pozovite metodu SetCheck() na korijenskim vorovima i recite im da rekurzivno j
ponite odabir svih sadranih vorova.

320 | Programiranje C#
Implementacija dogaaja gumba Copy
kad znate odabrati datoteke i odredinu mapu, spremni ste za obradu dogaaja
J|j'a korisnik pritisne gumb Copy. Prva stvar koju trebate uiniti je uzeti popis odabra-
l i datoteka. Ono to traite je polje Filelnfo objekata, ali ne znate koliko objekata
Jg.biti na popisu. To je pravi zadatak za ArrayList. Dodijelite zadau popunjavanja
metodi G etF ile List():
private void btnCopy_Click (
object sender, System.ventArgs e)

Kf ^ List<FileInfo> fileList = GetFileList();

K bjasnit u tu metodu prije nego to nastavimo dalje s metodom za obradu doga-


gaja.

inje odabranih datoteka


[fepnite stvaranjem nove instance objekta List za uvanje nizova koji e predstavljati
g|ye odabrane datoteke:
& private List<FileInfo> GetFileList()
{
// Pravi nesortirano polje imena datoteka
gg; List<string> fileNames = new List<string>();

l i ja biste uzeli imena odabranih datoteka, moete proi kroz izvorinu TreeView kon-
fliolu:
foreach (TreeNode theNode in tvwSource.Nodes)
{
GetCheckedFiles(theNode, fileNames);
}
B )a biste vidjeli kako to funkcionira, uite u metodu GetCheckedFilesf). Ona je prilino
Jednostavna: ispituje vor koji joj je proslijeen. Ako taj vor nema potomaka (node.
Pddes.Count == o), onda je list. Ako je taj list odabran, uzmite punu putanju (poziva-
$jjemmetode GetParentString() za taj vor) i dodajte ju u parametar ArrayList:
private void GetCheckedFiles( TreeNode node,
List<string> fileNames )
1
// Ako je list...
if ( node.Nodes.Count == o )
{
// Ako je vor odabran...
if ( node.Checked )
{
// uzmite punu putanju i dodajte ju u arrayList
string fullPath = GetParentString( node );
fileNames.Add( fullPath );
}
}

Poglavlje 13: Programiranje Windows aplikacija | 321


Ako vor nije list proite rekurzivno niz stablo pronalazei vorove potomke:
else
i
foreach (TreeNode n in node.Nodes)
{
GetCheckedFiles(n,fileNames);
}
}
}
To vraa List popunjen sa svime imenima datoteka. Natrag u metodi GetFileList()
upotrijebite objekt List da napravite drugi objekt L ist, koji e ovaj put sadravati
stvarne F ileln fo objekte:
List<FileInfo> fileList = new List<FileInfo>();

Primjetite da su koriteni List objekti sigurni za tip kako bi se osiguralo da prevoditelj


obiljei svaki objekt dodan u kolekciju koji nije tipa F ilelnfo.
Sad moete prolaziti kroz imena datoteka u fileL ist uzimati ih i s njima instancirati
objekte Fileln fo . Provjerom svojstva Exists moete utvrditi radi li se o datoteci ili
mapi. Ono e vratiti false ako je objekt F ile koji ste napravili zapravo mapa. Ako je
F ile, moete ga dodati u novi ArrayList:
foreach (string fileName in fileNames)
{
Filelnfo file = new FileInfo(fileName);

if (file.Exists)
{
fileList.Add(file);
}
}

Sortiranje popisa odabranih datoteka


Poeljno je da popis odabranih datoteka sortirate od veih prema manjima tako da na
odredinom disku zauzme to manje prostora. Prema tome, morate sortirati ArrayList
objekt. Moete pozvati So rt() metodu, ali kako e ta metoda znati kako sortirati File-
lnfo objekte?
Da biste rijeili ovaj problem morate kao parametar proslijediti IComparer<T> suelje.
Napraviti emo klasu FileComparer koja e implementirati generiko suelje za F ile -
lnfo objekte:
public class FileComparer : IComparer<FileInfo>
{
Ova klasa ima samo jednu metodu, Compare(), koja uzima dva F ileln fo objekta kao
argumente:
public int Compare(FileInfo filel, Filelnfo file2){

322 | Programiranje C#
Uobiajen pristup je da metoda vrati vrijednost 1 ako je prvi objekt (filel) vei od dru-
gog (file2), da vrati vrijednost -1 ako je istina suprotno, i da vrati vrijednost 0 ako su
objekti jednake veliine. U ovom sluaju, elite sortirati od veih prema manjima pa
biste trebali zamijeniti vrijednosti koje metoda vraa.

Budui da je ovo jedina prim jena m etode Compare, razborito je da ovaj


specifian nain sortiranja (od veih prema m anjim a) ukljuite u samu
Compare m etodu. Alternativa je da so rtiran je ide od m anjih prema
veim a, a da pozivanje m etode obrne rezultate kao to ste vidjeli u pri-
m jeru 12-1.

Da biste ispitali duljinu Filelnfo objekta morate pretvoriti parametre object u F ile-
Info objekte (to je u redu jer znate da ova metoda nikad nee primiti nita drugo):
if (filel.Length > file2,Length)
{
return -1;
i
if (filel.Length < file2.Length)
{
return l;
}
return 0;
}
}
Vratimo se metodi G etFile L ist() gdje ste se upravo spremali instancirati referencu
IComparer i proslijediti je metodi Sort() fileList:
IComparer<FileInfo> comparer = ( IComparer<FileInfo> ) new FileComparer();
fileList.Sort(comparer);

Kad je to uinjeno, moete vratiti fileList pozivajuoj metodi:


return fileList;

Pozivajua metoda je bila btnCopy_Click. Prisjetite se da ste otili u metodu GetFile-


List () u prvom redu metode za obradu dogaaja!
protected void btnCopy_Click (object sender, System.EventArgs e)
{
List<FileInfo> fileList = GetFileList();

U ovom trenutku vratili ste sortirani popis F ile objekata od kojih svaki predstavlja
datoteku odabranu u izvorinoj TreeView kontroli.

Sad moete prolazei kroz popis kopirati datoteke i aurirati korisniko suelje:
foreach (Filelnfo file in fileList)
{
try
f
lblStatus.Text = "Copying " +
txtTargetDir.Text + "\\" +

Poglavlje 13: Programiranje Windows aplikacija I 323


file.Name +
Application.DoEvents();

file.CopyTo(txtTargetDir.Text + "\\"
file.Name,chkOverwrite.Checked);
}

catch (Exception ex)


{
HessageBox.Show(ex.Hessage);
}
}
lblStatus.Text = "Done.;

Kako idete dalje, prikaite napredak u natpisu lblStatus i pozovite Application.DoE-


vents() da biste korisnikom suelju omoguili da se osvjei. Zatim pozovite metodu
CopyTo() za datoteku, proslijedivi joj odredinu mapu uzetu iz polja za tekst i Boolean
zastavicu koja pokazuje da li datoteka treba biti prepisana ako ve postoji.
Primjetit ete da zastavica koju prosljeujete metodi vrijednost polja za potvrdu
chkOwerWrite. Svojstvo Checked daje true ako je polje za potvrdu oznaeno, a false
ako nije.
Kopiranje je umotano u blok try jer puno toga moe krenuti krivo prilikom kopiranja
datoteka.
To je sve - kopirali ste datoteke!

Obrada dogaaja gumba Delete


Kod za obradu dogaaja Delete je jo jednostavniji. Prva stvar koju trebate napravite
je da pitate korisnika je li siguran da eli obrisati datoteku:
protected void btnDelete_Click
(object sender, System.EventArgs e)
{
System.Windows.Forms.DialogResult result =
MessageBox.Show(
"Are you quite sure?", // Poruka
"Delete Files", // Natpis
MessageBoxButtons.OKCancel, // Gumbi
MessageBoxIcoh.Exclamation, // Ikone
MessageBoxDefaultButton.Button2); // Podrazumijevani gumb

Moete koristiti statiku metodu Show() iz MessageBox i proslijediti joj poruku koju
elite prikazati, naslov Delete F ile s kao niz znakova i zastavice kako slijedi:
MessageBox.OKCancel trai dva gumba: OK i Cancel.
MessageBox.IconExclamation govori da elite prikazati ikonu usklinika.
MessageBox.DefaultButton.Button2 postavlja drugi gumb (Cancel) kao podrazu-
mijevani izbor.

324 | Programiranje C#
ad korisnik pritisne OK ili Cancel rezultat se prosljeuje natrag kao enumerirana
pjijednost System.Windows.Forms.DialogResult. Moete provjeriti ovu vrijednost da
-vidite je li korisnik odabrao OK:
if (result == System.Windows.Forms.DialogResult.OK)
{
kko jest, moete dobiti popis imena datoteka fileNames i prolazei kroz njega brisati
jvaku od njih:
Arraylist fileNames = GetFileList();

foreach (Filelnfo file in fileNames)


{
try
{
lblStatus.Text = "Deleting " +
txtTargetDir.Text + "\\" +
file.Name +
Application.DoEvents();

iy
file.Delete();
}

catch (Exception ex)


{
MessageBox.Show(ex.Message);
}
}
lblStatus.Text = "Done.";
Application.DoEvents();

: Ovaj kod je identian kodu za kopiranje osim to je metoda koju se poziva nad dato-
tekom Delete().

] Prim jer 13-1. Iz v o rn i kod F ile C op ie r aplikacije


!Sregion Using directives

j using System;
i using System.Collections;
:using System.Collections.Generic;
- using System.ComponentModel;
using System.Data;
;; using System.Drawing;
using System.IO;
using System.Windows.Forms;

Hendregion

H I I I <remarks>
P !U File Copier - Windows Forms demonstracijski program
III (c) Copyright 2005 Liberty Associates, Ine.
U ! </remarks>

Poglavlje 13: Programiranje Windows aplikacija | 125


Primjer 13-1. Izvorni kod PileCopier aplikacije (nastavak)

namespace FileCopier
{
III <summary>
III Obrazac za demonstraciju Windows Forms implementacije
I I I </summary>
partial class frmFileCopier : Form
{
private const int MaxLevel = 2;
public frmFileCopier()
{
InitializeComponent();
FillDirectoryTree( tvwSource, true );
FillDirectoryTree( tvwTarget, false );
}

III <summary>
III Ugnijeena klasa koja zna kako usporeivati
/// dvije datoteke koje elimo sortirati od vee prema manjoj.
I I I </summary>
public class FileComparer : IComparer<FileInfo>
{
public int Compare(FileInfo filel, Filelnfo file2)
{
if ( filel.Length > file.Length )
{
return -1;

if ( filel.Length < file 2 .Length )


{
return 1;
}
return 0;

public bool Equals(FileInfo x, Filelnfo y) {


throw new NotImplementedException();

public int GetHashCode(FileInfo x) {


throw new NotImplementedException();
}
}
private void FillDirectoryTree( TreeView tvw, bool isSource )
{
// Popunjava tvwSource, the Source TreeView,
II sa sadrajem
// lokalnog tvrdog diska.
I I Najprije brie sve vorove.
tvw.Nodes.Clear();

326 | Programiranje C#
primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
II Uzima logike diskove i stavlja ih
// u korijenske vorove. Popunjava polje s
// logikim diskovima na stroju.
string[] strDrives = Environment.GetLogicalDrives();

// Prolazi kroz diskove i dodaje ih stablu.


Il Koristi blok try/catch pa ako disk nije spreman,
// npr. prazna disketa ili CD,
// nee biti dodan na popis.
foreach ( string rootDirectoryName in strDrives )

try
{

// Popunjava polje sa mapama prve razine.


// Ako disk nije spreman
II bit e izbaena iznimka.
DirectoryInfo dir =
new DirectoryInto( rootDirectoryName );

dir.GetDirectories(); // Forsira iznimku ako disk nije spreman

TreeNode ndRoot = new TreeNode( rootDirectoryName );

// Dodaje vor za svaku korijensku mapu.


tvw.Nodes.Add( ndRoot );

// Dodaje vorove podmapa.


// Ako je Treeview izvor,
// onda uzima i imena datoteka,
if ( isSource )
{

GetSubDirectoryNodes(
ndRoot, ndRoot.Text, true,l );
}
else
{
GetSubDirectoryNodes(
ndRoot, ndRoot.Text, false,l )
}
}
// Hvata pogreke poput
I I diska koji nije spreman,
catch
{
}
Application.DoEvents();
}
} // Z at v ar a FillSourceDirectoryTree

Poglavlje 13: Programiranje Windows aplikacija | 327


Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)

I I I <summary>
I I I Uzima sve podmape ispod
I I I proslijeenog vora mape.
I I I Dodaje ih u stablo mapa.
I I I Proslijeeni parametri su roditeljski vor
/// za tu podmapu,
I I I puna putanja podmape,
I I I i Boolean zastavicu koja govori
I I I da li treba uzeti datoteke iz podmape.
I I I </summary>
private void GetSubDirectoryNodes(
TreeNode parentNode, string fullName, bool getFileNames,
int level )
{
DirectoryInfo dir = new DirectoryInfo( fullName );
DirectoryInfo[] dirSubs = dir.GetDirectoriesO;

// Dodaje vor-potomak za svaku podmapu.


foreach ( DirectoryInfo dirSub in dirSubs )
{
// Ne prikazuje skrivene mape
if ( ( dirSub.Attributes & FileAttributes.Hidden )
!= 0 )
{
continue;
}

I I I <summa ry>
III Svaka mapa ima punu putanju.
III Trebamo ju podijeliti na kosim crtama,
III i upotrijebiti samo
III zadnji vor u stablu.
III Potrebno je dvaput napisati kosu crtu
/// jer je ona inae
III znak za prekid
I I I </summary>
TreeNode subNode = new TreeNode( dirSub.Name );
parentNode.Nodes.Add( subNode );

// R e ku rz iv no pozi va Ge t S u b Di rect oryNodes.

if ( level < MaxLevel )


. {
GetSubDirectoryNodes(
subNode, dirSub.FullName, getFileNames, level+l )
}
}
if ( getFileNames )
{
// Uzima datoteke za ovaj vor.
Filelnfof] files = dir.GetFilesO;

328 | Programiranje C#
mtimjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
I I Nakon spremanja vorova,
// sada sprema datoteke iz mapa.
foreach ( Filelnfo file in files )
{
TreeNode fileNode = new TreeNode( file.Name );
parentNode.Nodes.Add( fileNode );
}

}
I II <summary>
I II Pravi sortirani popis svih
I I I datoteka odabranih za kopiranje
III u odredinu mapu
I I I </summary>
private void btnCopy_Click( object sender,
System.EventArgs e )
{
// Uzima popis

List<FileInfo> fileList = CetFileList();

l // Kopira datoteke

I foreach ( Filelnfo file in fileList )

try
{
// Osvjeava natpis da prikazuje napredak
lblStatus.Text = "Copying " + txtTargetDir.Text
+ file.Name + "...
Application.DoEvents();

// Kopira datoteku u odredinu mapu


file.CopyTo( txtTargetOir.Text + "\\" +
file.Name, chkOverwrite.Checked );
}

catch ( Exception ex )
{
I I Moda elite neto vie od
I I prikazivanja poruke
MessageBox.Show( ex.Message );

}
lblStatus.Text = "Done.";
Application.DoEvents();

I I I <summary>
III Govori korijenu svakog stabla da poniti
III odabir svih vorova ispod
I I I </summary>

Poglavlje 13: Programiranje Windows aplikacija | 329


P r i m j e r 13 - 1. I z v o r n i k o d F i l e C o p i e r a p l i k a c i j e ( n a s t a v a k )

private void btnClear_Click( object sender, System.EventArgs e )

^ // Uzima vor na najviem poloaju za svaki disk


// i govori mu da se rekurzivno oisti
foreach ( TreeNode node in tvwSource.Nodes )

SetCheck( node, false );

}
}
I I I <summary>
III Ako korisnik pritisne Cancel zatvara se
I II </summary> _ .. .
private void btnCancel_Click(object sender, EventArgs e)

Application.Exit();

}
I I I <summary>
III S popisom i polje na raspolaganju
III popunjava popis imenima
III svih odabranih datoteka
I I I </summary>
// Popunjava ArrayList sa punim putanjama
// odabranih datoteka
private void GetCheckedFiles( TreeNode node,
List<string> -fileNames )
{
// Ako je list...
i-f ( node.Nodes.Count == 0 )

// ako je vor odabran...


i-f ( node.Checked )

// uzima punu putanju i dodaje ju na arraylist


string fullPath = GetParentString( node );
fileNames.Add( fullPath );

}
}
else // Ako ovaj vor nije list

// ako ovaj vor nije list


foreach ( TreeNode n in node.Nodes )

GetCheckedFiles( n, fileNames );

}
}
I I I <summary>
III Uzima vor i vraa
III punu putanju
/// </summary>

330 | Programiranje C#
Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
private string GetParentString( TxeeNode node )
{
// Ako je ovo korijenski vor (c:\) vraa tekst
iF ( node.Parent == nuli )
{
return node.Text;
}
else
{
// Vraa se nagore i uzima putanju,
// zatim dodaje ovaj vor i kosu crtu
// Ako je ovaj vor list, ne dodaje kosu crtu
return GetParentString( node.Parent ) + node.Text +
( node.Nodes.Count == 0 ? : "\\" );
}
}
I I I <summary>
III Dijele ju operacije brisanja i kopiranja
III Stvara sortirani popis
III odabranih datoteka
I I I </summary>
private List<FileInfo> GetFileList()
{
// Stvara nesortirano polje punih putanja datoteka
List<string> FileNames = new List<string>();

I I ArrayList FileNames = new ArrayList();

// Popunjava FileNames ArrayList s


// punom putanjom svake datoteke za kopiranje
Foreach ( TreeNode theNode in tvwSource.Nodes )
{
GetCheckedFiles( theNode, FileNames );
' }

// Pravi popis za uvanje FilelnFo objekata


List<FileInFo> FileList = new List<FileInFo>();
// ArrayList FileList = new ArrayList();

// Za svako ime datoteke iz nesortiranog polja,


// ako ime odgovara datoteci (a ne mapi)
// dodaje ga na popis datoteka
Foreach ( string FileName in FileNames )
{
// Stvara datoteku s imenom
FilelnFo File = new FileInFo( FileName );

// Provjerava postoji li na disku


// i ne uspjeva ako je mapa
iF ( File.Exists )
{

Poglavlje 13: Programiranje Windows aplikacija I 331


Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
I I I klju i vrijednost je datoteka
// Da li bi bilo lake kad bi imali praznu vrijednost?
fileList.Add( file );
}
}
// Stvara instrancu suelja IComparer
IComparer<FileInfo> comparer = ( IComparer<FileInfo> )
new FileComparer();

// Prosljeuje komparator metodi za sortiranje tako da je popis


// sortiran s metodom za sortiranje.
fileList.Sort( comparer );
return fileList;

I I I <summary>
III Provjerava de li korisnik zaista eli brisanje.
III Pravi popis i brie svaku datoteku.
I I I </summary>
private void btnDelete_Click( object sender, System.EventArgs e )
{
// Pita korisnika je li siguran
System.Windows.Forms.DialogResult result =
MessageBox.Show(
"Are you quite sure?", // Poruka
"Delete Files", // Natpis
MessageBoxButtons.OKCancel, // Gumbi
HessageBoxIcon.Exclamat ion, // Ikone
MessageBoxDefaultButton.Button2 ); // Podrazumijevani gumb

// Ako je korisnik siguran...


if ( result == System.Windows.Forms.DialogResult.OK )
{
// prolazi kroz popis i brie datoteke.
// Uzima popis odabranih datoteka.
List<FileIn-fo> fileNames = GetFileList();

foreach ( Filelnfo file in fileNames )


{
try
{
// Aurira natpis da prikazuje napredak
lblStatus.Text = "Deleting " +
file.Name +
Application.DoEvents();

// Brisanje!
file.Delete();
}

catch ( Exception ex )
{

312 | Programiranje C#
primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
I I Moda elite neto vie od
// prikazivanja poruke
MessageBox.Show( ex.Message );
}
}
lblStatus.Text = "Done.1;
Application.DoEvents();
}
}
I I I <summary>
III Uzima punu putanju odabrane mape
I II i kopira ju u txtTargetDir
I I I </summary>
private void tvwTargetDir_AtterSelect(
object sender,
System.Windows.Forms.TreeViewEventArgs e )
{
// Uzima punu putanju za odabranu mapu
string theFullPath = GetParentString( e.Node );

// Ako nije list zavravat e s obrnutom kosom crtom


// Brie kosu crtu
if ( theFullPath.EndsWith( "\\" ) )
{
theFullPath =
theFullPath.Substring( 0 , theFullPath.Length - 1 );
}
// Dodaje putanju u polje za tekst
txtTargetDir.Text = theFullPath;

I I I <summary>
III Obiljeava svaki vor ispod tekueg
'III s trenutnom vrijednosti obiljeenog vora
I I I </summary>
private void tvwSource_AfterCheck( object sender,
System.Windows.Forms.TreeViewEventArgs e )
{
// Poziva metodu za rekurzivni prolaz.
// e.node je vor koji je korisnik odabrao.
// Stanje oznake za potvrdu je ve promijenjeno
// dok ste stiglo do ovog mjesta.
// Zbog toga elimo proslijediti
// stanje e.node.Checked.
if(e.Action != TreeViewAction.Unknown)
{
SetCheck(e.Node, e.Node.Checked );
}

I I I <summary>
/// rekurzivno postavlja ili brie oznake za potvrdu
I I I </summary>

Poglavlje 13: Programiranje Windows aplikacija | 333


Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak)
private void SetCheck( TreeNode node, bool check )
{
// Trai sve vorove potomke ovog vora
foreach ( TreeNode n in node.Nodes )
{
n.Checked = check; // Potvruje vor

// Ako je to vor u stablu, rekurzivno prolazi


if ( n.Nodes.Count != 0 )
{
SetCheck( n, check );
}
}
}
private void tvwExpand(object sender, TreeViewCancelEventArgs e)

TreeVieu tvw = ( TreeView ) sender;


bool getFiles = tvw == tvwSource;
TreeNode currentNode = e.Node;
string fullName = currentNode.FullPath;
currentNode.Nodes.Clear();
GetSubDirectoryNodes( currentNode, fullName, getFiles, 1 );
}
}
}

XML komentari za dokumentaciju


C # jezik podrava novi stil komentara za dokumentaciju sa tri kose crte (///). Moete
vidjeti ove komentare razbacane svuda u primjeru 13-1. Visual Studio editor prepo-
znaje ove komentare i pomae da ih se ispravno oblikuje.
C # prevoditelj sakuplja ove komentare u X M L datoteku. Moete ju napraviti zada-
vanje /doc prekidaa prilikom prevoenja u naredbenom retku. Na primjer, moete
prevesti program iz primjera 13-1 sa ovom naredbom:
esc Formi.cs /doc:XMLDoc.XML

Isti ishod moete postii u Visual Studiju pritiskom na ikonu projekta FileCopier u pro-
zoru Solution Explorer, odabirom opcije View Propery Pages u Visual Studio izborniku
i zatim odabirom opcije Build property page. Potvrdite polje XMLDocumentation File
i upiite ime X M L datoteke koju elite napraviti, na primjer FileCopier.XML.
Dio datoteke koja je napravljena za aplikaciju FileCopier iz prijanjih odlomaka je
prikazan u primjeru 13-2.

334 | Programiranje C#
13 -2 . O d l o m a k X M L d a t o t e k e s a d o k u m e n t a c i j o m z a a p l i k a c i j u z a k o p i r a n j e d a t o t e k a
lpm)er
fo
<assembly>
<name>FileCopier</name>
</assembly>
<members>
cmember name="T:FileCopier.frmFileCopier">
<summary>
Obrazac za demonstraciju Windows Forms implementacije
</summary>
</member>
cmember name="F:FileCopier.frmFileCopier.components">
<summary>
Required designer variable.
1 </summary>
</member>
j: cmember name="M:FileCopier.frmFileCopier.Dispose(5ystem.Boolean)>
- <summary>
Clean up any resources being used.
i </summary>
i </member>
cmember name="M:FileCopier.frmFileCopier.InitializeComponent">
<summary>
Required method for Designer support - do not modify
the contents of this method with the code editor.
</summary>
</member>
cmember name="M:FileCopier.frmFileCopier.GetSubDirectoryNodes
(System.Windows.Forms.TreeNode,System.String,System.Boolean,System.Int32)>
<summary>
Uzima sve podmape ispod
proslijeenog vora mape.
Dodaje ih u stablo mapa.
Proslijeeni parametri su roditeljski vor
za tu podmapu,
puna putanja podmape,
i Boolean zastavicu koja govori
da li treba uzeti datoteke iz podmape.
</summary>
</member>
cmember name="M:FileCopier.frmFileCopier.btnCopy_Click
(5ystem.Object,5ystem.EventArgs)">
<summary>
Pravi sortirani popis svih
datoteka odabranih za kopiranje
u odredinu mapu
</summary>
</member>

Dokument je velik, i iako je ljudima itljiv, nije previe koristan u tom obliku. Moete,
meutim, napisati XSLT datoteku da prevede XML u HTM L, ili moete uitati XML
dokument u bazu podataka za dokumentaciju. Takoer moete povui datoteku iz

Poglavlje 13: Programiranje Windows aplikacija I 335


prozora File Explorer u Windows Explorer koji osigurava zgodno suelje za itanje
XM L-a, kao to je prikazano na slici 13-9.

3 C :\D o cum ea ts and S ettin g s\Je ss e \M y D o cu m e n ts\W o rd D o cu m e n ts\2 0 0 5 Books\C ... Q j [ n l [ x ]


Fife Edit View Favor
avorite Toob Help &
B** ' t - [ij) [g| ^Fawnw^Media ^ 0 - ^ 0 *U [*] 3 121
Addreis [Qc:*xum en tsaxlSeKhqsV ssse\frty Opcuments\WofdOocuments\3005 6ooltS\C Sharp 4e\SourceV3wpter 13\fileCoplet^i Q G o
gRetocForni (gPaacard^ (EjIdentjttes - S>Safaiotes - [j^FIlIFornB - {gSave jpHQ8........;j U t o Ostornti **

1
<3x
<3xml vorsio
vorsion= l.Q* 1>
<ri'ime>F(leCoplorc/nsrne>
<ri'ime>F(leCoplorc/nsrne>
<assemb
embly>

</s
</ssen>bl

one
- on
n>bly>
c/tnembof>
- cmembers?
ember n am e='
e='T :F ll eCo
<summary5Forrn dem
eCopl er.frm Fl leCo
de m on str
eCopl er->
str o tln g Vll
Vl ln dow
dow s F orr
or r n s lmp
lmplem
lemenlj
enljr
ljr tlo
tlon</
n</5uin maiy>
s$1$
- <menienibet narn ee' ee 'F iFileC
leCopI
opIe r .frm FIleCleCoplople r.compoueni
ompoue nis nis *?
<sumrna
umrnarnary>Requlred d e sl gner ner v eHobl e.c /tu inm srv j
</rtember>
- cmember nai nain9*,M
*,M:F llelle Copl
op le r .fnnF
nn F lleC
le Copl
ople r .D ls p os e <S ysten steni
en i.Bo ol ean)
ean )* :
<su
<summ arv>Clean up o n y r e s ou r c e s bel bel n g used.<
used.</summary>
</member ber>
- <member ber name="
name="M :FJIeC Ie Copl
op le r .fr m Fll e Copl
op le r .I n l tlo ll z eC om poneni
onen i'>
<su
<summary>Requlrod m e t h od f o r De slg ner ne r su pport
pport - do n ot m odif od ifv
ifv th e con ten ts of th l s m e th o d
w r t h t b e c ade
ade edieditor .</sutnm% nm%rj>
c/member?
oii
oiiern
ernber n3me 3me=M :F l l e C o ple r .f r m F lleC o p le r .G e tSub0lrSub0 lre lr e c tory
or y N od o s
(8 y s te m .W l ndo w s .F o r m s .T r ee N ode /8y st e ro .8 t rln rl n g ,S y st e ro .B o o l e o n ,8 y s l e m .l n t 32 )',
)',>
<sumniary>Gets ell tb e subd ub dl re c tories
ories balova th e pe ss e d In d lre lr e c to r y no d e . Add s to th e d l re c toryor y
t r ee . T h e per
pe r e m e te r s p e ss e d In ere th e per pe re n t n ode od e fo r th l s s ubdlr
ubd lre
lr e c tory
or y , th e tuli pet
peth neni
neni e
of th l s s ubdlr
ubd lrs
lr s cto
cto r y , ond o Boolee ol een
ee n to In dlce te tvh eth er o r not to g e t th e fil fil es In the
su bdlr eotar tar y.</sum
summ ery ery>
</rnem
</rnember>ber>
- cmember name me M :F H e C o p l e r .fnn F l le Co p l e r .b tn Co p y_ C tl c k (S y st e m .O b J e ct/ ct/8 y st e m .e w en tA rg s ) , >
<sum
<sum marymary>Creo reote on o r der de r e d list of ell th e se l e c to d Hles, les, c o p y to th e to rg e t dlrect lrecto
ctor y<A unnnary>
unnnary>
</member ber>
cmember n s m e= 'M :F lleC op l er .f r m Flle ll ec o p te r .b tnC leo r_ C Hck( ck (S y ste
ste m .O b Je c t/f l y s t e m .even tA r g s )'>
)'>
<summar/>Tell th e r oot of oo c h tro e to un ch ock ell th e node nod e s belowc/
belowc/sururna rurnaiy>
</t
</tnembeo
- cmember nam na m e =*M
=* M :F l le C opl
op le r .f r m F ll e C opl
op le r .b tnC e n c e L c l le k ( Sy s te m .O b j e ct, ct,S y st e m .E ven tA r g s) * >
csuntm
untmar
tmary
ary>on cenc en c al , exltc/summ3rv>
</member ber>
<n)
<n)Bmber ber name=*M
name=*M :Flle lle Copl
opler .fm iF| le Cop Coplor .G etC h ecke dFlle lle s
S My Comput
puter

Slika 13-9. Pregled XML dokumentacije u Internet Exploreru

336 | Programiranje C#
_________________________ POGLAVLJEM
Pristup podacima kroz AD&NET

Mnoge aplikacije trebaju pristupati bazi podataka. .NET kostur prua irok skup obje-
kata za povezivanje s bazom podataka. Ove klase se nazivaju zajednikim imenom
ADO.NET.
ADO.NET izgleda vrlo slino kao ADO, njegov prethodnik. Kljuna razlika je da je
ADO.NET prirodni dio .NET kostura (nije samo omota za OLEDB) i da je to prven-
stveno nepovezana podatkovna arhitektura. U nepovezanoj arhitekturi podaci se uzi-
maju iz baze podataka i pohranjuju lokalno. Vi radite s tim podacima na lokalnom
raunalu i spajate se na bazu podataka samo kada elite promijeniti zapise ili uzeti
nove podatke.
Postoje znatne prednosti kod odvajanja podatkovne arhitekture od baze podataka. Naj-
vea prednost je da aplikacija, bilo da se izvodi na Webu ili lokalno, predstavlja manje
optereenje za posluitelj baze podataka, to aplikaciju moe uiniti prilagodljivijom.
Veze prema bazi podataka se intenzivno koriste i teko je odravati tisue (ili stotine
tisua) istovremenih veza. Nepovezana arhitektura omoguava utedu sredstava.
ADO.NET se spaja na bazu podataka da uzme podatke i zatim se ponovno spaja da
aurira podatke koje ste promijenili. Veina aplikacija provodi veinu vremena jed-
nostavno itajui podatke i prikazujui ih. ADO.N ET osigurava neovisni podskup
podataka koji ete koristiti za itanje i prikazivanja podataka.

Relacijske baze podataka i SQL


Iako je mogue napisati cijelu knjigu o relacijskim bazama podataka i jo jednu o
SQL-u, bitna svojstva ovih tehnologija nisu teka za razumijevanje. Baza podataka je
spremite podataka. Relacijska baza podataka organizira podatke u tablice. Razmo-
trite Northwind demonstracijsku bazu podataka koja dolazi uz Microsoft SQL Server
i Microsoft Access.

337
Tablice, zapisi i stupci
Northvvind baza podataka opisuje izmiljenu tvrtku koja kupuje i prodaje prehram
bene proizvode. Podaci su podijeljeni u trinaest tablica: Customers, Empl0yees
Orders, Order Detaild, Products i tako dalje.

Svaka tablica u relacijskoj bazi podataka je organizirana u u redove, gdje svaki red
predstavlja jedan zapis (engl. record). Redovi su organizirani u stupce. Svi redovi u
tablici imaju istu strukturu stupaca. Na primjer, tablica Orders ima tri stupca: Orde-
rlD, CustomerlD, OrderDate.

Za bilo koju narudbu trebate znati ime kupca, adresu, ime osobe za kontakt itd
Mogli biste spremiti tu informaciju sa svakom narudbom, ali to bi bilo neuinkovito
Umjesto toga koristite drugu tablicu koja se zove Customers i u kojoj svaki red pred-
stavlja jednog kupca. U tablici Customers postoji stupac CustomerlD. Svaki kupac ima
jedinstveni identifikator i to polje je oznaeno kao primarni klju (engl. primary key)
za tu tablicu. Primarni klju je stupac ili kombinacija vie stupaca koji jednoznano
identificiraju zapis u tablici.

Napomena za VB6 programere koji prelaze na AD0.NET


ADO.NET je poneto drugaiji u odnosu na ADO. Dok uite kako implementirati
nove ADO.NET funkcionalnosti, vjerojatno ete si esto postavljati pitanja kao to su
Gdje je MoveNext() metoda?" ili Kako da provjerim gdje je kraj datoteke?".
Kod ADO.NET-a, funkcionalnost koja je bila u sklopu Record Sets se sada nalazi na
dva mjesta. Upravljanje podacima i uzimanje podataka provodi se kroz suelje Idata-
Reader, dok se podrka za nepovezane operacije nalazi u (znatno pojaanim) DataSet
i DataTables.
DataTables moe biti shvaeno kao polje koje sadri DataRows. Pozivanjem metode Move-
F irst() u ADO.NET-u bi bilo isto kao i pristupanje prvom indeksu polja. Provjera
gdje je kraj datoteke odgovara provjeri da li tekui indeks odgovara gornjoj granici
polja. elite postaviti obiljeiva za odreeni zapis? Samo napravite varijablu i dodije-
lite joj indeks tekueg zapisa (ne trebate posebno BookMark svojstvo).

Tablica Orders koristi CustomerlD kao vanjski klju. Vanjski klju (engl. foreign key)
je stupac (ili kombinacija vie stupaca) koji je primarni klju za neku drugu tablicu.
Tablica Orders koristi CustomerlD (primarni klju za tablicu Customers) da odredi
koji kupac je napravio narudbu. Da biste odredili adresu za narudbu, moete kori-
stiti CustomerlD klju da biste potraili zapis kupca u tablici Customers.
Ovakva upotreba vanjskih kljueva je posebno korisna prilikom prikazivanja veza tipa
jedan prema vie" ili vie prema jednom" izmeu tablica. Odvajanjem informacija
u tablice koje su povezane vanjskim kljuevima, izbjegavate ponavljanje informacija
u zapisima. Jedan kupac, na primjer, moe imati vie narudbi, ali je neuinkovito

338 | Programiranje C#
|: navljati informacije o kupcu (ime, telefonski broj, dozvoljeni limit i tako dalje) u
?sva.ik,;i zapis naru
rud
dbe. Pro ces uklan jan jaja suv
uvin
inih
ih informacija iz zapisa i njihovo
preba- civanje u zasebnu tablicu se zove normalizacija.

Normalizacija
Normalizacija ne sam o da omoguava uinkovitije koritenje baze podataka, ve i sma-
njuje vjerojatnost gubitka. Ako bi zadrali ime kupca u tablici Customers i u tablici
, Orders, onda bi postojao rizik da se promjena u jednoj od tablica odrazi i na drugu
tablicu- Zato, ako biste promijenili adresu kupca u tablici Customers, ta promjena se
ne bi vidjela u svakom redu tablice Orders (i dosta truda bi bilo potrebno da osigurate
da se promjene vide). Dranjem CustomerlD identifikatora u tablici Orders, moete
slobodno mijenjati adresu u tablici Customers i promjena e se automatski odraziti
j na svaku narudbu.
Ba kao to programeri u C # jeziku ele da prevoditelj primjeti pogreke prilikom
prevoenja, a ne prilikom izvoenja, tako i programeri baza podataka ele da im baza
podataka pomogne da izbjegnu mogunost gubitka podataka. Prevoditelj pomae da
se izbjegnu pogreke u C# jeziku nametanjem pravila jezika (na primjer, ne moete
koristiti varijablu koju niste definirali). SQL Server i druge moderne relacijske baze
podataka izbjegavaju pogreke nametanjem ogranienja koja moete sami definirati.
Na primjer, baza podataka Customers oznaava CustomerlD kao primarni klju. To
I stvara ogranienje primarnog kljua u bazi podataka koje osigurava da je svaki Custo-
merlD jedinstven. Ako u bazu podataka dodate kupca Liberty Associates, lnc. iji
identifikator CustomerlD ima vrijednost LIBE i onda pokuate dodati kupca Liberty
Mutual Funds sa vrijednosti LIBE identifikatora CustomerlD, onda bi baza podataka
odbacila taj drugi zapis zbog ogranienja primarnog kljua.

Nazivni referencijalni integritet


Relacijske baze koriste nazivni referencijalni integritet (engl. deelarative referential inte-
grity) za postavljanje ogranienja na veze izmeu razliitih tablica. Na primjer, moete
postaviti ogranienje nad tablicom Orders koje namee da nijedna narudba ne moe
imati CustomerlD identifikator osim ako CustomerlD ne predstavlja valjan zapis u tablici
Orders. To pomae da se izbjegnu dvije vrste pogreaka. Prvo, ne moete unijeti zapis
sa identifikatorom CustomerlD koji nije valjan. Drugo, ne moete obrisati zapis kupca
ako se taj CustomerlD koristi u nekoj od narudbi. Integritet podataka je prema tome
zatien.

SQL
Najpopularniji jezik za izradu upita i rad s bazama podataka je SQL. SQL je dekla-
rativni jezik, za razliku od proceduralnih jezika, i moe proi neko vrijeme dok se
naviknete raditi sa deklarativnim jezikom kad ste se ve navikli na proceduralne jezike
kao sto je C# .

f
M Poglavlje 14: Pristup podacima kroz AD0.NET | 339
SQL je baziran na upitima. Upit (engl. query ) je deklaracija koja vraa skup zapisa i
baze podataka.
Na primjer, moete poeljeti da vidite sve zapise iz tablice Customers u kojima je
adresa kupca u Londonu. Da biste to napravili, napiite:
Select CustomerID, CompanyName from Customers where city = 'London'

Ovaj kod vraa sljedeih est zapisa:


CustomerID CompanyName

AROUT Around the Horn


BSBEV B's Beverages
C0N5H Consolidated Holdings
EASTC Eastern Connection
NORTS North/South
SEVES Seven Seas Imports

SQL je sposoban za mnogo monije upite. Na primjer, zamislite da direktor kompa-


nije Northwind eli znati koje proizvode je kupac Vins et alcohols Chevalier kupio u
estom mjesecu 1996. godine. To je neto kompliciranije. Tablica Order Details zna
identifikator ProductlD za sve proizvode u svim narudbama. Tablica Orders zna koji
su CustomerID identifikatori povezani sa narudbom. Tablica Customers zna Custome-
rID za kupca, a tablica Products zna koje je ime proizvoda za ProductlD. Kako sve to
povezati? Sljedeim upitom:
select o.OrderID, productName
from [Order Details] od
join orders o on o.OrderID = od.OrderID
join products p on p.ProductlD = od.ProductlD
join customers c on o.CustomerID = c.CustomerID
where c.CompanyName = 'Vins et alcools Chevalier'
and orderDate >= '7/1/1996' and orderDate <= '7/31/1996'

Ovaj upit trai od baze podataka da uzme OrderID identifikator i ime proizvoda iz
tablica kojih se to tie. Prvo treba pogledati u tablicu Order Details (koju smo skra-
eno nazvali od), zatim to povezati s tablicom Orders za svaki zapis u kojem je OrderID
u tablici Order Details jednak kao OrderID u tablici Orders.

Kad poveete te tablice, moete rei ili Uzmi svaki zapis koji postoji u bilo kojoj od
tih tablica" (ovo se zove vanjski spoj (engl. outer join)) ili kako sam napravio u ovom
sluaju, Uzmi samo one zapise koji postoje u obje tablice" (ovo se zove unutarnji spoj
(engl. inner join)). To jest, unutarnji spoj odreuje da se uzmu samo zapisi u tablici
Orders koji odgovaraju zapisima u tablici Order Details po polju OrderID (on o.Orde-
rid-od.Orderid).
r<,
^ SQL spojevi su standardno postavljeni da budu unutarnji spojevi.

340 I Programiranje C#
SQL deklaracija pita bazu podataka da napravi unutarnji spoj sa tablicom Orders i
pritom uzme svaki, red u kojem je ProductID u tablici Products jednak kao ProductID
u tablici Order Details.
Zatim napravite unutarnji spoj sa kupcima za one redove gdje je CustomerlD jednak u
tablicama Orders i Customers.

fja kraju, napravite ogranienje tako da se kao rezultat uzmu samo oni redovi u kojima
je CompanyName ona tvrtka koju traite, a datumi su u estom mjesecu.
Ova kolekcija ogranienja pronalaze samo tri odgovarajua zapisa:
OrderID ProductName

10248 Oueso Cabrales


1 0 24 8 Singaporean Hokkien Fried Mee
1 0 24 8 Mozzarella di Giovanni

Ovaj izlaz pokazuje d aje bila samo jedna narudba (10248) u kojoj je kupac imao tra-
eni identifikator i u kojoj je datum bio esti mjesec 1996. Ta narudba je proizvela
tri zapisa u tablici Order Details i koristei identifikatore proizvoda u ova tri zapisa,
dobivate ime proizvoda iz tablice Products.
Moete koristiti SQL ne samo za pretraivanje i uzimanje podataka, ve i za stvara-
nje, auriranje i brisanje tablica i openito upravljanje i rad sa sadrajem i strukturom
baze podataka.
Za puno objanjenje SQL jezika i kako ga to bolje iskoristiti, preporuam vam knjigu Tran-
sactSQL Programming (u izdanju 0 Reilly Media). Ako koristite SQL bazu podataka, a da
to nije SQL Server, onda bi bilo dobro da prouite SQL Pocket Guide (takoer u izdanju
0 Reilly Media) jer svaki dobavlja moe koristiti malo drugaiji SQL dijalekt".

Objektni model ADO.NET-a


Objektni model ADO.NET-a je bogat, ali prilino jednostavan, skup klasa. Najvanija
od tih klasa je DataSet. Ona predstavlja podskup cijele baze podataka koji je pohranjen
na lokalnom raunalu bez stalne veze s bazom podataka.
Periodino ete spojiti DataSet sa roditeljskom bazom podataka, aurirati je sa promje-
nama koje ste napravili na DataSet te aurirati DataSet sa promjenama u bazi podataka
koje su napravili ostali procesi.
Da bi bio djelotvoran, DataSet mora biti robustan podskup baze podataka, obuhva-
ajui ne samo par redova jedne tablice, nego i skup tablica zajedno s metapodacima
potrebnim za prikazivanje veza i ogranienja originalne baze podataka. To je (to ne
predstavlja iznenaenje) ono to ADO.NET prua.
DataSet je sastavljen od DataTable objekata kao i od DataRelation objekata. Ovim
objektima se pristupa kao svojstvima objekta DataSet. Svojstvo Tables vraa kolekciju
DataTableCollection koja sadri sve DataTable objekte.

Poglavlje 14: Pristup podacima kroz ADO.HET j 341


DataTable i DataColumn objekti
DataTable moe biti napravljen programski ili kao rezultat upita u bazi podaP
DataTable ima vei broj javnih svojstava ukljuujui kolekciju Columns koja vraa olgt
ColumnCollection objekt koji se sastoji od DataColumn objekata. Svaki od DataColI"*1
objekata predstavlja stupac u tablici.

DataRelation objekti
Osim kolekcije Tables, DataSet ima i Relations svojstvo koje vraa DataRelationCollei
tion kolekciju koja se sastoji od DataRelation objekata. Svaki DataRelation objekt p re d
stavlja vezu izmeu dvije tablice kroz DataColumn objekte. Na primjer, u Northwindbal|l
podataka tablica Customers je u vezi sa tablicom Orders preko stupca CustomerID.
Priroda ove veze je od jednog prema vie, ili od roditelja prema potomku. Za bilo koj||
narudbu, bit e samo jedan kupac, dok bilo koji kupac moe biti zastupljen u vill
narudbi.

Redovi
Kolekcija Rows vraa skup redova za tablicu. Koristite ovu kolekciju da ispitate rezuk'Jl
tate upita u bazi podataka, prolazei kroz redove da bi ispitali svaki zapis. Programerif"
koji su koristili ADO su esto zbunjeni time to nema objekta RecordSet sa njegovirti1^
moveNext i movePrevious naredbama. Kod ADO.NET-a, ne prolazite kroz DataSet, ve'
umjesto toga pristupite tablici koju trebate i onda moete proi kroz kolekciju Rows,
obino sa foreach petljom. Vidjet ete to u prvom primjeru u ovom poglavlju.

Adapter podataka
DataSet je apstrakcija relacijske baze podataka. A DO.N ET koristi DataAdapter kao
most izmeu DataSet objekta i izvora podataka, a to je baza podataka. DataAdapter
prua Fill() metodu za uzimanje podataka iz baze i punjenje DataSeta.

DBCommand i DBConnection objekti


DBConnection objekt predstavlja vezu sa izvorom podataka. Ova veza moe biti dije-
ljena izmeu vie razliitih naredbenih objekata. DBCommand objekt omoguava da
poaljete naredbu (tipino SQL deklaraciju ili pohranjenu proceduru) bazi podataka.
esto ti objekti implicitno budu napravljeni kad napravite DataAdapter, ah im moete
i eksplicitno pristupiti, kao to ete vidjeti u sljedeem primjeru.

DataAdapter objekt
Umjesto da vee DataSet objekt preblizu arhitekturi baze podataka, ADO.NET koristi
DataAdapter objekt da posreduje izmeu DataSet objekta i baze podataka. Time se
DataSet objekt odvaja od baze podataka i omoguava da jedan DataSet predstavlja
vie izvora podataka.

342 | Programiranje C#
fnativa stvaranju DataSet (i DataAdapter) objekta je da napravite DataReader. Data-
fgr prua spojeni pristup kolekciji tablica samo za prosljeivanje i samo za itanje
ljenjem ili S Q L deklaracije ili pohranjene procedure. DataReader objekti su jed -
i n i objekti idealni za ispunjavanje kontrola podacima i zatim prekidanje veze s
dinskom bazom podataka.

etak rada s AD0.NET-om


%ta teorije! Napiimo neki kod i provjerimo kako sve ovo radi. Rad s ADO.NET-om
ie biti sloen, ali za mnoge upite model je iznenaujue jednostavan.
jvotn primjeru napravit ete jednostavan Windows obrazac s padajuim popisom
%jstomers . Napunit ete padajui popis s podacima iz tablice Customers u Nor-
jfivind bazi podataka.
Ibnite tako da napravite DataAdapter objekt:
ft'" SqlDataAdapter Data A dapte r =
f e new SqlDataAdapter(
||i commandString, connectionString);

|)vaSQL parametra su commandString i connectionString. CommandString je SQL iskaz


,?oji e generirati podatke koje elite u DataSet:
string commandString =
"Select CompanyName, ContactName from Customers";

je niz znakova potreban da se spojite s bazom podataka. Na mom


| dn ne ct ion Strin g
Raunalu je pokrenut SQL Server tako da imam pouzdanu vezu s bazom podataka:
string connectionString =
"server=localhost; trusted_connection=true; database=northwind'';

.,ko nemate instaliran SQL Server, odaberite Quickstart Tutorials u Microsoft .NET
ramework SDK skupini programa (nakon to instalirate Visual Studio ili .NET Frame-
work SDK). Pojavljuje se Web stranica koja nudi opciju da instalirate Microsoft SQL
jerver Desktop Engine (MSDE). Nakon to instalirate MSDE, podesite QuickStart
irutorials (to e napraviti Northwind pokusnu bazu podataka). Da biste koristili ovu
bazu podataka, trebate sljedei niz znakova za spajanje:
"server=(local)\\NetSDK; Trusted_Connection=yes; database=northwind"

Kad imate DataAdapter spremni ste napraviti DataSet i napuniti ga s podacima koje ete
uzeti s pomou SQL iskaza za odabir:
DataSet DataSet = new DataSet();
DataAdapter.Fill(DataSet,"Customers");

To je to. Sad imate DataSet i moete postavljati upite i raditi s podacima. DataSet ima
kolekciju tablica, a vas zanima samo prva jer ste uzeli samo jednu tablicu:
DataTable dataTable = DataSet.Tables[o];

Poglavlje 14: Pristup podacima kroz AD0.NET I 343


Moete izvui redove koje ste uzeli SQL iskazom i dodati podatke na padajui popis-
foreach (ataRow dataRow in dataTable.Rows)
{
lbCustomers.Items.Add(
dataRow["CompanyName] +
" ( + da ta Ro w[ "C on ta ct Na me "] + ")" );
}
Padajui popis je popunjen imenom tvrtke i imenom osobe za kontakt iz tablice u bazi
podataka, prema SQL iskazu koji je proslijeen. Primjer 14-1 sadri kompletan izvorni
kod za ovaj primjer.

Primjer 14-1. Rad sa ADO.NET-om


ttregion Using directives

using System;
using System.Collections.Ceneric;
using System.ComponentModel;
using System.Data;
using System.ata.SqlClient;
using System.rawing;
using System.Windows.Forms;

#endregion

namespace WorkingWithADONET
{
partial class ADONetFormi : Form
{
public ADONetForml()
{
InitializeComponent();

// Uspostavlja vezu s mojim posluiteljem, northwind db

string connectionString = "server=localhost;" +


"Trusted_Connection=yes; database=northwind";

// Uzima zapise iz tablice kupaca


string commandString =
"Select CompanyName, ContactName from Customers";

// Stvara naredbeni objekt za skup podataka


// i OataSet
_SqlDataAdapter DataAdapter =
new SqlDataAdapter(
commandString, connectionString );

DataSet DataSet = new DataSet();

// Popunjava objekt dataset


DataAdapter.Fill( DataSet, "Customers" );

344 | Programiranje C#
((primjer 14-1. Rad sa ADO.NET-om (nastavak)
// Uzima jednu tablicu iz DataSet
DataTable dataTable = DataSet.Tables[0 ];

// Za svaki red u tablici prikzuje informacije


foreach ( DataRow dataRow in dataTable.Rows )

lbCustomers.Items.Add(
dataRow["CompanyName"] +
(" + dataRow["ContactName"l + ")" )
}

| Sa samo nekoliko redova koda izvukli ste skup podataka iz baze i prikazal, te podatke
| upadajucem popisu, kao to je prikazano na slici 14-1.

AlfredsFuttakiste(Mana Andere) A
Ana TfujilloEmparedadosyhelados (Ana TiujillN
Anlonio Moreno Taquen'a (AnlonioMoreno)
Aroundthe Hom (Ihomas Haidy) "
Baglunds snabbkop (ChdstinaBaglund)
BlauerSee Detkatessen(Hama Moos)
Blondesdds!pereelfils(FrederiqueOteaiK)
BoMo Comidaspreparadas (MartinSommet)
Bon app'(Laurence LebJian)
Bottom-DollarMaikets (Szabelh Lincoin)
B'sBeverages (MelonaAshvrorth)
CactusComidasparalevaz(PatricioSimpson)
Cenirocomeraal Modezuma (FrandscoChanc
Chop-sueyChinese(Yang Wang)
Comereio Minero(PednoAfonso)_____ jjg|

'fj-,Slika 1 4 -1 . Rezultat primjera 1 4 -1

Osam redova koda je obavilo sljedee zadatke:


Stvaranje niza znakova za povezivanje:
string connectionString = "server=localhost;" +
"Trusted_Connection=yes; database=northwind";

Stvaranje niza za iskaz select:


string commandString =
"Select CompanyName, ContactName from Customers";

Stvaranje DataAdapter objekta i prosljeivanje nizova za povezivanje i odabir:


li SqlDataAdapter DataAdapter =
new SqlDataAdapter(
commandString, connectionString);

Stvaranje novog DataSet objekta:


DataSet DataSet = new DataSet();

Poglavlje 14: Pristup podacima kroz AD0.NET | 345


Popunjavanje DataSet objekta podacima iz tablice Customers s pomou Data
Adapter:
Da ta Ad apter. F ill(Da taSet," Custom ers ");

Izdvajanje podataka iz DataAdapter objekta:


Data Ta bl e data Table = Da ta Se t. Tables[o j;

Koritenje DataTable podataka za popunjavanje padajueg popisa:


forea ch (Dat aRow dat a Row in da taTa ble. Rows)
{
I b Cu st om er s.I t e m s .Add(
dataRow ["Comp any Name"] +
" (" + dataRow["ContactName"] + ")" );
}

Koritenje OLE DB upravljanih izvora podataka


etiri upravljana izvora podataka su trenutno dostupna s ADO.NET-om: SQLServer
Managed Provider, OLE DB Managed Provider, ODBC Managed Provider i upravljani
izvor podataka za Oracle. Prethodni primjer je koristio SQLServer Managed Provider
koji je optimiziran za SQL Server i ogranien za rad sa SQL Server bazama podataka.
Univerzalnije rjeenje je OLE DB Managed Provider koji e se spojiti na bilo koji OLE
DB upravljani izvor podataka, ukljuujui Access.
Moete ponovno napisati primjer 14-1 da radi s Northvvind bazom podataka koristei
Access umjesto SQL Servera uz samo nekoliko manjih izmjena. Prvo, trebate promije-
niti niz znakova za povezivanje:
string conne ction Str ing = "
p ro vi de r= Mi cr os of t.1 E T .O L E D B .4. 0 ; "
+ "data source = c: \\nwin d.md b";

Ovaj upit se spaja s Northvvind bazom podataka na C: disku (tona putanja kod vas
moe biti drugaija).
Z atim promijenite DataAdapter objekt iz SqlDataAdapter u OLEDBDataAdapter:
Ol e D b D ataAdapter Da ta A dapter =
new Ol e D b D ataAdap ter (command Stri ng , co nnectio n String) ;

Takoer obavezno dodajte using deklaraciju za OleDb imenski prostor:


using Syst em.D ata. OleD b;

Ovaj projektni predloak se nastavlja dalje kroz dva upravljana izvora podataka. Za
svaki objekt ije ime klase poinje sa ,,Sql postoji odgovarajua klasa koja poinje sa
,,01eDb. Primjer 14-2 prikazuje kompletnu OLE DB inaicu primjera 14-1.

Primjer 14-2. Koritenje ADO Managed Providera


ffregion Using direct ives

using System;

346 I Programiranje C#
Primjer 14-2. Koritenje ADO
Managed Providera (nastavak)
using System.Collections.Ceneric;
using System.ComponentModel;
using System.Data;
using System.Data.01eDb;
using System.Drawing;
\using System.Windows.Forms;

tfendregion

: namespace Us ingADOManagedProvider
J
partial class ADONetFormi : Form
: {
public ADONetForml()

InitializeComponent();
" Povezivanje s Northwind bazom podataka

string connectionString
"provider=icrosoft.JET.OLE8.4.o-
+ "data source = c: Wnwind.mdb'1,

// Uzima zapise iz tablice kupaca


string commandstring =
"Select CompanyName, ContactName from Customers";

// rDaatraSent redbeni POdataka

OleDbDataAdapter Da taAdapter = new


01 eDbDataA dapt er( commandstring,
connecti onSt ring );

DataSet DataSet = new DataSet();

" PPunJava objekt skupa podataka


DataAdapter.Fill( DataSet, "Customers" );

// Uzima jednu tablicu iz DataSet


DataTable dataTable = DataSet.Tables[oj;

n Za svaki red u tablici prikazuje podatke


^oreach ( DataRow dataRow in dataTable.Rows )

lbCustomers.Items.Add(
dataRow["CompanyName"] +
j ( + dataRow["ContactName"] + ")" );

}
}

z u
ovom sluaju je identian onom iz
prethodnog primjera (slika 14-2).

Poglavlje 14: Pristup podacima kroz AD0.NET


BI ADO.Net Form 1
AlfredsFutterkiste(Mana Andere)
Ana TnjjilloEmparedadosyhelados(AnaTnjjillo)
Antonio Moreno Taqueria (Antonio Moreno)
Aroundthe Hotn (Ihomas Hardy)
Berglundssnabbkop (ChristinaBerglund)
BlauerSee Delikatessen(Harma Moos)
Blondetpreelfils(FredefiqueOteauic)
BolidoComidaspreparadas (MartinSommef)
Bon app'(Laurence Lebihan)
Bottom-Doilar Markets (Eteabeth Lincoln)
B'sBeverages(UctoriaAshvvorth)
CactusComidasparallevar(PatridoSimpson)
Centrecomeraal Mocte2 uma (FrandscoChang)
Chop-sueyChinese (Vang Wang)
Comercio Mineiro(PedroAtfonso)
ConsolidatedHoldingspzabeth Brenvn)
Drachenblut Delikatessen(SvenOttlieb)

Slifea 14-2. Koritenje ADOManage Providera

n Data Source Configuration Wizard


Choose a Data Source Type
T h is s te p p r o v id e s y o u w ith d if fe r e n t so u rc e s o f d a ta .

Select the type of DataSource you wish to use:

liEHaiEEia Local Web Service Object


Database File

Lets you dioosethedatabase objerts you are Interested In and creates a DataSetin the
current project as a result

S l i k a 1 4 - 3 . a r o b n j a k D a t a S o u r c e C o n f ig u r a t io n

OLE DB. Managed Provider je univerzalni}! nego SQL Managed Provider i moe se
koristiti za spajanje na SQL Server kao i na bilo koji OLE DB objekt. Kako je SQL
Server Provider optimiziran za SQ L Server, uinkovitije je koristiti izvor po ata
koji je specifian za SQL Server prilikom rada s SQL Serverom. S vremenom, vect broj
specijaliziranih upravljanih izvora podataka e biti dostupan.

348 | Programiranje C#
|d $ kontrolama za podatke
. d isprobajmo drugaiji pristup koji je vie deklarativan. Napravite novi Windows
ijrms Solution (nazovite ga DeclarativeDataDisplay). Poveajte obrazac i promi-
|f)ite mu *me u iDeclarativeDB.es i dodajte naslov Declarative Data Base. Povucite
ata&ridVievv na obrazac.
'a(j je na svom mjestu, pojavit e se izbornik Action. Pritisnite padajui popis da iza-
%rete izvor podataka. Pritisnite Add Project Data Source kako biste pokrenuli arob-
njaka Data Source Configuration. Pritisnite Next da odaberete tip podataka, kao to
prikazano na slici 14-3.
(Odaberite Database i pritisnite Next. To vas vodi do arobnjaka Data Source Configu-
.ation gdje moete odabrati New Connection (slika 14-4).

I: Data Source Configuration VVizard


m
Choose Your Data Connection
The Oata Source objeets vfill come from this connection.

Choose from the Hst of data connections currently in Server Explorer or add a new
connection if the one youwant is not listed.

jV jj I New Connection..

Connection Detafc
Provider:

. Connection string:

<Badc
UC

J S lik a 1 4 - 4 . O d a b i r v e z e

Upiite odgovarajuu informaciju za Connection Properties, kao to je prikazano na


* slici 14-5.
n
f ; Obavezno pritisnite gumb Test Connection prije nego to pritisnete OK. U sljedeem
koraku pritisnite ,,Yes, save the connection as i potvrdite Include sensitive data, kao
$to je prikazano na slici 14-6.

Poglavlje 14: Pristup podacima kroz AB0.NET j 349


Connaction Properties

S p e c i f y t h e f o l o w i i g t o c o n n e c t t o S Q L S e r v e r d a t a :l
. S e tec t o r e n ter a serv er n am e:
)&ACH ,Y ,:J j R e fres h |

e n t e r r f c r m a t to n t o lo g o n t o t h e se r v e r :
0 O s e W h d o w s N T Integrztgd Securltf
U s e a s o e c iflc u s e r ID a n d p a s s w o rd :
.
U se r ID : l
Psiswofd: ( * ............................................................... i
0 S a v e m y p a ss w c rd
S e i e c t o r e n t e r a d a ta b a s e n a ft e ;
| N crlh v v in d __ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
0 A t t a c h a d a t a b a s e f il e a s a f il e n a m e :

L _ " " ~ ~

Slika 14-5. Podeavanje svojstava veze

Data Source Configuration Wizard


Save connection string to the application configuration f
Provide a name forstoringconnection Information
i
Storing connection stiings inyoui applicaiion configurationfiteeases mainlenance and
deploymenf. To save the conneion stringintheapplication configurationfile,entera name in
theboxandthencltckNex(.

Do youvf&nt to sa v e th e connection to th e Application configuration file?

0 Yes.savetheconnectionas;
)NorthwindConnedion
0 Includesensilivedata(e.g.passvvoid)inconnectionstring.

Cancel < Back Next > Fir'ibh

Slika 14-6. Pohranjivanje osjetljivih podataka

Bit e prikazan sadraj objekta DataSet koji ste odabrali. U ovom sluaju, proirite,
tablice i proirite tablicu Customers. Odaberite sve stupce osim stupca Region (da b ij
pokazali da upit ne mora vratiti sve stupce) i pritisnite Finish.

350 | Programiranje C#
ja b e rite jeziak za mreu podataka i zatim Edit Columns. Uredite zaglavlja stupaca
^smislen tekst, kao sto je prikazano na slici 14-7.

B o r td c c A m P ra p e rttes

listi s
; > f t e 3d c n l y F ls e
, 'j V i s b f e T ru e
k .W ld ih 100
-C .
t jA u lo S ltt O lt e r ia N one
,; ^ C o n ( e x t M e n u S t d p in o n e j
C o m p sn v N am e
v iR e s e ta tte T ru e
- 'S o r t M o d e Automate
. T o o r n p T e r t

.;D a t a P r f ip e r ty N a i T e C o n ip a n y N sr n e
rtiTag
OtSPlf 1 %
H e s tfs r T e jr t ' ; x C o tn p a n y N a m e
B M lsr
C o tm iT y p e O d f 3G i ( d v t 0w T e x l 8o x C o i n
H ead erT C T t
C o fc n r iH a a c te tT e r t.

Sliku 14-7. Ureivanje stupaca


fc ,
.Pokrenite program. Podaci su povezani kao na slici 14-8.

n e tlO ' C arp an y ' C o n te c t ' TU e : A d d re tt E P v n a


lA Jfr e d t F t A le tk is le jJe s s e l to e fly jR e p te te r te l iv e jo b e ie S u . S 7 iB e ii n I i 22f
' tA n a T tie fc > E m p o c e d e d o sy h e le d o * {A n e T i u il o |0w n a j A v d e . d e 1 C c n t U u e i n 2222 i M & d c o O . F . ] o 5021
^ jA r V o n s o M o r e n o T a g u e r la A n lo n io M a e n o J 0w n e j M e l e d e i o s 2312 jM h e c o O .F . 06023
r jA io u n d lh e H a n (d e t te U b er tp jS e te t R e p te te n ta ir / e 120H e n o v e t S o jA c to n 01720
B e ig k jtd s s n a b b k o o .O r it ih e B a a k n d |0i d e i A d r a r h t i M a B e i g u r tv ijg e n 8 jU t e i $95822
B le u e i S e e O e H te te ts e n jH e n n e M o o s | S eie t R e p ie s e n te tiv e F a t t e t s t i . 57 iM a o th e im 88306
B lo o Je s d d s l p fere e t Ih F t d e r iq u e C l e u t , jM a r k e B n p M a n e g 24. p l a c e K ^ b e t [S tr b o u g 6k
BbdoCcrnidasp t e & j r s d e r M e d in S o cn m ei (O w rw ] C / A i a q u L 67
jM a d b d i 28023
....... ii L e u ren ce L eb h en j 0w n *| 12. n j e d e t B o u w $ iM a se fc 113008
i iB o t to m -D o lla f M a k e i s
E fa eb eth Ih c o h f c c c a r t h g M a v jg e , 123T a v r a $ s e n B l v d . T * w etse n 1T 2F 8M 4
jB tB e v e ie g e s i/ i d a id A th v r e iih jS e le t R e p ie te n le tiv e |F a u n t t e y C i r c u s London j E C 25N T
; C a J u C o m r d e t M i tevai .P e ln c to S iT ip to n j$abiAgent :Ceio333 0u e n o 3A i r e i iU H O
[C e r tto c a n e ic i a l M o c te z u m a [F r e n c isc o C h e n g [M a rk etin g M e n e g a |:S i e n a s d e G i e n e d a 9333 M 64^ 0. F . | 05B22
iC h o p -t u e y C i m e t e jY a n Q W ig jo *w w j H e u p I s I f . 29 B ern 13012 ~

lika 14-8. Mrea podataka

^ogledajte podruje ispod mree podataka i vidjet ete tri objekta: northwindDataSet
f stomerDataConvertor i customersTableAdapter kao to je prikazano na slici 14-9.

Poglavlje 14: Pristup poadma kroz AD0.NET | 351


DccluroliW
cDstof)i.play-M
lcrotocve'apmcPtl.nviipnm
tM
U* <M PioK<t a* 0v) C >*to fvrt*
____ .... ___ i&Ji&Au
l>X. /Sl^OB^lOeiWV(.
I;, l i i a S ^ D*d * Bv9M X W < l pr(5j
:o ' " 6 tC
3oc*d
i Pl-*oU
pov
BeO
Msnsp<ar
Uaflfttfmncas
Qfloectoajrtoac
S tterth*i*aS<t,iu

r;t!!^ScMiooE*plffSJOM
SVVmT
njdaUGridAe! Srjten.WWow/*os.M*&t

Slika 14-9. Ispitivanje objekata u podruju ispod mree podataka

Svaki od njih predstavlja objekt ija svojstva moete podesiti tako to ete ga pritisnuti;1;
i podesiti svojstva u prozoru Properties.

Programsko popunjavanje mree podataka


Ako ba inzistirate da runo napravite ove objekte, onda ih moete i runo povezati j
sa mreom podataka.
Napravite novi ProgrammaticDataDisplay. Promijenite ime .cs datoteke i obrasca i,
prikladno podesite naslov. Povucite DataGiidView na mjesto, ali ignorirajte izbornik
Action.
Pritisnite obrazac desnom tipkom mia i odaberite View Code s kontekstnog izboN
nika. Konstruktoru dodajte ove redove koda: 51
string connectionString = "server=localhost; + I
"Trusted_Connection=yes; database=northwind ;
P
string commandString =
"Select CompanyName, ContactName, ContactTitle,
+ "Phone, Fax trom Customers";

// Pravi DataSet i popunjava ga


SqlDataAdapter DataAdapter =
new SqlDataAdapter( commandString, connectionString ); ,8
DataSet DataSet = new DataSet();
I
DataAdapter.Fill( DataSet, "Customers" );

// povezuje DataSet s mreom


dataGridViewl.DataSource =
DataSet.Tables["Customers"].DefaultView;

352 | Programiranje C#
ovomsluaju runo podeavate nizove znakova za povezivanje i odabir i onda stva-
^teSqlDataMaptei iDataSet da uzmete podatke. Zatim povezete DataSource svojstvo
ataCr.idView sa Default pogledom tablice Customers koju ste uzeli.
%bodno moete postaviti svojstvo od DataGridView programski ili deklarativno te
'-oete mijeati i usporeivati pristupe. Ali oito, Microsoft je obavio veliki posao
iko bi vam omoguio povlaenje i isputanje kontrola za veze s bazama podataka na
jbrazac radi jednostavnijeg meudjelovanja s ADO.NET-om.

odeavanje DataSet objekta


' oete precizno kontrolirati svaki aspekt stvaranja DataSet objekta umjesto da kori-
ste standardne postavke. To moete uiniti deklarativno ili programski.
'prethodnom primjeru, kad ste napravili DataSet objekt, proslijedili ste commandString
dpnnectionString:
SqlDataAdapter DataAdapter =
new SqlDataAdapter(commandString, connectionString);

vi nizovi znakova su interno dodijeljeni objektima SqlCommand i SqlConnection,


Mom. Umjesto toga, moete eksplicitno napraviti ove objekte.
i:
sljedeem primjeru, dodat ete klasi etiri nova lana:
private System.Data.SqIClient.SqlConnection myConnection;
plivate System.Data.DataSet myDataSet;
private System.Data.SqlClient.SqlCommand myCommand;
private System.Data.SqlClient.SqlDataAdapter DataAdapter;

jeza je napravljena instanciranjem SqlConnection objekta s nizom za povezivanje:


stiing connectionString = "server=(local)\\NetSDK;" +
"Trusted_Connection=yes; database=northwind";
myConnection = new System.Data.SqlClient.SqlConnection(connectionString);

bnda je eksplicitno otvorena:


myConnection.Open();

;ko zadrite ovu vezu moi ete ju ponovno upotrijebiti (kao to ete vidjeti u sljede-
Bm primjeru) ili koristiti njezinu podrku za transakcije.

Obj ek tu DataAdapter m o et e dozvoliti da napravi vezu io n d a ju ekspli-

$ citno otvoriti ili se na nju pozivati koritenjem k o d a k a o to je:

SqlConnection myConnection = myAdapter.Connection

atim eksplicitno napravite DataSet objekt i postavite jedno od njegovih svojstava:


j' myDataSet = new System.Data.DataSet();
inyDataSet.CaseSensitive=true;

jPstavljanje CaseSensitive svojstva na true ukazuje da se u usporedbama nizova zna-


bva unutar DataTable objekata razlikuju velika i mala slova.

Poglavlje 14: Pristup padadmakrozADO.NET | 353


Sada eksplicitno napravite SqlCommand objekt i dodijelite mu objekt za vezu i tekst
naredbe:
myCommand = new System.Data.SqlClient.SqlCommand()
myCommand.Connection=myConnect ion;
myCommand.CommandText = "Select * from Customers";

Konano napravite SqlDataAdapter objekt i dodijelite mu SqlCommand objekt koji ste


upravo napravili. Onda uputite DataAdapter objekt kako da preslika stupce tablice
koristei tablicu koju traite i uputite SqlAdapter objekt da napuni DataSet objekt:
DataAdapter = new System.Data.SqlClient.SqlDataAdapter();
DataAdapter.SelectCommand= myCommand;
DataAdapter.TableMappings.Add("Table","Customers");
DataAdapter.Fill(myDataSet);

K a d ste to napravili, spremni ste d a popunite DataCridView (primjetite da sam ovaj put
koristio standardno ime za DataCrid):
dataGridl.DataSource=
myDataSet.Tablesf"Customers].DefaultView;

Primjer 14-3 sadri kompletan izvorni kod.

Primjer 14-3. Prilagoavanje DataSet objekta


#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;

#endregion

namespace CustomizedDataSet
{
partial class CustomizedDataSet : Form
{
private System.Data.SqlClient.SqlConnection myConnection;
private System.Data.DataSet myDataSet;
private System.Data.SqlClient.SqlCommand myCommand;
private System.Data.SqlClient.SqlDataAdapter DataAdapter;

public CustomizedDataSet()
{
InitializeComponent();
string connectionString = "server=localhost;" +
"Trusted_Connection=yes; database=northwind";
myConnection = new
System.Data.SqlClient.SqlConnection( connectionString );
myConnection.Open ();

354 | Programiranje C#
p rim jer 1 4 -3 . P r i l a g o a v a n j e D a t a S e t o b j e k t a ( n a s t a v a k )

I I Pravi DataSet i postavlja svojstvo


myDataSet = new System.Data.DataSet();
myDataSet.CaseSensitive = true;

I I Pravi SqlCommand objekt i dodjeljuje


I I vezu i iskaz select
myCommand = new System.Data.SqlClient.SqlCommand();
myCommand.Connection = myConnection;
myCommand.CommandText = "Select * from Customers";

I I Pravi DataAdapter objekt i prosljeuje


I I SOL Command objekt te uspostavlja preslikavanje tablice
DataAdapter = new System.Data.SqlClient.SqlDataAdapter();
DataAdapter.SelectCommand = myCommand;
DataAdapter.TableMappings.Add( "Table", "Customers" );

I I Govori objektu DataAdapter da popuni DataSet


DataAdapter.Fill( myDataSet );

I I Prikazuje ga u mrei
dataGridViewl.DataSource =
myDataSet.Tables["Customers"].DefaultView;
}
}
}

Poglavlje 14: Pristup podacima kroz AD0.NET | 355


POGLAVLJE 15 ______________________
Programiranje ASP.NET aplikacija
i Web usluga

Programeri piu sve vie aplikacija koje se izvode preko Weba.


Postoji mnogo oitih prednosti takvog pristupa. Kao prvo, ne morate se baviti deta-
ljima korisnikog suelja, nego moete prepustiti Internet Exploreru i ostalim pre-
glednicima da obave veinu posla za vas. Druga, vjerojatno vanija, prednost je da
je distribuiranje i nadograivanje aplikacije bre, jednostavnije i jeftinije. Najvanija
prednost je da se Web aplikacija moe izvoditi na bilo kojoj platformi, od strane bilo
kojeg korisnika, na bilo kojoj lokaciji, a ovo je tee izvesti (dodue ne i nemogue) sa
aplikacijama temeljenim na pametnom klijentu.
Trea prednost Web aplikacija je distribuirana obrada, iako pametni klijenti (engl.
smart clients) sve vie zadiru i u to podruje. Koritenjem Web aplikacija jednostavno
je osigurati obradu na strani posluitelja a Web prua standardne protokole (na pri-
mjer, HTTP, H TML i XML) koji olakavaju izradu vieslojnih aplikacija.
.N ET tehnologija za izradu Web aplikacija (i dinamikih Web stranica) je ASP.NET
2.0 koja u svojim System.Web i System.Web.UI imenskim prostorima prua velik broj :|
tipova za izradu Web aplikacija. ASP.NET nudi velik broj kontrola i srodnih alata,
ukljuujui alate za provjeru podataka, prikaz datuma, oglaavanje, meudjelovanje ;
s korisnicima i tako dalje. Veina toga ne zahtijeva pisanje koda. |
Tema ovog poglavlja je preplitanje ASP.NET-a i programiranje u C # jeziku a tojeg
izrada Web Forms aplikacija i Web usluga. Uloga C # programera u ASP.NET razvojuf
je pisanje metoda za obradu dogaaja koje odgovaraju na akcije korisnika. Veina .;
metoda za obradu dogaaja e ili dodati podatke u bazu podataka ili uzeti podatke g
iz baze i uiniti ih dostupnim kontrolama. Za pregled samo ASP.NET-a, pogledajte j
knjigu Programiranje ASP.NET koju sam napisao zajedno s danom Hurwitzom. |
Web Forms obrasci uvodi metode brzog razvoja (kao to su one koritene u Windows |
Forms obrascima) u razvoj Web aplikacija. Kao i kod Windows Forms obrazaca, povla
ite i sputate kontrole na obrazac i zatim piete pozadinski kod. Koritenjem tehno-g
logije Web Forms aplikacija se izvodi na Web posluitelju, a korisnici meudjeluju S|
njom kroz standardni preglednik. J

356
I.N ET Web usluge se proiruju na koncept distribuirane obrade zbog izrade kompo-
Inenti ije metode mogu biti pozvane preko Interneta koritenjem standardnih proto-
ikola. Te komponente mogu biti napisane u bilo kojem .NET jeziku i komuniciraju
Iskoritenjem otvorenih protokola koji ne ovise o platformi. Na primjer, burzin poslu-
|itelj moe pruiti Web uslugu koja uzima simbol dionice sa pominog prikaza kao
jf parametar i vraa cijenu. Aplikacija moe kombinirati tu uslugu s nekom drugom uslu-
|gom druge tvrtke koja takoer uzima simbol dionice, ali vraa podatke o kompaniji.
^Projektant aplikacije se moe usredotoiti na stvaranje novih vrijednosti s pomou
ovih usluga, umjesto kopiranja iste usluge za svoju aplikciju.

|Ovo poglavlje opisuje programiranje Web Forms obrazaca i Web usluga koritenjem
Ijezika C# .
I
^Razumijevanje Web Forms obrazaca
ASP.NET 2 .0 Web Forms obrasci su nasljednici jako uspjenih ASP.
0%
d N ET 1.x obrazaca, koji su naslijedili ASP stranice. Cilj ASP.NET 2.0
____I obrazaca je smanjivanje koliine koda za 70% u odnosu na ASP 1.x.
To znai da je Web programiranje sve vie deklarativno, a ne program-
sko. To jest, vi deklarirate kontrole na obrascu radije nego da piete (i
ponovno piete) kod.

Jo uvijek postoji mogunost pisanja koda (uvijek moete pisati kod),


ali za veliku veinu potreba, pisat ete mnogo manje koda sa ASP.NET
2 .0 nego sa 1.x.

jPVeb Forms obrasci implementiraju programski model u kojem se Web stranice dina-
Bmiki stvaraju na Web posluitelju za dostavu pregledniku preko Interneta. Sa Web
lpForms obrascima stvarate ASPX stranicu s manje-vie statikim sadrajem koji se
pastoji od H TM L-a i Web kontrola i piete C# kod kako biste dodali dodatni dina-
^(niki'Sadraj. C # kod se izvodi na posluitelju, a proizvedeni podaci se spajaju s
deklariranim objektima na stranici kako bi bila napravljena HTML stranica koja se
||alje natrag pregledniku.

Postoje tri kritine toke iz prethodnog odlomka kojih se treba primiti i koje treba
ijttiati na umu kroz cijelo ovo poglavlje:

||; Web stranice mogu imati i HTML i Web kontrole (to je opisano kasnije u ovom
poglavlju).
| i Sva obrada se obavlja na posluitelju (moete imati obradu na strani klijenta kori-
tenjem skriptnib jezika, ali to nije dio ASP.NET-a).

|| Web usluge mogu, naravno, biti napisane na bilo kojem jeziku i na bilo kojoj platformi. Bit Web usluga je
||?da su neovisne o platformi. Za potrebe ove knjige fokusirat emo se na stvaranje i koritenje Web usluga
|g.spomou .NET-a.

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 357


Ako koristite ASP.NET Web kontrole, ono to preglednik vidi je samo HTML
(s iznimkom da nekim preglednicima nove generacije mogu biti poslane i neke
skripte).
Ukratko, Web Forms obrasci su napravljeni tako da se mogu pregledati s pomou bilo
kojeg preglednika Weba ako se stvaraju na posluitelju koji generira ispravan HTML
kod koji je podran od strane preglednika. Programiranje logikog dijela obrasca
moete obaviti u bilo kojem .NET jeziku. Ja u naravno koristiti C# . Kao i Windows
Forms obrasce i Web Forms obrasce moete napraviti u Notepadu (ili nekom drugom
editoru) umjesto da koristite Visual Studio. Mnogi projektanti e tako i uiniti, ali
Visual Studio prua mnogo laki nain za izradu i testiranje Web Forms obrazaca.
Web Forms obrasci dijele korisniko suelje na dva dijela: vidljivi dio (korisniko sue-
lje) i logiku koja se nalazi iza njega. To je vrlo slino razvoju Windows Forms obrazaca
kao to je prikazano u poglavlju 13. To se naziva razdvajanje koda. Svi primjeri u knjizi
koriste razdvajanje koda iako je C# kod mogue napisati u istoj datoteci u kojoj se
nalazi korisniko suelje.

U inaici 2 .0 ASP.NET-a, Visual Studio koristi prednost parcijalnih


klasa, dozvoljavajui da stranica sa kodom bude mnogo jednostavnija
,' nego to je bila u inaici 1.x. Poto su razdvojeni kod i deklarativne
stranice dio iste klase, vie nema potrebe za zatienim varijablama
koje bi upuivale na kontrole stranice i projektant moe sakriti kod za
inicijalizaciju u posebnu datoteku.

Stranica s korisnikim sueljem je pohranjena u datoteku s nastavkom imena ,aspx.


Kad izvodite obrazac posluitelj stvara HTM L koji se alje klijentovom pregledniku.
On koristi bogate tipove za Web Forms obrasce koji se nalaze u System.Web i System.
Web.UI imenskim prostorima .N ET FCL-a.
Koritenjem Visual Studija programiranje Web Forms obrazaca ne moe biti jedno-
stavnije: otvorite obrazac, povucite neke kontrole na njega i napiite kod za obradu
dogaaja. I, napisali ste Web aplikaciju.
S druge strane, ak i uz Visual Studio pisanje robustne i kompletne Web aplikacije
moe biti obeshrabrujui zadatak. Web Forms obrasci pruaju bogato korisniko sue-
lje. Broj i sloenost Web kontrola su uvelike uveani posljednih godina, a oekivanja
korisnika vezana za izgled Web aplikacija su porasla u skladu s tim.
U dodatku, Web aplikacijama je svojstveno da su distribuirane. Tipino, klijent nee
biti u istoj zgradi kao i posluitelj. Za veinu Web aplikacija prilikom izrade korisni-
kog suelja morate uzeti u obzir odziv mree, brzinu veze i preformanse mrenog poslu-
itelja. Informacije mogu putovati izmeu klijenta i posluitelja i nekoliko sekundi.

Dogaaji Web Forms obrazaca


Web Forms obrasce pokreu dogaaji. Dogaaj (engl. event) predstavlja ideju da se
neto dogodilo" (pogledajte poglavlje 12 za detaljniji opis dogaaja).

358 | Programiranje C#
Uobiajeno je da ASP.NET metode za obradu dogaaja vraaju void i uzimaju dva
parametra. Prvi param etar predstavlja objekt koji izaziva dogaaj. Drugi param etar,
argument dogaaja, sadri informaciju specifinu za dogaaj, ako postoji. Za veinu
dogaaja, argum ent dogaaja je tipa EventArgs, koji ne izlae ni jedno svojstvo. Za
neke kontrole, argum ent dogaaja moe biti tipa izvedenog iz EventArgs koji moe
izloiti svojstva specifina za taj tip dogaaja.

U Web aplikacijama posluitelji tipino obrauju veinu dogaaja i zahtijevaju povrat


informacija. ASP.NET podrava samo ogranien skup dogaaja kao to su pritisci na
gumbe i promjene teksta. To su dogaaji koji mogu izazvati znaajnu promjenu za raz-
liku od Windows dogaaja (kao to je prelazak pokazivaa mia iznad objekta) koji
se mogu dogoditi mnogo puta dok korisnik radi.

Povratni dogaaji u usporedbi s nepovratnim


Povratni (engl. postback ) dogaaji su oni koji izazivaju da obrazac trenutno bude
poslan natrag posluitelju. Meu njih spadaju dogaaji temeljeni na pritisku gumba,
kao to je Button Click dogaaj. Nasuprot tome, mnogi dogaaji (tipino dogaaji
izmjene teksta) su nepovratni (engl. nonpostback) dogaaji zato to se obrazac ne vraa
odmah posluitelju. Umjesto toga, kontrole pohranjuju dogaaje do sljedeeg povrat-
nog dogaaja.

Kontrolam a sa nepovratnim dogaajim a m oete nam etnuti da se pona-


A ka kontrole s povratnim dogaajim a m ijenjanjem njihovog svoj-
_s?, stva AutoPostBack na true.

Stanje
Stanje (engl. State) Web aplikacije je trenutna vrijednost svih kontrola i varijabli za tre-
nutnog korisnika u trenutnoj sesiji. Bitno svojstvo Weba je da je to okoli koji nema
stanja. To znai da se prilikom svakog slanja posluitelju stanje prije slanja gubi, osim
ako projektant ne uloi velike napore da sauva znanje o toj sesiji. ASP.NET prua
podrku za odravanje stanja korisnikove sesije.

Kad god je stranica poslana posluitelju on je ponovno napravi od poetka prije nego
to ju vrati pregledniku. ASP.NET osigurava mehanizam koji automatski odrava sta-
nje posluiteljskih kontrola (ViewState) neovisno o HTTP sesiji. Prema tome, ako
ponudite popis a korisnik odabere neke njegove opcije, taj odabir se uva nakon to je
stranica poslana na posluitelj i zatim ponovno vraa klijentu.

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga I 359


c H TTP sesija odrava iluziju veze izmeu korisnika i W eb aplikacije,
usprkos injenici da je Web okoli bez stanja i veza.

ivotni ciklus Web Forms obrasca


Svaki zahtjev za stranicom upuen Web posluitelju uzrokuje niz dogaaja na poslu-
itelju. Oni, od poetka do kraja, tvore ivotni ciklus stranice i svih njenih kompo-
nenti. ivotni ciklus poinje sa zahtjevom za stranicom, koji uzrokuje to da posluitelj
uita tu stranicu. Kad se zahtjev izvede, stranica se brie iz memorije. Od poetka
do kraja ivotnog ciklusa, cilj je vratiti odgovarajui H TM L izlaz pregledniku koji
je uputio zahtjev. ivotni ciklus stranice je obiljeen sljedeim dogaajima, od kojih
svaki moete sami obraditi ili moete prepustiti ASP.NET posluitelju da ih obradi na
podrazumijevan nain:
Inicijalizacija
Inicijalizacija je prva faza ivotnog ciklusa bilo koje stranice ili kontrole. Potrebna
je da bi sve postavke potrebne za vrijeme trajanja zahtjeva bile inicijalizirane.
Uitavanje Vieu/State svojstva kontrole
View State svojstvo kontrole je popunjeno. View State informacija dolazi iz skri-
vene varijable u kontroli koja se koristi da odri stanje kroz sve povratne puteve
prema posluitelju. Kostur stranice analizira ulazni niz znakova iz ove skrivene
varijable i postavlja se svojstvo ViewState. Moete ga promijeniti koritenjem
metode LoadViewState ( ) . To omoguava ASP.N ET-u da upravlja stanjem kontrole
prilikom uitavanja stranice tako da kontrola nije svaki put iznova postavljena u
podrazumijevano stanje prilikom slanja stranice.
Obrada poslanih podataka
Prilikom ove faze obrauju se podaci poslani posluitelju. Ako neki od tih poda-
taka izazove auriranje svojstva ViewState, onda se to izvodi s pomou metode
LoadPostData().

Uitavanje
Ako se pojavi potreba, poziva se CreateChildControlsf) metoda da napravi i inici-
jalizira posluiteljske kontrole u kontrolnom stablu. Stanje se ponovno postavlja
i kontrole obrasca sadre podatke s klijentove strane. Moete modificirati fazu
uitavanja obradom dogaaja Load sa OnLoadf) metodom.

Slanje promjena
Ako se dogodi promjena stanja izmeu trenutnog stanja i prethodnog stanja, doga-
aji promjene se izazivaju preko RaisePostDataChangedEventf).
Obrada povratnih dogaaja
Dogaaj na klijentskoj strani koji je prouzroio slanje podataka se obrauje.

360 | Programiranje C#
Stanje prije generiranja
Ovo vam je zadnja ansa da koritenjem OnPreRender() metode promijenite
izlaz prije vraanja podataka klijentu.
Snimanje stanja
Na poetku ivotnog ciklusa sauvano stanje je u.tano iz skrivene varijable
Sad je pohranjeno natrag u skrivenu varijablu i ostaje kao objekt niza koji
ce zavriti povratni put do klijenta. To moete zaobii koritenjem SaveView-
S tate () metode.
Generiranje
U ovoj fazi se stvara izlaz koji se vraa natrag pregledniku. To moete premo-
stiti koritenjem Render metode. Ako je potrebno, poziva se CreateChildCon-
tro ls O za izradu i imcijaliziranje posluiteljskih kontrola u stablu kontrola.
Odlaganje
Ovo je zadnja faza ivotnog ciklusa. Tada vam se prua prilika da napravite
zavrno ciscenje , oslobaanje referenci dragocjenih resursa, kao to su veze
prema baz, podataka. Moete ju modificirati koritenjem isposef) metode.

Izrada Web Forms obrasca


Kako biste napravili jednostavan Web Forms obrazac koji e bit, koriten u sljedeem
primjeru, pokrenite Visual Studio .NET i odaberite File-.New Web Site. S izbornika
New Web Site odaberite C# jezik i odaberite ASP.NET Web Site kao predloak koji
ete korisnu. Konano, smjestite Web stranicu negdje na disku (pri dnu dijalokog
okvira), kao sto je prikazano na slici 15-1.

Slika 15-1. Izrada nove Web stranice

Visual Studio stvara mapu ProgrammingCSharPWeb u mapi koju ste naznaili i unutar
te mape stvara Default.aspx stranicu (za korisniko suelje), Default.aspx.cs (za kod)

Poglavlje 15: Pr09ramiranjeASP.NETaplikacija i Web usluga I 361


i Data mapu (trenutno je prazna, ali se koristi za spremanje .mdb datoteka ili drugih
datoteka specifinih za podatke).

Iako vie ne koristi projekte za Web aplikacije, Visual Studio sadri


datoteke rjeenja (engl. solution files) da bi vam omoguio da se brzo
vratite Web stranici ili samostalnoj aplikaciji koju razvijate. Datoteke
rjeenja se uvaju u mapi koju moete zadati odabirom opcije Tools -*
Options, kao to je prikazano na slici 15-2.

General
A d d -in/M ac ros S ecurity
A u to R e co ve r
D ocum ents [ c : \D o c u m e n t s a n d S e ttin g s \3 e s s e \M y Docum ents\V isual ShK | j B row s e... |
F o nts and Colors

St) Help Lo c atio n o f Visual Studio ite m tem p la tes :-------------------------------------- (------------------------
lm p o r l:/ x p o r t S ettings [ c :\D o c u m e n t s a n d S e t t in g s \ _ J ^ s s ^ y _ p o c u ^ n ^ s u o l 5 t u ( j | B row s e... j
In te rn a tio n a l S ettings
0 S h o w O u tp u t w in dow w hen build s ta r ts
K ey b oa rd
0 Alw ays s h o w E rror L is t if buifd fln is hes w ith e rro rs
T ask List
0 T rac k Ac ttv e Ite m ln S olution Ex ptorer
W e b 0 row s er
0 Show a d v anc ed bulkJ confkgurations
(50 P erform an ce
0 A l w a y s s how s olution
0 S ave ne w p ro je c ts w hen c re a ted
0 W a rn us er vvhen th e p ro je c t lo c a tio n is n o t tru s te d

VB D e fa u lts
V C + + Directories
V C + + P ro je c t S ettings

[ OK j[ Cancel }

Slika 15-2. Spremanje datoteke rjeenja

Datoteke s kodom
& ----- Napomena za ASP.NET 1.1 programere: model razdvajanja koda za
<T ASP.NET je promijenjen. U inaicama 1.x, datoteka s kodom je detim-
f rala klasu koja je izvodila iz Page. Ona je sadravala varijable instanci
za sve kontrole na stranici, sa eksplicitnim povezivanjem dogaaja uz
upotrebu delegata i .aspx stranice dobivene od klase.
U inaici 2 .0 , ASP.NET stvara samo jednu klasu od ,aspx stranice i
definicija parcijalne klase iz datoteke s kodom.
A SP N ET moe zakljuivati instance kontrole i izvoditi povezivanja
dogaaja iz koda prilikom prevoenja. Prema tome, nova datoteka s
kodom sadri samo kod koji trebate, kao to su metode za obradu doga-
aja, ali ne i varijable instanci ili eksplicitne veze izmeu dogaaja^
Nove datoteke s kodom su jednostavnije, lake ih je odravati i uvijek
su usklaene sa ,aspx stranicama.

Pogledajmo malo podrobnije .aspx datoteke i datoteke s kodom koje generira Vis .
Studio. Ponite tako to ete preimenovati Default.aspx u HelloWeb.aspx. Da ist e

362 | Programiranje C#
. .jir.ili. zatvorite datoteku Deault.aspx i zatim pritisnite desnom tipkom mia njeno
. jrjjc u prozoru Solution Explorer. Odaberite Rename i upiite ime HelloWeb.aspx.
Tako ete promijeniti ime datoteke, ali ne i klase. Da biste promijenili ime klase priti-
snite desnom tipkom mia ,aspx stranicu, odaberite View Code i zatim preimenujte
5'ldasu HelloWeb_aspx. Vidjet ete malu liniju pokraj imena. Pritisnite ju i otvorit ete
pametnu oznaku koja omoguava promjenu imena klase. Pritisnite Rename Defa-
;ult_aspx u HelloWeb_aspx i Visual Studio e se pobrinuti da se svaka pojava Defa-
j ult_aspx zamijeni s pravim imenom, kao to je prikazano na slici 15-3.

g p u b lic p s r tia l c la s s H e llo K e l) a Bpad


( S 3
R en am e 'Default_aspx' to HelloW eb_aspx'
L>
j R enam e with preview ...

iS lika 1 5 -3 . M ije n ja n je i m e n a k l a s e

|||(Na nesreu, (barem u beta inaici) ime klase nije promijenjeno u HelloWeb.aspx pa se
^1: vratite u HelloWeb.aspx datoteku i promijenite ClassName atribut direktive stranice u
!|| HeltoWeb_aspx:
i|fL. <%@ Page Language="C#" CompileWith="HelloWeb.aspx.es"
m
Sag; ClassName=HelloWeb_aspx" %>

U HTML pogledu datoteke UelloWeb.aspx moete vidjeti da je u tijelu stranice obra-


f t zac zadan koritenjem standardne HTML oznake za obrazac:
1 <form id="Formi" runat="server">

|:W eb Forms pretpostavlja da trebate barem jedan obrazac da biste upravljali korisni-
kim djelovanjem i stvara jedan kad otvarate projekt. Atribut runat="server" je klju
itave magije posluiteljske obrade. Bilo koja oznaka s ovim atributom se uzima kao
(posluiteljska kontrola koju izvodi ASP.NET kostur na posluitelju. Unutar obrasca
Visual Studio je postavio div oznake kako bi omoguio umetanje kontrola i teksta.
Kad ste napravili prazan Web Forms obrazac prva stvar koju biste mogli poeljeti ui-
niti je dodati tekst na stranicu. Prebacivanjem u HTML pogled moete dodati skriptu
i HTML kod direktno u datoteku ba kao to to moete kod klasinog ASP-a. Doda-
vanje sljedeeg reda koda u tijelo HTML stranice e prikazati pozdravnu poruku i
trenutno lokalno vrijeme na stranici:
: Hello World! It is now <% = DateTime.Now.ToString() %>

Oznake <% i %> funkcioniraju jednako kao i u klasinom ASP-u, pokazujui da kod
dolazi izmeu njih (u ovom sluaju C# ). Znak =odmah na poetku oznake izaziva da
ASP.NET prikae vrijednost, jednako kao poziv metode Response.Write(). Jednako
tako biste mogli napisati red koda:
Hello World! It is now
<% Response.Write(DateTime.Now.ToString()); %>

Poglavlje 15: Programiranje ASP.NETaplikacija i Web usluga | 363


Pokrenite stranicu pritiskom na F5 tipku (ili spremite stranicu i otvorite ju u pregledniku) .
Trebali biste vidjeti niz znakova ispisan u prozoru pregledmka, kao na slici 15-4.

' 3 U n t it le Page - M icro so ft In tern et Ex p lorer

Fite E d it V iew F a v o rite s Tools H e lp ...............L

) Pac>. - 0 @ [1 ^ P 5 rc h ^ F a v crto # j
Address l^rhttp:;/localho5t:27396/Programmin9CSharpWeb/HelloWeb.aspx v j 0 Go

Hello world. Itis now 11/9/2004 8:28:51 AM

locai intranet
^]Done

S l i k a 1 5 - 4 . H e l lo W o r l d u A S P . N E T 2 . 0

Omoguavanje otkrivanja pogreaka


Kad pritisnete F5 pokreete program za otkrivanje
jam o prim jetn i da nem ate W e b . c o n f i g datoteku za ovu aplikaciju (koja je potrebna za
otkrivanje pogreaka) i pojavit e se dijaloki okvir Debugging N ot Enabled, kao na

slici 15-5.
U ovom dijalokom okviru je podrazum ijevano - ^ c i j ^ p u n i t e (iako
je p o t r e f i o , da dvorite) datoteku. P ritisnite O K da biste om ogu ili otkri-
vanje pogreaka za aplikaciju.

Debugging Not Enabled


The page car,not te rrn tn debug mode because debugging is not enabled ti the Web.conflg f e
Wh3t v/ould you like to do?

Modily the Web.canfigfile to enable debugging.


A Debugging should be sabled Inthe Web.config Se before^eploving the
^ Web site to a production envfronrnent.

ORun vvithout debugging. (Egulvalent to Ctrt+F5 )

| OK |[ Cancel Help

_______ ___________ _______


7 u k T l5^ D y a l o s k i 7k v i r k o j i ^ H d ^ ^ p o g rea k a b e z V e b .c o n f
d a toteke

364 1 Programiranje C#
odavanje kontrola
psluiteljske kontrole moete dodati na Web Forms obrazac na tri naina: upisi-
vanjem HTML koda u HTM L stranicu, povlaenjem kontrola s palete s alatima na
|jesign stranicu ili programski, dodavajui ih prilikom izvoenja. Na primjer, pretpo-
stavimo da elite koristiti gumbe kako biste korisniku omoguili da odabere jednog
fjd tri isporuitelja iz Northwind baze podataka. Moete napisati sljedei HTML kod
'.'unutar <form> elementa u HTML prozoru:
<asp:RadioButton GroupName="Shipper" id="Speedy"
text = "Speedy Express" Checked="True" runat="server">
</asp:RadioButton>
<asp:RadioButton GroupName="Shipper" id="United"
text = "United Package runat="server">
</asp:RadioButton>
<asp:RadioButton GroupName="Shipper" id="Federal"
text = "Federal Shipping" runat="server">
</asp:RadioButton>

^sp oznake deklariraju posluiteljske ASP.NET kontrole koje se zamijenjuju obinim


HTML kodom kad posluitelj obrauje stranicu. Kad pokrenete aplikaciju, preglednik
prikae tri radio-gumba u grupi. Oznaavanjem jednog, ostali prestaju biti oznaeni.
Isti efekt moete postii povlaenjem tri gumba s Visal Studio palete na obrazac, ili to
jeak jednostavnije, moete povui Radio Button List na obrazac, to e deklarativno
jipravljati skupom radio-gumba. Kad to napravite, otvara se pametna oznaka koja vas
.jpoziva da odaberete izvor podataka (to vam omoguava da se poveete s kolekcijom,
jiioda onom koju ste uzeli iz baze podataka) ili ureivanje stavki. Odabir Edit Items
jjpcije otvara Listltem Collection Editor, gdje moete dodati tri radio-gumba)
pvaki radio-gumb ima podrazumijevano ime Listltem, ali moete urediti njegov tekst i
.vrijednost u svojstvima Listltem, gdje takoer moete odluiti koji je od radio-gumba
Podrazumijevano oznaen, kao to je prikazano na slici 15-6.

Kontrole moete dodati na stranicu u jednom od dva naina rada. Stan-


dardni nain je FlowLayout s kojim kontrole dodajete od vrha prema
dnu kao u Microsoft Word dokumentu. Drugi nain je GridLayout kod
kojeg su kontrole poredane u pregledniku koritenjem apsolutnog pozi-
cioniranja (x i y koordinata)..

\ biste preli iz jednog u drugi nain rada, promijenite pageLayout svojstvo doku-
menta u Visual Studio .NET-u.
IMoete popraviti izgled radio-gumba mijenjanjem svojstava u prozoru Properties,
Ukljuujui pismo, boju, broj stupaca, smjer ponavljanja (okomito je podrazumije-
vano) i tako dalje.

rebacujte se izmeu Design i Source prozora da vidite uinak promjena, kao to je


rikazano na slici 15-7.

Poglavlje 15: ProgramiranjeASP.NET aplikacija i Web usluga | 365


Listltem Collection Editor

S l i k a 15-6. U r e iv a n j e k o l e k c i j e L i s t l t e m s

t ; Programmin&CSharpWeb - Microsoft Ocvoiopmcnt Environment


Format layout Toob

V -----
SciobonEspibrer-SoMion'Proofanifrt... ? X

itionProgremminpCSharpVVab(2) (l profeti)
jC:\
jC:\Doc
Documefits and 5et
5ettm<js\
tm<js\Jese
Jese\
se\My Oocu
Oocun

B
Oata
HelaWeb.
eb.aspx
i, Vfeb.Canfig

f^'oWtonExpiorerj^ttW5Yiew
Propertles w ? x
R adloB
loB utt
uttonU
onUstl Sys
System.Web.
eb.UI.WebCo
bCortn

l ^ e a tO ir e r tio n ) ' ;Vertlcal


"" ;;. rtu
ifsttnio
!tTablndex ;0
,TextAl?r Rtf*
TootTip
.'V^ktottonGroup

a^uBHHBoo D aU Sour celD


el D
;Thecontrol1 D ofan ata5
ta5oifee that w* be
used as the dat
data souce.

S l i k a 15-7. P r e b a c i v a n j e i z m e u D e sig n i S o u r c e p r o z o r a n a k o n d o d a v a n j a g r u p e r a d io - g u m b a

366 i Programiranje C#
posluiteljske kontrole
Web Forms obrasci nude dva tipa posluiteljskih kontrola. Prve su posluiteljske HTML
kontrole. To su HTML kontrole koje oznaavate s pomou atributa runat=Server.
; alternativa oznaavanju HTML kontrola kao posluiteljskih kontrola je koritenje ASP.

\;i;T posluiteljskih kontrola koje se jo nazivaju ASP kontrole ili Web kontrole. ASP
kontrole su razvijene da dopune i zamijene standardni skup HTML kontrola. ASP kon-
trole osiguravaju dosljedan objektni model i dosljednije nazive atributa. Na primjer, ako
koristite HTML kontrole postoji veliki broj naina da se obrade upisani podaci:
<input type="radio">
dnput type="checkbox">
<input type="button">
; <input type="text">
<textarea>

Svaki od njih se ponaa drugaije i uzima druge atribute. ASP kontrole pokuavaju
: normalizirati skup kontrola dosljednim koritenjem atributa unutar objektnog modela
ASP kontrola. ASP kontrole koje odgovaraju prethodnim HTML posluiteljskim kon-
trolama su:
<asp:RadioButton>
<asp:CheckBox>
<asp:Button>
<asp:TextBox rows="l">
<asp:TextBox rows="5">

ovog poglavlja je posveen ASP kontrolama.

Povezivanje podataka
Razne tehnologije su nudile programerima mogunost povezivanja kontrola s poda-
cima tako da, ako bi podaci bili promijenjeni, kontrole bi odmah reagirale na tu
ipromjenu. Ali, povezane kontrole bi imale ogranienu funkcionalnost i izgledale bi

Projektanti ASP.NET-a su naumili rijeiti ove probleme i pruiti niz robustnih kon-
trola povezanih s podacima, koje bi pojednostavnile prikaz i promjenu podataka bez
rtvovanja izvedbe i kontrole preko korisnikog suelja. U inaici 2 .0 proirili su listu
povezivih kontrola i pruili vie gotovih funkcionalnosti.
Uprethodnom dijelu ste fiksno podesili radio-gumbe na obrascu, jedan za svaki od tri
isporuitelja u Northvvind bazi podataka. To ne mora biti najbolji nain da se to uini.
;:Ako promijenite isporuitelje u bazi podataka, morate iznova povezati kontrole. Ovaj
Ldio opisuje kako moete dinamiki napraviti ove kontrole i povezati ih s podacima u
bazi podataka.

iMoete poeljeti napraviti radio-gumbe temeljene na podacima u bazi podataka jer ne


' moete znati prilikom projektiranja koji e tekst sadravati gumbi ili koliko gumba e
jbiti potrebno. Da biste to postigli, povezat ete RadioButtonList sa izvorom podataka.

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 367


Napravite novu Web stranicu DisplayShippers i povucite RadioButtonList na obra^
zac. Ovaj put, umjesto odabira opcije Editltem iz Common RadioButtonList Taskj
pritisnite Choose Data Source... Otvara se dijaloki okvir Choose Data Source k '
na slici 15-8. t

Slika 15-8. Odabir izvora podataka

Pritisnite padajui izbornik Select a data source i odaberite <New Data Source>. Dalje tre-
bate odabrati izvor podataka iz tipova podataka u vaem raunalu. Odaberite Databasei
otvara se dijaloki okvir Configure Data Source, kao stoje prikazano na slici 15-9.

368 | Programiranje C#
daberite New da konfigurirate novi izvor podataka i otvorit e se dijaloki okvir
onnection Properties. Odaberite ime posluitelja, kako se elite spojiti na posluitelj
'Ime baze podataka. Obavezno pritisnite Test Connection da testirate vezu. Kad sve
%de radilo pritisnite OK (slika 15-10).

C o n n e c t io n P r o p e r t ie s

~c* i
Spedfy the fo lorttng to ccroect to SQL Server dota:
i . Sdect ot onter a server nama:

C ACK
2. Enter In/ormaSonTo log ort to the server!
~a . [ I
O U s e Wlndow$ NT Integrated Security

<) Use e specfic user ID and pa$$word:

C*i TertCcnneriMjij'^

&lika 1 5 -1 0 . K o n f ig u r ir a n je s v o js t a v a v e z e

akon toga svojstva veze e biti upisana u dijaloki okvir Configure Data Source. Pre-
gledajte ih i ako su u redu pritisnite Next. Na sljedeoj stranici arobnjaka dajte ime
vezi (na primjer, NorthWindConnectionString) ako ju elite pohraniti u konfiguracijsku
|atoteku koju ete moi ponovno koristiti.
~ad pritisnete Next dobit ete priliku da odaberete stupce koje elite uzeti ili zadate
~QL deklaraciju ili pohranjenu proceduru za uzimanje podataka.
V'
Otvorite padajui popis Tables i odaberite tablicu isporuitelja. Odaberite polja Ship-
,erID i CompanyName kao to je prikazano na slici 15-11.

V r- Kad ste ve ovdje, moete pritisnuti AdvancedOptions gumb da biste


l vidjeli koje su vam druge opcije dostupne.
'' i .':

'Pritisnite Next i testirajte upit kako bi provjerili dobivate li natrag vrijednosti koje
(oekujete (slika 15-12).

Poglavlje 15: Programiranje ASP.NETaplikacija i Web usluga | 369


Con
Configure Data Source
ource - SglData
ataSourc
urcel
Configure Select Statem ent
Setecl how you want the comiol to oot data from the databaso.

How wotid ygu Vte to retrieve dala from vou databasc?

Speofr coirm s from a tat* cr view


OspeOfy a custcm SQL iiaiam ent or stored focie
Table or Vtew Cblicre
Name; ........

[sHppers__
Colim s:
Katun or*yurtquerows
!|0S*hWwlO
si__ -
Phc*

T (SNpperlD], (ComparrjNamoJ FRCM [3* pe J

l Cancei ~ji <Sack ~|l > TI

Slika 15-11. Konfiguriranje izvora podataka

C o n fjg u re D a t a S o u r c e .-J5< qiPataSouree1

TestQuerv
x e c u te th e s elec t s ta te m e n t to p re v ie w th e result.

' To p ,e v te w t e data re tu m e d bv t e d a ta sou.ce, c fc k T e s t Q ue .y . To c o m a te te t e w lta td , c fck F rts h .

SELECT Statement:
SELECT [ShfpperlD]. [Companyt4ame] FROM [Shtooefs]

| Cancei |1 < Badc |, - I !

S l i k a 1 5 - 1 2 . T e s t ir a n je u p it a

Sad je vrijeme da RadioButtonList poveete s izvorom podataka koji ste upravo napra-
vili RadioButtonList (poput veine popisa) pravi razliku izmeu vrijednosti koja ce
biti prikazana (na primjer, imena tvrtke za dostavu) i vrijednosti tog odabira (na p
mjer, identifikatora tvrtke za dostavu). Postavite ova polja u arobnjaku koristei paa
jui popis, kao to je prikazano na slici 15-13.

370 | Programiranje C#
Choose Data Source
Choose a data source fbr the RadioButtonltst Items and a data field to tand fbr either the tex t or value,
or both.

Select a data source:

[ip ID ataSourcel v jj

Select a data field to display in the RadioSuttonUst:

[companyName .v~j

Select a data field for the value o f the RadloButtonUst:

Refresh Schema

Slika 15-13 Povezivanje polja sa kontrolom radio-gumba

Moete poboljati izgled i osjeaj radio-gumba povezivanjem sa tablicom Shippers.


; Odaberite popis radio-gumba i postavite njegova svojstva u prozoru Properties.

Ispitivanje koda
Prije nego krenemo dalje trebate obratiti panju na nekoliko stvari. Kad pritisnete F5
da pokrenete aplikaciju, ona se pojavljuje u pregledniku Weba i vide se radio-gumbi
prema oekivanjima. Odaberite View -* Source i vidje ete da je pregledniku poslan
obini H TML, kao to je prikazano na slici 15-14.
Primjetne da HTM L ne sadri RadioButtonList. Kod sadri tablicu sa elijama unutar
Ikojih su standardni HTM L ulazni objekti i natpisi. ASP.NET je preveo kontrole koje
je. dodao projektant u HTM L razumljiv bilo kojem pregledniku.

Zlonamjeran korisnik moe napraviti poruku koja izgleda kao valjana


poruka obrasca, ali u koju je postavio vrijednost polja koje nikad niste
dodali u obrazac. To mu moe omoguiti da odabere opciju koja nije
ponuena (na primjer, opciju za povlatene kupce) ili ak da pokrene
zlonamjeran SQ L iskaz. Trebate biti posebno oprezni s izlaganjem
vanih podataka kao to su primarni kljuevi u HTM L kodu te se tre-
bate pobrinuti da ono to primite od korisnika ne bude ogranieno na
ono to je predvieno obrascem. Za vie informacija o sigurnosnim
pitanjima u .NET-u, pogledajte stranicu http://msdn.microsoft.com/
security/.

Poglavlje 15: Programiranje ASP.NETaplikadja i Web usluga | 371


a .Un.tUled Paga - M ic ro s o ft In te rn e t E x .., L J l i X f t %

0 ;'-; & H iS , i;P 5e ' &**


A>lwtjjfllit^:/AxahQ8t:333S/DepbyhppefsA)efaulLaspx Q
0Robcform * @ Pd SKds (jjij jdeotiiies : i *s

O Speed yEjT T s j
O United Packjge Pite t t Format vie
view_ Help _ .............. .. ..........................................
........ ...
... ...................... .. ............ .........
......... . ; '-
OFtdera) Shjppntg
<1DCCtiPEhtmlPueuc"-//V3C//0TDrmi. 1.1//en" "trti:p://rww.*<3.org/TR/xhtnilll/OTO/xhtni1ll.td~>
<
<hhtm ea1dxm t1 lns>'http://w*w.w3.org/l999/xhtm1" >
i Uhtrle
ettaUdl>
edPage
)/otd1yt<
lax/
>formmeihod'postactlorc'Dafau1t.aspx" 1dforml'>
<1nout typ9"'h1dderVnam
<d1v>
p-'~ vIEVTSTATE
I f t i i ufat lmuvalr>/;V
</d1v>
Q 3ehP bOCW
BTJH
aQ2
lU
wl-N
:G2
lCuy2M xiJD
OkAO
TPE2eftW
M 9A
e2gFI0
C0s2
PQAW2dAng
zI2
BRDkW cj85P r5g
aI
PC
l O1
r8
hSP
e.iivz
iv3
ivzFO
pY
gy
oj
ovd
gW5k
cbpZ
yr2
vQ
yrv alF
gQ
- M
O U3ei2W
/> K5ievaCH3lcjHOVrfSpdGVkIFBhY2Th75i
<div><table1d-"ftad1oBirttonLlsil'' border."0">
Por'ftad<ltorB >ucton<L cls
dxtl1_r0tp
ru>tspe1edd-y"RG
adx1poreBsust<to
/n
laLblesltxl_/0t"d>type-rad1onameftad^oBirtionL^stlvalufi-"!" /x1abe!
for"fiad10ButconLlstl_lSonltedPackage</n
< / t r> r ^ ^ id - ''R d '
* oB u tto laLblesltxl_/ltM
d>xype-"rad1o" na/ne-'-RadloBirttonUstl" valueO" /xlabol
. *t? 'fSg;] for"Rad<1A
oe'u
><
tt^
orn^i.d
1sJi<
l_lf2)_1JtFeude"rA
aladsihol6piprlxntg
o<
n/
(.!iastlbe_12x'/ttd
yp
>e-"radloname-'Rad1oBirtxont.1stl"value-"3" /xlabel
. . '* < A r>
' </4foU
</ ?>
rm
</
</b
hodyl>
tm >

Slika 15-14. HTML kod koji ASP.NETalje pregledniku

Dodavanje kontrola i dogaaja


Dodavanjem jo nekih kontrola moete napraviti kompletan obrazac koji korisnici
mogu upotrijebiti. To ete uiniti dodavanjem prikladnije pozdravne poruke (,,Wel-
come to Northwind), polja za ime korisnika, dva nova gumba (Order i Cancel) i
teksta koji osigurava povratnu informaciju prema korisniku. Slika 15-15 prikazuje
gotov obrazac.
Ovaj obrazac nee dobiti nikakvu nagradu za dizajn, ali njegova upotreba e razjasniti
velik broj kljunih pitanja o Web Forms obrascima.
<* *
Nikad nisam upoznao programera koji misli da ne moe napraviti savr-
eno korisniko suelje. Istovremeno, nisam nikad upoznao progra-

71? mera koji je to izveo. Oblikovanje korisnikog suelja je jedna od onih
vjetina (kao to je poduavanje) za koju svi mislimo da posjedujemo,
ali sam o nekolicina vrlo talentiranih to radi dobro. Kao programer
znam svoja ogranienja: piem kod, a netko drugi taj kod smjeta na
stranicu i razmatra pitanja upotrebljivosti.

Primjer 15-1 sadri kompletan H TM L za ,aspx datoteku

372 | Programiranje C#
3 Choose Sh ipper
per - Micr
Microsoft In tern et Explore r
F lle E d it V le w F a vo rite s Tool H elp

' 0 0 S e a rch ' ^ JF avo rites j0 J

A ddress ://lo c a lh o s t:3 3 3 5 /D is p la y S h lp p e rs /S h ip p e r.aspx ii0 G o


g Ro bo FO Tm 0 P a s s c a r d s Id e n titie s * jjgSafenotes L in k s

-v
Welcome to NorthWind
Your i ---------------------------- i
name: *------------- -----------------
O Speedy Express

Shipper: OUnited P^ a g e
OFederal Shipping

| O rd e r | [ C a n c e l |

@ D one ! ; i > Local in tra n e t

Slika 15-15. Kompletan obrazac za isporuitelje

ifrimjer 15-1. .aspx datoteka


| P Page Language="C#" CompileWith-"Shipper.aspx.cs
i ClassName="Shipper_aspx" %>

JiDOCTYPE html PUBLIC -//W3C//DTD XHTML 1.1//EN"
| http://www.w3.org/TR/xhtmlll/DTD/xhtmlll.dtd">

|html xmlns="http://www.w 3 .org/1 9 9 9 /xhtml" >


,/ihead runat=server">
<title>Choose Shipper</title>
Vhead>
j:body>
<form id="forml" runat="server">
<table style="width: l66px; height: 33px">
<tr>
<td colspan="2" style=height: 20px">Welcome to NorthWind</td>
</tr>
<tr>
<td>Your name:</td>
<tdxasp:TextBox ID="txtName' Runat=serverx/asp:TextBoxx/td>
</tr>
<tr>
<td>Shipper:</td>
<td>
<asp:RadioButtonList
ID=RadioButtonListl"
Runat="server"
DataSourceID="SqlDataSourcel"

Poglavlje 15: Programiranje ASP.NETaplikacija i Web usluga I 373


Primjer 15-1. ,aspx datoteka (nastavak)
DataTextField="CompanyName"
DataValueField="ShipperID">
</asp:RadioButtonList>
<asp:SqlDataSource
ID="SqlDataSourcel"
Runat="server"
SelectComniand="SELECT [ShipperID], [CompanyName]
FROM [Shippers]"
ConnectionString=
"<%$ ConnectionStrings:NorthWindConnectionString %> >
</asp:SqlDataSource> <br />
</td>
</tr>
<tr> ,
<tdxasp:Button ID="btnOrder" Runat=server Text="Order' /></td>
<tdxasp:Button ID="btnCancel" Runat=server
Text="Cancel" / x/ td >

</tr>
<tr>
<td colspan=" 2 " x asp : Labe l id="lblMsg"
runat=serverx/asp: Labelx/td>
</tr>
</table>
</form>
</body>
</html>

Kad korisnik pritisne Order gumb provjerit ete je li korisnik unio svoje ime i pruiti
povratnu informaciju o odabranom isporuitelju. Zapamtite, prilikom projektiranja
ne moete znati ime isporuitelja (ono se uzima iz baze podataka) pa ete u padajuem
popisu morati provjeriti odabrano ime (i identifikator).
Da biste sve to postigli prebacite se u Design nain rada i dvaput pritisnite gumb
Order. Visual Studio e prikazati stranicu s kodom i generirati metodu za obradu
dogaaja za Click dogaaj gumba.
A^
^ I Da bismo pojednostavnili ovaj kod neemo provjeravati je li korisnik
<*, J upisao ime u polje za tekst. Za vie informacija o kontrolama za pro-
vjeru koje ovo olakavaju, pogledajte knjigu Programiranje ASP.NET.

Dodajte kod za obradu dogaaja i postavite natpis tako da preuzme tekst iz polja za
tekst te tekst i vrijednost iz RadioButtonList:
void btnOrder_CUck( object sendei, EventArgs e )
{
lblMsg.Text = "Thank you " + txtNaine.Text.Triin() + You chose " +
rblShippers.Selectedltem.Text.T o S t rin g O + " whose ID is " +
rblShippers.SelectedValue.ToStringO;

374 | Programiranje C#
p u b lic p a c c ia l c la s s Shipx> er__ asi> x
<
p ro te c te d o v e r r id e |
Onlnit (System.EventArgs e)

v o id b tn O rd e r_ C nload(5ystem.EvertArg5
f t ~jnloatfLGMplBtfc p>y5temtEventArgs e)
lb lM s g . T e x c = OnPrelnit (System.EventArgs e)
r b lS h ip p e r s )* * Or>PreLoad(Sys(:em.EverrtArgse)
r b lS h ip p e r s OnPreRender (System.EventArgs e)
f t OnPreRenderComplete (5ystem.EventArgs e)
f t OnSaveStateCompIete (System.EventArgs e)
f t OnUnload (5ystem.EventArgs e)

Slika 15-16. Premoivanje dogaaja OnLoad

Ponite tipkati OnLoad i kad je istaknuto pritisnite Tab. Bit e napravljen kostur metode,
ali njegovo podrazumijevano tijelo izbacuje NotImplementedException iznimku.
(Obriite iznimku i zamijenite ju s kodom:
rblShippers.Selectedlndex = 0;

:To odabira prvi gumb iz RadioButtonList. Postoji problem s ovim rjeenjem. Ako
pokrenete aplikaciju vidjet ete da je odabran prvi gumb, ali ako odaberete drugi (ili
(trei) gumb i pritsnete OK, vidjet ete da je prvi gumb ponovno odabran. Izgleda da

nie izvede OnLoad dogaaj, a u toj metodi za obradu dogaaja ponovno postavljate

;jne i kad je vraena natrag pregledniku kao rezultat pritiska na gumb OK.
Da biste to rijeili odabiranje prvog radio-gumba smjestite u i f deklaraciju koja pro-
vjerava da lije stranica vraena:
protected override void OnLoad(EventArgs e)
{
if ( UsPostBack )
{
rblShippers.Selectedlndex = 0;
}
}

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 375


put ova vrijednost je f a l g ^ itelju na 0 b radu (gdje se izvodi metoda za
pritisnete stranica ce in P . on [oga se vraa korisn iku . Ovaj put, svojstvo
obradu dogaaja btnOrde i f deklaracije se ne izvodi, a korisnikov
IsPostBack je postavljeno na tru e ko . ,y
d t , j , sauvan, kao S,o j . pnkaaano na 1 5 ' .

3 Choos Shipper - Microsoft Internet Explorer


oose Ship
F lle e d it V ie w F a r o r te s T oo ls H elp
sa ; [* ][*]

.t M r e s s ! .. u o -a cm te n la u S h io D e rs /S h ip p e r. a s p x
Address^htlpi//toca1htJgj J j j j ^MgYgJ j PP^j L_- ! Ec - - l - ^ , lasav e -Logatf ::i: ilinkS
U1 - .............. -

V le lc c ro e to N o r th V T m d ____________________

Your namet jj^se_LAejJl----------


OSpeedyExpress
United Package
Shipper OFederal Shipping

fo sn u m . ,?
S T y o u Jesse Liberty. Vou chose United Package whore .a

} l i k 7 l 5 A Z K ^ i Pri vraani Strank


Kom pletan kod obrasca je prikazan u prim jeru 15 -2 .

Prim jer 15-2. Kod obrasca za Shipper.aspx.cs

using System;
using System.Data;
using System.Configuiation;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.l)I;
using System.W e b .U I .WebControls;
using System.Web.UI.WebControls.WebParts,
usine System.Web.UI.HtmlControls,

public partial class Shipper_aspx


{
protected override void OnLoad(Eventftrgs e)

^ i-f ( tIsPostBack )
{

376 | Programiranje C#
primjer 15-2. Kod obrasca za Shipper.aspx.cs (nastavak)
rblSbippers.SelectedIndex = 0;

)
}
void btnOrder_Click( object sender, EventArgs e )
{
lblMsg.Text = "Thank you '' + txtName.Text.Triin() + Vou chose
rblShippers.SelectedItem.Text.ToStrifig() + " whose IO is " +
rblShippers.SelectedValue.ToStringO;
}

Napomena za ASP l .l programere: kao stoje ranije spomenuto, aspx.cs


datoteka je sad uvelike pojednostavnjena. Moete se pozvati na stavke
na stranici (na primjer, lblMsg) bez deklariranja zatienih lanova i sav
projektantov kod je skriven dozvoljavajui vam da se usredotoite samo
na logiki dio aplikacije.

Web usluge
Ifl'.N E T Web usluge pomau da napiete komponente ije metode mogu biti pozvane
preko Interneta koritenjem bilo kojeg .NET programskog jezika. Projektanti koji rade
$ E W eb usluge mogu stvarati jednu na temelju druge, koristei prednost povezanosti kao
f j kljunog svojstva Weba. Stvaranje nove vrijednosti uzima prednost nad ponovnim
i otkrivanjem ve poznatih stvari.
Popis Web usluga koje mogu biti korisne projektantima i krajnjim korisnicima se ini
|neogranien. Trgovina knjigama bi mogla pruati Web uslugu koja uzima ISBN i vraa
Scijenu i podatak o dostupnosti knjige. Web usluga hotela moe uzimati datum dolaska
(i odlaska i broj gostiju, a vraati rezervaciju. Neka druga Web usluga moe uzimati
^telefonski broj i vratiti ime i adresu. Takoer usluga moe pruati informacije o vre-
! menskim prilikama i lansiranjima svemirskih atlova.
; Jedna aplikacija moe povezati usluge stotina manjih Web usluga iz cijelog svijeta. To
|Webu daje novu dimenziju: ne samo da se dobijaju i razmjenjuju infromacije, ve se i
fpozivaju metode i izvode aplikacije.

SOAP, VVSDL i otkrivanje


Za izradu Web usluga potreban je jednostavan, opeprihvaen protokol za izlaganje
i pozivanje metoda Web usluga/ Simple Object Access Protocol (SOAP) je predloen

I/ Ovaj pogled na SOAP je uvelike RPC-orijentiran zato to .NET rako ohrabruje projektante da koriste
SOAP. Zapravo, SOAP je razvijen da alje poruke, ali .NET arhitektura implementira slanje poruka za
pozivanje metoda i pristupanje svojstvima na udaljenom objektu.

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 377


World Wide Web konzorciju 1999. godine. On ima prednost jer je utemeljen na X M U '>
i koristi standardne komunikacijske protokole za Internet.
1
Od 1999. godine pojam SOAP je prestao neto predstavljati zbog toga
to rije objekt u nazivu zavarava. SOAP nema veze s objektima, ve
*,' sa slanjem poruka. Druga, novija, promjena je izvedeni naziv: Service
Oriented Architecture Protocol.

SOAP je jednostavan protokol za slanje poruka koji se temelji na XML-u, HTTP-u i ?


SMTP-u. Za klijent koji koristi Web usluge koje podravaju SOAP poeljna su, ali ne i
obavezna, jo dva protokola: opis metoda koje prua pojedina usluga a koje klijent moe
razumijeti i koristiti te opis svih takvih usluga dostupnih na pojedinoj Web lokaciji ili '
URL-u. .NET kostur prua prvi od ta dva opisa s pomou jezika Web Service Descrip-
tion Language (WSDL) koji je tvrtka Microsoft razvila zajedniki s IBM-om i drugima.
WSDL je XML shema koja se koristi za opis dostupnih metoda Web usluga tj. njenog
suelja.

Podrka na posluiteljskoj strani


Sva podrka potrebna za izradu Web usluga se nalazi unutar .NET kostura i pruaju
je klase unutar System.Web.Services imenskog prostora. Izrada Web usluge ne zahti-
jeva od vas posebno programiranje. Vi samo trebate napisati kod za implementaciju,
dodati [WebMethod] atribut i pustiti posluitelju da obavi ostalo. O atributima se detalj-
nije govori u poglavlju 18.

Podrka na strani klijenta


Web uslugu koristite pisanjem klijentskog koda koji se ponaa kao da komunicira
izravno s lokalnim objektom, ali koji zapravo komunicira s posluiteljem i to preko
posrednika. Posao posrednika je da predstavlja posluitelja na klijentskom raunalu,
da zapakira klijentove zahtjeve u SOAP poruke koje se alju posluitelju i da pribavi
odgovore koji sadre rezultat.

Izrada Web usluge


Da bismo ilustrirali metode za implementaciju Web usluga u C # jeziku koritenjem
klasa usluga .NET kostura izradit emo jednostavan kalkulator i onda pristupiti nje-
govim metodama preko Weba.
Ponite sa zadavanjem Web usluge. Da biste to uinili, definirajte klasu koja nasljeuje
od klase System.Web.Services. WebService. Najlaki nain da napravite ovu klasu je da
otvorite Visual Studio i napravite novu C # Web stranicu. U dijelu Templates odaberite
ASP.NET Web Service i Web uslugu nazovite CalculatorWS, kao to je prikazano na
slici 15-18.

378 | P ro g ra m ira n je C#
Temptetes:
$ ASP.NET W eb Site
II
ASP.NET W e b S v lc e
^ P e rs o n a l W eb Site S ta rte r Kit
^ ASP.NET C rystal Reports W eb Site
g TjA dd New Online T em plate...

|A Webs ite fo r cre atln g m . VVeb je rv ice s

Location: Jpile System J ^ | e n ts | i2 0 0 5 Books\C 5harp 1e\Source\Chapter 15\CakulatorW S S rovfse... '|


Lenguage: jv is u a lC ir

.: r ok j Cancel I

. Slika 15-18. Izrada Web usluge

Visual Studio .N ET stvara kostur Web usluge i prua primjer metode usluge koju
..moete zamijeniti vlastitim kodom, kao to je prikazano u primjeru 15-3

f g l Primjer 15-3. Kostur Web klase kojeg je generirao Visual Studio .NET
uski; System.Web;
:.using System.We b.Services;
' using System.Web.Services.Protocols;

;[WebServiceBinding(CorvformanceClaims=WsiClainis.BPlo, EmitCorvformanceClaims = truejl


:public class Service : System.Web.Services.HebService {

[HebMethod]
public string HelloWorld() {
return "Hello World;
}
|] '
|Da biste od tog kostura dobili kalkulator, zamijenite HelloWorld metodu sa pet drugih
|metoda: Add(), Sub(), Mult(), Div() i Pow(). Svaka uzima dva parametra tipa double,
|izvodi traenu operaciju i vraa vrijednost istog tipa. Na primjer, evo koda za izrau-
navanje zadane potencije broja:
public double Pow(double x, double y)
{
double retVal = x;
for (int i = o;i < y-l;i++)

retVal *= x;
}
return retVal;
}

P oglavlje 15: P ro g ra m ira n je ASP.NETa p likacija i W eb usluga | 379


Da biste izloili svaku od tih metoda kao W eb uslugu samo dodajte [WebMethod] atribut
prije deklaracije svake metode:
[UebMethod]

Ne trebate izloiti sve metode klase kao Web usluge. Moete odabrati metode dodava-
njem [WebMethod] atributa samo onim metodama koje elite izloiti.

To je sve to trebate uiniti - .NET se brine za ostalo.

WSDL i im enski prostori


Vaa Web usluga koristit e WSDL XML dokument da opie krajnje toke koje su
dostupne preko Weba. Unutar bilo kojeg WSDL dokumenta moraju biti koriteni
XML imenski prostori kako bi krajnje toke imale jedinstvena imena. Podrazumije-
vani XML imenski prostor je http://tempuri.org, ali to ete trebati promijeniti prije
nego to Web uslugu uinite javno dostupnom.
Moete promijeniti XML imenski prostor koritenjem Web Service atributa.
[WebService(Namespace=
"http://www.LibertyAssociates.com/WebServices/")]

Ne oekuje se da se na ovoj URL adresi nalazi dokument. URL adrese se koriste jer
su praktian izvor jedinstvenih imena.

Primjer 15-4 prikazuje kompletan izvorni kod za Web uslugu kalkulatora.

Primjer 15-4. Web usluga kalkulatora


using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[UebServiceBinding(ConformanceClaims=VteiClaims.BPlO,EmitConfoimanceaaims = true)]
public class Service : System.Web.Services.HebService {

[WebMethod]
public double Add( double x, double y )
{
return x + y;
}

[WebMethod]
public'double Sub( double x, double y )
{
return x - y;
}
[WebMethod]
public double Mult( double x, double y )
{
return x * y;

380 | Programiranje C#
s i [klebtfethod]
|; public double Div( double x, double y )

return x / y;

;bMethod]
jlic double Pow( double x, double

double retVal = x;
for ( int i = o; i < y - 1 ; 1++ )
{
retVal *= x;
I' )
return retVal;

;f:

^.Testiranje Web usluge


i Ako pozovete preglednik izvoenjem programa u Visual Studiju ,NET vidjet ete auto-
klimatski generiranu posluiteljsku Web stranicu koja opisuje Web uslugu, kao to je

g p k a 1 5 -1 9 . P r i k a z t e s t n e s t r a n i c e W eb u s lu g e
j
Odabir metode vas dovodi do stranice koja opisuje metodu i omoguava vam da ju

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 381


a S e rv ice Web S e rv ice - M icrosoft In te rn e t E x p lo re r
F)le Edit v ie w Pavorites Tools Help

Back - Q 0 [| i i ^Psearch ^Favorite 0 - ^ liS ' !..J S S S i -Ji


A dd ress [ f f l h ttp ://lo c a lte t:1 9 9 7 5 /c ilc u la to rw S S 5 e rv ic e .a s m K m p = P o w _______________________________ ;^1 Q Go
B R o ta F o m * E jp a s ra rd s - [g jlde nti ssj> jg j[S a fm o te * g jF H I F o n n s - Ig S a v a - . t t f f f f j j j * H t j B , . ; [ u * ; jB e io g '

S e rv ic e
Clickhei-ofordcompletelistofoperations.

Pow

To t e s tth e o peration using th e H TTP P O S T proto col, d i c k tho I n vo k e button.

P a A jm e te r V alue

x: lEZ!
IC
jjn v o k e i

: lo c a l h lra n et

Slika 15-20. Testiranje Pow() metode Web usluge

Ako upiete vrijednost tri u prvo polje, etiri u drugo polje i pritisnete Invoke, zatrait
ete od Web usluge da tri podigne na etvrtu potenciju. Rezultat je u XML stranici
koja opisuje izlaz, kao to je prikazano na slici 15-21.

-3 h ttp
ttp://l
//localhost: 1997 5 /C a lcul ator W S/Se
S/S e m ce .as.
F ite E d it V ie w F a v o rlte s T o o ts H e lp -

B ack ' [ * } H ) j P S ech ^ F a v o r lte s Q \ 0 - %

Address g ) http://localhost:19975/CalculatDrWSyService.asmxAow m^ B Go
3 RoboForm Passcsrds ** {EUIdtentittes ^ j|Q Safenotes -li Unks

<?xml v e rsio n = "1 .0 " en cod in g= uu tf -8 " ?>


<double xm lnsnh t t p :/ / t e m p u r i .o r g / " > 8 K / d o u b le >

JDone i j^ localintranet

Slika 15-21. Pozivanje Pow() metode

Pregledavanje WSDL saetka


Puno posla automatski obavlja umjesto vas. H TM L stranice koje opisuju Web uslugu
i njene metode se stvaraju i ukljuuju veze do stranica na kojima se metode mogu
testirati.

382 | Programiranje C#
I . Sve Web usluge mogu bici opisane u WSDL datotekama. WSDL doku
. ^^^uuienc moete
: vidjeti dodavanjem nastavka ?WSDL URL adresi Web usluge:
http://localhost:l9975/CalculatorWS/Service.asmx?wsdl

Preglednik prikazuje WSDL dokument, kao na slici 15-22.

a ht
htttp:/
p://lo
/local
calhos
host:19975/CalculatorWS/Service.asrox?wsdl
t:19975/CalculatorWS/Service.asrox?wsdl - Micro
crosoft Internet Explor
lorer
File Edlt View Favorltes Toob Help m
;#
@Back '
; Adress j
li) lsi ii ^Psearcb ^FavoriSM
http://localhost:19975/Calci.iiatDfWS^erf:e.asm>;?wsdl
0 - ^ ' J [*10 M I i %
gRobcform - @Passcards * jb] IdenOties - (gjsafenotes - [SjFlllForrrts * I*}Sav w0 . r.-iT (L^Hide ; trk* ^eioglheT"

10
<?xrr>l vefsion=" . " encoding=utf-8n ?>
- <wsdl:definitions tvriifas. tp://sdu!inrts.>iifilso^p.t3nj/ivsill/scKJ|i/-
:-!Tn)r>yAri,= h n i i : / / n i i c r o s a h . c o n > / i ' r s t n / n u n u / t a ; H r > U i t c l i i n q / '
.,Tnlns:?o'tpc-nc=''litt|)://S(:h(*nk)i.Kinloo|).Qrg/soop/fiii(:ortiiiq/"
v.m!ns:n.ime-="hU|)://schi?nuis.xinlsoap.org/vfsc1l/inirMO/" tn;="hllp://lempuri.org/"
xrnlri2: littp :// vfv<vj.vv3.f)rcj/20Ul/XMl.5}clierrwt" .imlns: ;iVrpi>;="liU|i://5Cl)eitifis.xnilsofip.tjry/vt
3r, 1 1
^rni['?;Mtp="ht jj://scluim><i;.Mf)ilsffcip,f>ry/vv.sc l/ h U p /' t,3<geWamesp3ce=ufittp://tempurl.orq/*
niliv>:'v;t.dl=*t\Up;//5><;Jt3q\os.xmlsnop.org/vfsd1/">
- <wsl;types>
- <s:schema e1errientFormDefai.i! Mquo1ified" f3rgetr'I.aine?pace=Hhttp://tempuri.org/*>
- <s:element n3me=Add">
- <s:complesType>
- <s-. sequoce>
<s:element minOccurs=l" maxOccurs="l" narne=x" tvpe='s:doubte" />
< s : e le r n e n t m m O c c a r s = l maxOcct/rse"l name=y type="s:double />
</s:sequence>
</s:comple:<Type>
</<..elsmem>
- <s:etement name=AddResponse">
- <s:comples.Type>
- <s:sequence>
<s:e!ement rninOccurs"l maxOccurs="lMnaine=*AddResuU ivpes^ double" />
</v.sequence>
</s:comple:<Type>
</'s:e1emenl>
- <s:element name=*Sub">
- <s:complexType>
- <s:sequence>
<s:element minOccurs="l maxOccur$=l" nameax" type= s:doub1e" />
<s:element nunOccurss l maxOccuis="l* name-y" lype="s:doub1e />
</s:sequence>
</s:compleKType>
</s:e1ement>
- <s: element name-"8ubResponse*>
- <s:complexType>
. - <s:sequence>
<s:element minOccurs= lmaxOccurs=*l" names*8ubResuK type=*s:double* />
<r/s:sequence>
</s:compleKType>
</s:element>
Spone I, _.:<Q Local miranet

Slika 1 5 - 2 2 . Pregled W S D L saetka

Detalji WSDL dokumenata su izvan dometa ove knjige, ali moete vidjeti d aje svaka
metoda potpuno opisana u strukturnom XML formatu. To je informacija koju SOAP
j. koristi kako bi omoguio klijentskom pregledniku da pozove metode Web usluge na
posluitelju.

, Stvaranje posrednika
a
& Prije nego napravite klijentsku aplikaciju za meudjelovanje sa Web uslugom kalkula-
f, tora, prvo ete napraviti posredniku klasu. Da kaem jo jednom, to moete uiniti

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 383


runo ali to bi bio teak posao. Programeri iz Microsofta su napravili alat wsdl koji '
stvara izvorni kod za posrednika temeljen na informacijama u W SDL datoteci.

Da biste napravili posrednika, upiite wsdl u Visual Studio odzivniku i zatim putanju
do W SDL saetka. Na primjer, moete upisati:
usdl http://localhost:1 9 9 7 5 /CalculatorWS/Service.asmx?wsdl

Rezultat je C # klijentska datoteka Servicel.cs iji se odlomak pojavljuje u primjeru


15-5.

P r im je r 15-5. P r im je r k l i j e n t s k o g k o d a z a p r i s t u p W eb u s lu z i k a l k u l a t o r a

/......... ............................... - ............ ....................................................


// <autogenerated>
// This code was generated by a tool.
// Runtime Version: 2 .0.40607.16
//
// Changes to this file may cause incorrect behavior and
// will be lost if the code is regenerated.
// </autogenerated>
//............. ........................ - ................... ................... ............ .............. .

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

//
// This source code was auto-generated by wsdl, Version=2.0.40607.16.
II

/ / / <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.Des ignerCategoryAtt ribute (" code")]
[System.Web.Services.WebServiceBindingAttribute(Naine="ServiceSoap",
Namespace="http://tempuri.org/)]

public class Service : System.Web.Services.Protocols.SoapHttpClientProtocol

{
private System.Threading.SendOrPostCallback AddOperationCompleted;

private System.Threading.SendOrPostCallback SubOperationCompleted;

private System.Threading.SendOrPostCallback MultOperationCompleted;

private System.Threading.SendOrPostCallback DivOperationCompleted;

private System.Threading.SendOrPostCallback PowOperationCompleted;

I I I <remarks/>
public Service() {

384 | Programiranje C#
'primjer 15-5. Primjer klijentskog koda za pristup V/eb usluzi kalkulatora (nastavak)
this.Uri = "http://localhost:19975/CalculatorklS/Service.asmx";
}
I I I <remarks/>
public event AddCompletedEventHandler AddCompleted;

I I I <remarks/>
public event SubCompletedEventHandler SubCompleted;

I I I <remarks/>
public event MultCompletedEventHandler MultCompleted;

/// <remarks/>
public event DivCompletedEventHandler DivCompleted;

I I I <remarks/>
public event PowCompletedEventHandler PowCompleted;

I I I cremarks/>
[System.Web .Services.Protocols.SoapDocumentMethodAttribute
("http: //tempuri.org/Add", RequestNamespace="http://tempuri.org/",
ResponseNamespace="http://tempuri.org/",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public double Add(double x, double y) {
object[] results = this.Invoke("Add, new object[] {

i y});
return ((double)(results[o]));

I I I <remarks/>
public System.IAsyncResult BeginAdd(double x, double y, System.AsyncCallback callback,
object asyncState) {
' return this.BeginInvoke("Add", new object[] {

y}, callback, asyncState);

I I I <remarks/>
public double EndAdd(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((double)(results[0]));
}

|| I I I <remarks/>
-( public void AddAsync(double x, double y) {
this.AddAsync(x, y, nuli);
t }

I I I <remarks/>
public void AddAsync(double x, double y, object userState) {
if ((this.AddOperationCompleted == nuli)) {

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga | 385


Primjer 15-5. Primjer klijentskog koda za pristup VJeb usluzi kalkulatora (nastavak)
this.AddOperationCompleted = new
System.Threading.SendOrPostCallback(
this.OnAddOperationCompleted);
}
this.InvokeAsync("Add", new object[] {
x,
y}, this.AddOperationCompleted, userState);
}
private void OnAddOperationCompleted(object arg) {
if ((this.AddCompleted != nuli)) {
System.Web.Services.Protocols.
InvokeCompletedEventArgs invokeArgs =
((System.Web.Services.Protocols.InvokeCompletedEventArgs)
(arg));

this.AddCompleted(this,
new AddCompletedEventArgs(invokeArgs.Results,
invokeArgs.Error, invokeArgs.Cancelled,
invokeArgs.UserState));
}
}

I I I <remarks/>
II...

Ovaj kompleksan kod sastavio je W SDL alat za izradu posrednikog DLL-a potrebnog
za izradu klijenta. Datoteka koristi puno atributa, ali sa vaim poznavanjem C# jezika
moete donekle shvatiti kako neki od njih funkcioniraju.
Datoteka poinje deklariranjem S ervicel klase izvedene iz SoapHttpClientProtocol
koja se pojavljuje u imenskom prostoru System.Web.Services.Protocols:
public class Servicel :
System.Web.Services.Protocols.SoapHttpClientProtocol

Konstruktor postavlja URL svojstvo naslijeeno od SoapHttpClientProtocol na URL


,asmx stranice koju ste ranije napravili.
Deklaracija metode Add() ukljuuje atribute koji osiguravaju SOAP podrku za izvo-
enje udaljeni h poziva.
W SDL aplikacija pruila je i asinkronu podrku za metode. Na primjer, za metodu
Add() napravio je i BeginAdd() i EndAdd(). To omoguava interakciju s Web uslugom
bez naruavanja performansi.

Testiranje Web usluge


Da biste testirali Web uslugu, napravite jednostavnu C # konzolnu aplikaciju. Priti-
snite desnom tipkom mia projekt i dodajte Servicel.cs datoteku koju ste napravili iz
konzolnog prozora. Visual Studio e za vas napraviti posrednika theWebSvc.

386 I Programiranje C#
|Kad je to uinjeno, moete pozvati Pow() metodu kao da je to metoda na lokalno
dostupnom objektu:
for (int i = 2;i<i0; i++)
for (int j = l;j <lO;j++)
{
Console.WriteLine(
"{0} to the power of {1 } = {2 }", i, j,
theWebSvc.Pow(i, j));
}
I Ova jednostavna petlja stvara tablicu potencija brojeva od dva do devet, prikazujui
za svakog potencije od jedan do devet. Kompletan izvorni kod i izvadak izlaza prika-
5 anje u primjeru 15-6.

^ P r im je r 1 5 - 6 . K l ije n t s k i p r o g r a m z a t e st ira n j e W eb u slu g e k a l k u l a t o r a

|-#region Using directives

?' using System;


K using Systa.Collections.Generic;
kusing System.Text;

fordrcgior

i namespace CalculatorTest
d
i class Program
I i
|.7/ Program za testiranje Web usluge
public class Tester
{
i public static void Hain()
I {
i. Tester t = new Tester();
t.Run();
}

public void Run()


; {
int vari = 5 ;
int var2 = 7 ;

// Instanciranje posrednika Web usluge


Service theWebSvc = new Service();

// Pozivanje metode za zbrajanje


Console.WriteLine( "{0} + { 1 } = {2}, v an , var2,
theWebSvc.Add( vari, var2 ) );

// Gradi tablicu uzastopnim pozivanjem metode pow


for ( int i = 2 ; i < 1 0 ; i++ )
for ( int j = 1 ; j < 1 0 ; j++ )
{

Poglavlje 15: Programiranje ASP.NET aplikacija i Web usluga I 387


Primjer 15-6. Klijentski program za testiranje Web usluge kalkulatora (nastavak)
Console.WriteLine( "{0} to the power of {1 } = {2}",
i, j, theWebSvc.Pow( i, j ) );
}
}
}
}
}

Izlaz (odlomak):
5 + 7 = 12
2 to the power of 1 = 2
2 to the power of 2 = 4
2 to the power of 3 = 8
2 to the power of 4 =: 16
2 to the power of 5 =: 32
2 to the power of 6 =. 64
2 to the power of 7 =: 12 8
2 to the power of 8 == 256
2 to the power of 9 == 512
3 to the power of 1 == 3
3 to the power of 2 == 9
3 to the power of 3 := 27
3 to the power of 4 := 81
3 to the power of 5 ;= 243
= 72 9
3 to the power of 6 >
3 to the power of 7 = 2187
3 to the power of 8 = 6561
3 to the power of 9 = 19683

388 | Programiranje C#
POGLAVLJE 16

Sastavljanje u cjelinu

U ovom poglavlju ete koristiti mnoge od vjetina koje ste dosad stekli za izradu skupa
integriranih aplikacija. Svrha ovih aplikacija je da na stranicama Amazon.com potrae
relativne rezultate prodaje mojih knjiga o C# jeziku, ASP.NET-u i VB.NET-u.

Ukupni dizajn
Da biste vidjeli kako dvije razliite tehnologije funkcioniraju skupa, napravit ete dvije
neovisne aplikacije (samostalnu aplikaciju klijenta Web usluge i ASP.NET aplikaciju)
povezane preko pozadinske baze podataka. Preciznije, napravit ete stolnu aplikaciju
'H koja uzima podatke od Web usluge s Amazon.com-a i sprema ih u tablicu SQL Server
i baze podataka te ih prikazuje u ASP.NET aplikaciji.
: SQLbaza podataka je vrlo jednostavna. Nazvana je AmazonSalesRanks i sastoji se od
jedne tablice, Booklnfo, kao sto je prikazano na slici 16-1.

Sva polja u ovoj tablici mogu imati vrijednost nula jer ne moete kon-
trolirati koja je informacija dostupna na Amazon.com-u u odreenom
trenutku. Da bi dizajn napravili robustnijim, mogli biste uzeti da ISBN
broj bude primarni klju i da odbacite sve podatke vraene bez ISBN-a.
To je ostavljeno itatelju za vjebu.

Izrada klijenta Web usluge


i U nastojanju da informacije uini dostupnim projektantima i suradnicima (Web stra-
; nicama koje omoguavaju svojim posjetiteljima da kupe knjige preko Amazon.com-a)
I; tvrtka Amazon je izradila skup Web usluga. Za dodatne informacije pogledajte http://
www.amazon.com/gp/aws/landing.html.

Sadraj na stranicama A m a z o n . c o m je zatien autorskim pravima i ne m o e se koristiti bez pismene


.j dozvole. Ta dozvola je susretljivo osigurana za ovu knjigu iza koritenje na mojoj W e b stranici na adresi
; http://unvw. L iberty A ssociates.com .

389
im Design Table 'Booklnfo' in 'AmazonSale...
Colum n Nam a I D a ta T yp e l l e n g t h i A llow Nulls
char 10 V
title v archa r 500 V
publisher va rc h a r 500 V
p ub D ate v archa r 500 V I
link va rc h a r 1000 V
ra nk in t 4 V
la stU p d a te d a te tim e 8 V 1
te ch n o lo g v va rc h a r 500 V 1
a u th o r va rc h a r 500 V

Colum ns J

D escription
D e f a l t Value
Pracision u
S'.'il
Id e n titv NO
Id e n titv Soed
lc te n titv In cre m erit
Is P.ovvGuid Uo
Form ula
C ollatlon < d a ta b a s e d e fa u lt>

S lika 1 6 -1 . I z r a d a B o o k l n f o t a b l i c e

Aplikacija za analiziranje Web stranica


U prethodnim izdanjima ove knjige prvo smo implementirali samostalnu aplikaciju
kao aplikaciju za uzimanje podataka sa stranica Amazon.com.
U to vrijeme istaknuo sam da je analiziranje funkcioniralo odlino sve dok Amazon
izlistava poloaje na ljestvici tono na ovaj nain, ali ako se on promijeni, analiziranje
nee uspjeti. Svaki put kad Amazon izmijeni stranicu, morate aurirati ovaj program.
U dodatku, istaknuo sam da su podaci na Amazon.com stranicama zatieni autor-
skim pravima i da postoje ozbiljna pravna i etika pitanja o analiziranju Amazonovih
stranica.
Od objave prethodnih izdanja, Amazon je postavio zatitu na svoje stranice koja
blokira jednostavne aplikacije za analiziranje. Poto je istovremeno pruio znatnu
podrku za Web usluge, u ovom poglavlju e biti implementirana aplikacija koja e
koristiti te Web usluge, a analiziranje stranica emo obraditi u poglavlju 21, gdje emo
(legalno i etiki) analizirati moje Web stranice.

390 I Programiranje C#
Vaa aplikacija e koristiti ove Web usluge i trebat ete preuzeti skup alata za progra-
mere Amazon Web Services. Tri stvari koje ete trebati su associatesTag (daje ga Ama-
zoni, subscriberID (daje ga Amazon) i odgovarajuu .usdl datoteku (javno dostupna
na Amazon Web Services stranicama).

Izrada posrednika za Amazon


I
Kao i kod svih Web usluga kojima se pristupa preko .NET-a, morate napraviti posred-
niku klasu za klijenta. To moete uiniti tako da nabavite WSDL dokument koji daje
Amazon i zatim ga prevedete s naredbom za odzivnik:
S wsdl /o:Amazon.cs AmazonWebServices.wsdl
i!
i izrada aplikacije
I Napravite novu samostalnu aplikaciju AmazonWebServiceClient i obavezno u mapu
(( projekta kopirajte Amazon.cs datoteku, koju ste napravili iz ,wsdl datoteke. Dodajte
i datoteku projektu tako to ete pritisnuti projekt desnom tipkom mia i odabrati Add
i Existing Item.
' Amazon prua daleko vie podataka o knjigama i proizvodima nego to je nama
( potrebno, pa emo izvui samo dio njih.
i Pored toga, Amazon prua i metode koje vraaju informaciju o kolekciji knjiga, ali
( zasad emo pojednostavniti proces (pritom rtvujui performanse) traei jednu po
j jednu knjigu.
Da biste to uinili, napravit ete XML datoteke koje e sadravati ISBN brojeve knjiga
koje elite pronai. Podijelio sam ovo po tehnologijama tako da imam CSharpISBN.
xml datoteku, VBNETISBN.xml datoteku i ASPNET_ISBN.xml datoteku.
Primjer 16-1 prikazuje izvadak iz jedne od tih datoteka.

Primjer 16-1. CSharpISBN.xml


<isbns>
<isbn>193l83654X</isbn>
<isbn>0130461334</isbn>
<isbn>1893115593</isbn>
<isbn>0 l3 0 6 2 2 2 l4 </isbn>
<isbn>1861007043</isbn>
<isbn>1 8 6 1 0 0 4 9 8 2 </isbn>
<isbn>0672320711</isbn>
<isbn>0 5 9 6 0 0 1 8 l9 </isbn>
<isbn>07356l2897</isbn>
<isbn>07356l2900</isbn>
<isbn>0596003099</isbn>
<isbn>0596003765</isbn>
<isbn>0072133295</isbn>
<isbn>0672322358</isbn>
<isbn>0072193794</isbn>
<isbn>067232122X</isbn>
<isbn>1588801926</isbn>

Poglavlje 16: Sastavljanje u cjelinu | 391


Primjer 16-1. CSharplSBN.xml (nastavak)
<isbn>0672321521</isbn>
<isbn>0735615683</isbn>
<isbn>0201729555</isbn>
</isbns>

Naravno, problem je to ove datoteke esto budu zastarjele (neke knjige


<r su rasprodane a nove postanu dostupne). Rijeit emo ga kasnije u
i* ovom poglavlju.

Dok se uitavaju ISBN brojevi trae se relevantne vrijednosti (naslov, izdava, poloaj)
na Amazon Web stranicama i spremaju se u tablicu baze podataka. Jednostavan pada-
jui popis se tada aurira kako bi se prikazao napredak. Kad su sve knjige unesene,
sustav postaje neaktivan dok ne protekne preostalo vrijeme izmeu sesija. Moete
zapoeti novu sesiju pritiskom na gumba Now. Korisniko suelje je namjerno napra-
vljeno to je mogue jednostavnije.
Primjer 16-2 sadri cijelu aplikaciju iza kojeg slijedi analiza.

Primjer 16-2. SalesRankDBWebServices


ftregion Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

#endregion

namespace AmazonVJebServiceClient
{
partial class AmazonklebServiceClient : Form
{
private int timeRemaining;
const int WaitTime = 900; // 15 minuta
private string connectionString;
private System.Data.SqlClient.SqlConnection connection;
private System.Data.SqlClient.SqlCommand command;

public AmazonWebServiceClient()
{ .
InitializeComponent();
}
private void AmazonWebServiceClient Load( object sender, EventArgs e )
{
// Niz za povezivanje s bazom Sales Rank Database
connectionString =
"server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";

392 I Programiranje C#
primjer 16-2. SalesRankDBWebServices (nastavak)
U Stvara objekt veze i inicijalizira s
// nizom za povezivanje,
connection =
new System.Data.SqlClient.SqlConnection( connectionString );

/ / Stvara SqlComman objekt i pridruuje vezu


command -
new System.Data.SqlClient.SqlCommand();

command.Connection = connection;
timeRemaining = i; // Kad se prvi put pokrene uzima informacije.
UpdateButton();

private void btnStart_Click( object sender, Eventftrgs e )


{
// Ukljuuje mjera vremena
updateTimer.Enabled = updateTimer.Enabled ? false : true;
UpdateButton();

private void btnNow_Click( object sender, EventArgs e )

timeRemaining = 2;
}

private void UpdateButton()


{
btnStart.Text = updateTimer.Enabled ? "Stop" : "Start";
}

private void updateTimer_Tick( object sender, EventArgs e )

if ( updateTimer.Enabled )
txtClock.Text = ( --timeRemaining ).ToString() + " seconds";
else
txtClock.Text = "Stopped";

if ( timeRemaining < l )
{
timeRemaining = WaitTime; // reset the clock

// Pravi skup podataka na temelju xml datoteke


DataSet BookData = new DataSet();
BookData.ReadXml( aspnet_isbn.xml" );

// Prolazi kroz datoteku i poziva GetInfoFromISBN za


// svaki pronaeni ISBN broj
foreach ( DataRou Book in BookData.Tables[o].Rows )
{

Poglavlje 16: Sastavljanje u cjelinu | 393


Primjer 16-2. SalesRankDBV/ebServices (nastavak)
string isbn = Book[o].ToStringO I
GetInfoFromISBN( isbn, "ASPNET" );

BookData = new DataSet();


BookData.ReadXml( "csharplsbn.xml" );
foreach ( DataRon Book in Bo ok Da ta . T ab le s[ o] . R ow s )

string isbn = Book[o] .ToStringO;


GetInfoFromISBN( isbn, "CSFIARP );

BookData = new DataSet();


BookData.ReadXml( "VBnetIsbn.xml" );
foreach ( DataRow Book in BookData.Tables[o].Rows )

* string isbn = Book[0 ].ToStringO;


GetInfoFromlSBN( isbn, VBMET" );

}
}
}
private void GetInfoFromISBN( string isbn, string technology )

if ( isbn.Length != 10 )
return;

AWSProductData productData = new AWSProductData();


ItemLookup lookup = nuli;
try

ItemLookupRequest req = new ItemLookupRequest();


req.IdType = ItemLookupRequestIdType.ASIN;
req.ltemld = new string[l];
req.ItemId[o] = isbn;
/ / req.Searchlndex = "Books";

lookup = new ItemLookupO;


lookup.AssociateTag = "libertyassociaOOA";
lookup.Subscriptionld = "0SD959SZV6KXV3BKE2R2 ;
lookup.Request = new ItemLookupRequest[l];
lookup.Request[o] = req;
}
catch ( System.Exception e )
{
lblStatus.Text = e.Message;
}
ItemLookupResponse response;
Items info;
Item[] items;
Item item;

394 | Programiranje C#
primjer 16 -2 . SalesRankDBWebServices (nastanak)
int salesRank = -l;
string author = string.Empty;
string pubDate = string.Empty;
string publisher = string.Empty;
string title = string.Empty;
string strURL = string.Empty;

response = productData.ItemLookup( lookup );


info = response.Items[0 ];
items = info.Item;
item = itemsjo];
salesRank = item.SalesRank == nuli
? - 1 : Convert.ToInt32(item.SalesRank);
author = FixOuotes( item.ItemAttributes.Author[o] );
pubDate = FixOuotes(item.ItemAttributes.PublicationDate);
publisher = FixOuotes(item.ItemAttributes.Publisher);
title = FixOuotes(item.ItemAttributes.Title);
strURL = item.DetailPageURL;

catch ( System.Exception ex)


{
lblStatus.Text - ex.Message;
}

// Aurira popis
string results = title + " by " + author + " +
publisher + ", " + pubDate + ". Rank: salesRank;
IbOutput.Items.Add( results );
lbOutput.Selectedlndex = IbOutput.Items.Count i;
// Aurira bazu podataka
string commandstring = @"Update Booklnfo set isbn = " +
isbn + , title = "' + title + ", publisher = 1,1 +
publisher + pubDate = '" + pubDate + rank = "
salesRank + ", link = + strURL + '", lastUpdate =
System.DateTime.Now + technology = '" +
technology + author = "' +
author + "' where isbn = '" +
isbn +

command.CommandText = commandstring;
try
{
// Ako nijedan red nije promjenjen, ovo je novi zapis
connection.Open();
int numRowsAffected = command.ExecuteNonOuery();
if ( numRowsAffected == o )

commandstring = @"Insert into Booklnfo values ('" +


isbn + , ' + title + + publisher +
pubDate + + FixOuotes( strURL ) + +

Poglavlje 16; Sastavljanje u cjelinu | 395


Primjer 16-2. SalesRankDBWebServices (nastavak)
salesRark + ", "' +
System.DateTiine.Now +
"' + technology + + author +

command.CommardText - commandString;
command.ExecuteNonOuery();
}
}
catch ( Exception ex )
{
lblStatus.Text = ex.Message;
lbOutput.Items.Add( "Unable to update database!" );
lbOutput.Selectedlndex = lbOutput.Items.Count - 1;
}
finally
{
connection.Close(); // isti
}
} // Zatvoreno za GetlnfoFromlSBN

private string FixOuotes( string s )


{
if ( s == nuli )
return string.Empty;
return s.Replace( ... );

)
} // Kraj klase
} // Kraj imenskog prostora

Program zatim deklarira niz znakova za povezivanje, zajedno sa SOLConnection i SOLCom-


mand objektima koji e biti inicijalizirani kad se obrazac uita:
private string connectionString;
private System.Data.SqlClient.SqlConnection connection;
private Systen,.Data.SqlClient.SqlCommand command;

Moete postaviti Load dogaaj pritiskom na obrazac i prebacivanjem s Properties na


Events. Dvaput pritisnite Load dogaaj i kostur metode za obradu dogaaja uitava-
nja je napravljen za vas. Unutar metode stvorit ete niz znakova za povezivanje (ovaj
primjer koristi pouzdanu vezu i moda ete morati upisati korisniko ime i zaporku,
ovisno o tome kako je baza podataka konfigurirana) i tada e objekti za povezivanje i
naredbeni objekti biti konfigurirani:
private void AmazonWebServiceClient_Load( object sender, EventArgs e )
{
connectionString =
"server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";
connection =
new System.Data.SqlClient.SqlConnection( connectionString );
command =
new System.Data.SqlClient.SqlCommand();

command.Connection = connection;

396 I Programiranje C#
;'arijabla timeRemaining je inicijalno postavljena na jednu sekundu, a gumbi su auri-
rani da postave tekst na gumbu Start:
timeRe main ing = i; // Kad se pokrene, uzima informacije.
UpdateButton();

vaki put kad mjera vremena otkuca poziva se u p d a t e T i m e r T i c k metoda. Ako mje-
ra vremena radi (korisnik nije pritisnio Stop) varijabla t i m e R e m a i n i n g se smanjuje za
jedan, a kad padne na nulu, vrijeme je za obradu:
if ( updateTimer.Enabled )
txtClock.Text = ( --timeRemaining ).ToString() + " seconds";
else
txtClock.Text = "Stopped";

if ( timeRemaining < i )
{
Prvi korak je da se mjera vremena postavi na vrijednost WaitTime (konstanta jednaka
|15 minuta) i da se obrade ,xml datoteke:
timeRemaining = WaitTime; // Resetiranje sata
p DataSet BookData = new DataSet();
i try
{
BookData.ReadXml( "aspnet_isbn.xml" );

fStvara se skup podataka u kojem svaki red predstavlja unos u XML datoteci. Kad su
gfnjige uitane, uzimate svaki ISBN i pozivate pomonu metodu GetInfoFromISBN i pro-
||ljeujete joj kao parametar ISBN i tehnologiju11pod kojom e broj biti spremljen u
gpazu podataka:
foreach ( DataRow Book in BookData.Tables[0].Rows )

string isbn = Book[o].ToString();


GetInfoFromISBN( isbn, "ASPNET" );

petlnfoFromlSBN je srce programa - to je dio u kojem kontaktirate Amazon Web Ser-


|lce uslugu.

arvi korak je da osigurate da duljina ISBN-a iznosi tono deset znakova (puna provjera
p i koristila regularan izraz kako bi se osiguralo da ISBN sadri devet cjelobrojnih
Irijednosti iza kojih dolazi ili cjelobrojna vrijednost ili slovo X i onda bi se izvela pro-
Ipera kontrolnog zbroja na ISBN-u (zadnja znamenka je kontrolna vrijednost), ali to
|ostavljeno itatelju za vjebu).

>flmazon.cs datoteka definira vei broj korisnih objekata. U primjeru emo koristiti
NSProductData, ItemLookup i ItemLookupRequest kao i Item objekte i kolekcije. Evo
koraka:

Poglavlje 16: Sastavljanje u cjelinu | 397


1. Deklarirajte novu instancu AVJSProductOata koji se ponaa kao posrednik prema
Amazon Web usluzi.
2 P o z o v i t e I t e m L o o k u p m e t o d u n a i ns tanci A W S P r o d u c t D a t a prosl j euju i k a o p a r a -
m e t a r i s p r a v n o inicijaliziranu i n s t a n c u I t e m Look up.

3. U z m i t e I t e m L o o k u p R e s p o n s e objekt.

4. Izdvojite polje Items i iz njega uzm ite prvi objekt (pom ak 0), a to je Items,
5. P o g l edaj te I t e m s v ojst vo t o g I t e m s o b j e k t a a to je polje I t e m s objeka ta.

6. U z m i t e prv i I t e m u po lju i iz n j e g a izv ucite s v e i n f o r m a c i j e o knjizi ko j e ste

traili.

D a bi o v o radilo, m o r a t e p r v o n apraviti i n s t a n c u I t e m L o o k u p R e q u e s t i n j e g o v o IDType


svojst vo postaviti n a e n u m e r i r a n i tip I t e m L o o k u p R e q u e s t I D T y p e . A S I N :

ItemLookupRequest req = new ItemLookupRequest(),


req.IdType = ItemLookupRequestIdType.ASIN;

Inicijalizitajte n j e g o v o I t e m l d polje d a p r i h v a a j e d a n ni z z n a k o v a i p ostavite vrijed-

nost tog niza n a ISBN k o j e g traite:

req.Itemld = new string[l];


req.ItemId[o] = isbn;

Zatim instancirajte I t e m L o o k u p o b j e k t i post a v i t e n j e g o v a sv o jstva A s s o c i a t e T a g i

SubscriptionID:
lookup.AssociateTag = "libertyassociaOOA ;
lookup.Subscripticnld = "Your ID Here ;

Inicijalizitajte njegovo Request svojstvo da bude polje od jednog objekta i postavite taj
objekt da bude ItemLookupRequest objekt kojeg ste napravili ranije:
lookup.Request = new ItemLookupRequest[l];
lookup.Request[0] = req;

Primjetite da koristimo idiom pozivanja metoda kao to .N ET pretpo-


stavlja, ali ono to se zapravo dogaa je da mi koristimo SOAP za raz-
71 mjenu poruka s Amazonom. Mi pitamo reci mi to zna o ovoj knjizi ,
' a Amazon uzvraa porukom evo podataka o knjizi .

S p r e m n i ste napra vit i zahtjev. N a p r a v i t e to u b l o k u t r y k a k o biste uhvatili izn imk e

koje se m o g u pojaviti k o d o b r a d e . P o n i t e p o z i v a n j e m I t e m L o o k u p m e t o d e :

response = productData.ItemLookup( lookup );

Respone s a d a n e bi t r e b a o biti nuli. M o e t e d o d a t i k o d z a


biste obradili p r a z a n o d g o v o r o d A m a z o n a (o vdje je izostavljen d a b. b & J
stavniji). I t e m svoj stvo v r a a I t e m s o b j e k t koji predsta vlj a polje I t e m objekata. U z e .,

prvi I t e m o b j e k t koji s a d r a v a i n f o r m a c i j e o knjizi k o j u ste trai i.

info = response.Items[o];
items = info.Item;
item = items[0];

398 I Programiranje C#
|Sad " l02616 P stavitl lokalne varijable da prihvate vrijednosti koje ste uzeli. FixOuotes
j metoda je pomona metoda za pretvaranje jednostrukih navodnika u primljenom nizu
'-znakova tako da ne stvaraju probleme u bazi podataka:
salesRank = item.SalesRank == nuli ? -i : Convert. To I nt32( ite m,Sale sRank);
author - FixOuotes( item.ItemAttributes.Author[o] );
pubDate = FixOuotes(item.ItemAttributes.PublicationDate);
publisher = FixQuotes(item.ItemAttribirtes.Publisher);
title = FixOuotes(item.ItemAttributes.Title);
strURL = item.DetailPageURL;

|Kad imate ove informacije spremni ste aurirati padajui popis i, to je vanije auri-
4 rati bazu podataka.

\Prilikom auriranja baze podataka prvo ete probati Update iskaz. Ako je broj zahvae-
n ih redova jednak nula, red jo ne postoji u bazi podataka pa ete unijeti vrijednosti.

Program bi bio sigurniji kada biste koristi li parametrizirane upite. Upiti


u ovom primjeru su obini kako bi bio to jednostavniji.

K a d je ovo uinjeno moete prijei na sljedei ISBN.

p rik a z iv a n je re z u lta ta
;Ov(!j ptu napraviti emo nove ASP.NET Web stranice AmazonSalesRanks za prikaz infor-
macija koje vraa Amazon. Povucite tri GridView objekta na obrazac, ali ih nemojte
vezivat! s podacima. Uiniti emo to runo. Primjer 16-3 prikazuje kompletnu ,aspx
stranicu, ukljuujui poruku ispisanu iznad tablice, naslove tablica, oznaku zadnjeg
p n ra n ja tpolje za tekst koje se koristi za odluivanje koliko redova e biti prikazano

I;
frim jer 1 6 -3 . P r ik a z iv a n je r ez u lt a t a

Page Language="C#" CompileWith="Default.aspx.cs'' ClassNaijie=*Default_aspx" %>


i
|iCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtmlll/DTD/xhtmlll.dtd">

|fml xmlns="http://www.w 3 .org/ 1 9 9 9 /xhtml" >


|j6dd runat="server''>
,. <title>Sales Ranks</title>
/head>
body>
(form id="formi" runat="server">
| <div>
The data found here is from the Amazon Web Service and
is stored in a local database. The data is updated every 15

minutes. This is a work in progress.<br />

Poglavlje 16: Sastavljanje u cjelinu | 399


Primjer 16-3. Prikazivanje rezultata (nastavak)
&nbsp;<br />
<b>ASP Titles</b>
<asp:GridView ID="gvASP" Runat="server"
OnRowDataBound="RowDataBound"
AutoGenerateColumns="false"
HeaderStyle-BackColor="PapayaWhip"
BorderColor="#000099"
AlternatingRowStyle-BackColor="LightGrey"
HeaderStyle-Font-Bold=true
Width="900">
<Columns>
<asp:TemplateField HeaderStyle-Width = 10 >
<HeaderTemplate>
Position
</HeaderTemplate>
<ItemTemplate>
<asp:Label Runat="server" ID="Labell">
<%# rowNumber %></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderTemplate>
Title
</HeaderTemplate>
<ItemTemplate>
<a href="h t t p ://ww w.am azon.com/exec/obidos/ASIN/ <%#
val("isbn")%>/ target="_blank"><%# Eval("title") %></3>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundFleld HeaderText="Author"
ReadOnly=true" DataField="Author" />
<asp:BoundField HeaderText="Publisher"
ReadOnly="true" DataField="Publisher" />
<asp:BoundField HeaderText="Publish Date"
ReadOnly="true" DataField="pubDate" />
<asp:BoundField HeaderText="Rank"
ReadOnly="true" DataField="Rank"
DataFormatString="{o:NO}"
ItemStyle-HorizontalAlign="right" />
</Columns>
</asp: Gri Wew >
<br />
<b>ASP Titles</b>
<asp:GridView ID="gvCSharp" Runat="server
OnRowDataBound="RowDataBound"
AutoGenerateColumns="-false"
HeaderStyle-BackColor="PapayaWhip"
BorderColor="#000099"
AlternatingRowStyle-BackColor="LightGrey"
HeaderStyle-Font-Bold=true
Width="900>
<Columns>
<asp:TemplateField HeaderStyle-Width ="10'>

400 | Programiranje f#
primjer 16-3. Prikazivanje rezultata (nastavak)
<HeaderTemplate>
Position
</HeaderTemplate>
<ItemTemplate>
<asp:Label Runat="server" ID=Label 2 ">
<%# rowNumber %></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<FleaderTemplate>
Title
</HeaderTemplate>
<ItemTemplate>
<a href = http :// www.amazon.com/exec/obidos/ASIN/<&#
Eval("isbn")%>/" target="_blank"><%# Eval("title") %></a>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText=Author"
ReadOnly="true" DataField=Author" />
<asp:BoundField HeaderText=''Publisher"
ReadOnly=true" DataField="Publisher" />
<asp:BoundField FleaderText=''Publish Date"
ReadOnly="true DataField="pubDate />
<asp:BoundField HeaderText="Rank"
ReadOnly="true" DataField="Rank
DataFormatString="{o:NO}"
ItemStyle-HorizontalAlign="right" h
</Columns>
</asp:GridView>
<br />
<b>VB Titles</b>
<asp:Grid\/iew ID="gvVBNet' Runat="server"
OnRowDataBound="RowDataBound"
AutoGenerateColumns="'false"
1HeaderStyle-BackColor="PapayaWhip"
BorderColor="#000099"
AlternatingRowStyle -BackColor="LightGrey"
HeaderStyle-Font-Bold=true
Width="900">
<Columns>
<asp:TemplateField HeaderStyle-Width ="io">
<FleaderTemplate>
Position
</HeaderTeinplate>
<ItemTemplate>
<asp:Label Runat="server" ID="Label 3 ">
<%# rowNumber fo</asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<FleaderTemplate>
Title
</HeaderTemplate>

Poglavlje 16: Sastavljanje u cjelinu I 401


Primjer 16-3. Prikazivanje rezultata (nastavak) ii >svoj tekst prikazati broj stupca. Mehanizam za dodjeljivanje broja stupca je obra'
i kasnije u ovom poglavlju.
<ItemTemplate>
<a href="http://www.amazon.com/exec/obidos/ASIN/<%# <asp:TemplateField HeaderStyle-Width ="io">
Eval("isbn")%>/" target="_blank"x%# Eval("title") 35></a> <HeaderTemplate>
</ItemTemplate> Position
</asp:TemplateField> </HeaderTemplate>
<asp:BoundField HeaderText="Author" <ItemTemplate>
ReadOnly="true" DataField="Author" /> <asp:Label Runat="server" ID="Labell"><%# rowNumber %></asp:Label>
<asp:BoundField HeaderText="Publisher" </ItemTemplate>
ReadOnly="true" DataField="Publisher" />
<asp:BoundField HeaderText="Publish Date" Z a p o t p u n o objanjenje polja predloaka idrugih elemenata koritenih
ReadOnly="true" DataField="pubDate" />
na ovoj stranici pogledajte knjigu P r o g ra m ir a n je A S P . N E T .
<asp:BoundField HeaderText="Rank"
ReadOnly="true" DataFleld^Rank"
DataFormatString="{0:NO}"
ItemStyle-HorizontalAlign=right" /> Drugi stupac je takoer predloak polja, ovaj put sa zaglavljem T itle stupca. Sam
</Columns> naslov jc prikazan zadavanjem vrijednosti naslovnog stupca u tekuem redu skupa
</asp:GridView>

<asp:Label ID="lblLastUpdate" Runat="server"


^podataka s kojim je GridView objekt povezan te okruujui taj naslov s hipervezom
Text="Last Update''x/asp:Label> prema odgovarajuoj stranici na Amazon.com. To naslov pretvara u hipervezu koju
<br /> korisnik moe pritisnuti.
Number to show in grid:
<asp:TemplateField>
<asp:TextBox ID="txtShowRecords" Runat="server"
<HeaderTemplate>
Width="4 8 px" Heigbt="22px"
Title
AutoPostBack="True''x/asp:TextBox>
</HeaderTemplate>
</div>
i. <ItemTemplate>
</form>
<a bref="http://www.amazon.com/exec/obidos/ASIN/<8># Eval("isbn")%>/" tar-
</body> get="_blank"><%# Eval("title) %></a>
</html>
</ItemTemplate>
Kljuni aspekt HTML-a je izrada tri objekta GridView. Sva tri rade jednako pa emo </asp:TemplateField>

se usredotoiti na prvi: Pogledajmo ovo odvojeno. Prvi element je polje predloka:


<asp:GridView ID="gvASP" Runat="server" <asp:TemplateField>
OnRowDataBound="RowDataBound </asp:TemplateField>
AutoGenerateColumns="false"
HeaderStyle-BackColor=''PapayaWhip" i Unutar polja predloka se nalaze dva elementa predloka: zaglavlje i stavka. Zaglavlje
BorderColor="#000099" r Je prilino jednostavno. Sadri jednostavan tekst (iako bi mogao biti i bilo koji HTML
AlternatingRowStyle-BackColor="LightGrey" kod):
HeaderStyle-Font-Bold=true
Width="900"> <headertemplate>
Title
GridView je nazvan gvASP. Neka od svojstava su postavljena, a najvanije od njih je </headerteitiplate>
metoda za obradu dogaaja OnRowDataBound te Boolean svojstvo AutoGenerateColumns
Predloak stavke je neto zahtjevniji:
koje je postavljeno na vrijednost False. To vam omoguava da preuzmete izravnu kon-
<itemtemplate>
trolu nad stupcima preko elementa columns:
<a href="http://www.amazon.com/exec/obidos/ASIN/<!6# Eval("isbn")%>/"
<columns> target="_blank"x%# Eval("title") %></a>
</itemtemplate>

</columns>
Prvi stupac unutar elementa columns je element predloka stupca. On omoguava da f a*
Naslov (i ostale n i z o v e znakova) trebate provjeriti da vidite ispisuju li
ubacite kontrole u stupac. U prvoj instanci dodat ete Headertemplate (koji se koristi se ispravno.
za izradu zaglavlja stupca) sa tekstom Position i asp:label kontrolom. Ta oznaka ce

402 | Programiranje C# Poglavlje 16: Sastavljanje u cjelinu | 403


Promotriti emo ga izvana prema unutra, Prva stvar koju treba primjetiti je poetak 14
normalne hiperveze:
<a href"ht tp ://www.amazon.com/exec/obidos/ASIN/

Meutim, hipervezi je dodan rezultat procjene vrijednosti ISBN-a iz povezanih


podataka:
<%# Eval(''isbn") %>/"

Oznaka hiperveze ima atribut:


taiget="_blank"

Ovaj atribut uzrokuje da se hiperveza otvori u novoj instanci preglednika. Tijelo hiper-
veze (prikazani tekst hiperveze) je takoer evaluirana vrijednost:
Eval("title")

Ako povezani podaci sadre naslov Programming Visual Basic .NET , Second Edition
i ISBN 0596004389 , ova stavka emitira sljedei HTML:
<a href=http://www.amazon.com/exec/obidos/ASIN/0596004389/
targe1:="_ blank">Programming Visual Basic .NET 2 nd Edition </a>

Prva dva stupca su sloenija. Prvi zato stoje potrebno malo truda da se napravi rowNum-
ber (pogledajte sljedei kod), a drugi zato to trebamo umotati povezanu vrijednost
(ISBN i naslov) u hipervezu. Sljedea etiri stupca su jednostavnija zato to su samo
povezani s podacima.

Prvi povezani stupac u zaglavlju ima tekst Author, oznaen je kao read0nly i povezan
je sa stupcem za autore u redu DataSet tablice s kojom je ovaj GridView povezan:
<asp:BoundColumn HeaderText=Author"
ReadOnly="true"
DataField="author"/>

Primjetite da je ovo samozatvarajui element, kao i sljedea tri povezana stupca:


<asp:BoundColumn HeaderText="Publisher"
ReadOnly="true"
DataField="publisher7 >
<asp:BoundColumn HeaderText="Publish Date"
ReadOnly="true"
DataField="pubdate"/>
<asp:BoundColumn HeaderText="Rank"
ReadOnly=true"
DataField="Rank" DataFormatString=''{0 :N0 }"
ItemStyle-HorizontalAlign=''Right"/>

Implementiranje tablice
Kompletan izvorni kod za datoteku s kodom je prikazan u primjeru 16-4, nakon ega
slijedi analiza.

404 | Programiranje C#
primjer 16-4. D atoteka s kodom za SalesDisplay
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sy stem.W e b .U l .WebControls.WebParts;
using S yste m .W e b .U I .HtmlControls;

public partial class Default_aspx

protected int showRecords;


protected int totalASP;
protected int totalCSharp;
protected int rowNumber = 0 ; r

protected override void 0nLoad( EventArgs e )


{
// Inicijalizira broj zapisa tako da bude prikazano do 7
if ( U s P o stB ack )
{
showRecords = 7;
}
// Ako je povrat informacija, uzima broj
// iz polja za tekst
else
{
showRecords = Convert.ToInt32( txtShowRecords.Text );
}

// Povezivanje s bazom podataka


string connectionString =
"server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";

// Odabir zapisa za prikazivanje


string commandstring =
@"Select top " + showRecords +
" * from Booklnfo where technology = 'ASPNET' order by rank";
SqlDataAdapter dataAdapter =
new SqlDataAdapter( commandstring, connectionString );
DataSet dataSet = new DataSet();
dataAdapter.Fill( dataSet, "aspBooklnfo" ); // prva tablica

commandstring = @''Select top " + showRecords + " *


from Booklnfo where technology = 'CSHARP' order by rank";
dataAdapter = new SqlDataAdapter( commandstring, connectionString );
dataAdapter.Fill( dataSet, "csBooklnfo" ); // Druga tablica

commandstring = @"Select top " + showRecords +


* from Booklnfo where technology = 'VBNET' order by rank";
dataAdapter = new SqlDataAdapter( commandstring,

Poglavlje 16: Sastavljanje u cjelinu | 405


P rim je r 16-4. D ato teka s kodom za S alesD isplay (nastavak)
connectionString );
dataAdapter.Fill( dataSet, "vbBooklnfo" ); // Trea tablica

// Stvara pregled podataka i povezuje s tablicom


DataVieu aspDataView =
dataSet.Tablesfo].DefaultView;
gvASP.DataSource = aspDataView;
gvASP.DataBird();

rowNumber = 0;

DataVieu csDataView = dataSet.Tables[l].DefaultView;


gvCSharp.DataSource = csDataView;
gvCSharp.DataBird();

rowNumber = 0 ;

DataView vbDataView = dataSet.Tables[2].DefaultView;


this.gvVBNet.DataSource = vbDataView;
gvVBNet.DataBind();

// txtShowRecords.DataBind();
lblLastUpdate.Text = Last updated: " +
dataSet.Tables [2]. Rows [0] [" lastUpdate" ]. T o S t r i n g O ;

}
void RowDataBound( object sender, GridViewRowEventArgs e )
{
this.rowNumber++;
}
}
Program zapoinje deklariranjem veeg broja lokalnih varijabli, od kojih je najvanija
r o w N u m b e r koja je inicijalizirana na 0:

protected int rowNumber = 0;

Varijabla lanica showRecords se koristi za voenje evidencije o tome koliko zapisa


treba prikazati, a kad je stranica prvi put prikazana showRecords je postavljena na vri-
jednost sedam (sigurna i razumna podrazumijevana vrijednost). Prilikom uzastopnih
slanja stranice posluitelju, ta varijabla je postavljena na vrijednost koja se nalazi u
polju za tekst:
private void Page_Load(object sender, System.EventArgs e)
{
if (! IsPostBack )
{
showRecords = 7;
}
else
{
showRecords = Convert.ToInt32(txtShowRecords.Text);
}

406 | Programiranje C#
Dalje u metodi za obradu dogaaja uitavanja stranice, uspostavljena je veza s bazom
podataka i baza podataka je pretraena po tehnologiji11 (to jest, ASP.NET ili C # ili
VB.NET):
string connectionString =
"server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";

string commandString =
@"Select top " + shouRecords +
" * from Booklnfo where technology = 'A5PNET' order by rank";
SqlDataAdapter dataAdapter =
new SqlDataAdapter(commandString, connectionString);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet,"aspBookInfo"); // Prva tablica

Isto je uinjeno za sve ostale upite. Kad su napravljene tablice za skup podataka, napra-
vljen je pogled na podatke za prvu tablicu. To je pogled na ASP.NET rezultate:
DataView aspDataView =
dataSet.Tables[0],DefaultView;

S ovim pogledom na podatke je povezan objekt GridView:


gvASP.DataSource = aspDataView;
gvASP.DataBind();

Kad je to uinjeno, varijabla lanica rowNumber je ponovno postavljena na nulu (uskoro


emo objasniti kako se ona mijenja) i sljedei pogled na podatke je napravljen i pove-
zan sa svojim GridView objektom:
rouNumber = 0;
DataVieu csDataView = dataSet.Tables[l].DefaultView;
gvCSharp.DataSource = csDataView;
gvCSharp.DataBind();

Ovo se ponavlja jo jednom za treu tablicu:


rowNumber = 0;
DataVieu vbDataView = dataSet.Tables[2].DefaultView;
this.gvVBNet.DataSource = vbDataView;
gvVBNet.DataBind();

Konano, natpis IbILastUpdate je postavljen iz polja lastUpdate u tablici:


lblLastUpdate.Text = "Last updated: " + dataSet.Tables[2].Rows[0]["lastUpdate"].
ToStri ngO ;

Obrada RowDataBound dogaaja


Vjerojatno se sjeate da ste stvaranjem GridView objekta povezali RowDataBound dogaaj
s metodom RowDataBound. Zapravo, napravili ste to za sva tri GridView objekta. Kad god
je element povezan s tablicom, obraen je u ovoj metodi. Sve to ta metoda radi je da
poveava za jedan broja redova rowNumber:
public void Item_Bound(Object serder, GridViewItemEventArgs e)
{
rowNumber++;
}

Poglavlje 16: Sastavljanje u cjelinu | 407


Uinak je da svaki put kad je neki element povezan s G r id V ie w objektom, rowNumber
poveava za jedan i zatim prikazuje u prvom stupcu napravljenom po predloku da' S
relativan poloaj unutar G r id V ie w objekta.

Pretraivanje po kategorijama
Pozivanje metode Web usluge za svaki ISBN koji elite provjeriti ni izdaleka nije naju
inkovitiji mogui pristup. Ne samo da to ukljuuje vie slanja i primanja podataka do
Amazona, ve je i vjerojatno da e ISBN brojevi koje stavite na popis zastariti gotovo
istovremeno kad novi konkurenti iziu na trite, a drugi budu rasprodani.

Na sreu, Amazon moe pretraivati po kategorijama. U sljedeem (i zadnjem) ponavlja-


nju ovog programa, oslobodit ete se X M L datoteka s ISBN brojevima i umjesto toga
jednostavno pitati Amazon za sve knjige u ASP.NET, C# i VB.N ET kategorijama.
Da ovaj primjer pojednostavnimo, iz svake kategorije emo uzeti samo deset knjiga
Amazon Web Services je vrlo fleksibilna usluga, ali za ovaj primjer ete koristiti samo
minimum mogunosti.

Napravite kopiju projekta SalesRankDBV/ebServices i nazovite ga AmazonWebServi-


ceClientSearching. Modificirat ete ga kao sto je prikazano u primjeru 16-5.

P rim je r 16-5. SalesRankD BW ebServices02


#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

ftendregion

namespace AmazonWebServiceClient
{
partial class AmazonWebServiceClient : Form
{
private int timeRemaining;
const int WaitTime = 900; // 15 minuta
private string connectionString;
private System.Data.SqlClient.SqlConnection connection;
private System.Data.SqlClient.SqlCommand command;

public AmazonWebServiceClient()
{
InitializeComponent();
}

private void AmazonWebServiceClient_Load( object sender,


EventArgs e )
{

408 | Programiranje C#
primjer 26-5. SalesRankDBWebServices02 (nastavak)
// Niz za povezivanje s bazom podataka Sales Rank Database
connectionString =
"server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";

// Stvara objekt veze i inicijalizira ga s


// nizom za povezivanje,
connection =
nevi System.Data.SqlClient.SqlConnection( connectionString );

// stvara SqlCommand objekt i dodjeljuje vezu


command =
new System.Data.SqlClient.SqlCommand();

command.Connection = connection;
timeRemaining = l; I I Kad se pokrene uzima informacije
UpdateButton();

}
private void btnStart_Click( object sender, EventArgs e )
{
// Ukljuuje mjera vremena
updateTimer.Enabled = updateTimer.Enabled ? false : true;
UpdateButton();

}
private void btnNow_Click( object sender, EventArgs e )
{
timeRemaining = 2;
}
private void UpdateButton()
{
btnStart.Text = updateTimer.Enabled ? "Stop" : "Start";
}
private void updateTimer_Tick( object sender, EventArgs e )
{
if ( updateTimer.Enabled )
txtClock.Text = ( --timeRemaining ).ToString() + " seconds";
else
txtClock.Text = "Stopped";

if ( timeRemaining < 1 )
{
timeRemaining = klaitTime; I I Resetira sat
timeRemaining = klaitTime;
GetInfoFromAmazon( "ASP.NET", "ASPNET" );
GetXnfoFromAmazon( "C#", "CSHARP" );
GetInfoFromAmazon( "VB.NET, "VBNET" );
}
}

Poglavlje 16: Sastavljanje u cjelinu | 409


P r im je r 1 6 -5 . S a l e s R a n k D B W e h S e r v ic e s 0 2 ( n a s t a v a k )

private void GetInfoFromAmazon( string keyword, string technology )


{
AWSProductData productData = new AWSProductData();

ItemSearch srch = nuli;


try

ItemSearchRequest req = new ItemSearchRequest();


req.Keywords = keyword;
req.SearchIndex = "Books";

srch = new ItemSearch();


srch.AssociateTag = ,,libertyassociaOOA";
srch.Subscriptionld = 11 Your Subscription ID ;
srch.Request = new ItemSearchRequest[l];
srch.Request[0] = req;

}
catch ( System.Exception e )
{
lblStatus.Text = e.Hessage;
}
ItemSearchResponse response;

int salesRank = -i;


string isbn = string.Empty;
string author = string.Empty;
string pubDate = string.Empty;
string publisher = string.Empty;
string title = string.Empty;
string strURL = string.Empty;

Items[] responseltems = nuli;


try
{
// Uzima ItemSearchResponse
response = productData.ItemSearch( srch );

II Items vraa Items


responseltems = response.Items;

foreach ( Items items in responseltems )

II Item svojstvo od Items je polje Item objekata


Item[] array0fltem = items.Item;

foreach ( Item item in array0fltem )

isbn = FixOuotes( item.ItemAttributes.ISBN );


salesRank = item.SalesRank ==
nuli ? -l : Convert.ToInt32( item.SalesRank );
author = F i x O u o t e s ( item.ItemAttributes.AuthorjO] );
pubDate = FixOuotes(

410 [ Programiranje C#
i primjer 16-5. SalesRankDB\VebServices02 (nastavak)

item.ItemAttributes.PublicationDate);
publisher = FixOuotes( item.ItemAttributes.Publisher );
title = FixOuotes( item.ItemAttributes.Title );
strURL = item.DetailPageURL;
// Aurira polje za tekst
string results = title + " by " + author + ": " +
publisher + ", " + pubOate + . Rank: " + salesRank;
lbOutput.Items.Add( results );
lbOutput.Selectedlndex = lbOutput.Items.Count - l;

H Aurira bazu podataka


string commandString = "Update Booklnfo set isbn = +
isbn + ', title = + title + publisher = '" +
publisher + , pubOate = +
pubDate + "', rank = " +
salesRank + ", link = '" +
strURL + , lastUpdate = "' +
System.DateTime.Now + technology = ," +
technology + author = '" +
author + "' where isbn = '" +
isbn +

command.CommandText = commandString;
try
{
II Ako nijedan red nije promijenjen, ovo je novi zapis
connection.Open();
int numRowsAffected = comand.CxecuteNonOuery();
if ( numRowsAffected -= 0 )
{
commandString = @"Insert into Booklnfo values ('" +
isbn + " , " + title + " , + publisher +
> +
pubDate + "', '" + FixOuotes( strURL ) + "', "
+ salesRank + ", +
System.DateTime.Now +
'" + technology + ", '" + author +

command.CommandText = commandString;
command.ExecuteNonOuery();

}
catch ( Exception ex )
{
lblStatus.Text = ex.Hessage;
lbOutput.Items.Add( "Unable to update database! );
lbOutput.Selectedlndex = lbOutput.Items.Count - l;

finally
{
connection.Close(); // isti
}
Application.DoEvents(); // Aurira korisniko suelje

Poglavlje 16: Sastavljanje u cjelinu | 411


P rim je r 16-5. SalesRankD BW eb Services02 (nastavak)

}
catch ( System.Exception ex)
{
lblStatus.Text = ex.Message;
}

} // Zatvara GetlnfoFromAmazon

private string FixOuotes( string s )


{
if ( s == nuli )
return string.Empty;
return s.Replace( );

}
} // Kraj klase
} // Kraj imenskog prostora

U ovoj inaici kompletan kod za rad s X M L datotekama je uklonjen. Metoda Getlnfo-


FromlSBN je zamijenjena sa GetlnfoFromAinazon. Umjesto da napravite ItemLookupRequest
objekt, pravite KeywordRequest objekt:
private void GetlnfoFromAmazonj string keyword, string technology )
{

AWSProductData productData = new AWSProductData();

ItemSearch srch = nuli;


try
{
ItemSearchRequest req = new ItemSearchRequest();
req.Keywords = keyword;
req.SearchIndex = "Books ;

srch = new ItenrSearch();


srch.AssociateTag = "libertyassociaOOA";
srch.Subscriptionld = "Your Subscription ID";
srch.Request = new ItemSearchRequest[l];
srch.Request[Oj = req;

}
Primjetite da je keyword svojstvu dodijeljen proslijeeni keyword parametar (to jest, C#,
A S P . N E T ili V B . N E T ) . Svojstvo Searchlndex ograniava pretraivanje na knjige (radije
nego, na primjer, CD-ove).
Kad je napravljen ItemSearchRequest objekt ugradite ga u ItemSearch objekt koji sadri
AssociateTag i SubscriptionID.

Ono to dobijete natrag je ItemSearchResponse objekt. Svojstvo Items ovog objekta je


polje Item objekata. Svaki Item objekt ima Item svojstvo koje je, to ne iznenauje,
polje Item objekata. U ovim Item objektima nai ete informacije o odgovarajuim
knjigama.

412 | Programiranje C#
__________ DIO III
CLR i .NET kostur
POGLAVLJE 17

Sklopovi i rad s inaicama

iOsnovna jedinica .N ET instaliranja je sklop (engl. assembly). Sklop je kolekcija dato-


5 teka koja izgleda kao jedna DLL ili izvrna datoteka (EXE). Kao to je ranije reeno,
;DL1. ovi su kolekcije klasa i metoda koje su povezane s programom i izvode se samo
; kad je potrebno.
i Sklopovi su .NET jedinice ponovne upotrebe, praenja inaica, sigurnosti i razvoja.
Ovo poglavlje detaljno razmatra sklopove, ukljuujui arhitekturu i sadraj sklopova,
privatne sklopove i zajednike sklopove.
; Osim koda objekata za aplikaciju, sklopovi sadre resurse kao to su .gif datoteke,
'. definicije tipova za svaku klasu koju definirate kao i druge metapodatke o kodu i
|podacima.

:PE datoteke
uSklopovi su na disku Portable Executable (PE) datoteke. PE datoteke nisu novost.
i Format .N ET PE datoteke je isti kao i obinih Windows PE datoteka. PE datoteke se
^implementiraju kao DLL-ovi ili EXE datoteke.
Fiziki, sklopovi se sastoje od jednog ili vie modula (engl. module). Moduli su sastavni
Idijelovi sklopova. Sami za sebe, moduli se ne mogu izvoditi. Moraju biti ukljueni u
l.sklopove da bi bili korisni.
Instalirat ete i iznova upotrijebiti sav sadraj sklopa kao jedinicu. Sklopovi se uita-
|vaju na zahtjev i nee biti uitani ako nisu potrebni.

Metapodaci
|Metapodaci (engl. metadata) su podaci pohranjeni u sklopu koji opisuju tipove i
jpnetode sklopa i pruaju druge korisne informacije o sklopu. Za sklopove se esto
|kae da sami sebe opisuju jer metapodaci potpuno opisuju sadraj svakog modula.
iMetapodaci su deteljno obraeni u osamnaestom poglavlju.

415
Sigurnosna granica
Sklopovi ine sigurnosne granice kao i granice tipa. To jest, sklop je doseg za tipove
koje sadrava, a definicije tipova ne mogu prelaziti izmeu sklopova. Naravno, moete
se pozivati na tipove izvan granica sklopa navoenjem reference traenog sklopa u raz-
vojnom okoliu ili naredbenom redu prilikom prevoenja. Ono to ne moete imati je
definicija tipa koja vai u dva sklopa.
Interni modifikator pristupa ograniava pristup (metodi, na primjer) na trenutni
sklop.

Manifesti
U okviru svojih metapodataka svaki sklop sadri manifest (engl. manifest). On opisuje
to se nalazi u sklopu: podaci za identificiranje (ime, inaica i tako dalje), popis tipova
i resursa u sklopu, popis modula, plan povezivanja javnih tipova s kodom za implemen-
taciju i popis sklopova na koje se poziva ovaj sklop.
ak i najjednostavniji program ima manifest. Moete ga ispitati koritenjem ILDasm
alata koji je dio razvojnog okolia. Kad otvorite manifest s ILDasm alatom, EXE pro-
gram iz primjera 12-3 izgleda kao na slici 17-1.
1
f EventsWithDelegates.exe - R DASM IB u Jg fe l

B V 1____________
MANI FEST
( M P EventsWithDelegates
9 EventsW ithDelegates.Propertles
EventsW ithDelegates.Clock
EventsW fthDelegates.DisplayClock.
EventsW ithDelegates.LogCurrentTime
EventsVflthDelegates.Test
M EventsVVithDelegates.TimelnfoEventArgs

1:
l 1
M

Slika 17-1. I L D a s m p r i k a z p r i m j e r a 12-3

Manifest se nalazi u drugom redu. Ako ga dvaput pritisnete otvara se Manifest prozor
kao na slici 17-2.
Ova datoteka slui kao plan sadraja sklopa. U prvom redu moete vidjeti referenc i
prema sklopu mscorlib na koji se poziva ova, ali i svaka druga .N ET aplikacija, msco
l ib je sklop jezgrene biblioteke za .N ET i dostupan je na svakoj .N ET platformi.

416 | Programiranje C#
Rhd Rnd
RndNert.
// Metaata uersion: u 2 . 0.40607
.assenbly xt rn nscorlib
<
.publickeyt 0ken * (07 70 SC 56 19 34 EQ 89 )
.uer 2: 0:3600:0
>
.assenbly extern Systen
<
,pUt)lickeytOken * (07 70 SC 56 19 34 E O 89 )
.uer 2 : 0: 3600 :0
>assemt>ly EuentsHithOelegateS
{
.custon instance uoid [nscorlib]Systen.Reflection. nssenblyConpanyOttribu:'
.custon instance uoid [nscorlib]Syste.Reflection. OssenblyTradenarkfittri;
.custon instance uoid [nscorlib)Systen.Reflection. OssenblyCopyriyhtOttri

.custon instance uoid [nscorlib]Systen.RefLection.RssenblyProductflttribu;

.custon instance uoid [nscorlib]SysteA.Reflection. OssenblyDescriptionfttt-


.custon instance uoid [nscorlib]Systen.Reflection. RssenblyConfigurationn
.custon instance uoid (nscorlib]Systen.Reflection. RssenblyTitleOttribute

// The follouing custon attribute is added autonatically, do not unc


// .custon instance uoid [nscorlib)Systen.Diagnostics.ebuggableRttribu

.custon instance uoid [nscQrllb]Systen.Runtine.ConpilerSeruices.CoApilat


.hash algorithn 0x00008004
.uer 1: 0:1676:14337 ,
<t~-^ >: ' I . . . . >f

S lika 1 7 -2 . M a n i fe s t p r o z o r

Sljedei red sklopa je referenca na sklop iz primjera 12-3. Moete takoer vidjeti da
se ovaj sklop sastoji od samo jednog modula. Zasad moete zanemariti ostatak meta-
podataka.

Sklopovi s vie modula


Sklopovi mogu sadravati vie od jednog modula, ali Visual Studio 2005 to ne podr-
ava. Sklop s jednim modulom ima samo jednu datoteku koja moe biti EXE ili DLL
datoteka. Taj modul sadri sve tipove i implementacije za aplikaciju. Manifest sklopa
jeugraen unutar ovog modula.
Svaki modul ima svoj manifest koji je odvojen od manifesta sklopa. Manifest modula
'sadrava popis sklopova koje taj modul referencira. Osim toga, ako modul deklarira
neke tipove, oni su upisani u manifest zajedno s kodom za implementiranje modula.
Modul takoer moe sadravati resurse koji su mu potrebni.
Sklop s vie modula sastoji se od vie datoteka (nijedna ili vie EXE datoteka i nijedna
ili vie DLL datoteka, iako morate imati barem jednu EXE ili DLL datoteku). Mani-
fest sklopa se u ovom sluaju moe nalaziti u zasebnoj datoteci ili moe biti ugraen u
jednom od modula. Kad je sklop referenciran, prilikom izvoenja se uitava datoteka
(koja sadri manifest i zahtijevani moduli po potrebi.

Poglavlje 17: Sklopovi i rad s inaicama | 417


Izrada sklopa svie modula
Da bismo prikazali upotrebu sklopova s vie modula, u sljedeem primjeru je napra-
vljen par vrlo jednostavnih modula koje moete smjestiti u jedan sklop. Prvi modul
je Fraction klasa. Ona e vam omoguiti da radite s razlomcima. To je prikazano u
primjeru 17-1.

P r im j e r 1 7-1. K l a s a F r a c t io n

#region Using direetives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ProgCS
{
public class Fraction
{
private int numerator;
private int denominator;

public Fraction( int numerator, int denominator )


{
this.numerator = numerator;
this.denominator = denominator;
}
public Fraction Add( Fraction rhs )
{
if ( rhs.denominator != this.denominator )
{
return new Fraction(
rhs.denominator * numerator +
rhs.numerator * denominator,
denominator * rhs.denominator);
}
return new Fraction(
this.numerator + rhs.numerator,
this.denominator );
}
public override string T o S t r i n g O
{
return numerator + "/" + denominator;
}
}
}

418 I Programiranje C#
primjetite da je Fraction klasa u ProgCS imenskom prostoru. Puno ime klase je ProgCS.
Fraction.

Klasa Fraction uzima dvije vrijednosti u svoj konstruktor: numerator i denominator.


Takoer postoji Add() metoda koja uzima drugi razlomak i vraa zbroj, pretpostavlja-
jui da razlomci imaju zajedniki nazivnik. Ova klasa je namjerno napravljena jedno-
stavno, ali e prikazati funkcionalnost potrebnu za ovaj primjer.
Pruga klasa je MyCalc za robustan kalkulator. To je prikazano u primjeru 17-2.

P r im jer 1 7 -2 . K a l k u l a t o r

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ProgCS
{
public class MyCalc
{
public int Add( int vali, int val 2 )
{
return vali + val2;
}
public int Mult( int vall, int val2 )
{
return vali * val 2 ;
}
}
}
Jo jednom, MyCalc je dosta ogoljena klasa kako bi pojednostavnili primjer. Primjetite
da se i MyCalc takoer nalazi u ProgCS imenskom prostoru.
To je dovoljno za izradu sklopa. Upotrijebite Assemblylnfo.cs datoteku za dodavanje
metapodataka u sklop. Upotreba metapodataka je obraena u poglavlju 18.

M o e t e napisati vlastitu A s s e m b ly In fo .c s datoteku, ali najjednostavniji


pristup je d a prepustite Visual Studiju d a to uini za vas.

Visual Studio izrauje samo sklopove s jednim modulom.


Moete napraviti resurs s vie modula koritenjem /addModules opcije u naredbenom
redu. Najlaki nain da prevedete i izradite sklop s vie modula je upotrebom make-
file datoteke koju moete napraviti u Notepadu ili bilo kojem drugom programu za
ureivanje teksta.

Poglavlje 17: Sklopovi i rad s inaicama I 419


a
A k o niste upoz nat i s makefile d a t o t e k a m a , n e brinite. O v o je s a m o
#%, primjer koji koristi makefile datoteke i to s a m o d a se z aobi u trenutna
' * ; ogranienja Visual Studija. A k o je potrebno, m o e t e koristiti makefile
da toteku k a k o je p o n u e n o , b e z p o t p u n o g razumijevanja s v a k o g reda.
Z a vie informacija pogledajte knjigu M a n a g i n g P r o je c t s w it h m a k e u
izdanju 0 Reilly M e d i a , Ine.

Primjer 17-3 prikazuje kompletnu makefile datoteku (koja je odmah nakon toga
detaljno objanjena). Da biste izveli ovaj primjer stavite makefile datoteku (s imenom
makefile ) u mapu zajedno s kopijama datoteka Calc.cs, Fraction.cs i Assemblylnfo.cs.
Pokrenite .N ET naredbeni prozor i pozicionirajte se u tu mapu. Pozovite nmake pro-
gram bez ikakvog modifikatora. \J podmapi\bin ete pronai SharedAssembly.dll.

P r im j e r 1 7 -3 . K o m p l e t n a m a k e f i l e d a t o t e k a z a s k l o p s v i e m o d u l a

ASSEMBLY= MySharedAssembly.dll

BIN=.\bin
SRC=.
DEST=.\bin

CSC=csc /nologo /debug+ /d :DEBUG /d:TRAE

MODULETARGET=/t:module
LIBTARGET=/t:library
EXETARGET=/t:exe

REFERENCES=System.dll

MODULES=$(DEST)\Fraction.dll $(DEST)\Calc.dll
METADATA=$(SRC)\AssemblyInfo.cs

ali: $(DEST)\MySharedAssembly.dll

# Assembly metadata placed in same module as manifest


$(DEST)\$(ASSEMBLY): $(METADATA) $(M0DULES) $(DEST)
$(CSC) S(LIBTARGET) /addmodule:$(MODULES: =j) /out:$@ %s

# Add Calc.dll module to this dependency list


$(DEST)\Calc.dll: Calc.cs $(DEST)
$(CSC) S(MODULETARGET) /r :$(REFERENCES; = ;) /out:$@ %s

# Add Fraction
$(DEST)\Fraction.dll: Fraction.cs $(DEST)
$(CSC) $(MODULETARGET) / r :$(REFERENCES: =;) /out:$@ %s

$(D EST) ::
!if !EXISTS($(DEST))
mkdir $(DEST)
lendif

Makefile datoteka poinje definiranjem sklopa koji elite napraviti:


ASSEMBLY= MySharedAssembly.dll

420 | Programiranje C#
Zatim se zadaju mape koje ete koristiti - izlaz se smjeta u bin podmapi'U trenutne
niape a izvorni kod uzima iz trenutne mape:
SRO.
DEST=Abin

l Hapravite sklop kako slijedi:


$(DEST)\$(ASSEMBLY): $(METADATA) $(M0DULES) $(DEST)
$(CSC) $(LIBTARGET) /addmodule:$(MODULES: =;) /out:$@ %s

Time se dobija sklop (MySharedAssembly.dll) u odredinoj mapu (bin). Ovo upuuje


l'nmake program (program koji izvodi makefile datoteku) da izgradnja $(DEST)\$(AS-
Sli.VlBLV; ovisi o tri druga navedena odredita i prua naredbeni red potreban za
izradu sklopa.
Metapodaci su definirani ranije kao:
METADATA=$(SRC)\AssemblyInfo.cs

Moduli su definirani kao dva DLL-a:


MODULES=$(DEST)\Fraction.dll $(DEST)\Calc.dll

Naredbeni red stvara biblioteku i dodaje joj module, smjetajui izlaz u datoteku
MySharedAssembly.dll:
$(DEST)\$(ASSEMBLY): $(m et a dat a ) $(mod ul es ) $(DEST)
5(CSC) S(LIBTARCET) /addmodule:$(MODULES: =;) /out:$@ %s

Da biste ovo postigli, nmake program mora znati kako da napravi module. Prvo upu-
tite nmake program kako da napravi Calc.dll. Za to trebate izvorni kod Calc.cs. Zadajte
programu nmake sljedeu naredbu da napravi DLL:
$(DEST)\Calc.dll: Calc.cs $(DEST)
$(CSC) $(MODULETARGET) /r :$(REFERENCES: =;) /out:$@ %s

Zatim uinite isto za Fraction.dll:


$(DEST)\Fraction.dll: Fraction.es $(DEST)
$(CSC) S(MODULETARGET) /r:$(REFERENCES: =;) /out:$@ %s

Rezultat izvoenja programa nmake s ovom makefile datotekom su tri DLL datoteke:
Fraction.dll, Calc.dll i MySharedAssembly.dll. Ako otvorite MySharedAssembly.dll s
ILDasm alatom, vidjet ete da sadri samo manifest, kao na slici 17-3.

Poglavlje 17: Sklopovi i rad s inaicama | 421


Ako prouite manifest, vidjet ete metapodatke za biblioteke koje ste napravili, kao
na slici 17-4.

.pub
pubUcfcegtaite (87
.urr 7: 0: 3600:8
jss*wi>ly Hji
Hjisnarcafls
flsit01
01ii ii)

sssi i Si! - ! jj j; I
:SiSSE Si! S|
SS .SSS Si! < S

nash a igorltbi* 8x00008006 >


.ver 1: 0: 1691:26822
filt Fractlon.dll A AB Ft C1 10 27 9C 80 59 65 67 // *
.nasn - (SA C8 02 i
ro i* ftftw t
".Ti f * FI *
7(1 k 91 F >
.c la ss M l * r n p ubl
ublic pi-
pi-ogCS.Fractlon

ern public progCS .ngiCde

;rS!!:=T!SS5!SiSli--
.ltugebase 8x80600900
Fil Ugneiit 0x 00060200
's t* th r strv e 0x00188000
.subsusteft 0x8003 U H1K00WS_CU1
c a rd a gs 0x00080081 // U.0HI.V
// |nage base: 8x 06240000

S l i k a 17-4. M a n i fe st z a M y S h a r e d A s s e m b l y .d l l

Prvo vidite vanjski sklop za jezgrenu biblioteku (mscorlib), nakon koje slijede dva
modula, ProgCS.Fraction i ProgCS.myCalc.
Sad imate sklop koji se sastoji od tri DLL datoteke: MySharedAssembly.dll s manife-
stom i Calc.dll i Fraction.dll s potrebnim tipovima i implementacijama.

Ispitivanje sklopa
Da biste koristili ove module napravit ete upravljaki program. To je prikazano u pri -
mjeru 17-4. Spremite ovaj program kao Test.cs u istoj mapi s ostalim modulima.

P r im je r 1 7 - 4 . U p r a v l ja k i p r o g r a m z a i s p it iv a n je m o d u l a

namespace Progxamming_CSharp
1
using System;

public class Test

// Main nee uitati dijeljeni sklop


static void Main()
{

422 | Programiranje C#
p r im je r 17-4. U p r a v l ja k i p r o g r a m za is p it iv a n je m o d u la ( n a s t a v a k )

Test t = new Test();


t.UseCS();
t.UseFraction();

// Ovaj poziv uitava sklop myCalc


// ali i sklop mySharedAssembly
public void UseCS()
{
ProgCS.myCalc calc * new ProgCS.myCalc();
Console.WriteLine("3+5 = (0}\n3*$ = {l}">
calc.Add(3,5), calc.Mult(3,5));
}

// Ovaj poziv dodaje sklop Fraction


public void UseFraction()
{
ProgCS.Fraction fraci = new ProgCS.Fraction(3,5);
ProgCS.Fraction frac2 = new ProgCS.Fraction(l,5);
ProgCS.Fraction frac3 = fraci.Add(frac2);
Console.WriteLine("{o) + (l) = {2}",
fraci, frac2, frac3);
}
}
}
Za potrebe ovog prikaza vano je da u Main() ne stavite kod koji ovisi o modulima. Ne
elite da se moduli uitaju zajedno s Main() metodom jer tako Fraction i Calc objekti
nee biti smjeteni u Main(). Kad pozovete UseFraction i UseCalc moi ete vidjeti da
su moduli individualno uitavani.

Uitavanje sklopa
Sklop se uitava u aplikaciju koritenjem AssemblyResolver u procesu koji se zove ispi-
tivanje (engl. probing). Unutar .N ET kostura Assembiy Resolver se automatski poziva
i ne trebate ga eksplicitno pozvati. Njegov posao je da uita program.

Tri DLL-a napravljena ranije moraju se nalaziti umapi ukojoj se izvodi


primjer 17-4 ili u podmapi te mape koja se nalazi uputanji (popisu pod-
mapa koji je sastavio korisnik i koje se nalaze ispod korijenske lokacije
koja je navedena u konfiguracijskoj datoteci aplikacije).

Postavite toku prekida u drugom redu Main() metode, kao to je prikazano na slici
17-5.
Prekinite izvoenje kod toke prekida i otvorite prozor Modules. Samo dva modula su
uitana, kao to je prikazano na slici 17-6.

Poglavlje 17: Sklopovi i rad s inaicama I 423


Programming_C5harp.Test l^MainO
namespace Progv9Htttiing_CShai:p
t
using Sy3tem;

public cla ss Test


i i <
// main Mili not load the shared asserttoly
s t a t ic void Hain()
U
li
<
t . UseCS ();
t.T Jse F cactionl);

Slika 17-5. Toka prekida u Main() metodi

8.0. 4060..
060...
... 7/25/
25/2004
2004 9:06 ...
C :\W lfC O WS\as$emUy\GAC^M.
; 0 v s t o s t u tid 2.0. 4060..
060...
... 7/7/2004 7:04 PM
C:\WIHDOWS\$ambt>W9.C _M..
'l a Syt
ytam.Wr<Jowr<Jows.Ft*fre.dl 2.0. 4060
4060...
60...7
...7/7/2004 7:04 PM
C:\WINDOW S\assent4y\6AC_M.
|0 System. 2.0. 4060
4060..
60...
... 7/7/2004 7:04 PM
>i3 Syji
Syjiem.
em.[>avr avrtio.< C:\V4lN D C W S\*ent*)r\GACJ 4. 8.0.4060
4060..
60...
... 6/25/
25/2004
2004 6:09 PM
0 M od J& T ettOriv a-r th o it.e a C:\D o Ctm entj and SettrQi\JdS- 2.0. 4060
4060...
... 7/7/2004 7:04 PM
C :AW U C O W S\aueny \W C 3 .. 2.0. 4060
4060...
60...7
...7/7/2004 7.04 PM
i a Sy*lwn.Oota.<*
C:lWlNDO
NDOWS\aem
aemtty\GACGAC-J*.J*.
: a S n te n .M i C A O ocurenls a rd SeC. 1.0. V691
691... 6/78/78/2004
004 4:05 PM
! 0 M od J e T tD rl* er exa C :\D oam 0rUs and s e t t in ^ J .
I .................. ..... m
j________ _ -
p-ftothietcsmrtvet.t>\_ __
'

_
"rojujmcr dxefccti.veij
Onatnespoce H oduleTestD elv et

lo n d t n e a n o r e d M a e n ia l?

f e a t t - ne T e * t ( ) ;
:l t.U s e C S ( ) ;
C .U s e F ra e tlo o O ;

... gji^

Slika 17-6. Dva uitana modula

---- Ako Test.cs niste razvili kao dio Visual Studio .NET rjeenja, pozovite
**, Svstem.Diagnostics .Debugger. Launch() metodu malo prije drugog reda
'V d , u HainQ metodi. To omoguava da odaberete program za otkrivanje
pogreaka koji elite koristiti (obavezno prevedite Test.cs koritenjem
opcija /debug i /r: HySharedAssembly.dll).

Pozovite prvu metodu i gledajte Modules prozor. im uete u UseCS, Assei)lyLoadMvidi<h


treba module iz MySharedAssembly.dll. DLL je uitan i iz njegovog manifesta AssanblyLoa
der vidi da treba Calc.dll, koji se takoer uitava, kao stoje prikazano na slici V-

424 I Programiranje C#
tttkias ' - 9X
N*e Patti Hi Cocto Sym M Statut Vaston Ttrestacrp
J]crseorlb.dl C :VW b 3 w ^ W *efT \6 :J . . . v' No N oirtrD cfebj... 2.0. 7/7/2004 74.-00646H..'H !
iivttiMtuUd CAWWOO'NS\ttr**>\GfeC _M... y No No vytrfccfe ba. . ... 7/25/2004 9:06...
eo.roeo..
eo...
tt)System.Wndow.Forni5.<* C:^wi>cOWS\siint*r\GaC_M... V No N a ir o to b lN .. 2.0. 7/7/2004 7:044060...PM I
111Sfilem.di C.\W lM 30W \*!n*#\GAC J^... Yes No Novytrfcctsbe... j.a-1060... 7/7/2004 7;04 fW ;
ij)Systern-C*Jwhp3.c# C ;\W W X>*S\*!errtt*ViAi:,J4... r N> NonrrC ohbe... 2.0. 7/7/2004 7.044060P.M I
li)ldieTeitnnr.vtf>I.cir r A / t a ii r m u and SW *js\Xft... y No H0 5yoW5fca... 6 0.4060. 6/25/2004 6:00 fM ;
ii) SvflemCMiJ.iJ C:\WffC Ow S\ierrt*r\GAC 3... v No Ud t r o t * * toa.. 2.0. 7/7/2004 7:044060.P.t
!*)Syitem.xrrt.ci C;VW
MDOW
S\KrttAGAC>t- Y i-e Nos/otJdstea..
ea...
... 2.0.4060... 7/7/2004 7:04 PM 1
C:\0txi*iw iU andSette>0rtJM... No S y otrtbaded. C\Gotisnents4ndSctu.. 9 1.0. 1691.... 8/10/20044.051*41
C:\DocumenU and SetthQS\M ... No Y 5y<rtHb toadcA. C I O c c u m ii ard 1.0.1691.... 6/16/2004 3:54 PM)
C:\Occtm eritt and Settr^sUes... No V StTr**4t kuded. c :\P o as ne ntm ) Seta... u 6/10/2004 3.-S4 PM i

.- MocMeTtitDrtecr.cs [
1/M xifeT ettC*tve<.Tt

Pi'og
Pi'ogCs.c
Cs.cvc&lo c a lc < ne Pr oo CS .yC l oI)
Can
Ca n cole. W titeL
teLlne ( -J * s - (Q|Xnl*S c 11
c alc .Addt
ddt 3, S 1 , c a lc .H U le f 3 , S 1

Slika 17-7. Moduli uitani na zahtjev

Kad uete u Fraction uitava se zadnji DLL.

Privatni sklopovi
I ; Sklopovi mogu biti privatni (engl. private)i dijeljeni (engl. shared). Privatni sklopovi su
1 namijenjeni za koritenje u samo jednoj aplikaciji. Dijeljeni sklopovi su namijenjeni za
| upotrebu izmeu vie aplikacija.
| Svi sklopovi koje ste dosad napravili su privatni. Kad prevodite aplikaciju podrazumi-
t jeva se da je napravljen privatni sklop. Sve datoteke za privatni sklop se uvaju u istoj
i mapi (ili u stablu podmapa). To stablo mapa je odvojeno od ostatka sustava, jer nita
H drugo osim jedne aplikacije ne ovisi o njenom sadraju i moete jednostavno prebaciti
ovu aplikaciju na drugo raunalo tako da kopirate mapu i podmape.
fiPrivatni sklop moe imati bilo koje ime koje odaberete. Nije vano ako se to ime kosi
. sa imenima sklopova u drugim aplikacijama. Imena su lokalna za aplikaciju.
Ranije su se prilikom instaliranja DLL-ova na raunalu (COM DLL-ova) unosili zapisi
u Registry. Bilo je teko izbjei oneienje registra beskorisnim zapisima. U svakom
sluaju, ponovna instalacija programa'na drugom raunalu nije bila trivijalna. Od
kada se koriste sklopovi nita od toga vie ne vai. S privatnim sklopovima, instalira-
n je postaje jednostavno kao kopiranje datoteka u odgovarajuu mapu.

Dijeljeni sklopovi
Moete napraviti sklopove koje e dijeliti vie aplikacija. To e vam moda biti potrebno
jako napiete opu kontrolu ili klasu koju mogu koristiti drugi projektanti. Ako elite
(dijeliti sklop, on mora zadovoljiti odreene uvjete.
(Prvo, sklop mora imati jako ime (engl. strong name). Jaka imena su globalno jedinstvena.

Poglavlje 17: Sklopovi i rad s inaicama | 425


Nitko drugi ne moe stvoriti isto jako ime kao vi jer sklop koji je napra-
vljen s jednim privatnim kljuem zajameno ima drukije ime nego
sklop koji je napravljen s drugim privatnim kljuem.

Drugo, dijeljeni sklop mora biti zatien od novijih inaica, tako da svaka nova inaica
koju objavite mora imati novi broj inaice.
Konano, da biste dijelili sklop, stavite ga u Global Assembly Cache (GAC). Ovo je
podruje sustava datoteka koje CLR rezervira za uvanje dijeljenih sklopova.

Kraj DLL pakla


Sklopovi oznaavaju kraj DLL pakla. Sjetite se ovakve situacije: instalirate aplikaciju A
na raunalu, a pritom se u mapu Windows uita vei broj DLL-ova. Sve radi odlino
mjesecima. Onda instalirate aplikaciju B i odjednom, neoekivano, aplikacija A ne
radi. Aplikacija B nije nikako povezana s aplikacijom A. to se onda dogodilo? Usta-
novi se, to shvatite poslije, da je aplikacija B zamijenila DLL-ove koje aplikacija A treba
i odjednom aplikacija A poinje posrtati, slijepo i bezumno.
Kad su DLL-ovi izumljeni, prostor na disku je bio jako vaan i ponovno koritenje
DLL-ova se inilo kao dobra ideja. Teorija je bila da bi DLL-ovi trebali biti kompatibilni
unatrag pa bi automatsko auriranje bilo bezbolno i sigurno. Ali kao to je moj bivi ef
Pat Johnson esto govorio, ,,u teoriji, teorija i praksa su iste, ali u praksi nikad nisu.
Kad bi novi DLL bio dodan na raunalo, stara aplikacija, koja je veselo radila svoj
posao u jednom kutu raunala, odjednom se povezala s DLL-om koji nije u skladu s
njenim oekivanjima i hej! Poela je plesati ples smrti. Ova pojava je dovela do toga
da kupci postanu opravdano sumnjiavi prema instaliranju novog softvera ili ak au-
riranju postojeih programa i to je jedan od razloga zato se raunala s Windowsima
ine nestabilnima. Koritenjem sklopova, cijela ova nona mora nestaje.

Inaice
Dijeljeni sklopovi u .NET-u su jedinstveno identificirani preko imena i inaice. GAC
omoguava da starije inaice sklopa budu dostupne zajedno uz noviju inaicu.

Dostupnost starijih inaica zajedno s novima se primjenjuje samo na ele-


mente u GAC-u. Privatni sklopovi ne trebaju i ne posjeduju ovo svojstvo.

Broj inaice sklopa moe izgledati ovako: 1 : 0 : 2 2 0 4 : 2 1 (etiri broja odijeljena dvoto-
kama). Prva dva broja (.1:0) su oznake inaice i podinaice, trei broj je izgradnja, a
etvrti (21) je prepravka.
Kad dva sklopa imaju razliite glavne ili sporedne brojeve inaice, onda se obino
smatraju nekompatibilnim. Ako imaju razliite brojeve izgradnje, onda mogu, ali ne
moraju biti kompatibilni, a ako imaju razliite brojeve revizije, onda se smatraju defi-
nitivno kompatibilnim. To izvrsno zvui u teoriji, ali CLR Assembly Resolver ignorira
ovo pravilo i slui samo da podsjeti projektanta, pa se ne namee prilikom izvoenja.

426 | Programiranje C#
jaka imena
Da biste koristili dijeljene sklopove, morate zadovoljiti dva zahtjeva:
Morate moi tono zadati koji sklop elite uitati.
Morate osigurati da sklop nije neovlateno mijenjan i da je sklop koji se uita auto-
riziran od stvarnog tvorca tog sklopa. Da biste to uinili, sklop treba digitalni
potpis prilikom izrade.
Oba ova zahtjeva su zadovoljena koritenjem jakih imena. Jaka imena moraju biti glo-
balno jedinstvena i koristiti javni klju za ifriranje. Jako ime je niz heksadecimalnih
znamenki i ljudima je teko za itanje.

Da bi napravili jako ime, generira se par kljueva (jedan privatni i jedan javni) za jedan
ili vie sklopova. Kombinacija je napravljena od imena i sadraja datoteka u sklopu
i onda ifrirana s privatnim kljuem za sklop. Oznaka javnog kljua (kombinacija
cijelog kljua od osam bajtova) je smjetena u manifest zajedno s javnim kljuem. Taj
postupak se zove potpisivanje sklopa.

ifriranjejavnog kljua
Jaka imena su utemeljena na tehnologiji ifriranja javnim kljuem. Bit ifriranja jav-
nim kljuem je da se naprave dva kljua. Podaci ifrirani prvim kljuem mogu biti
deifrirani samo s drugim. Podaci ifrirani s drugim kljuem mogu biti deifrirani
samo s prvim.
Razdijelite prvi klju kao javni klju kojeg bilo tko moe imati. uvajte drugi klju kao
privatni klju tako da nitko osim vas nema pristup.
Reciprona veza izmeu kljueva omoguava bilo kome da ifrira podatke vaim jav-
nim kljuem i onda moete deifrirati te podatke koritenjem privatnog kljua. Nitko
drugi nema pristup podacima nakon to su ifrirani, ukljuujui osobu koja ih je
ifrirala.
Slino, moete ifrirati podatke vaim privatnim kljuem i onda bilo tko moe dei-
frirati podatke s javnim kljuem, lako ovo znai da su podaci otvoreno dostupni, to
jami da ste samo vi mogli stvorili te podatke. Ovo se zove digitalni potpis.

Kad aplikacija uita sklop, CLR koristi javni klju za dekodiranje kombinacije dato-
teka iz sklopa kako bi se osiguralo da datoteke nisu neovlateno mijenjane. Time se
ujedno sprjeava da se ime kosi s imenima drugih sklopova.
Jako ime moete napraviti koritenjem sn pomonog programa:
sn -k c:\myStrongName.snk

Zastavica -k pokazuje da elite novi par kljueva upisan u navedenu datoteku. Moete
nazvati datoteku kako elite. Zapamtite, jako ime je niz bajtova i nije namijenjeno da
ga ljudi itaju.

Poglavlje 17: Sklopovi i rad s inaicama I 427


Jako ime i sklop moete povezati koritenjem atributa: m
using System.Runtime.CompilerServices; M
[assembly: AssemblyKeyFile("c:\myStrongName.key")]
1V';

Atributi su detaljno obraeni u poglavlju 18. Zasad, moete staviti ovaj kod na vrh
datoteke da povee jako ime koje ste napravili s vaim sklopom.

Global Assembly Cache


Kad ste napravili jako ime i povezali ga sa sklopom, sve to preostaje jest postaviti !
sklop u GAC. To moete uiniti s pomou ga cu til pomonog programa:
gacutil /i MySharedAssembly.dll

Ili moete otvoriti File Explorer i pozicionirati se u %SystemRoot%\assembly>a Explo-


rer se pretvara u GAC pomoni program.

Izrada dijeljenog sklopa


Najbolji nain da shvatite dijeljene sklopove je da napravite jedan. Sad se vratimo pri-
janjem projektu s vie modula (primjeri 17-1 do 17-4) i pozicionirajte se u mapu koja
sadri datoteke Calc.cs i F raction.cs.
Pokuajte ovaj eksperiment: potraite bin mapu za upravljaki program i provjerite da
nemate lokalnu kopiju MySharedAssembly DLL datoteke.

Referencirani sklop (MySharedAssembly) bi trebao imati svojstvo CopyLo-


cal postavljeno na false.

Pokrenite program. Trebao bi se zaustaviti uz izbaenu iznimku koja oznaava da pro-


gram ne moe uitati sklop:
Unhandled Exception: System.IO.FileNotFoundException: File or assembly name
MySharedAssembly, or one of its dependencies, was not found.
File name: MySharedAssembly"
at Programming_CSharp.Test.UseCS()
at Programming_CSharp.Test.Main()

Sad kopirajte DLL-ove u stablo mapa upravljakog programa, pokrenite ponovno pro-
gram i ovaj put bi trebali vidjeti da radi kako treba.
Pretvorimo MySharedAssembly u dijeljeni sklop. To se izvodi u dva koraka. Prvo, napra-
vite jako ime za sklop i onda stavite sklop u GAC.

Korak 1: Napravite jako ime


Napravite par kljueva otvaranjem naredbenog prozora i upisivanjem sljedee
naredbe:

428 | Programiranje C#
sn -k keyFile.snk

|ad otvorite AssemblyInfo.cs datoteku u projektu za MySharedAssembly.dll i promije-


liite ovaj red:
[assembly: AssemblyKeyFile("") ]

|ina sljedei nain:


[assembly: AssemblyKeyFile("keyFile.snk")]

Jflo postavlja datoteku kljua za sklop. Ponovno napravite sklop s istom makefile datote-
|kom kao ranije i onda otvorite rezultirajui DLL u ILDasm alatu i pogledajte manifest.
);Jrebali biste vidjeti javni klju, kao to je prikazano na slici 17-8.

r .publickey (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00
00 24 00 00 b2 b3 41 31 00 04 00 00 01 00 01 00 .$ ..RSA1
11 13 95 3C 41 19 2B 41 28 29 E8 AF DE 8C A2 04
88 22 BD 4F A9 E1 FS 57 2C 2DE2 43 CF C3 68 4E . .0.. u'- C
F7 C7 72 E8 55 94 8b 11 KA 66 30 F6 D4 22 DB OD . -r.O fO
6E D8 A OD bD 58 28 10 E9 75 D8 BF CC 82 2A EB
04 19 D5 Cl 88 B0 C
20 8F 9B DC 29 AC 46F A
D5 CB 6 SC 58 43 08 ODE9
3 CD 87 3C F8 92 7A 84 \XC
E3 47 84 AD 58 73 3E OD AD 71A
DC0 A2 FA 15 14 A3

G,.Vs>.
88 17 01 AC F3 A3 7AF8 59 BC 3A 16 CB AB 34 cs ........ z.Y.;.. 4.
ImiSlika 17-8. Manifest datoteke MySharedAssembly.dll
Dodavanjem jakog imena potpisali ste ovaj sklop (tone vrijednosti e biti drugaije
jtehego na slici). Da bi pokazali da imena iz GAC-a i reference u klijentovom manifestu
^odgovaraju, htjet ete uzeti jako ime iz DLL-a. Da biste to uinili, pozicionirajte se u
fpmapu u kojoj se nalazi DLL i unesite sljedee u odzivniku:
siv
sn -T MySharedAssembly.dll

Pomoni program sn razlikuje velika i mala slova. Ne piite sn -t.

-Rezultat bi trebao izgledati otprilike ovako:


I B S t - ' P u b lic key to k e n is o ifa d 8 e o f0 9 4 ia 4 d

|Ova vrijednost je skraena vrijednost javnog kljua za taj sklop koja se naziva oznaka
javnog kljua (engl. public key token).

lU klonite DLL-ove iz strukure mapa testnog programa i pokrenite ga ponovno. Tre-


# bao bi se ponovno prekinuti. Iako ste dali ovom sklopu jako ime, niste jo registrirali
JUsklop u GAC-u.

H Korak 2: Smjestite dijeljeni sklop u GAC


| Sljedei korak je da smjestite biblioteku u GAC. Da bi to uinili, otvorite prozor Explo-
| rer i pozicionirajte se u %SystemRoot% mapu. Kad dvaput pritisnete Assembly pod-
i j mapu Explorer se pretvara u GAC preglednik.
H
moete povui i i ispustiti u GAC preglednik ili moete pozvati ovaj pomo-
J|ni program za odzivnik:
Gacutil /i mySharedAssembly.dll

Poglavlje 17: Sklopovi i rad s inaicama | 429


kazana u GAC pregledniku vrijednosti koju je vratio sn pomoni program:
Public key token is 01fad8eCrf0941a4d

Ovo je prikazano na slici 17-9.

Assembly Name ______________________ Version Culture 1 Public Key Tokari


d|| Microsoft,StdForm at 7 .0 . 3300.0 b03f5f7flld50a3a
iSijMIcrosoft.VisualBasic 8 .0 . 1200.0 b03f5f7fl Id50a3a
5|Microsoft.VisualBasic.Com patibility 8 .0 . 1200.0 b03f5f7f Ud50a3a
^ M i c r o s o f t .VisualBasic,Compatibility .Data 8 . 0 , 1200.0 b03f5f7fl Id50a3a
jS b Microsoft. VisualBasic.Vsa 8 . 0 . 1200.0 b03F5f7flld50a3a
iiSiJMicrosoft .VisualC 8 .0 . 1200.0 b03f5f7f 1Id50a3a
afijM icro so ft. VisualC. ApplicationVerifier 1.0.0.0 b03f5f7fl Id50a3a
2l| M icrosoft, VisualC. VSCo deParser 8 .0 . 1200.0 b03f5f7flld50a3a
il^ jM icrosoft. VisualC. VSCodeProvider 8 . 0 , 1200.0 b03f5f7flld50a3a
^ M i c r o s o f t .VisualStudio 2 .0. 3600.0 b03f5f7flld50a3a
i{j Microsoft. VisualStudio.CornmandBars 8.0. 0.0 b03f5f7flld50a3a
SpjiJMicrosoft .VisualStudio.Configuration 2.0.3600.0 b03f5f7flld50a3a

Slika 17-9. GAC

Kad je to obavljeno, imate dijeljeni sklop kojem moe pristupiti bilo koji klijent. Obnovite
klijent tako to ete ga ponovno napraviti i pogledati njegov manifest, kao na slici 17-10.

.assembly extern MySharedAssentbly


publickeytoken - (A5 92 9F 01 02 E0 C4 73 )
.ver 1:0:535:29377
>

Slika 17-10. Manifest

U manifestu je MySharedAssembly naveden kao vanjski sklop, a javni klju odgovara


vrijednosti prikazanoj u GAC-u. Vrlo dobro. Vrijeme je da to probate.
Zatvorite lLDasm i pokrenite kod. Trebao bi raditi kako treba, iako ne postoje DLL-
ovi za ovu biblioteku u njegovoj izravnoj putanji. Upravo ste napravili i upotrijebili
dijeljeni sklop.

Ostali potrebni sklopovi


Manifest.sklopa sadri i reference prema drugim sklopovima. Svaka takva referenca
ukljuuje ime drugog sklopa, broj inaice, potrebnu kulturu i neobavezne oznake jav-
nih kljueva drugih sklopova (digitalne potpise).
Kultura je niz znakova koji predstavlja jezine i nacionalne karakteristike prikaza za
osobu koja koristi va program. Kultura odreuje, na primjer, da li e datumi biti u
formatu mjesec/dan/godina ih dan/mjesec/godina.

430 I Programiranje C#
POGLAVLJE 18
Atributi i refleksija

Diljem ove knjige, naglaavao sam da .NET aplikacija sadri kod, podatke i metapo-
|datke. Metapodaci predstavljaju informacije o podacima, to jest, informacije o tipo-
vima, kodu, sklopovima i tako dalje, koje su pohranjene zajedno s programom. Ovo
rnpoglavlje prouava kako nastaju neki od tih metapodataka i kako se upotrebljavaju.
|Atributi (engl. attributes) predstavljaju mehanizam za dodavanje metapodataka, kao
to su instrukcije prevoditelju i drugi podaci o vaim podacima, metodama, klasama
j samom programu. Atributi se umeu u metapodatke i vidljivi su s pomou alata
S|;BLDasm i drugih alata za itanje metapodataka.
| Refleksija (engl. reflecetion) je proces kojim program moe itati svoje metapodatke ili
metapodatke drugog programa. Kae se da program odraava sebe ili neki drugi pro-
igram izvlaei metapodatke iz odraenog sklopa i koristei ih da informira korisnika
|ili promijeni ponaanje programa.

Atributi
r
JAtribut je objekt koji predstavlja podatke koje elite povezati s nekim elementom u
forogramu. Element kojem dodajete atribut predstavlja cilj (engl. target) atributa. Na
pprimjer, atribut:
[NoIDispatch]

l je povezan s klasom ili sueljem da ukae kako bi ciljna klasa trebala izvoditi iz
|IUnknown a ne iz IDispatch prilikom izvoenja u COM. Programiranje COM suelja je
E,detaljno obraeno u poglavlju 22.
|U poglavlju 17 vidjeli ste ovaj atribut:
[assembly: AssemblyKeyFile("c:\\myStrongName.key")]

431
Time se umeu metapodaci u sklop da zadaju jako ime programa.-

Atributi
Neki atributi su prueni kao dio CLR-a ili kostura. Pored njih moete napraviti i atri-
bute koji su prilagoeni vaim potrebama.

Veina programera e koristiti atribute koje prua kostur, iako izrada vlastitih atributa
$
moe biti moan alat kad se kombinira s refleksijom. To je opisano kasnije u ovom
poglavlju.
2
Ciljevi atributa
Ako pogledate u CLR vidjet ete veliki broj atributa. Neki atributi se primjenjuju na I
sklop, drugi na klasu ili suelje, a neki, kao [VJebMethod], na lanove klase. Ovi objekti ?
predstavljaju ciljeve atributa. Mogui atributi su deklarirani u AttributeTargets enu- 1
meraciji i detaljno su opisani u tablici 18-1. i

Tablica 18-1. Mogui ciljevi atributa

Im e lana U p o tre b a

A li Prim jenjuje se na jed a n od sljedeih elem en ata: sklop, klasa, konstruktor, delegat, enumeracija,
dogaaj, polje, suelje, m etod a, m odu l, p aram etar, povratna vrijednost ili struktura

A s s e tn b ly Prim jenjuje se na sklop

C la s s Prim jenjuje se na klasu

C o n s tru c to r Prim jenjuje se na konstruktor

D e le g a te P rim jenjuje se na d ele g at

Enum Prim jenjuje se na enum eraciju

Event Prim jenjuje se na dogaaj

F ie ld P rim jenjuje se na polje

In t e r f a c e Prim jenjuje se na suelje

M e th o d Prim jenjuje se na m etod u

M o d u le Prim jenjuje se na m odu l

P a ra m e te r Prim jenjuje se na p a ra m etar m eto d e

P ro p e rty Prim jenjuje se na svojstvo ( g e t ili s e t , ako je im p le m entirano )

R e t u r n V a lu e P rim jen ju je se na p o vra tnu vrijednost

S tru c t Prim jenjuje se na strukturu

Atribut sklopa zapravo obavlja vie od samog umetanja metapodataka. C# prevoditelj pazi na ovaj atri-
but (kao i na neke druge) koji izaziva poseban nain rada. U ovom sluaju, ita klju datoteke i koristi taj
klju za digitalni potpis sklopa. Obino, meutim, atributi su samo statiki metapodaci koje se umee u
sklop.

432 | Programiranje C#
primjena atributa
primjenite atribute na njihove ciljeve stavljanjem u uglate zagrade odmah ispred cilj-
anog elementa (osim u sluaju sklopova, kad ih stavljate na poetak datoteke).

i jdoete kombinirati atribute slaganjem jednog iznad drugog:


[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(".\\keyFile.snk")]

1Xo moe biti izvedeno i odvajanjem atributa zarezima:


[assembly: AssemblyDelaySign(false),
assembly: AssemblyKeyFile("A\keyFile.snk")]

A tribute sklopa m orate sm jestiti nakon svili using deklaracija te prije


|||p: koda.

'S'iV' Mnogi atributi se koriste za meudjelovanje s COM-om, o emu emo detaljnije govo-
riti u poglavlju 22. Ve ste vidjeli primjenu jednog atributa ([WebMethod]) u poglavlju
16. Vidjet ete i upotrebu drugih atributa, kao sto je [Serializable] iz poglavlja 19.
System.Reflection imenski prostor stavlja na raspolaganje vei broj atributa, ukljuujui
atribute za sklopove (kao sto je AssemblyKeyFileAttribute), konfiguriranje te atribute
za inaice. Jedan od atributa s kojim ete se najee susretati u svakodnevnom C#
programiranju (osim ako ne radite s COM modelom) je [S erializable]. Kao to ete
vidjeti u poglavlju 19, sve to trebate napraviti kako biste osigurali da klasa moe biti
serijalizirana na disk ili na Internet je dodavanje atributa [Serializable] klasi:
[Serializable]
class MySerializableClass

.tijlv1Oznaka atributa se stavlja u uglate zagrade odmah prije cilja atributa, a u ovom sluaju

to je deklaracija klase.
I Kljuna injenica o atributima je da znate kad su vam potrebni. Zadatak koji trebate
W iobaviti diktirat e njihovu upotrebu.
#

Prilagoeni atributi
Moete napraviti atribute koji su prilagoeni vaim potrebama te ih koristiti prilikom
izvoenja kako vam odgovara. Pretpostavite, na primjer, da va odjel eli voditi evi-
denciju o ispravljenim pogrekama. Ve imate bazu podataka o svim pogrekama, ali
biste htjeli povezati svaki izvjetaj o pogreci s odreenim ispravkom u kodu.
Mogli biste dodati komentare u kod:
// Pogreka 3 2 3 . Ispravio lesse Liberty 1/1/2005.

To biste lako uoili u izvornom kodu, ali ne postoji veza prema pogreci 323 u bazi
podataka. Prilagoeni atribut bi mogao biti upravo ono to traite. Zamijenili biste
komentar s neim kao to je ovo:

i
Poglavlje 18: Atributi i refleksija | 433
[BugFixAttribute(323,"lesse Liberty ,1/1/2005",
Comment="3edna manje")]

Onda biste mogli napisati program da ita metapodatke, nae ova zabiljekeo ispra
vljenoj pogreci te da aurira bazu podataka. Atribut bi imao ulogu komentara ali
bi takoer omoguavao da programski pribavite podatke s pomou alata koje biste
sami napisali.
.

a
Ovo je. m oda neprirodan prim jer jerb i atributi bili prevedeni u finalni

kod za isporu ku .

.A*

Deklariranje atributa
Atributi, kao i veina drugih stvari u C# jeziku, su ukljueni u klase. Da biste napra-
vili prilagoeni atribut izvedite novu prilagoenu klasu atributa iz System.Attribute-
public class BugFixAttribute : System.Attribute

Trebate uputiti prevoditelja s kojim elementima ovaj atribut moe biti upotrijebljen (tj.
trebate zadati cilj atributa). To zadajte (s im drugim nego) atributom:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

AttributeUsage je atribut koji se primjenjuje na atribute - metaatribut. On prua, ako


vam je tako drago, meta-metapodatke, to jest, podatke o metapodacima. Konstruk-
toru atributa AttributeUsage prosljeujete dva argumenta. Prvi argument je skup zasta-
vica koje ukazuju na cilj, u ovom sluaju klasu i njen konstruktor, polja, metode i
svojstva. Drugi argument je zastavica koja ukazuje da li navedeni element moe primiti
vie od jednog takvog atributa. U ovom primjeru, AllowMultiple je postavljen na true,
pokazujui da se lanovima klase moe dodijeliti vie atributa BugFixAttribute.

Imenovanje atributa
Novi prilagoeni atribut u ovom primjeru je nazvan BugFixAttribute. Uobiajeno je
dodati rije attribute imenu atributa. Prevoditelj to podrava tako to dozvoljava da
nazovete atribut skraenim imenom. Prema tome, moete napisati:
[BugFix(l23, "lesse Liberty", "01/01/05", Comment="3edna manje")]

Prevoditelj e prvo potraiti atribut BugFix te, ako ga ne pronae, potrait e BugFix-
Attribute.

Konstruiranje atributa
Atributi uzimaju dva tipa parametara: pozicijske i imenske. U BugFix primjeru, pro-
gramerovo ime, identifikator pogreke i datum su pozicijski parametri, a komentar

434 | Programiranje C#
|fe imenski parametar. Pozicijski parametri se prosljeuju kroz konstruktor po redosli-
Ijedu deklariranom u konstruktoru:
public BugFixAttribute(int buglD, string programmer,
string date)
{
this.buglD = buglD;
this.programiner = programmer;
this.date = date;
}
Ijmenovani parametri su implementirani kao polja ili kao svojstva:
public string Comment
{
get
{
return comment;
i
set
{
comment = value;
}
}
RUobiajeno je za pozicijske parametre napraviti svojstva samo za itanje:
public int BuglD
{
get
{
return buglD;
}
}

Upotreba atributa
gptKad ste definirali atribut moete ga poeti koristiti tako to ete ga staviti odmah
|ispred cilja. Kako biste testirali BugFixAttribute iz prethodnog primjera, sljedei pro-
igram stvara jednostavnu klasu MyMath i dodjeljuje joj dvije metode. Dodijelite BugFixAt-
. tributes atribut klasi za biljeenje povijesti odravanja koda:
[BugFixAttribute(l 2 1 ,"Desse Liberty","01/03/05")]
[BugFixAttribute(l07/Desse Liberty", "01/04/05",
Comment="Fixed off by one errors'!)]
public class MyMath

' Ovi atributi se pohranjuju s metapodacima. Primjer 18-1 prikazuje kompletan program.

b P rim jer 18-1. R ad s p rilag o e nim a tr ib u tim a


ftregion Using directives

If using System;
k ,using System.Collections.Ceneric;
Ejlusing System.Text;

Poglavlje 18: Atributi i refleksija | 435


P r im je r 1 8 -1 . R a d s p r i l a g o e n i m a t r i b u t i m a ( n a s t a v a k )

ttendregion

namespace CustomAttributes
{
// Stvara prilagoeni atribut koji e biti dodijeljen lanu klase
[AttributeUsage( AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true )]
public class BugFixAttribute : System.Attribute
{

// Podaci privatnih lanova


private int buglO;
private string comment;
private string date;
private string programmer;

// Konstruktor atributa za
// pozicijske parametre
public BugFixAttribute
( int buglD,
string programmer,
string date )
{
this.buglD = buglD;
this.programmer = programmer;
this.date = date;
}

// Pristupnik
public int BuglD
{
get
{
return buglD;
}
}
// Svojstvo za imenovani parametar
public string Comment
{
get
{ .
return comment;
}
set
{
comment = value;
}
}

436 | Programiranje C#
Primjer 18-1. R ads prilagoenim atributima (nastavak)
II Pristupnik
public string Date
{
|:S get
{
return date;
}
}
II Pristupnik

1' public string Programmer


{
I* get
{
Sli return programmer;
}
}
}
1
i , // ********* Dodjeljivanje atributa klasi ********

[BugFixAttribute( 121, "lesse Liberty", "01/03/05" )]


[BugFixAttribute( 107, "lesse Liberty", "01/04/05",
Comment = "Fixed off by one errors" )]
I public class MyMath
I {

public double DoFuncl( double paraml )


{
return paraml + DoFunc2( paraml );
}

public double DoFunc2( double paraml )


{
return paraml / 3;
}

public class Tester


1
public static void Main()
{
MyMath mm = new MyMath();
Console.WriteLine( "Calling DoFunc(7). Result: {0}",
mm.DoFuncl( 7 ) );
}
I

Kao to moete vidjeti, atributi nemaju nikakvog utjecaja na izlaz programa. Zapravo,
1 u ovom trenutku imate samo moju rije da atributi uope postoje. Brzi pogled na
metapodatke koritenjem ILDasm alata otkriva da su atributi ipak na svom mjestu

Poglavlje 18: Atributi i refleksija | 437


(slika 18-1). U sljedeem dijelu vidjet ete kako doprijeti do metapodataka i kako ih
upotrijebiti u programu.

f C:\
C:\Ooc
Oocume
uments and Settings
Settings\Jesse\My D ocum ent
en tsVW ordD ocum e ...
...
F lle V ie w H elp
MANIFEST
9 C u s to m A ttrib u te s
ff 9 C u s to m A ttrib u te s ,P ro p e rtie s
ft C u s to m A ttrib u te s .B u g F ix A ttrib u te
.da ss public a u to ansi b e fo rtfifc ld in it
e x te n d s [rn s c o r lib ]S y s te m .A ttrib u te
| .cus tom in s ta n c e v o id [m s c o riib 3 S y s te m .A ttr ib u te U s a g e A ttr ib u te :: .c to r ( v a lu e ty p e [m s c o rlib ]S y s te m .A ttrib u te T a ro e ts ,i =
v b u g lD : p r iv a te in t3 2
v c o m r r ie n t: p r iv a te s trin g
v d a te : p r iv a te s tr in g
v p ro g ra m m e r : p r iv a te s tr in g
B9 . c t o r : v o id ( in t3 2 ,s tr in g ,s tr in g )
9 g e t _ 8 u g ID : in t3 2 ( )
9 g e t_ C o m m e n t: s tr in g ( )
R I g e t_ D a te : s tr in g ( )
S I g e t_ P ro g ra m n ie r : s tr in g ( ) S
i . v b ld fc tr in g )
A B u glD : in s ta n c e in t3 2 ( )
A C o m m e n t: in s ta n c e s trin g O
A D a te : in s ta nc e s trin g O
A P ro g ra m m e r : in s ta n c e s trin g O
ft C u s to m A ttrib u te s .M y M a th
.d a s s public a u to ansi b e fo re fie ld in it
.c u sto m in s ta n c e v o id C u s to m A ttrib u te s .B u g F ix A ttrib u te :: .c to r (in t3 2 , ...
.cus tom in s ta n c e v o id C u s to m A t t r ib u te s . B u g F ix A tt r ib u te : :. c to r ( in t3 2 , . ..
E3 . c t o r :v o id ()
9 D o F u n d : flo a t 6 4 ( f lo a t 6 4 )
9 D o F u n c 2 : flo a t 6 4 ( f lo a t 6 4 )
C u s to m A ttrib u te s .T e s te r
.d a s s public a u to ans i b e fo re fie ld in it
9 . c t o r : v o id ()
M a in ; v o id ()

.a ss em b ly C u s to m A ttrib u te s
i
i . v e r 1 :0 :1 6 9 1 :2 7 5 3 1
[\

S l i k a 1 8 -1 . M e t a p o d a c i u s k l o p u

Refleksija
Da bi atributi u metapodacima bili korisni trebate nain da im pristupite, a idealno
bi bilo da im pristupate prilikom izvoenja. Klase u imenskom prostoru Reflection
zajedno sa System.Type klasom osiguravaju podrku za ispitivanje metapodataka i rad
s njima.
Refleksija se openito koristi za sljedea etiri zadatka.

438 | Programiranje C#
Pregledavanje m e ta p od a tak a
Mogu ga koristiti alati i pomoni programi koji ele prikazati metapodatke.

i Otkrivanje tipova
Omoguava da ispitate tipove u sklopu te da s njima radite ili da ih instancirate.
To moe biti korisno prilikom izrade prilagoenih skripti. Na primjer, mogli biste
omoguiti korisnicima da meudjeluju s programom koritenjem skriptnog jezika
kao to je JavaScript ili neki novi jezik koji ste sami smislili.
Kasno p oveziv anje s m etod am a i svojstvim a
Omoguava progameru da pozove svojstva i metode na objektima koji su dina-
miki distancirani uz upotrebu otkrivanja tipova. Ovo se naziva i dinamiko pozi-
vanje (engl. d y nam ical invocation).
Stvaranje tipova p rilikom izv oenja
Najekstremnija upotreba refleksije je izrada novih tipova tijekom izvoenja i nji-
hova upotreba za izvoenje zadataka. To biste mogli koristiti kada se prilagoena
klasa napravljena za vrijeme izvoenja izvodi bre nego openitiji kod napravljen
prilikom prevoenja.

Pregled metapodataka
(J ovom dijelu upotrijebit ete C# podrku za refleksiju da biste uitali metapodatke
iz MyMath klase.

Zaponite uzimanje objekta tipa Memberlnfo. Taj objekt iz System.Reflectiori imenskog


prostora postoji radi otkrivanja atributa lanova te za pristupanje metapodacima:
System.Reflection.Memberlnfo inf = typeof(MyMath);

Pozovite typeof operator na tip MyMath koji e vratiti objekt tipa Type koji izvodi iz
Memberlnfo.

#4
Klasa Type je srce klasa refleksije. Type uahuruje reprezentaciju tipa
objekta. Ta klasa je primarni nain za pristupanje metapodacima. Type
izvodi iz Memberlnfo te uahuruje informacije o lanovima klase (meto-
dama, svojstvima, poljima, dogaajima i tako dalje).

Sljedei korak je da pozovete GetCustomAttributes na objekt Memberlnfo prosljeujui


* tip atributa kojeg elite nai. Natrag dobijate polje objekata tipa BugFixAttribute:
object[] attributes;
attributes =
inf.GetCustomAttributes(typeof(BugFixAttribute),false);

Sad moete iterirati kroz polje i ispisati svojstva BugFixAttribute objekta. Primjer 18-2
zamjenjuje Tester klasu iz primjera 18-1.

Poglavlje 18: Atributi i refleksija | 439


Primjer 18-2. Upotreba refleksije
public static void Main()
{
MyMath mm = new MyMath();
Console.WriteLine("Calling DoFunc(7). Result: {o}",
mm.DoFuncl(7));

// Uzima informacije o lanovima i koristi


// ih za uzimanje prilagoenih atributa
System.Reflection.MemberInfo inf = typeof(MyMath);
object[] attributes;
attributes =
inf.CetCustomAttributes(
typeof(BugFixAttribute), false);

// Iterira kroz atribute i


// uzima svojstva
foreach(Object attribute in attributes)
{
BugFixAttribute bfa = (BugFixAttribute) attribute;
Console.WriteLine(\nBugID: {0 }'', bfa.BuglD);
Console.WriteLine(Programmer: {o}, bfa.Programmer);
Console.WriteLine("Date: {o}'', bfa.Date);
Console.WriteLine("Comment: {o}", bfa.Comment);
}
}
Kad stavite ovaj zamjenski kod u primjer 18-1 i pokrenete program vidjet ete ispis
metapodataka.

O tk riv a n je tip o v a
Refleksiju moete koristiti za istraivanje sadraja sklopa. Moete pronai tipove pri-
druene modulu; metode, polja, svojstva, dogaaje pridruene tipu kao i potpise svih
metoda tipa, suelja koja tip podrava i osnovnu klasu tipa.
Da biste poeli, dinamiki uitajte sklop koritenjem Assembly. Load() statike metode.
Klasa Assembly za potrebe refleksije uahuruje sam sklop. Jed an od potpisa Load
metode je:
public static Assembly.Load(AssemblyName)

U sljedeem primjeru proslijedite jezgrenu biblioteku metodi Load(). Datoteka Mscor-


lib.dll sadri jezgrene klase .N ET kostura:
Assembly a = Assembly.Load("Mscorlib");

Kad je sklop uitan moete pozvati GetTypes() da vratite polje Type objekata. Objekt
Type je kljuni dio refleksije. Type predstavlja deklaracije tipa (klase, suelja, polja,
vrijednosti i enumeracije):
Type[] types = a.GetTypes();

440 | Programiranje C#
Sklop vraa polje tipova koje moete prikazati u foreach petlji, kao to je prikazano u
primjeru 18-3. Kako ovaj primjer koristi klasu Type, trebat ete dodati direktivu using
za imenski prostor System.Reflection.

P rim jer 1 8 -3 . R e f le k s i j a s k lo p a

ttregion Using directives

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

ttcndregion

namespace ReflectingAnAssembly
{
public class Tester
{
public static void Main()
{
// to je u sklopu
Assembly a = Assembly.Load( "Mscorlib1' );
Type[] types = a.GetTypes();
foreach ( Type t in types )
{
Console.WriteLine( "Type is {o}", t );
}
Console.WriteLine(
"{0 } types found", types.Length );
}
}
}
Izlaz ovog programa bi popunio mnogo stranica pa ga neu cijelog navoditi. Evo krat-
kog odlomka:
Type is Systera.Object
Type is ThisAssembly
Type is AssemblyRef
Type is System.ICloneable
Type is System.Collections.IEnumerable
Type is System.Collections.ICollection
Type is System.Collections.IList
Type is 5ystem.Array
2373 types found

Ovaj primjer je dao polje s tipovima iz jezgrene biblioteke i ispisao ih jedan po jedan.
Polje je u mom raunalu sadravalo 2373 unosa.

U inaici 1.1 sam u svom raunalu naao 1426 unosa. Deki iz M icro-
softa su bili vrijedni!
A*
_ _ >,'

Poglavlje 18: Atributi i refleksija | 441


Refleksija tipa
Moete reflektirati samo jedan tip izMscorlib sklopa. Da biste to uinili izdvojite tipi^
sklopa s pomou typeOf ili metode GetType(), kao to je prikazano u primjeru I 8 - 4

Primjer 18-4. Refleksija tipa


#region Using direetives

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

#endregion

namespace ReflectingOnAType
{
public class Tester
{
public static void Main()
{
// Ispitivanje tipa
Type theType =
Type.GetType(
"System.Reflection.Assembly" );
Console.WriteLine(
"\nSingle Type is {o}\n", theType );
}
}
}

Otkrivanje svih lanova tipa


Moete od tipa Assembly zatraiti sve njegove lanove koritenjem metode GetMem-
bers() klase Type koja daje popis svih metoda, svojstava i polja, kao to je prikazano
u primjeru 18-5.

Primjer 18-5. Refleksija lanova tipa


# region Using direetives

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

#endregion

namespace ReflectingOnMembersOfAType
{
public class Tester
{
public static void Main()

442 | Programiranje C#
'ipritnjer 18-5. Refleksija lanova tipa (nastavak)

{
// Ispitivanje objekta
Type theType =
Type.GetType(
"System.Reflection.Assembly" );
Console.WriteLine(
\nSingle Type is {0 }\n", theType );

// Uzima sve lanove


Memberlnfo[] mbrInfoArray =
theType.GetMembers();
foreach ( Memberlnfo mbrlnfo in mbrInfoArray )
{
Console.WriteLine( "{0} is a {l}",
mbrlnfo, mbrlnfo.MemberType );
}

}
Jo jednom, izlaz je prilino dug, ali u izlazu ete vidjeti polja, metode, konstruktore
i svojstva kao u ovom odlomku:
System.Type GetType(System.String, Boolean, Boolean) is a Method
System.Type[] GetExportedTypes() is a Method
System.Reflection.Module GetModule(System.String) is a Method
;|! System.String get_FullName() is a Method

Pronalaenje metoda tipa


Moete se usredsrediti samo na metode, iskljuujui polja, svojstva i ostale lanove.
Da biste to uinili, uklonite poziv metode GetMembers():
Memberlnfo!] mbrInfoArray =
theType.GetMembers();

; i dodajte poziv metode GetMethods():


mbrInfoArray = theType.GetMethods();

/Izlaz sada sadri samo metode:


Boolean Equals(System.Object) is a Method
System.String ToStringO is a Method
System.String CreateOualifiedName(
System.String, System.String) is a Method
Boolean get_GlobalAssemblyCache() is a Method

Pronalaenje samo odreenih lanova tipa


Konano, da biste jo vie suzili pretragu moete upotrijebiti FindMembers metodu da
naete samo odreene lanove tipa. Na primjer, moete suziti pretragu na metode ije
ime poinje sa Get.

. Da biste suzili pretragu upotrijebite FindMembers metodu koja uzima etiri parametra:

Poglavlje 18: Atributi i refleksija | 443


MemberTypes
MemberTypes objekt koji zadaje tip lana kojeg traite. Tu spadaju Ali, Constructor,
Custom, Event, Field, Method, Nestedype, Property i Typeinfo. MemberTypes.Method
ete koristiti da naete metodu.
BindingFlags
Enumeracija koja nadzire na koji nain refleksija izvodi pretragu. Postoji mnogo
BindingFlags vrijednosti, ukljuujui IgnoreCase, Instance, Public, S ta tic itd.

MemberFilter
Delegat (pogledajte poglavlje 12) koji filtrira popis lanova u Memberlnfo polju
objekata. Koristite filtar Type.FilterName koji je polje klase Type koje filtrira p0
imenu.
Object
Vrijednost znakovnog niza koju filtar koristi. U ovom sluaju prosljeujete Get*
da biste nali samo one metode koje poinju sa Get.
Kompletan popis dobiven filtriranjem ovih metoda je prikazan u primjeru 18-6.

Primjer 18-6. Traenje odreenih lanova


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

flendregion

namespace FindingParticularMembers
{
public class Tester
{
public static void Main()
{
// Ispituje jedan objekt
Type theType = Type.GetType(
"System.Reflection.Assembly" );

// Samo lanovi koji su metode na Get


MemberInfo[] mbrInfoArray =
theType.FindMembers( MemberTypes.Method,
BindingFlags.Public |
BindingFlags.Static 1
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.OeclaiedOnly,
Type.FilterName, "Get*" );
foreach ( Memberlnfo mbrlnfo in mbrInfoArray )
{

444 | Programiranje C#
P r im je r 18-6. Traenje odreenih lanova (nastavak)
Console.WriteLine( " { 0 } is a { i } 1',
mbrlnfo, mbrInfo.MemberType );
}
}

i
t Kasno povezivanje
H' Kad pronaete metodu mogue ju je pozvati koritenjem refleksije. Na primjer, moda
v budete htjeli pozvati Cos() metodu iz klase System.Math koja vraa kosinus kuta.

Moete, naravno, pozvatiCos() i u normalnom toku koda, ali refleksija


vam omoguava povezivanje s tom metodom tijekom izvoenja. To
se zove kasno povezivanje (engl. late binding) i nudi fleksibilnost oda-
bira objekta za povezivanje tijekom izvoenja i njegovog programskog
pozivanja. To moe biti korisno kod izrade prilagoene skripte koju bi
mogao pokrenuti korisnik ili kad radite s objektima koji moda nisu
dostupni prilikom prevoenja. Na primjer, koritenjem kasnog povezi-
vanja program moe meudjelovati s programom za provjeru pravopisa
ili nekom drugom komponentom pokrenutog komercijalnog programa
za obradu teksta kao to je Microsoft Word.

Da biste pozvali Cos() prvo uzmite informacije o tipu iz System.Math klase:


Type theMathType = Type.GetType(''Systeni.Math");

S tom informacijom o tipu moete dinamiki uitati instancu klase koristei statiku
metodu klase Activator. Poto je Cos() statika metoda ne trebate praviti instancu
System.Math klase (a i ne moete jer System.Math nema javni konstruktor).
Activator klasa sadri etiri metode (sve su statike) koje moete koristiti da napra-
vite objekte (lokalno ili udaljeno) ili da uzmete reference postojeih objekata. Te etiri
Vmetode su:
i CreateComlnstanceFrom
: Stvara instace COM objekta.
CreatelnstanceFrom
Stvara referencu na objekt iz odreenog sklopa i tipa imena.
; GetObject
Koristi se kod rasporeivanja objekata. Rasporeivanje (engl. marshaling) je
detaljno obraeno u poglavlju 19.
Createlnstance
Stvara lokalne ili udaljene instance objekta. Na primjer:
Object theObj = Activator.CreateInstance(someType);

Poglavlje 18: Atributi i refleksija | 445


Vratimo se natrag na primjer metode Cos() gdje sad imate jedan objekt: Type objekt
imenu theMathType koji ste napravili pozivanjem GetType metode.

Prije nego to moete pozvati metodu nad objektom morate uzeti metodu koju trebate
od objekta theMathType. Da biste to uinili pozvat ete GetMethod() proslijedivi '
potpis Cos metode.

Potpis metode, sjetit ete se, je ime metode (Cos) i njeni parametri tipa. Cos()imasamo
jedan parametar: realan broj (double). Meutim, Type.GetMethod uzima dva pararne
tra. Prvi je ime metode koju traite, a drugi su parametri. Ime se prosljeuje kao niz
znakova, a parametri kao polje tipova:
Methodlnfo Cosinelnfo =
theMathType.GetMethod(''Cos",paramTypes);

Prije poziva GetMethod() morate pripremiti polje tipova:


Type[] paramTypes = new Type[l];
paramTypes[0 ]= Type.GetType("System.Double");

Ovaj kod deklarira polje Type objekata i zatim popunjava prvi element (paramTypes[oj)
s tipom koji predstavlja realan broj (double). Uzmite tip koji predstavlja double poziva-
njem statike metode Type.GetType() i prosljeivanjem niza System.Double.
Sad imate Methodlnfo objekt na kojem moete pozvati metodu. Da biste to uinili
morate proslijediti objekt nad kojim se metoda poziva i stvarnu vrijednost parametara
opet u polju. Postoje metoda statika, prosljedite objekt theMathType (da je Cos() bila
metoda instance, mogli biste upotrijebiti theObj umjesto theMathType).
Object[] parameters = new Object[l];
parameters[0] = 45 * (Math.PI/180); // 45 stupnjeva u radijanima
Object returnVal = Cosinelnfo.Invoke(theMathType,parameters);
a
Napravili ste dva polja. Prvo, paramTypes, sadri parametre tipa. Drugi,
parameters, sadri stvarnu vrijednost. Ako bi metoda uzela dva argu-
menta, trebali biste deklarirati ova polja da sadre dvije vrijednosti.
Ako metoda nije uzela nijednu vrijednost, opet moete napraviti polje,
ali mu veliinu postavite na nula!
Type[] paramTypes = new Type[0];
Ispravno je iako izgleda udno.

Primjer 18-7 prikazuje dinamiko pozivanje Cos() metode.

Primjer 18-7. Dinamiko pozivanje metode


#region Using directives

using System;
using System.Collections.Generic;
using System.Reflectlon;
using System.Text;

446 I Programiranje C#
( Primjer 18-7. Dinamiko pozivanje metode (nastavak)
Kendregion

| namespace DynamicallyInvokingAMethod
U
\ public class Tester
I {
i public static void Main()
\ (
) TyPe theMathType = Type.GetType( "System.Math" );
// Kako System.Math nema javni konstruktor
// izbacit e iznimku.
//Object theObj =
// Activator.CreateInstance(theMathType);

// Polje s jednim lanom


Type[] paramTypes = new Type[l];
paramTypes[o] = Type.GetType( "System.Double" );

// Uzima informacije o metodi Cos()


Hethodlnfo Cosinelnfo =
theMathType.GetMethod( "Cos", paramTypes );

// Popunjava polje sa stvarnim parametrima


Object[] parameters = new Objectfl];
parameters[o] = 45 * ( Math.PI / 1 8 0 ); // 45 stupnjeva u radijanima
Object returnVal = J
Cosinelnfo.Invoke( theMathType, parameters );
Console.WriteLine(
"The cosine of a 45 degree angle {o}",
returnVal );

i)
Jl'Puno posls da bi se pozvala jedna metoda. Mo ovoga pristupa je u tome da moete
^upotrijebiti refleksiju kako biste nali sklop na korisnikovom raunalu, postavili upit
||o dostupnim metodama i dinamiki pozvali neku od tih metoda.

Poglavlje 18: Atributi i refleksija I 447


POGLAVLJE 19
Rasporeivanje i rad na daljinu

Dani integriranih programa koji rade u sklopu jednog procesa na jednom raunalu
su, ako ne mrtvi, onda barem ozbiljno ranjeni. Dananji programi se sastoje od sloe-
nih komponenata koje se izvode u vie procesa, esto preko mree. Web je omoguio
distribuirane aplikacije na nain koji je bio nezamisliv ak i prije nekoliko godina a
trend je prema distribuciji odgovornosti.

Drugi trend je prema centralizaciji poslovne logike na velikim posluiteljima. Iako se


ovi trendovi ine kontradiktorni, zapravo su sinergistiki: poslovni objekti se centrali-
ziraju dok korisniko suelje i dio srednjeg sloja postaju distribuirani.
Krajni uinak je taj da objekti trebaju moi komunicirati meusobno na daljinu.
Objekti koji se izvode na posluitelju i koji upravljaju Web korisnikim sueljem tre-
baju moi meudjelovati s poslovnim objektima koji su smjeteni na centraliziranim
posluiteljima u korporacijskim sjeditima.

Proces pomicanja objekta preko granice zove se rasporeivanje po vrijednosti (engl.


marshal by value). Granice postoje na razliitim razinama apstrakcije u programu.
Najoitija granica je izmeu objekata koji se izvode na raliitim raunalima .
Proces pripremanja objekta za daljinski pristup se naziva rasporeivanje (engl. marsha-
ling). Na jednom raunalu, objekti e se moda trebati rasporeivati preko konteksta,
aplikacijskih domena ili granica procesa.

Proces je u biti aplikacija koja se izvodi. Ako objekt programa za obradu teksta eli
meudjelovati s objektom u proraunskoj tablici, oni moraju komunicirati preko gra-
nica procesa. Procesi su podijeljeni u aplikacijske domene. Aplikacijske domene (engl.
application domains) su dalje podijeljene u razne kontekste (engl. contexts ). Aplikacij-
ske domene djeluju kao jednostavni procesi, a konteksti stvaraju granice unutar kojih
se nalaze objekti sa slinim pravilima. Objekti e povremeno biti rasporeivani preko
granica konteksta i aplikacijskih domena kao i preko granica procesa i raunala.
Kad se objekt rasporeuje po vrijednosti to izgleda kao da ga se alje kroz icu od jed-
nog raunala prema drugom, kao to se kapetan Kirk teleportira na povrinu planeta
nekoliko stotina kilometara ispod orbitirajueg USS Enterprisea.

448
U Z vjezdanim s ta z am a , Kirk je zaista premjeten na planet, ali u .NET-u to je iluzija.
Ako stojite na povrini planeta, mogli biste misliti da vidite pravog Kirka i razgovarate
s njim, ali uope ne razgovarate s Kirkom ve s posrednikom, moda hologramom, iji
je posao da prenese vau poruku do Enterprisea, pravom Kirku.
Izmeu vas i Kirka postoji vei broj odvoda". Odvod (engl. sink) je objekt iji je posao
da provodi pravila. Na primjer, ako vam Kirk pokua rei neto to bi moglo utjecati
na razvoj vae civilizacije, odvod primarne direktive bi mogao zabraniti prijenos.
' Kad pravi Kirk odgovori, on alje odgovor kroz razne odvode dok poruka ne doe do
posrednika i posrednik vam ne prenese poruku. Vama se ini kao da je Kirk zapravo
tamo, ali on se zapravo prikrada iza vas da osujeti va podli plan. Naalost, ispada da
je gospodin Sulu cijelo vrijeme kontrolirao hologram. Vie sree u sljedeoj epizodi.
, Stvarni prijenos vae poruke obavlja kanal (engl. channel). Posao kanala je da zna
kako prenijeti poruku od Enterprisea do planeta. Kanal radi s formaterom (engl. for-
matter). Formater osigurava da je poruka u odgovarajuem formatu. Moda govorite
samo vulkanski, a jadni kapetan ne. Formater moe prevesti poruku na standardni
federacijski jezik i prevesti Kirkov odgovor sa standardnog federacijskogjezika natrag
na vulkanski. ini se da razgovarate jedan s drugim, ali formater (poznat kao univer-
\ zalni prevoditelj u Zvjezdanim stazama) neprimjetno omoguava komunikaciju.
, Ovo poglavlje opisuje kako se objekti mogu prenositi preko raznih granica i kako
\ posrednici i zamjenski elementi mogu stvoriti iluziju da je va objekt prenesen kroz mre-
ni kabel do raunala na drugom kraju ureda ili svijeta. Pored toga, ovo poglavlje opi-
suje uloge formatera, kanala i odvoda te primjenu ovih koncepata u programiranju.

Aplikacijske domene
Proces je u biti aplikacija koja se izvodi. Svaka .NET aplikacija se izvodi unutar vla-
stitog, procesa. Ako imate otvorene programe Word, Excel i Visual Studio, imate tri
procesa koji se izvode. Ako otvorite Outlook, pokree se novi proces. Svaki proces
Wi je podijeljen na jednu ili vie aplikacijskih domena. Aplikacijska domena djeluje kao
proces, ali koristi manje resursa.
Aplikacijske domene mogu se neovisno pokretati i zaustavljati. One su sigurne, jed-
nostavne i svestrane. Aplikacijska domena moe pruiti otpornost na pogreke. Ako
W pokrenete objekt u drugoj aplikacijskoj domeni i onda se izvoenje prekine, to e sru-
iti aplikacijsku domenu, ali ne i cijeli program. Moete zamisliti Web posluitelj koji
koristi aplikacijsku domenu za izvoenje korisnikovog koda. Ako u kodu ima neki
problem, Web posluitelj moe nastaviti raditi.
Aplikacijsku domenu uahurava instanca klase AppDomain, koja prua vei broj metoda
i svojstava. U tablici 19-1 je skraeni popis.

Poglavlje 19: Rasporeivanje i rad na daljinu | 449


Tablica 19-1 Metode i svojstva AppDomain klase

Metoda ili svojstvo Detalji

CurrentDomain Javno statiko svojstvo koje vraa aplikacijsku dom enu za trenutnu dretvu

CreateDomain() Preoptereena javna statika m etoda koja stvara novu aplikacijsku dom enu

GetCurrentThreadID() Javna statika m etoda koja vraa id en tifikato r tren u tn e dretve

Unload() Javna statika m etoda koja brie zadanu aplikacijsku dom enu

FriendlyName Javna svojstvo koje vraa lako prijateljsko im e za aplikacijsku dom enu

DefineDynamicAssembly() Preoptereena javna statika m etod a koja definira dinam iki sklop u trenutnoj
aplikacijskoj dom eni

ExecuteAssembly() Javna m etoda koja izvodi zadani sklop

GetData() Javna m etoda koja uzim a vrijed nost sprem ljenu u tren u tn o j aplikacijskoj domeni

Load() Javna m etoda koja uitava sklop u trenutnu aplikacijsku dom enu

SetAppDomainPolicy() Javna m etoda koja postavlja sigurnosna pravila za trenu tn u aplikacijsku domenu

SetData() Javna m etoda koja stavlja podatke u zadano svojstvo aplikacijske dom ene

Aplikacijske domene takoer podravaju mnotvo dogaaja (ukljuujui AssemblyLoad,


AssemblyResolve, ProcessExit i ResourceResolve) koji se javljaju kad su sklopovi prona-
eni, uitani, izvedeni i ispranjeni.
Svaki proces ima incijalnu aplikacijsku domenu a moe imati i dodatne aplikacijske
domene ako ih napravite. Svaka aplikacijska domena postoji u tono jednom procesu.
Svi programi u ovoj knjizi su imali jednu aplikacijsku domenu - podrazumijevanu. Svaki
proces ima svoju podrazumijevanu aplikacijsku domenu. U mnogim, moda u veini
programa koje piete, podrazumijevana aplikacijska domena e biti sve to vam treba.
Meutim, postoje sluajevi kad jedna domena nije dovoljna. Mogli biste napraviti
drugu aplikacijsku domenu ako trebate pokrenuti biblioteku koju je napisao drugi
programer. Moda ne vjerujete biblioteci i elite ju izolirati u njenoj domeni tako
da, ako metoda u toj biblioteci prouzroi blokiranje programa, onda e samo izoli-
rana domena biti zahvaena. Da ste autor softvera Internet Information Server (IIS),
Microsoftovog posluitelja za Web stranice, mogli biste pokrenuti novu aplikacijsku
domenu za svaki dodatak ili svaku virtualnu mapu koju udomljavate. To bi osiguralo
otpornost na pogreke tako da, ako bi se jedna Web aplikacija blokirala, to ne bi utje-
calo na cijeli Web posluitelj.
Takoer je mogue da druga biblioteka zahtijeva drukiji sigurnosni okoli. Stvara-
nje druge aplikacijske domene omoguava da istovremeno postoje dva sigurnosna
okolia.
Svaka aplikacijska domena ima svoj sigurnosni sustav, pa aplikacijska domena moe
sluiti i kao sigurnosna granica.
Aplikacijske domene nisu dretve (engl. threads) i trebalo bi ih razlikovati od njih.
W in32 dretva u jednom trenuku postoji u samo jednoj aplikacijskoj domeni te moe
doznati (i izvijestiti) u kojoj aplikacijskoj domeni se izvodi. Aplikacijske domene se

4S0 | Programiranje C#
koriste da izoliraju aplikacije. Unutar aplikacijske domene u nekom trenutku moe biti
vie dretvi koje se izvode (pogledajte poglavlje 22).
Da biste vidjeli kako rade aplikacijske domene, postavimo sljedei primjer. Pretposta-
vimo da elite da program instancira klasu Shape, ali u drugoj aplikacijskoj domeni.
a*
Nema pravog razloga za smjetanje klase Shape u drugu aplikacijsku
domenu, osim da se prikae kako ovaj koncept funkcionira. Mogue je,
meutim, da sloenijim objektima zatreba druga aplikacijska domena
kako bi se osigurao drugi sigurnosni okoli. Nadalje, ako stvarate klase
koje bi mogle sudjelovati u riskantnom ponaanju, moda biste se htjeli
zatititi tako da ih pokrenete u drugoj aplikacijskoj domeni.

Normalno, uitali biste Shape klasu iz sklopa, ali da primjer ostane jednostavan, stavit
ete definiciju klase Shape u istu datoteku u kojoj se nalazi i sav drugi izvorni kod u ovom
primjeru (pogledajte poglavlje 17). Nadalje, u proizvodnom okoliu mogli biste pokre-
nuti metode Shape klase u odvojenoj dretvi, ali radi jednostavnosti ete zasad zanemariti
dretve (dretve su detaljno obraene u poglavlju 20). Izbjegavanjem ovih pitanja, moete
primjer sauvati jednostavnim i koncentrirati se na detalje stvaranja i koritenja aplika-
cijskih domena i premjetanja objekata preko granica aplikacijskih domena.

Stvaranje i upotreba aplikacijskih domena


Napravite novu aplikacijsku domenu pozivanjem statike metode CreateDomain() na
klasi AppDomain:
AppDomain ad2 =
AppDomain.CreateDomain("Shape Domain");

Time se stvara nova aplikacijska domena s prijateljskim imenom Shape Domain. Prijatelj-
sko ime (engl. friendly name) je pogodnost za programera. To je nain za programski
rad s domenom, bez znanja o internom prikazu domene. Moete provjeriti prijateljsko
ime domene u kojoj radite tako da provjerite svojstvo System.AppDomain.CurrentDo-
main.FriendlyName.

Kad ste instancirali AppDomain objekt, moete napraviti instance klasa, suelja itd.
koritenjem metode Createlnstance(). Evo njenog potpisa:
public ObjectHandle Createlnstance(
string assemblyName,
string typeName,
bool ignoreCase,
BindingFlags bindingAttr,
Binder binder,
object[] args,
Culturelnfo culture,
objectf] activationAttributes,
Evidence securityAttributes
);
i naina upotrebe:

Poglavlje 19: Rasporeivanje i rad na daljinu | 4S1


ObjectHandle oh = ad2.CreateInstance(
"ProgCSharp", // Ime sklopa
"ProgCSharp.Shape", // Ime tipa s imenskim prostorom
false, // Ignorira mala i velika slova
System.Reflection.BindingFlags.CreateInstance, // Zastavica
nuli, // Spona
new o bje d[] {3 , 5}, // Argumenti
nuli, // Kultura
.nuli, // Aktivacijski atributi
nuli ); // Sigurnosni atributi

Prvi parametar (ProgCSharp) je ime sklopa, a drugi parametar (ProgCSarp. Shape) je ime
klase. Ime klase mora biti potpuno kvalificirano.
Spona (engl. binder) je objekt koji omoguava dinamiko povezivanje sklopa prilikom
izvoenja. Njen posao je da vam omogui prosljeivanje informacija o objektu koji
elite napraviti, da napravi taj objekt za vas i da povee referencu na taj objekt. U veli-
koj veini sluajeva, ukljuujui ovaj primjer, koristit ete podrazumijevanu sponu
to se postie prosljeivanjem nuli.

Moete napisati vlastitu sponu koja bi mogla, na primjer, usporediti va identifikator s


bazom podataka i preusmjeriti povezivanje na drugi objekt, ovisno o vaem identitetu
i doputenjima.

Povezivanje (engl. binding ) se najee odnosi na dodavanje imena


objektu. Dinamiko povezivanje se odnosi na sposobnost dodavanja
imena kad se program izvodi, za razliku od sluaja kad se to radi tije-
kom prevoenja. U ovom prim jeru, Shape objekt je povezan s varija-
blom instance u vrijeme izvoenja preko metode Createlnstance() apli-
kacijske domene.

Zastavice za povezivanje pomau sponi da fino prilagodi svoj nain rada prilikom
povezivanja. U ovom primjeru, upotrijebite vrijednost Createlnstance enumeracije Bin-
dingFlags. Podrazumijevana spona obino povezuje samo javne klase, ali moete dodati
zastavice tako da spona povezuje i javne klase ako imate odgovarajua doputenja.
Kad povezujete sklop prilikom izvoenja nemojte ga postaviti tako da se uita u vri-
jeme prevoenja. Radije programski zadajte koji sklop elite i s njim poveite varijablu
prilikom izvoenja programa.
Konstruktor kojeg pozivate uzima dvije cjelobrojne vrijednosti koje moraju biti sta-
vljene u polje objekata (new ob jec tf] {3 ,5 })- Moete poslati nuli vrijednost za kulturu
jer ete koristiti podrazumijevanu (en) kulturu i neete specificirati aktivacijske ili
sigurnosne atribute.
Dobivate natrag identifikator objekta (engl. ob jed handle) - tip koji se koristi za proslje-
ivanje objekta (u omotanom stanju) izmeu vie aplikacijskih domena bez uitavanja
metapodataka omotanog objekta u svakom objektu kroz kojeg identifikator ObjectHan-
dle prolazi. Stvarni objekt moete dobiti pozivanjem Unwrap() metode na identifikatoru
objekta i pretvaranjem rezultirajueg objekta u stvarni tip, u ovom sluaju Shape.

452 | Programiranje C#
Createlnstance() metoda prua mogunost da napravite objekt u novoj aplikacijskoj
domeni. Ako biste napravili objekt koritenjem new operatora, bio bi napravljen u tre-
nutnoj aplikacijskoj domeni.

1 Rasporeivanje preko granica aplikacijskih domena


Napravili ste Shape objekt u Shape domeni ali mu pristupate preko Shape objekta u origi-
nalnoj domeni. D a biste pristupili Shape objektu u drugoj domeni, morate rasporediti
objekt preko granice domene.
Rasporeivanje je proces pripremanja objekta za prelazak preko granice. Jo jed-
nom, poput kapetana Kirka koji se teleportira na povrinu planeta. Rasporeivanje
se postie na dva naina: po vrijednosti ili po referenci. Kad se objekt rasporeuje po
vrijednosti (engl. marshal by value), radi se kopija objekta. To je kao da vas nazovem
preko telefona i zamolim da mi poaljete kalkulator, a vi nazovete trgovinu i kaete im
da mi dostave kalkulator koji je identian vaem. Mogu koristiti kopiju jednako kao i
original, ali unoenje brojeva na mojoj kopiji kalkulatora nema uinka na original.
Rasporeivanje po referenci je gotovo isto kao da mi poaljete svoj kalkulator. Evo
; kako to funkcionira. Vi mi zapravo ne dajete original ve ga, umjesto toga, drite u svo-
joj kui i aljete mi posrednika koji je jako pametan: kad ja pritisnem gumb na posred-
} niku, on alje signal originalu u vaoj kui i broj se pojavljuje tamo. Pritiskanje gumba
T- na posredniku za mene je jednako pritiskanju gumba na originalnom kalkulatoru.

'i Razumijevanje rasporeivanja s posrednicima


^ Analogije s kapetanom Kirkom i kalkulatorom su dobre to se tie analogije, ali to se
<T zapravo dogaa kad rasporedite objekt po referenci? CLR pozivajuem objektu prua
transparentni posrednik (TP).
j Metoda transparentnog posrednika je da uzme sve to zna o pozivu metode (povratnu
S vrijednost, parametre itd.) sa stoga i smjesti te informacije u objekt koji implementira
,y; IMessage suelje. To IMessage suelje se prosljeuje objektu RealProxy.
^ RealProxy je apstraktna osnovna klasa iz koje izvode svi posrednici. Moete imple-
H mentirati vlastiti stvarni posrednik (engl. real proxy) ili bilo koji od drugih objekata u
'i ovom procesu osim transparentnog posrednika. Podrazumijevani stvarni posrednik
e predati IMessage objekt seriji objekata odvoda.
i Bilo koji broj objekata odvoda moe biti koriten, ovisno o broju pravila koja elite
' provesti, ali zadnji odvodni objekt u lancu e staviti IMessage u kanal. Kanali su podi-
,) jeljeni na klijentske i posluiteljske kanale, a njihov posao je prenoenje poruka preko
granica. Kanali su odgovorni za razumijevanje transportnog protokola. Stvarni format
poruke tijekom prijelaza preko granica odreuje formater. .NET kostur prua dva for-
matera: SOAP formater (koji je podrazumijevani za HTTP kanale) i binarni formater
?' (koji je podrazumijevani za TCP/IP kanale). Moete napraviti vlastite formatere, a ako
y vam nije dosta samokanjavanja, i vlastite kanale.

Poglavlje 19: Rasporeivanje i rad na daljinu | 453


Kad je poruka prenesena preko granice primaju je kanal i formater na strani posluite-
lja koji ponovno sastavljaju IMessage i prosljeuju ga dalje kroz jedan ili vie objekata
odvoda na posluiteljskoj strani. Zadnji odvodni objekt u lancu odvodnih objekata je
StackBuilder ija je funkcija da preuzme IMessage i vrati ga natrag u okvir stoga tako
da izgleda kao poziv metode na posluitelju.

Zadavanje naina rasporeivanja


Da biste prikazali razliku izmeu rasporeivanja po vrijednosti i rasporeivanja po
referenci, u sljedeem primjeru uputit ete Shape objekt da se rasporedi po referenci
i dodijeliti mu varijablu lanicu tipa Point koju ete odrediti da se rasporeuje po vri-
jednosti.
Primjetite da svaki put kad napravite klasu koja se moe koristiti preko granice,
morate zadati kako e se rasporeivati. Objekti se obino ne mogu rasporeivati.
Morate poduzeti korake da kaete da se objekt moe rasporeivati po vrijednosti ili
po referenci.
Najlaki nain da kaete da se objekt rasporeuje po vrijednosti je da ga oznaite atri-
butom Serializable:
[Serializable]
public class Point

Kad je objekt serijaliziran, njegovo interno stanje se ispisuje u tok, bilo za rasporeiva-
nje ili za spremanje. Detalji serijalizacije su obraeni u poglavlju 21.
Najlaki nain da uinite da se objekt rasporeuje po referenci jest da izvedete njegovu
klasu iz MarshalByRefObject:
public class Shape : MarshalByRefObject

Klasa Shape imat e samo jednu varijablu lanicu -u p p erleft. Ta varijabla e biti Point
objekt koji sadri koordinate gornjeg lijevog kuta oblika.
Konstruktor za Shape e inicijalizirati svog Point lana:
public Shape(int upperLeftX, int upperLeftY)
{
Console.WriteLine( ''[{0}] Eventfl}",
System.AppDomain.CurrentDomain.FriendlyName,
"Shape constructor");
upperLeft = new Point(upperLeftX, upperLeftY);
}
Opskrbite Shape metodom za prikazivanje pozicije:
public void ShowUpperLeft()
{
Console.WriteLine( "[{o}] Upper left: {l},{2}",
System.AppDomain.CurrentDomain.FriendlyName,
upperLeft.X, upperLeft.Y);
}

454 | Programiranje C#
Takoer pruite i drugu metodu za vraanje njegove upperLeft varijable:
public Point GetUpperLeft()
{
return upperLeft;
}
point klasa je takoer vrlo jednostavna. Sadri konstruktor koji inicijalizira svoje dvije
varijable lanice i metode za uzimanje njihovih vrijednosti.
Kad uzmete Shape zatraite njegove koordinate:
sl.ShowllpperLeft(); // Trai od objekta da se prikae

Zatim zatraite da vrati svoju upperLeft koordinatu kao Point objekt koji ete
promijeniti:
Point localPoint = sl.GettlpperLeft();

localPoint.X = 500;
localPoint.V = 600;

Zatraite od Point da ispie svoje koordinate pa zatim to isto zatraite i od objekta


Shape. Hoe li se promjena na lokalnom Point objektu odraziti na Shape objekt? To
ovisi o tome kako se Point objekt rasporeuje. Ako se rasporeuje po vrijednosti,
localPoint objekt e biti kopija, a Shape objekt nee biti zahvaen promjenom vrijed-
nosti varijable localPoint. Ako, s druge strane, promijenite Point objekt da se raspo-
reuje po referenci, imat ete posrednika prema stvarnoj upperLeft varijabli a njegova
promjena promijenit e i Shape objekt. Primjer 19-1 to prikazuje. Obavezno napravite
primjer 19-1 u projektu ProgCSharp. Kad Main() instancira Shape objekt, metoda trai
ProgCSharp.exe.

Primjer 19-1. Rasporeivanje preko granica aplikacijske domene


Sregion Using directives

using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Reflection;
using System.Text;

Sendregion

namespace Marshaling
{

// Za rasporeivanje po referenci smjestite u komentar


// atribut, a izdvojite iz komentara osnovnu klasu
[Serializable]
public class Point // : MarshalByRefObject
{
private int x;
private int y;

Poglavlje 19: Rasporeivanje i rad na daljinu | 455


Primjer 19-1. Rasporeivanje preko granica aplikacijske domene (nastavak)

public Point (int x, int y)


{
Console.WriteLine( "[{0}] {l}">
System.AppDomain.CurrentDomain.FriendlyName,
"Point constructor");

this.x = x;
this.y = y;

public int X
{
get
{
Console.WriteLine( "[{0}] {l}",
System.AppDomain.CurrentDoinain.FriendlyName)
"Point x.get");

return this.x;
}

set
{
Console.WriteLine( "[{0}] {l}",
System.AppDomain.CurrentDomain.FriendlyName,
"Point x.set");
this.x = value;
}

public int V
{
get
{
Console.WriteLine( "[{0}] {l}",
System.AppDomain.CurrentDomain.FriendlyName,
"Point y.get");
return this.y;
}

set
{
Console.WriteLine( "[{0}] {l}",
System.AppDomain.CurrentDomain.FriendlyName,
"Point y.set");
this.y = value;
}
}
}
// Klasa shape rasporeuje po referenci
public class Shape : MarshalByRefObject
{

456 | Programiranje C#
primjer 19-1. Rasporeivanje preko granica aplikacijske domene (nastavak)
private Point upperLeft;

public Shape(int upperLeftX, int uppeiLeftV)


{
Console.WriteLine( "[{0 }] {l}",
System.AppDomain.CurrentDomain.FriendlyName,
"Shape constructor");

upperLeft = new Point(upperLeftX, upperLeftY);

public Point GetUpperLeft()


{
return upperLeft;
}

public void ShowUpperLeft()


{
Console.WriteLine( "[{0 }] Upper left: {l},{2 }",
System.AppDomain.CurrentDomain.FriendlyNaine,
upperLeft.X, upperLeft.Y);
}
}
public class Tester
{
public static void Main()
{

Console.WriteLine( "[{0 }] {i}",


System.AppDomain.CurrentDomain.FriendlyName,
"Entered Main'');

// Stvara novu aplikacijsku domenu


AppDomain ad2 =
AppDomain.CreateDomain("Shape Domain);

// Assembly a = Assembly.LoadFromCProgCSharp.exe");
// Object theShape = a.CreateInstance("Shape);
// Instanciranje Shape objekta
ObjectHandle oh = ad2.CreateInstance(
"Marshaling",
"Marshaling.Shape", false,
System.Reflection.BindingFlags.CreateInstance,
nuli, new objeetj] {3, 5},
nuli, nuli, nuli );

Shape si = (Shape) oh.Unwrap();

si.ShowUpperLeft(); // Zahtijeva od objekta da prikae

// Dobili ste lokalnu kopiju? Posrednika?


Point localPoint = sl.GetUpperLeft();

// Dodjeljuje nove vrijednosti

Poglavlje 19: Rasporeivanje i rad na daljinu I 457


localPoint.X = 500;
localPoint.Y = 600;

// Prikazuje vrijednost lokalnog Point objekta


Console.WriteLine( [{0}] localPoint: {l}, {2}",
M
System.AppDomain.CurrentDomain.FriendlyName,
localPoint.X, localPoint.Y);

si.ShowUpperLeft(); // Prikazuje vrijednost jo jednom


}
}
}
Prouite kod, ili jo bolje, stavite ga u program za pronalaenje pogreaka i proite 1
kroz njega. Izlaz otkriva da se Shape i Point konstruktori izvode u domeni Shape, ba 'M
kao i pristup vrijednostima Point objekata u Shape. |
''I

Svojstvo je postavljeno u originalnoj aplikacijskoj domeni a lokalna kopija Point


objekta je postavljena na vrijednosti 500 i 600. Kako se Point objekt rasporeuje po
vrijednosti, zapravo postavljate kopiju Point objekta. Kada od Shape zatraite da pri-
kae svoju varijablu lanicu upperLeft vidjet ete da je nepromijenjena.
Da biste zavrili eksperiment, komentirajte atribut na vrhu deklaracije Point objekta i
izvucite iz komentara osnovnu klasu:
// [serializable]
public class Point : MarshalByRefObject

Sad ponovno pokrenite program. Izlaz se dosta razlikuje:


[Marshaling.vshost.exe] Entered Main
[Shape Domain] Shape constructor
[Shape Domain] Point constructor
[Shape Domain] Point x.get
[Shape Domain] Point y.get
[Shape Domain] Upper left: 3,5
[Shape Domain] Point x.set
[Shape Domain] Point y.set
[Shape Domain] Point x.get
[Shape Domain] Point y.get
[Marshaling.vshost.exe] localPoint: 500, 600
[Shape Domain] Point x.get
[Shape Domain] Point y.get
[Shape Domain] Upper left: 500,600

Ovaj put dobijate posrednika za Point objekt i svojstva se postavljaju preko posrednika
na originalnu Point varijablu lanicu. Prema tome, promjene se odraavaju unutar
samog Shape.

Kontekst
Aplikacijske domene su podijeljene na kontekste. Kontekst (engl. context) se moe
shvatiti kao podruje unutar kojeg objekti dijele pravila koritenja. Ta pravila uklju-
uju sinkronizacijske transakcije (pogledajte poglavlje 20), i tako dalje.

458 | Programiranje C#
Kontekstno vezani i okretni objekti
Objekti mogu biti vezani za kontekst (engl. context-bound ) ili okretni (engl. agile).
su kontekstno vezani oni postoje u nekom kontekstu i da bi se s njima komuni-
55
ciralo, poruka mora biti rasporeena. Ako su okretni, objekti djeluju unutar kontek-
sta objekta koji ih je pozvao: njihove metode se izvode u kontekstu objekta koji ih je
pozvao pa rasporeivanje nije potrebno.
394- Pretpostavite da imate objekt A koji meudjeluje s bazom podataka pa je oznaen da
podrava transakcije. To ini kontekst. Svi pozivi metoda na objektu A se dogaaju
unutar konteksta zatite koji prua transakcija. Objekt A moe odluiti da poniti
transakciju, a sve akcije poduzete nakon zadnjeg odobrenja su ponitene.
Pretpostavite da imate drugi objekt, B, koji je okretan. Sad pretpostavite da objekt pro-
'?p sljeduje referencu baze podataka objektu B i zatim poziva metodu na B. Moda su A i B
T upovratnoj vezi, u kojoj e objekt B obaviti neki posao i zatim vratiti rezultate objektu A.
Kako je B okretan objekt, njegove metode djeluju u kontekstu objekta koji ih je pozvao.
Prema tome, bit e mu dodijeljena zatita koju prua objekt A. Promjene koje B napravi
u bazi podataka bit e ponitene ako A poniti transakciju jer se metode objekta B izvode
unutar konteksta objekta koji ih je pozvao. Do sada sve funkcionira kako treba.
i Da li bi objekt B trebao biti kontekstno vezan ili okretan? U razmotrenom sluaju B
je dobro funkcionirao kao kontekstno vezan. Pretpostavite da postoji jo jedna klasa,
t klasa C. Ona ne posjeduje transakcije i poziva metodu na objektu B koja mijenja bazu
podataka. Sad objekt A pokuava ponititi transakciju, ali na nesreu, posao koji je
B obavio za C se odvio u kontekstu klase C i zato nije imao podrku za transakcije.
Huh, taj posao ne moe biti poniten.
Da je B bio kontekstno vezan objekt kad ga je A napravio, onda bi B naslijedio kon-
s tekst objekta A. U tom sluaju, kada je C pozvala metodu na objektu B, ona bi trebala
t. biti rasporeena preko granice konteksta, ali kad bi B izveo metodu, onda bi to bilo u
kontekstu transakcije objekta A. Ovo je ve puno bolje.
' To bi funkcioniralo ako bi B bio kontekstno vezan, ali bez atributa. B bi naravno
... mogao imati atribute iz vlastitog konteksta, a ti bi atributi mogli nametnuti objektu B
da bude u razliitom kontekstu od objekta A. Na primjer, objekt B bi mogao imati tran-
si sakcijski atribut RequiresNew. U tom sluaju, objekt B bi u trenutku stvaranja dobio
, novi kontekst i zato ne bi mogao biti u kontekstu objekta A. Prema tome, kad bi objekt
A ponitio transakciju, posao koji je obavio objekt B ne bi mogao biti poniten. Mogli
biste oznaiti objekt B s vrijednou enumeracije RequiresNewjer je objekt B kontrolna
metoda. Kad objekt A poduzme akciju nad bazom podataka, on obavjetava objekt B
koji aurira kontrolu unosa. Ne elite da posao koji je obavio objekt B bude poniten
kad objekt A poniti svoju transakciju. elite da objekt B bude u vlastitom transakcij-
skom kontekstu, ponitavajui samo vlastite pogreke, a ne i one od objekta A.
{ Objekt prema tome ima tri izbora. Prvi izbor je da bude okretan. Takvi objekti djeluju
Si u kontekstu pozivatelja. Drugi izbor je da bude vezan za kontekst (to se postie izvo-
l i enjem iz ContextBoundObject, ali bez vlastitih atributa kako bi djelovao u kontekstu
ifc

Poglavlje 19: Rasporeivanje i rad na daljinu | 459


objekta koji ga je stvorio). Trei izbor je da bude vezan za kontekst ali sa atributima
konteksta tako da djeluje samo u kontekstu koji odgovara atributima.

to ete odabrati ovisi o tome kako e se objekt koristiti. Ako je objekt jednostavan
kalkulator koji moda zahtijeva sinkronizaciju ili transakcije ili podrku za kontekst,
onda bi ga trebali napraviti da bude okretan. Ako bi va objekt trebao koristiti kon-
tekst objekta koji ga stvara, trebali biste ga napraviti tako da bude vezan za kontekst
pozivatelja, ali bez atributa. Konano, ako va objekt ima svoje zahtjeve u vezi s kon-
tekstom, trebali biste mu dodijeliti odgovarajue atribute.

Rasporeivanje preko granica konteksta


Posrednik nije potreban kad pristupate okretnim objektima unutar jedne aplikacijske
domene. Kad objekt u jednom kontekstu pristupa kontekstno vezanom objektu u
drugom kontekstu, onda to ini preko posrednika i provode se dva pravila konteksta.
U tom smislu kontekst stvara granicu, a pravilo se provodi na granici izmeu dva
konteksta.
Na primjer, kad kontekstno vezan objekt oznaite atributom Systera. EnterpriseServi-
ces.Synchronization elite da sustav upravlja sinkronizacijom tog objekta. Svi objekti
izvan tog konteksta moraju prijei granicu konteksta kako bi se povezali s nekim od
objekata, a tada e se primjeniti pravilo sinkronizacije.

' - I Strogo govorei, oznaavanje dviju klasa Synchronization atributom ne


1 garantira da e one zavriti u istom kontekstu. Svaki atribut ima pravo
' tk*,' glasa o tome je li sretan u trenutnom kontekstu prilikom aktivacije.
Ako su dva objekta oznaena za sinkronizaciju, ali je jedan prozvan,
onda e biti smjeteni u razliite kontekste.

Objekti se, ovisno o tome kako su stvoreni, drugaije rasporeuju preko granica kon-
teksta:
Tipini objekti se uope ne rasporeuju. Okretni su unutar aplikacijskih
domena.
Objekti oznaeni atributom Serializable rasporeuju se po vrijednosti preko apli-
kacijskih domena i okretni su.
Objekti koji izvode iz MarshalByRefObject rasporeuju se po referenci preko apli-
kacijskih domena i okretni su.
Objekti koji izvode iz ContextBoundObject rasporeuju se po referenci preko apli-
kacijskih domena i preko granica konteksta.

Rad na daljinu
Osim to se mogu rasporeivati preko granica konteksta i aplikacijskih domena,
objekti se mogu rasporeivati i preko granica procesa pa ak i preko granica raunala.

460 | Programiranje C#
Kad je objekt rasporeen, bilo po vrijednosti ili po posredniku, preko granice izmeu
procesa ili raunala, onda se kae da se s njim radi na daljinu.

fI
Razumijevanje tipova posluiteljskih objekata
U .N ET kosturu postoje dva tipa posluiteljskih objekata s kojima se moe raditi na
daljinu: dobro poznati i klijentski aktivirani. Komunikacija sa dobro poznatim objek-
tima (engl. well known objects) se uspostavlja svaki put kad klijent poalje poruku. Za
razliku od klijentski aktiviranih objekata, ne postoji stalna veza sa dobro poznatim
objektima.

Dobro poznati objekti dolaze u dvije varijante: kao unikatni (engl. singleton) i s jed-
nim pozivom (engl. single call). Kod dobro poznatih unikatnih objekata sve poruke za
objekt, od svih klijenata, otpremaju se jednom objektu koji se izvodi na posluitelju.
On se stvara kad se klijent prvi put pokua spojiti s njim i postoji da bi pruio uslugu
bilo kojem klijentu koji mu moe pristupiti. Dobro poznati objekti moraju imati kon-
struktor bez parametara.

Ako se koristi dobro poznati objekt s jednim pozivom, svaku novu poruku od kli-
jenta obrauje novi objekt. To je vrlo korisno kod skupina posluitelja, gdje se nizovi
poruka od klijenta mogu naizmjence obraivati na razliitim posluiteljima ovisno o
optereenju.

Klijentski aktivirane objekte najee koriste programeri koji stvaraju specijalizirane


posluitelje koji pruaju usluge specijaliziranim klijentima koje takoer piu. U ovom
scenariju klijent i posluitelj stvaraju i odravaju vezu sve dok se potrebe klijenta ne
zadovolje.'

Zadavanje posluitelja sa sueljem


Najbolji nain da shvatite rad na daljinu je da proete kroz primjer. Napravit emo
jednostavnu klasu Calculator s etiri metode, poput one koju smo koristili u ranijoj
i
m raspravi o Web uslugama (pogledajte poglavlje 15) i koja implementira suelje prika-
& zano u primjeru 19-2.

Primjer 19-2. Suelje kalkulatora


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

s? Klijentski aktivirani objekti mogu biti manje robustni. Ako poziv klijentski aktiviranog objekta ne uspije,
projektant mora pretpostaviti da se objekt izgubio na posluitelju i mora ga ponovno stvoriti.
f

Poglavlje 19: Rasporeivanje i rad na daljinu | 461


Primjer 19-2. Suelje kalkulatora (nastavak)
namespace Calculator
{
public interface ICalc
{
double Add( double x, double y );
double Sub( double x, double y );
double Mult( double x, double y );
double Div( double x, double y );
}
}
Spremite ovo u datoteku lcalc.cs i prevedite u datoteku Calculator.dll. Da biste napra-
vili i preveli izvornu datoteku u Visual Studiju napravite novi projekt tipa C# Class
Library, unesite definiciju suelja u prozoru Edit i zatim odaberite Build iz Visual Stu-
dio izbornika. Alternativno, ako ste unijeli izvorni kod u Notepad ili neki drugi editora
teksta, moete prevesti datoteku iz odzivnika, unoenjem sljedee naredbe:
esc /t:library ICalc.cs

Postoje ogromne prednosti u implementiranju posluitelja s pomou suelja. Ako


implementirate kalkulator kao klasu, klijent se mora povezati s tom klasom da bi
deklarirao instance na klijentu. To uvelike umanjuje prednost rada na daljinu poto
promjene na posluitelju zahtijevaju da se aurira definicija klase na klijentu. Drugim
rijeima, klijent i posluitelj bi bili tijesno povezani. Suelja pomau razdvajanju dva
objekta. Zapravo, kasnije moete aurirati implementaciju na posluitelju, a sve dok
posluitelj ispunjava dogovor koji namee suelje, klijent se uope ne mora mijenjati.

Izgradnja posluitelja
Da biste napravili posluitelj koji je koriten u primjeru 19-3, napravite CalculatorSer-
ver.es u novom C # Console Application projektu (obavezno ukljuite referencu prema
Calculator.dll) i prevedite ga odabirom opcije Build s izbornika.
CalculatorServer klasa implementira ICalc. Ona izvodi iz MarshalByRefObject pa e
dostaviti posrednika kalkulatora klijentskoj aplikaciji:
class CalculatorServer : MarshalByRefObject, Calculator.ICalc

Implementacija je malo sloenija od jednog konstruktora i jednostavnih metoda koje


implementiraju etiri metode.
U primjeru 19-3 logiku za posluitelja stavit ete u Main() metodu CalculatorServer.es.
Va prvi zadatak je da napravite kanal. Koristite H TTP kao transportni mehanizam.
Moete koristiti HTTPChannel tip kojeg prua .NET:
HTTPChannel ehan = new HTTPChannel(65100);

Primjetite da registrirate kanal na TCP/IP ulazu 65100 (pogledajte raspravu o broje-


vima ulaza u poglavlju 21).

462 | Programiranje Cif


Zatim registrirajte kanal s pomou ChannelService CLR-a koritenjem statike metode
RegisterChannel:
ChannelServices.RegisterChannel(chan);

Ovaj korak informira .NET da ete pruati HTTP usluge na portu 65100, slino kao
to US radi na portu 80. Poto ste registrirali HTTP kanal, a niste osigurali formater,
pozivi metoda e koristiti SOAP formater kao podrazumijevani.
Sad ste spremni od klase RemotingConfiguration zahtijevati da registrira vae dobro
poznate objekte. Morate proslijediti tip objekta kojeg elite registrirati, zajedno sa
zavrnom tokom (engl. endpoint). Zavrna toka je ime koje e RemotingConfiguration
povezati s vaim tipom. Ona kompletira adresu. Ako IP adresa identificira raunalo,
a ulaz identificira kanal, onda zavrna toka pokazuje tonu uslugu. Da biste uzeli tip
objekta, moete koristiti typeof koji vraa Type objekt. Proslijedite puno ime objekta
iji tip elite:
Type calcType =
typeof( "CalculatorServerNS.CalculatorServer" );

Takoer, proslijedite enumerirani tip koji pokazuje registrirate li SingleCall ili


Singlet on:
RemotingConfiguration.RegisterWellKnownServiceType
( calcType, "theEndPoint",WellKnownObjectMode.Singleton );

Poziv RegisterWellKnownServiceType stvara posluiteljski niz odvoda. Sad ste spremni


za igru. Primjer 19-3 sadri cijeli izvorni kod posluitelja.

Primjer 19-3. Posluitelj kalkulatora


(fregion Using directives

using System;
using System.Collections.Generic;
using $ystem.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Text;

tfendregion

namespace CalculatorServerNS
{
class CalculatorServer : MarshalByRefObject, Calculator.ICalc
{
public CalculatorServerf)
{
Console.WriteLine( "CalculatorServer constructor" );
}
// Implementira etiri metode
public double Add( double x, double y )
{
Console.WriteLine( "Add {0} + {!}", x, y );

Poglavlje 19: Rasporeivanje i rad na daljinu I 463


Primjer 19-3. Posluitelj kalkulatora (nastavak)
return x + y;
}
public double Sub( double x, double y )
{
Console.WriteLine( "Sub {0} - {l}", x, y );
return x - y;
}
public double Mult( double x, double y )
{
Console.WriteLine( "Mult {0} * {l}", x, y );
return x * y;
}
public double Div( double x, double y )
{
Console.WriteLine( "Div {0} / {l}", x, y );
return x / y;
}
}
public class ServerTest
{
public static void Main()
{
// Stvara kanal i registrira ga
HttpChannel chan = new HttpChannel( 65100 );
ChannelServices.RegisterChannel( chan );

Type calcType =
Type.GetType( "CalculatorServerNS.CalculatorServer" );

// Registrira tip dobro poznatog objekta i govori posluitelju


// da povee tip sa zavrnom tokom "theEndPoint"
RemotingConfiguration.RegisterWellKnownServiceType
( calcType,
"theEndPoint",
WellKnownObjectMode.Singleton );

// "They also serve who only tand and wait." (Milton)


Console.WriteLine( "Press [enter] to exit..." );
Console.ReadLine();
}
}
}
Kad pokrenete program, on ispisuje poruku:
Press lenter] to exit...

i eka klijenta da zatrai uslugu.

464 | Programiranje C#
Izgradnja klijenta
Dok e CLR unaprijed registrirati TCP i HTTP kanal, vi ete trebati registrirati kanal
na klijentu ako elite primati povratne pozive ili ako koristite nestandardni kanal. U
ovom primjeru, moete koristiti kanal 0:
HTTPChannel chan = new HTTPChannel(O);
ChannelServices.RegisterChannel(chan);

Klijent se sada samo treba spojiti na daljinu prosljeujui objekt Type koji predstavlja
tip objekta kojeg treba (u naem sluaju ICalc suelje) i Uniform Resource Identifier
(URI) usluge.
Object obj =
RemotingServices.Connect
(typeof(Programming_CSharp.ICalc),
"http://localhost:65lOO/theEndPoint");

U ovom sluaju pretpostavljeno je da se posluitelj izvodi na lokalnom raunalu, pa


je URI http://localhost, nakon kojeg slijedi ulaz posluitelja (65100) i zavrna toka
koju ste deklarirali u posluitelju (theEndPoint).

Usluga bi trebala vratiti objekt koji je suelje koje ste traili. Onda ga moete pretvoriti
u suelje i poeti ga koristiti. Poto rad na daljinu ne moe biti zajamen (mrea moe
s' biti iskljuena, posluitelj moe biti nedostupan, itd.), trebali biste upotrebu staviti u
try blok:
try
{
Programming_CSharp.ICalc calc =
obj as Programming_CSharp.ICalc;
l
double sum = calc.Add(3,4);

Sad imate posrednik za kalkulator koji se izvodi na posluitelju, ali koji se moe koris-
titi i na klijentu, preko granica procesa ili, ako vam se vie svia, preko granica izmeu
raunala. Primjer 19-4 prikazuje kompletan klijent (da biste ga mogli prevesti morate
ukljuiti referencu na Calculator.dll kao to ste to uinili s CalcServer.cs).

Primjer 19-4. Klijent kalkulatora


(fregion Using direetives

using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Text;
:U.
#endregion

f namespace CalculatorClient
f f

Poglavlje 19: Rasporeivanje i rad na daljinu I 46S


Primjer 19-4. Klijent kalkulatora (nastavak)
class CalcClient
{
public static void Main()
{
int[] myIntArray = new int[3];

Console.WriteLine("Watson, come here I need you...");

// Stvara Http kanal i registrira ga.


// koristi ulaz o.
HttpChannel chan = new HttpChannel(o);
ChannelServices.RegisterChannel(chan);

Object obj = RemotingServices.Connect


(typeof(Calculator.ICalc),
"http://localhost:6 5 1 0 0 /theEndPoint");

try
{
// Pretvara objekt u suelje
Calculator.ICalc calc = obj as Calculator.ICalc;

// Koristi suelje za pozivanje metoda


double sum = calc.Add(3. 0 ,4 .0 );
double d i f f e r e n c e = calc.Sub(3,4);
double product = calc.Mult(3,4);
double quotient = calc.Div(3,4);

// Ispisuje rezultate
Console.WriteLine(3+4 = {0}", sum);
Console.WriteLine("3-4 = {0}", difference);
Console.WriteLine("3*4 = {0 }", product);
Console.WriteLine(3/4 = {0}", quotient);
}
catch( System.Exception ex )
{
Console.Writeline("Exception caught: ");
Console.WriteLine(ex.Message);
}
}
}
}
Nakon pokretanja posluitelj eka da korisnik pritisne Enter i tako mu javi da moe
prestati s radom. Klijent se pokree i prikazuje poruku na konzoli. Klijent zatim poziva
svaku od etiri operacije. Vidite da posluitelj ispisuje poruke nakon poziva metoda,
a zatim se rezultati ispisuju na klijentu.
To je tako jednostavno. Sad imate kod koji se izvodi na posluitelju i prua usluge
klijentu.

466 | Programiranje C#
Upotreba SingleCall
Da biste vidjeli razlika izmeu SingleCall i Singleton objekata promijenite jedan red
u posluiteljskoj Main() metodi. Evo postojeeg koda:
RemotingConfiguration.RegisterWellKnownServiceType
( calcType,
"theEndPoint",
WellKnownObjectMode.Singleton );

P rom ijenite o b je k t u SingleCall:

RemotingConfiguration.RegisterWellKnownServiceType
( calcType,
"theEndPoint,
WellKnownObjectMode. SingleCall);

Izlaz govori da je za obradu svakog zahtjeva napravljen novi objekt:


% Calculator constructor
t. Press [enter] to exit...
Calculator constructor
Add 3 + 4
Calculator constructor
' Sub 3 - 4
/3.V.
5 Calculator constructor
Mult 3 * 4
Calculator constructor
x Div 3 / 4

Razumijevanje metode RegisterWellKnownServiceType


Sto se zapravo dogodilo kad ste na posluitelju pozvali metodu RegisterWellKnownSer-
viceType()? Sjetite se da ste pribavili objekt Type za klasu Calculator:
Type.CetType("CalculatorServerNS.CalculatorServer");

Zatim ste pozvali RegisterWellKnownServiceType() prosljeujui Type objekt zajedno


sa zavrnom tokom i Singleton enumeracijom. To govori CLR-u da instancira Cal-
culator i povee ga sa zavrnom tokom.

Da biste to sami uinili trebali biste modificirati primjer 19-3 tako da Main() instan-
cira Calculator i onda ju proslijedi Marshal() metodi iz RemotingServices sa zavrnom
tokom kojoj elite pridruiti tu instancu Calculator. Modificirana metoda Main()
je prikazana u primjeru 19-5 i, kao to moete vidjeti, izlaz je identian onome iz
primjera 19-3.

Primjer 19-5. Runo instanciranje i pridruivanje klase Calculator zavrnoj toki


public static void Main()
{
HttpChannel chan = new HttpCbannel( 6 5 1 0 0 );
ChannelServices.RegisterChannel( chan );

CalculatorServerNS.CalculatorServer calculator =

Poglavlje 19: Rasporeivanje i rad na daljinu j 467


Primjer 19-5. Runo instanciranje i pridruivanje klase Calculator zavrnoj toki (nastavak)
new CalculatorServer();
RemotingServices.Marshal( calculator, "theEndPoint" );

// "Posluuju i one koji stoje i ekaju." (Milton)


Console.WriteLine( "Press [enter] to exit..." );
Console.ReadLine();
}
Ukupni uinak je da ste instancirali Calculator objekt i pridruili posrednika za rad
na daljinu zadanoj zavrnoj toki.
Moete prebaciti tu datoteku na klijenta i ponovno ju uspostaviti na klijentskom
raunalu. Da biste to uinili ponovno napravite kanal i registrirajte ga. Ovaj put,
meutim, otvorite fileStream s datotekom koju ste upravo kopirali s posluitelja:
FileStream fileStream =
new FileStream ("calculatorSoap.txt", FileMode.Open);

Zatim instancirajte SoapFormatter i pozovite D eserialize() metodu na formateru


prosljeujui ime datoteke i primajui natrag ICalc:
SoapFormatter SoapFormatter =
new SoapFormatter ();
try
{
ICalc calc=
(ICalc) SoapFormatter.Deserialize (fileStream);

Sad moete pozvati metode na posluitelju koristei ICalc koje djeluje kao posrednik
za objekt Calculator koji se izvodi na posluitelju i kojeg ste opisali u calculatorSoap.
txt datoteci. Kompletna zamjena za klijentsku Main() metodu je prikazana u primjeru
19-6. Ovom primjeru trebate dodati dvije using deklaracije.

Primjer 19-6. Zamjena za Main() metodu iz primjera 19-4 (klijent)


using System.I0;
using System.Runtime.Serialization.Formatters.Soap;

U ...

public static void Main()


{

int[] myIntArray = new int[3];

Console.WriteLine("Watson, come here I need you...");

// Stvara Http kanal i registrira ga


// koristi ulaz 0.
HttpChannel ehan = new HttpChannel(o);
ChannelServices.RegisterChannel(chan);

FileStream fileStream =

468 | Programiranje C#
Primjer 19-6. Zamjena za Main() metodu iz primjera 19-4 (klijent) (nastavak)
new FileStream ("calculatorSoap.txt", FileMode.Open);
SoapFormatter SoapFormatter =
rew SoapFormatter ();

try
{
ICalc calc=
(ICalc) SoapFormatter.Deserialize (fileStream);

// Koristi suelje za pozivanje metoda


double sum = calc.Add(3 .0 ,4 .0 );
double difference = calc.Sub(3,4);
double product = calc.Mult(3,4);
double quotient = calc.Div(3,4);

// Ispisuje rezultate
Console.WriteLine("3+4 = {0}", sum);
Con$ole.WriteLine("3-4 = {0 }", difference);
Console.WriteLine("3*4 = {0 }", product);
Console.WriteLine("3/4 = {0 }", quotient);
}
catch( System.Exception ex )
1
Con5ole.WriteLine(Exception caught: ");
Console.WriteLine(ex.Message);
}
}
Kad se klijent pokrene, datoteka se uitava sa diska i posrednik se opoziva. Ovo je
inverzna operacija rasporeivanja i serijaliziranja objekta na posluitelju. Kad ste to
obavili, moete pozivati metode na objektu Calculator koji se izvodi na posluitelju.

Razumijevanje zavrnih toaka


to se dogaa kad registrirate zavrnu toku u primjeru 19-5 (posluitelj)? Jasno,
posluitelj pridruuje zavrnu toku tipu. Kad se klijent spoji, zavrna toka se koristi
kao indeks u tablici tako da posluitelj moe pruiti posrednika pravom objektu (u
ovom sluaju kalkulatoru).
Ako ne pruite zavrnu toku s kojom bi klijent komunicirao, moete umjesto toga
upisati sve podatke o objektu Calculator u datoteku i fiziki dati tu datoteku klijentu.
Na primjer, mogli biste poslati tu datoteku vaem prijatelju i on bi ju mogao uitati u
svoje raunalo.

Klijent moe deserijalizirati objekt i ponovno uspostaviti posrednik kojeg moe


koristiti za pristup kalkulatoru na posluitelju (sljedei primjer m ije predloio Mike
Woodring koji je nekada radio u tvrtci DevelopMentor, koji slian primjer koristi da
pokae kako je zavrna toka jednostavno pogodnost za udaljeni pristup rasporeenim
objektima).

Poglavlje 19; Rasporeivanje i rad na daljinu I 469


Da biste vidjeli kako moete pozvati objekt bez poznate zavrne toke promijenite
Main() metodu iz primjera 19-3 jo jednom. Ovaj put, umjesto poziva metode Mar-
sh a ll() sa zavrnom tokom samo proslijedite objekt:
ObjRef objRef = RemotingServices.Marshal(calculator)

Marshal() vraa ObjRef objekt. On pohranjuje sve informacije potrebne za aktiviranje


udaljenog objekta i komuniciranje s njim. Kad dobijete zavrnu toku posluitelj stvara
tablicu koja pridruuje ObjRef objekt zavrnoj toki tako da posluitelj moe napraviti
posrednik kad klijent to zatrai. Objekt ObjRef sadri sve informacije potrebnu kli-
jentu da napravi posrednik i ObjRef moe biti serijaliziran.
Otvorite tok podataka za stvaranje nove datoteke i napravite novi SOAP formater
Moete serijalizirati ObjRef u tu datoteku pozivanjem metode Serializef ) na formateru
prosljeujui tok podataka i ObjRef koji je vratila Marshal metoda. Brzo! Sve informacije
koje trebate za stvaranje posrednika za va objekt imate zapisane u datoteku na disku
Kompletna zamjena za Main() metodu iz primjera 19-5 je prikazana u primjeru 19-7.
Takoer ete trebati dodati tri using deklaracije u CalcScrvcr.cs, kao to je prikazano.

Primjer 19-7. Rasporeivanje objekta bez dobro poznate zavrne toke


using System;
using System.I0;
using System.Runtime.Serialization.Formatters.Soap;

public static void Main()


{
// Stvara kanal i registrira ga
HttpChannel ehan = new HttpChannel(65100);
ChannelServices.RegisterChannel(chan);
// Napravite vlastitu instancu i izravno pozovite Marshal metodu
Calculator calculator = new Calculatorf);

ObjRef objRef = RemotingServices.Marshal(calculator);

FileStream fileStream =
new FileStream("calculatorSoap.txt",FileMode.Create);

SoapFormatter soapFormatter = new SoapFormatter();

soapFormatter.Serialize(fileStream,objRef);
fileStream.Close();

// "Posluuju i one koji stoje i ekaju." (Milton)


Console.WriteLine(
"Export'ed to CalculatorSoap.txt. Press ENTER to exit...");
Console.ReadLine();
}
Kad ga pokrenete, posluitelj dodaje datoteku calculatorSoap.txt u sustav datoteka.
Posluitelj zatim eka klijenta da se spoji. Mogao bi dugo ekati (nakon desetak minuta
se sam gasi).

470 | Programiranje C#
POGLAVLJE 20
Dretve i sinkronizacija

Dretve (engl. threads) su odgovorne za mukitasking unutar jedne aplikacije. Imenski


prostor System.Threading prua mnotvo klasa i suelja za upravljanje viedretvenim
programiranjem. Veina programera moda nikad nee trebati koristiti dretve eks-
plicitno, ali to je zato to CLR odvaja veinu podrke za dretve u klase koje pojedno-
stavljuju veinu zadataka. Na primjer, u poglavlju 21, vidjet ete kako napraviti vie-
dretveno itanje i pisanje tokova podataka bez preuzimanja nadzora na dretvama.
Prvi dio ovog poglavlja pokazuje kako praviti i prekidati dretve te kako raditi s njima.
ak i ako ne pravite dretve eksplicitno htjet ete osigurati da va kod moe obraivati
vie dretvi ako se izvodi u viedretvenom okoliu. Ovo je posebno vano ako stva-
rate komponente koje bi mogli koristiti drugi programeri u programu koji podrava
viedretveni rad. To je posebno vano projektantima koji razvijaju rad na daljinu i
Web usluge. Iako Web usluge (obraene u petnaestom poglavlju) imaju mnoge atri-
bute samostalnih aplikacija, one se izvode na posluitelju, openito im nedostaje kori-
sniko suelje i prisiljavaju projektante da razmiljaju o posluiteljskim pitanjima kao
to su uinkovitost i viedretvenost.
Drugi dio ovog poglavlja se vie fokusira na sinkronizaciju. Kad imate ograniene
resurse (kao to je veza prema bazi podataka), moda ete trebati ograniiti pristup
tom resursu od strane neke dretve u danom trenutku. Odlina analogija je sa zahodom
u avionu. elite dozvoliti pristup zahodu samo jednoj osobi u nekom trenutku. To se
izvodi postavljanjem brave na vrata. Kad putnici ele koristiti zahod, prvo probaju
drku na vratima. Ako je zakljuano, oni ili odu raditi neto drugo ili strpljivo ekaju u
redu s drugima koji ele pristupiti resursu. Kad resurs postane slobodan, jedna osoba
iz reda dobiva resurs koji se onda ponovno zakljuava.
Povremeno, razne dretve bi moda htjele pristupiti resursu u programu, kao to je
datoteka. Moglo bi biti vano da osigurate da samo jedna dretva u nekom trenutku
ima pristup resursu pa ete zakljuati resurs, dozvoliti toj dretvi pristup i onda otklju-
ati resurs. Programske brave mogu biti prilino sofisticirane, osiguravajui potenu
raspodjelu resursa.

471
Dretve
Dretve se tipino stvaraju kad elite da program radi dvije stvari odjednom. Na pri-
mjer, pretpostavimo da elite izraunati pi (3.141592653589...) na milijarditu deci-
malu. Procesor e veselo poeti raunati, ali nita se nee ispisivati na zaslonu. Poto
e to raunanje potrajati nekoliko tjedana, moda biste htjeli da procesor prui infor-
macije o napretku dok radi. U dodatku, moda bi htjeli osigurati gumb Stop tako da
korisnik moe zaustaviti raunanje u bilo kojem trenutku. Da bi dozvolili programu
da obrauje pritisak na gumb Stop trebat ete drugu dretvu izvoenja.

Jo jedna situacija kad ete esto koristiti dretve je kad morate ekati neki dogaaj kao
to je korisniki ulaz, itanje iz datoteke ili primanje podataka preko mree. Dozvolja-
vanje procesoru da obrati pozornost na drugi zadatak dok ekate (kao to je raunanje
jo 10000 vrijednosti broja pi) je dobra ideja i omoguava programu da radi jo bre.
U nekim situacijama dretve mogu usporiti program. Pretpostavite da osim izraunava-
nja broja pi elite odrediti Fibonaccijev niz (1 ,1 ,2,3,5,8,13,21...). Ako imate vieproce-
sorski sustav raunanje e se izvoditi bre ako svaki procesor rauna u posebnoj dretvi.
Ako imate jednoprocesorski sustav, (kao to veina korisnika ima), raunanje ovih vri-
jednosti u vie dretvi e se izvoditi sporije nego raunanje jedne pa onda druge u istoj
dretvi jer se procesor mora prebacivati izmeu dretvi. To dodatno optereuje sustav.

Pokretanje dretvi
Najjednostavniji nain da napravite dretvu je da napravite novu instancu klase Thread.
Thread konstruktor uzima jedan argument: delegate instancu. CLR prua klasu dele-
gata ThreadStart posebno za ovu svrhu. Ona upuuje na metodu koju zadate. To omo-
guava da se napravi dretva i da joj se kae: Kad se pokrene, izvedi ovu metodu".
Deklaracija ThreadStart delegata je:
public delegate void ThreadStart();

Kao to moete vidjeti, metoda koju pridruite ovom delegatu ne smije uzimati parame-
tre i mora vraati void. Prema tome, moete napraviti novu dretvu na sljedei nain:
Thread myThread = new Thread( new ThreadStart(myFunc) );

Na primjer, moete napraviti dvije radne dretve, jednu koja broji od nule:
public void Incrementer()
{
for (int i =0;i<1000;i++)
{
Console.WriteLine('Tncrementer: {0}", i);
}
}
i jednu koja broji prema od 1000 prema nuli:
public void Decrementer()
{
for (int i = 1000;i>=0;i--)

472 | Programiranje C#
{
Console.WriteLine("Decrementer: {0}", i);
}
}
Da biste ove metode izvodili u dretvama napravite nove dretve i inicijalizirajte ih s
ThreadStart delegatom. One e biti inicijalizirane odgovarajuim metodama'.
Thread t i = new Thread( new ThreadStart(Incrementer) );
Thread t2 = new Thread( new ThreadStart(Decrementer) );

Instanciranje dretvi ih ne pokree. Da biste pokrenuli dretve morate pozvati Start


metodu na Thread objektu:
tl.StartO;
t2.Start();

Ako ne poduzmete dalje korake, dretva se zaustavlja kad metoda vrati.


Vidjet ete kako zaustaviti dretvu prije nego to metoda vrati kasnije u
ovom poglavlju.

Primjer 20-1 prikazuje cijeli program i njegov izlaz. Trebat ete dodati using deklara-
ciju za System.Threading da prevoditelj uzme u obzir klasu Thread. Obratite panju na
izlaz gdje moete vidjeti kako se procesor prebacuje izmeu ti i t2.

Primjer 20-1. Upotreba dretvi


ftregion Using directives

using System;
using System.Collections.Ceneric;
using System.Text;
using System.Threading;

(tendregion

namespace UsingThreads
{
class Tester
{
static void Main()
{
// Pravi instancu klase
Tester t = new Tester();

Console.WriteLine( "Hello" );
// Izvodi se izvan statike Main
t.DoTestO;
}

public void DoTestj)


{
// Stvara dretvu za Incrementer

Poglavlje 20: Dretve i sinkronizacija | 473


Primjer 20-1. Upotreba dretvi (nastavak)
// prosljeuje ThreadStart delegata
// s adresom Incrementera
Thread ti =
new Thread(
new ThreadStart( Incrementer ) );

/./ Stvara ..dretvu za Decrementer


// prosljeuje delegata ThreadStart
// s adresom Decrementer
Thread t2 =
new Thread(
new ThreadStart( Decrementer ) );

// Pokree dretve
tl.Start();
t2.Start();

// Pokazna metoda, broji do lK


public void Incrementer()
{
for ( int i = 0; i < 1000; i++ )
{
System.Console.WriteLine(
"Incrementer: {0}", i );
}
}
// Pokazna metoda, broji unatrag od lK
public void Decrementer()
{
for ( int i = 1000; i >= 0; i-- )
{
System.Console.WriteLine(
"Decrementer: {0}", i );
}
}
}
}
Izlaz (odlomak):
Incrementer: 102
Incrementer: 103
Incrementer: 104
Incrementer: 105'
Incrementer: 106
Decrementer: 1000
Decrementer: 999
Decrementer: 998
Decrementer: 997

474 | Programiranje C#
Procesor dozvoljava prvoj dretvi da se izvodi sve dok ne doe do 106. Onda se ubacuje
druga dretva, brojei neko vrijeme unatrag od 1000. Zatim je opet prvoj dretvi dozvoljeno
da se izvodi. Kad ovaj primjer pokrenem za vee brojeve, primjeeujem da je svakoj dretvi
dozvoljeno da odbroji 100 brojeva prije nego to se izvedba prebaci na drugu dretvu.

Koliko e vremena biti dodijeljeno nekoj dretvi odreuje programa za raspo-


reivanje dretvi (engl. thread scheduler) i ovisi o mnogo imbenika kao to
su brzina procesora, zahtjevi prema procesoru od drugih programa itd.

Spajanje dretvi
Kad kaete dretvi da prestane s izvoenjem i prieka dok druga dretva ne zavri posao
tada ste spojili prvu dretvu s drugom. To je kao da ste povezali vrh prve dretve na rep
druge dretve, dakle spojili ih.
Da bi povezali dretvu 1 (ti) s dretvom 2 (t2), napiite:
t2.3oi.nO;

Ako se ova deklaracija izvede u metodi u dretvi ti, onda e ti stati i ekati dok se t2
ne zavri. Na primjer, mogli biste od dretve u kojoj se izvodi Main() zatraiti da saeka
sve ostale dretve da se zavre prije nego to ispie svoju zakljunu poruku. U sljedeem
odlomku koda pretpostavit emo da ste napravili kolekciju dretvi myThreads. Proite
kroz kolekciju spajajui trenutnu dretvu sa sljedeom dretvom iz kolekcije:
foreach (Thread myThread in myThreads)
{
myThread.3oin();
}

Console.WriteLine("All my threads are done.");

Poruka Ali my threads are done nee biti ispisana dok sve dretve ne zavre. U stvarnoj
situaciji mogli biste pokrenuti niz dretvi za obavljanje zadataka (na primjer, za ispis,
auriranje prikaza itd.) i ne nastavljati glavnu dretvu dok sve radne dretve ne zavre.

Odgaanje dretvi s pomou metode Sleep


Povremeno ete htjeti odgoditi dretvu na neko vrijeme. Na primje, mogli biste dretvu sata
zaustaviti na sekundu izmeu provjere sistemskog vremena. To omoguava prikaz novog
vremena otprilike jednom u sekundi bez troenja stotina milijuna taktova procesora.
Thread klasa prua javnu statiku metodu Sleep posebno za ovu svrhu. Metoda je pre-
optereena: jedna inaica uzima int a druga timeSpan objekt. Svaki argument je broj
milisekundi na koje elite odgoditi izvoenje dretve izraen ili kao int vrijednost (na
primjer, 2000 = 200 0 milisekundi ili dvije sekunde) ili kao timeSpan.

Iako timeSpan objekti mogu mjeriti trenutke (100 nanosekundi), zrnatost Sleep()
metode je u milisekundama (1000000 nanosekundi).

Poglavlje 20: Dretve i sinkronizacija | 475


Da bi odgodili dretvu na jednu sekundu moete pozvati statiku metodu Thread.Sleep
koja odgaa dretvu u kojoj je pozvana:
Thread.Sleep(lOOO);

Povremeno ete kao period odgaanja dretve navesti nulu. To programu za rasporeiva-
nje dretvi govori da biste htjeli da dretvu prepusti drugoj dretvi da se izvodi, ak i ako
bi program za rasporeivanje dretvi prvoj dretvi dao jo malo vremena za izvoenje.

Ako prom ijenite prim jer 20 -1 dodavanjem Thread. S le e p (i) iskaza nakon svake Write-
L in e(), izlaz e se znatno prom ijeniti:
for (int i =0 ;i<1 0 0 0 ;i++)
{
Console.WriteLine(
"Incrementer: {o}", i);
Thread.Sleep(l);
}
Ova mala promjena je dovoljna da svakoj dretvi da priliku da se izvodi dok druga
dretva ispisuje vrijednost. Izlaz odraava ovu promjenu:
Incrementer: 0
Incrementer: l
Decrementer: 1000
Incrementer: 2
Decrementer: 999
Incrementer: 3
Decrementer: 998
Incrementer: 4
Decrementer: 997
Incrementer: 5
Decrementer: 996
Incrementer: 6
Decrementer: 995

Prekidanje dretvi
Dretve se obino prekidaju nakon to obave posao. Meutim, moete narediti dretvi
da se prekine. Najbolji nain za to je da postavite KeepAlive Boolean zastavicu koju
dretvu moe povremeno provjeriti. Kad zastavica promijeni stanje (na primjer, sa true
na false) dretva se moe sama zaustaviti.
Alternativni nain je da pozovete T h r e a d . I n t e r r u p t za prekidanje izvoenja dretvi.
Konano, u oaju i ako zaustavljate aplikaciju moete pozvati Trhea d. Abor t. To e
izbaciti iznimku Thread AbortEx ception, koju dretva moe uhvatiti.
Dretva bi iznimku Thr eadA bortException trebala promatrati kao signal da isti tren
prekine s radom.
Moda biste htjeli prekinuti dretvu odgovarajui na neki dogaaj, kao kad korisnik
pritisne Cancel gumb. Metoda za obradu dogaaja za gumb Cancel bi mogla biti u
dretvi t i , a dogaaj koji se zaustavlja bi mogao biti u dretvi t2 . U metodi za obradu
dogaaja moete pozvati Abort na t i :

476 | Programiranje C#
tl. A b ort();

To e izbaciti iznimku u metodi koja se trenutno izvodi u dretvi t i koju dretva t i


moe uhvatiti.

U primjeru 20-2, stvorene su tri dretve i spremljene u polje Thread objekata. Prije
pokretanja dretve svojstvo IsBackground je postavljeno na true (pozadinske dretve su
potpuno jednake kao glavne dretve, osim to one ne prekidaju proces). Svaka dretva je
pokrenuta i imenovana (na primjer, Threadi, Thread2 i tako dalje). Prikazuje se poruka
koja govori da je dretva pokrenuta i onda se glavna dretva odgaa za 50 milisekundi
prije nego to se pokrene sljedea dretva.

Nakon to su sve tri dretve pokrenute i prolo je sljedeih 50 mlisekuni, prva dretva
se prekida pozivanjem Abort() metode. Glavna dretva se onda povezuje sa sve tri
dretve koje se izvode. Uinak toga je da se glavna dretva nee nastaviti dok sve ostale
dretve ne budu dovrene. Kad dretve zavre, glavna dretva ispisuje poruku: Ali my
threads are done. Kompletan izvorni kod je prikazan u primjeru 20-2.

Primjer 20-2. Prekidanje dretve


#region Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System,Threading;

#endregion

namespace InterruptingThreads
{
class Tester
{
static void Main()
' {
// Stvara instancu ove klase
Tester t = new Tester();

// Izvodi se izvan statike Main


t.DoTest();
}

public void DoTest()


{
// Stvara polje dretvi bez imena
Thread[] myThreads =
{
new Thread( new ThreadStart(Decrementer) ),
new Thread( new ThreadStart(Incrementer) ),
new Thread( new ThreadStart(Decrementer) ),
new Thread( new ThreadStart(Incrementer) )
};

Poglavlje 20: Dretve i sinkronizacija | 477


Primjer 20-2. Prekidanje dretve (nastavak)
II Pokree dretve
int ctr = l;
foreach (Thread myThread in myThreads)
{
myThread.IsBackground = true;
myThread.Start();
myThread.Name = "Thread" + ctr.ToStringO;
ctr++;
Console.WriteLine("Started thread {o}", myThread.Name);
Thread.Sleep(50);
}
II Zahtijeva prekidanje prve dretve
myThreads[0].Interrupt();

II Odmah prekida drugu dretvu


myThreads[l].Abort();

II eka da zavre sve dretve prije nego nastavi


foreach (Thread myThread in myThreads)
{
myThread.loin();
}
II Nakon to sve dretve zavre ispisuje poruku
Console.WriteLine("All my threads are done.");

II Pokusna metoda, broji unatrag od 100


public void Decrementer()
{
try
{
for (int i = 100; i >= 0; i--)
{
Console.WriteLine(
"Thread {0}. Decrementer: {l}",
Thread.CurrentThread.Name,
i);
Thread.Sleep(l);
}
}
catch (ThreadAbortException)
{
Console.WriteLine(
"Thread {0} aborted! Cleaning up...",
Thread.CurrentThread.Name);
}
catch (System.Exception e)
{
Console.WriteLine("Thread has been interrupted ");
}

478 | Programiranje C#
Primjer 20-2. Prekidanje dretve (nastavak)
finally
{
Console.WriteLine(
"Thread {0} Exiting. ",
Thread.CurrentThread.Name);
}
}

II Pokusna metoda, broji do 100


public void Incrementer()
{
try
{
for (int i = o; i < 100; i++)
{
Console.WriteLine(
"Thread {0 }. Incrementer: {l}",
Thread.CurrentThread.Name,
i);
Thread.Sleep(l);
}
}
catch (ThreadAbortException)
{
Console.WriteLine(
"Thread {0} aborted!",
Thread.CurrentThread.Name);
}
catch (System.Exception e)
{
Console.WriteLine("Thread has been interrupted");
}
finally
{
Console.WriteLine(
"Thread {0} Exiting. ",
Thread.CurrentThread.Name);
}
}
}
}
Vidite kako se prva dretva pokree i smanjuje vrijednost 100 na 99. Druga dretva se
pokree i sada se dvije dretve prepliu dok se ne pokrenu trea i etvrta dretva. Nakon
nekog vremena, Thread2 javi daje zaustavljena i onda javlja da izlazi. Malo poslije toga,
Threadl javlja da je prekinuta. Poto prekid eka da dretva bude u stanju ekanja, ona
ne mora biti prekinuta istog trena kao kad se poziva Abort metoda. Dvije preostale
dretve nastavljaju dok ne zavre, nakon ega prirodno izlaze, a glavna dretva, koja je
bila povezana na sve tri dretve, nastavlja kako bi ispisala izlaznu poruku.

Poglavlje 20: Dretve i sinkronizacija I 479


Sinkronizacija
Povremeno ete htjeti kontrolirati pristup nekom resursu tako da mu samo jedna dretva
u jednom trenutku moe pristupiti. Taj objekt je slian blagajni a dretve su kao ljudi
koji ekaju u redu. Sinkronizacija je osigurana s pomou lokota na objektu koji pomae
projektantima da sprijee drugu dretvu da pristupi dok prva dretva koristi objekt.
U ovom dijelu se opisuju tri sinkronizacijska mehanizma: klasa Interlock, iskaz lock
i Monitor klasa. Ali, prvo trebate napraviti dijeljeni resurs (najee je to datoteka ili
pisa). U ovom sluaju to je jednostavna cjelobrojna varijabla: counter. Poveavat ete
za jedan varijablu counter iz obije dretve.
Za poetak, deklarirajte varijablu lanicu i inicijalizirajte je na nulu:
int counter = 0;

Modificirajte Incremneter metodu tako da inkrementira counter varijablu lanicu:


public void Incrementer()
{
try
{
while (counter < 1000)
{
int temp = counter;
temp++; II Poveavanje

// Simulira obavljanje posla u ovoj metodi


Thread.Sleep(l);

// Dodjeljuje uveanu vrijednost


// varijabli brojaa
// i prikazuje rezultat
counter = temp;
Console.WriteLine(
"Thread {0}. Incrementer: {l}",
Thread.CurrentThread.Name,
counter);
1
1
Ideja je da simulirate posao koji bi mogao biti obavljan s kontroliranim resursima.
Ba kao to biste mogli otvoriti datoteku, obraditi njen sadraj i onda ju zatvoriti,
ovdje uitavate vrijednost varijable counter u privremenu varijablu, poveavate ju za
jedan, ekate jednu milisekundu da simulirate rad i onda dodijelite poveanu vrijed-
nost natrag varijabli counter.
Problem je to prva dretva ita vrijednost varijable counter (0) i dodijeljuje ju privremenoj
varijabli. Onda za jedan poveava privremenu varijablu. Dok obavlja svoj posao, druga
dretva uitava vrijednost varijable counter (jo uvijek 0) i dodijeljuje ju privremenoj vari-
jabli. Prva dretva zavri s poslom i onda dodjeljuje privremenu vrijednost (1) natrag vari-
jabli counter i prikazuje ju. Druga dretva ini isto. Ono to se ispisuje je 1,1. U sljedeem

480 | Programiranje C#
prolazu dogaa se isto. Umjesto da imate dvije dretve koje broje 1 ,2 ,3 , 4, vidjet ete 1,
2 , 3 , 3 , 4, 4. Primjer 20-3 prikazuje kompletan izvorni kod i izlaz za ovaj primjer.

Primjer 20-3. Simulacija dijeljenog resursa


(fregion Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

Sendregion

namespace SharedResource
{
class Tester
{
private int counter = 0 ;

s ta tic void Main()


{
// Stvara instancu ove klase
Tester t = new T ester();

// Izvodi se izvan statike Main


t.D oTestO ;
}

public void DoTest()


{
Thread t i = new Thread( new ThreadStart( Incrementer ) );
tl.IsBackground = true;
tl.Name = "ThreadOne";
tl.S ta r t{);
- Console.WriteLine( "Started thread {o }",
tl.Name ) ;

Thread t2 = new Thread( new ThreadStart( Incrementer ) );


t2.IsBackground = true;
t2.Name = "ThreadTwo";
t 2 .S t a r t () ;
Console.WriteLine( "Started thread { o }",
t2.Name );
t l.I o in ( );
t 2 .] o in ();

// Kad su sve dretve gotove isp isu je poruku


Console.WriteLine( "Ali my threads are done." );
}

// Pokusna metoda, b ro ji do lK
public void Incrementer()
{
try

Poglavlje 20; Dretve i sinkronizacija I 481


Primjer 20-3. Simulacija dijeljenog resursa (nastavak)

{
while ( counter < 1000 )
{
int temp = counter;
temp++; // Poveavanje

- // Simulira obavljanje posla


Thread.Sleep( l );

// Pridruuje poveanu vrijednost


// i prikazuje rezultat
counter = temp;
Console.WriteLine(
"Thread {0}. Incrementer: {l}",
Thread.CurrentThread.Name,
counter );
}
}
catch ( ThreadInterruptedException )
{
Console.WriteLine(
"Thread {0} interrupted! Cleaning up...",
Thread.CurrentThread.Name );
}
finally
{
Console.WriteLine(
"Thread {0 } Exiting. ",
Thread.CurrentThread.Name );
}
}
}
}

Izlaz:
Started thread ThreadOne
Started thread ThreadTwo
Thread ThreadOne. Incrementer: 1
Thread ThreadOne. Incrementer: 2
Thread ThreadOne. Incrementer: 3
Thread ThreadTwo. Incrementer: 3
Thread ThreadTwo. Incrementer: 4
Thread ThreadOne. Incrementer: 4
Thread ThreadTwo. Incrementer: 5
Thread ThreadOne. Incrementer: 5
Thread ThreadTwo. Incrementer: 6
Thread ThreadOne. Incrementer: 6

Upotreba klase Interlocked


CLR prua vei broj mehanizama za sinkronizaciju. Meu njima su opi sinkronizacij-
ski alati kao to su kritini dijelovi (u .NET-u se nazivaju lokoti) i Monitor klasa. Oba
alata su obraena kasnije u ovom poglavlju.

482 | Programiranje C#
Poveavanje i smanjivanje vrijednosti zajedanje tako uobiajen programerski model i
esto treba sinkronizajsku zatitu. Zbog toga CLR prua posebnu klasu Interlocked
samo za ovu svrhu. Interlocked ima dvije metode Increment i Decrement koji ne samo
da poveavaju i umanjuju vrijednost, ve to rade uz kontroliranu sinkronizaciju.
Promijenite Incrementer metodu iz primjera 20-3 na sljedei nain:
public void Incrementer()
{
try
{
while (counter < 1000)
{
int ter'ip = Interlocked.Increment(ref counter);

// Simulira obavljanje posla


Thread.Sleep(o);

// Prikazuje uveanu vrijednost


Console.WriteLine(
"Thread {o}. Incrementer: {l}",
Thread.CurrentThread.Name,
temp);
}
}
}
Blokovi catch i finally te ostatak programa ostaju nepromijenjeni u odnosu na pret-
hodni primjer.
Interlocked. Increment() oekuje samo jedan parametar: referencu na int vrijednost.
Poto se int vrijednosti prosljeuju po vrijednosti upotrijebite kljunu rije ref kao
to je opisano u poglavlju 4.

Increment() m etoda je preoptereen a i m oe uzim ati referencu na vri-


jed n o st long u m jesto lia int.

Kad je ova promjena uinjena pristup counter varijabli je sinkroniziran i izlaz je kao
to oekujete.
Izlaz (odlomak):
Started thread ThreadOne
Started thread ThreadTwo
Thread ThreadOne. Incrementer: l
Thread ThreadTwo. Incrementer: 2
Thread ThreadOne. Incrementer: 3
Thread ThreadTwo. Incrementer: 4
Thread ThreadOne. Incrementer: 5
Thread ThreadTwo. Incrementer: 6
Thread ThreadOne. Incrementer: 7
Thread ThreadTwo. Incrementer: 8
Thread ThreadOne. Incrementer: 9

Poglavlje 20: Dretve i sinkronizacija | 483


Thread ThreadTuo. Incrementer: 10
Thread ThreadOne. Incrementer: 11
Thread ThreadTwo. Incrementer: 12
Thread ThreadOne. Incrementer: 13
Thread ThreadTuo. Incrementer: 14
Thread ThreadOne. Incrementer: 15
Thread ThreadTwo. Incrementer: 16
Thread ThreadOne. Incrementer: 17
Thread ThreadTwo. Incrementer: 18
Thread ThreadOne. Incrementer: 19
Thread ThreadTwo. Incrementer: 20

Upotreba lokota
Iako je objekt InterLocked prikladan kad elite poveati ili smanjiti vrijednost, postojat
e situacije kad ete htjeti kontrolirati pristup i drugim objektima. Dakle potreban je
opi mehanizam za sinkronizaciju. U C # to prua znaajka lock.
Lock oznaava kritian dio koda pruajui sinkronizaciju zadanom objektu dok lokot
djeluje. Sintaksa znaajke lock je da se za objektu zatrai lokot i zatim izvede iskaz ili
blok iskaza. Lokot se uklanja na kraju bloka iskaza.
C # prua izravnu podrku za lokote s pomou kljune rijei lock. Proslijedite refe-
rencu na objekt i nakon kljune rijei dodajte blok iskaza:
lock(izraz) blok iskaza

Na primjer, moete ponovno promijeniti Incrementer da koristi lock iskaz na sljedei


nain:
public void Incrementer()
{
try
{
while (counter < 1000)

{
int temp;
lock (this)
{
temp = counter;
temp ++;
Thread.Sleep(l);
counter = temp;
}
// Pridruuje uveanu vrijednost
/ / i prikazuje rezultat
Console.WriteLine(
"Thread {o}. Incrementer: {l}",
Thread.CurrentThread.Name,
temp);
}
}

484 | Programiranje C#
Catch i finally blokovi i ostatak koda su nepromijenjeni u odnosu na prethodni primjer.
Izlaz ovog koda je identian onom koji je nastao koritenjem Interlocked klase.

Upotreba monitora
Objekti koje smo do sada koristili bit e dovoljni za veinu potreba. Meutim, za naj-
softiciranije upravljanje resursima, moda ete htjeti koristiti monitor. Monitor vam
preputa da odluite kad poeti i prestati sa sinkronizacijom i omoguava da ekate
dok drugi dio koda ne postane slobodan.
Kad poelite poeti sa sinkronizacijom, pozovite Enter() metodu monitora,
prosljeujui objekt koji elite zakljuati:
Monitor.Enter(this);

Ako monitor nije dostupan, pretpostavka je da se objekt koji je zatien monitorom


koristi. Moete raditi neto drugo dok ekate da monitor postane dostupan i onda pro-
bati ponovno. Takoer moete eksplicitno odabrati Wait() metodu i odgoditi dretvu
dok monitor ne postane dostupan i projektant ne pozove Pulse (uskoro e biti obja-
njeno). Wait() pomae da upravljate poretkom dretvi.

Na primjer, pretpostavite da uitavate i ispisujuete lanak s Weba. Radi uinkovito-


sti, elite ispisivati u pozadinskoj dretvi ali elite osigurati d aje barem deset stranica
uitano prije nego ponete.

Dretva za ispisivanje e ekati dok dretva za pribavljanje datoteke ne javi da je dovoljan


dio datoteke uitan. Ne elite povezati Join iskazom dretvu za uzimanje podataka
jer bi datoteka mogla imati stotine stranica. Ne elite ekati da se uitavanje zavri do
kraja, ali elite osigurati daje barem deset stranica uitano prije nego pone dretva za
ispisivanje. Wait() metoda je samo ulaznica.
Da biste ovo simulirali ponovno napiite Tester i dodajte metodu za umanjivanje.
Metoda za uveavanje broji prema gore do deset. Metoda za umanjivanje broji dolje
do nule. Ispada da ne elite poeti umanjivati osim ako je vrijednost varijable counter
barem pet.

U decrementer metodi, pozovite Enter na monitoru. Onda provjerite vrijednost varija-


ble counter i ako je manja od pet, pozovite Wait na monitoru:
if (counter < 5)
{
Monitor.Wait(this);
1
Poziv Wait() metode oslobaa monitor, ali javlja CLR-u da elite monitor natrag slje-
dei put kad bude slobodan. Dretve koje ekaju su obavijetene o prilici da nastave s
radom ako aktivna dretva pozove Pulse ():
Monitor.Pulse(this);

Poglavlje 20: Dretve i sinkronizacija I 485


Pulse() javlja CLR-u da je nastala promjena u stanju koja moe osloboditi dretvu koja
eka. Kad dretva zavri s monitorom, mora oznaiti kraj svog kontroliranog podruja
koda pozivom Exit() metode:
Monitor.Exit(this);

Primjer 20-4 nastavlja simulaciju osiguravajui sinkronizirani pristup varijabli counter


s pomou monitora.

Primjer 2 0 -4 . Upotreba objekta Monitor


ttregion Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

#endregion

namespace UsingAMonitor
{
class Tester
{
private long counter = 0;

static void Main()


{
II Stvara instancu klase
Tester t = new Tester();

// Izvodi se izvan statike Main


t.DoTest();
}

public void DoTest()


{
// Stvara polje dretvi bez imena
Thread[] myThreads =

new Thread( new ThreadStart(Decrementer) ),


new Thread( new ThreadStart(Incrementer) )
};

II Pokree dretve
int ctr = l;
foreach ( Thread myThread in myThreads )
{
myThread.IsBackground = true;
myThread.Start();
myThread.Name = "Thread" + ctr.ToStringO;
ctr++;
Console.WriteLine( "Started thread {0}", myThread.Name );
Thread.Sleep( 50 );

486 | Programiranje C#
Primjer 20-4. Upotreba objekta Monitor (nastavak)
}

// eka da sve dretve zavre prije nego nastavi dalje


foreach ( Thread myThread in myThreads )
{
myThread.Join();
}

// Kad sve dretve zavre nastavlja dalje


Console.WriteLine( "Ali my threads are done." );

void Decrementer()
{
try
{
// Sinkronizira ovaj dio koda
Monitor.Enter( this );

// Ako broja jo nije 10


// tada oslobaa monitor za ostale dretve koje ekaju
if ( counter < 1 0 )
{
Console.WriteLine(
"[{0}] In Decrementer. Counter: {l}. Gotta Wait!",
Thread.CurrentThread.Name, counter );
Monitor.Wait( this );
}

while ( counter > o )


{
long temp = counter;
temp--;
Thread. Sleep( i );
counter = temp;
Console.WriteLine(
[{o}] In Decrementer. Counter: {l}. ",
Thread.CurrentThread.Name, counter );
}
}
finally
{
Monitor.Exit( this );
}
}

void Incrementer()
{
try
{
Monitor. Enter( this );
while ( counter < 1 0 )
{

Poglavlje 20: Dretve i tmkronizacija j 487


Primjer 20-4. Upotreba objekta Monitor (nastavak)
long temp = counter;
temp++;
Thread.Sleep( 1 );
counter = temp;
Console.WriteLine(
"[{0}] In Incrementer. Counter: U ) ,
Thread.CurrentThread.Name, counter );

// Za sada je dosta poveavanja. Neka druga dretva


// preuzme monitor.
Monitor.Pulse( this );

}
finally

Console.WriteLine( "[{0}] Exiting... ,


Thread.CurrentThread.Name );
Monitor. Exit( this );

U o.om primjeru, d ecre.en.er rneroda je S e

nje s radom.

dretvama koje ekaju. , ,.


Kao drug, eksperiment, ponovno napiite Incrementer da poalje s.gnal , oslobo ,
monitor nakon svakog poveavanja:
void IncrementerO
{
try
{
while (counter < 10)
{
Monitor.Enter(this);
long temp = counter;
temp++;
Thread.Sleep(l);
counter = temp;
Console.WriteLine( ,
"[{0}] In Incrementer. Counter: (l) ,
Thread.CurrentThread.Name, counter);
Monitor.Pulse(this);
Monitor.Exit(this);
}

488 | Programiranje C#
Ponovno napiite i Decrementer mijenjajui iskaz i f u iskaz while u i sputajui vrijed-
nost sa deset na pet:
//if (counter < 10)
while (counter < 5 )

Krajnji uinak ovih dviju promjena je da Thread2, Incrementer, poalje signal metodi
Decrementer nakon svakog uveavanja. Dok je vrijednost manja od pet Decrementer
mora ekati. Kad vrijednost bude vea od pet Decrementer se izvodi do kraja. Kad je
gotova, dretva Incrementer moe nastaviti s izvoenjem. Izlaz je prikazan ovdje:
[Thread2] In Incrementer. Counter 2
[Threadl] In Decrementer. Counter 2. Cotta Wait!
[Thread2] In Incrementer. Counter 3
[Threadl] In Decrementer. Counter 3. Gotta Wait!
[Thread2] In Incrementer. Counter 4
[Threadl] In Decrementer. Counter 4. Cotta Wait!
[Thread2] In Incrementer. Counter 5
[Threadl] In Decrementer. Counter 4.
[Threadl] In Decrementer. Counter 3.
[Threadl] In Decrementer. Counter 2.
[Threadl] In Decrementer. Counter 1.
[Threadl] In Decrementer. Counter 0.
[Thread2] In Incrementer. Counter 1
[Thread2j In Incrementer. Counter 2
[Thread2] In Incrementer. Counter 3
[Thread2] In Incrementer. Counter 4
[Thread2] In Incrementer. Counter 5
[Thread2] In Incrementer. Counter 6
[Thread2] In Incrementer. Counter 7
[Thread2] In Incrementer. Counter 8
[Thread2] In Incrementer. Counter 9
[Thread2] In Incrementer. Counter 10

Stanja natjecanja i zastoji


.N ET biblioteka prua dovoljnu podrke za dretve pa ete rijetko sami stvarati dretve
i runo upravljati sinkronizacijom.
Sinkronizacija dretvi moe biti komplicirana, osobito u sloenim programima. Ako
odluite napraviti svoje dretve, morate se suoiti sa svim uobiajenim problemima sin-
kronizacije dretvi, kao to su stanja natjecanja i zastoji, te ih rijeiti.

Stanje natjecanja
Pretpostavite da imate dvije dretve i da je jedna odgovorna za otvaranje datoteke, a druga
za pisanje u datoteku. Vano je da nadzirete drugu dretvu tako da bude osigurano da je
prva dretva otvorila datoteku prije nego to nastupi druga dretva. U nekim uvjetima prva
dretva e otvoriti datoteku pa e druga dretva dobro raditi. Pod nekim nepredvienim
okolnostima, prva dretva nee moi dovriti otvaranje datoteke prije nego druga dretva
pokua upisati u nju i bit e izbaena iznimka (ili jo gore, program e se blokirati). To je
stanje nadmetanja i moe biti vrlo komplicirano otkriti pogreke koje do njega dovode.

Poglavlje 20: Dretve i sinkronizacija | 489


Ne smijete ostaviti ove dvije dretve da rade neovisno. Morate osigurati da Threadi
zavri prije nego Thread2 pome. Da biste to postigli mogli bi metodom Doin() spojiti
dretvu Thread2 na dretvu Threadi. Kao alternativu, moete koristiti Monitor i Wait()
za postizanje odgovarajuih uvjeta prije nastavljanja dretve Thread2.

Zastoj
Dok ekate da resurs postane slobodan postoji rizik da nastane zastoj (engl. deadlock ) koji
se uprogramerskim krugovima naziva i smrtonosni zagrljaj (engl. deadly embrace). Kad
nastane zastoj dvije ili vie dretve ekaju jedna drugu i nijedna se ne moe osloboditi.
Pretpostavimo da imate dvije dretve, ThreadA i ThreadB. ThreadA zakljua objekt
Employee i onda pokuava staviti lokot za red u datoteci. Ispada da je ThreadB ve
zakljuala taj red pa ThreadA eka.
Na nesreu, ThreadB ne moe aurirati red dok ne otkljua objekt Employee, koji je ve
zakljuala dretva ThreadA. Nijedna od dretvi ne moe nastaviti i nijedna dretva nee
otkljuati svoj resurs. ekaju jedna drugu u smrtonosnom zagrljaju.
Kao to je opisano, zastoj je lako uoiti i ispraviti. U programu koji izvodi mnogo
dretvi, zastoj moe biti teko uoiti, a kamoli ispraviti. Jedna smjernica je da dobijete
sve lokote koje trebate ili da oslobodite sve lokote koje imate. To jest, im ThreadA
uvidi da ne moe zakljuati red Row, trebala bi otkljuati objekt Employee. Slino, kad
ThreadB ne moe zakljuati objekt Employee, trebala bi osloboditi red Row. Druga vana
smjernica je da zakljuate to je mogue manji dio koda i da drite lokot to je krae
mogue.

490 | Programiranje C#
POGLAVLJE 21

Tokovi podataka

Za mnoge primjene podaci se dre u memoriji i pristupa im se kao da su trodimen-


zionalno kruto tijelo. Kad trebate pristup varijabli ili objektu, upotrebite ime i on
postaje dostupan. Kad elite prebaciti podatke u datoteku ili iz datoteke, preko mree
ili Interneta, onda podaci moraju tei.' U toku (engl. trcam) podaci teku poput kapi
vode u rijeci.

Krajnja toka toka najee je pomono spremite. Pomono spremite osigurava izvor
za tok, kao to jezero moe biti izvor rijeke. Pomono spremite je najee datoteka,
ali to moe biti i mrea ili Web konekcija.
D atoteke i mape su u .N ET kosturu apstrahirane klasama. Te klase pruaju metode i
svojstva za stvaranje, imenovanje, brisanje i ostale operacije s datotekama i mapama
na disku.

.N ET kostur podrava tokove sa spremanjem u predmemoriju i bez spremanja u pred-


memoriju (engl. cache), kao i klase za asinkroni ulaz i izlaz (U/I). Ako koristite asin-
kroni U/I moete uputiti .NET klase da itaju datoteku. Dok su one zaposlene uzima-
njem djelia datoteke s diska, program moe obavljati druge zadatke. Asinkroni U/I
zadaci javljaju kad su gotovi s poslom. Asinkrone klase su dovoljno mone i robustne
tako da se moe izbjei eksplicitno stvaranje dretvi (pogledajte poglavlje 20).
Tok u datoteku ili iz datoteke nije drukiji od toka preko mree, pa e u drugom diijelu
ovog poglavlja biti opisani tokovi koji koriste i TCP/IP i Web protokole.
Da bi tvorili tok podataka objekti e obino biti serijalizirani, odnosno upisani u tok
kao nizovi bitova. Sa serijalizacijom ste se ve susreli u poglavlju 19. .NET kostur
prua znatnu podrku za serijalizaciju i u zadnjem dijelu ovog poglavlja e biti obja-
njeni detalji upravljanja serijalizacijom objekta.

Podaci na Internetu se mogu slati i u datagramima.

491
Datoteke i mape
Prije nego to vidimo kako moemo prebaciti podatke u datoteke ili iz datoteka, prou-
imo podrku za upravljanje datotekama i mapama u .NET-u.
Klase koje trebate se nalaze u System.I0 imenskom prostoru. Meu njima je i File
klasa koja predstavlja datoteku na disku te Directory klasa koja predstavlja direktorij
(u Windowsima se direktorij naziva mapa).

Rad s mapama
Directory klasa izlae statike metode za stvaranje, prebacivanje i pretraivanje mapa.
Sve metode klase Directory su statike pa ih moete pozvati bez instance klase.
Klasa DirectoryInfo je slina klasi Directory samo to ne sadri nita osim lanova
instance (to jest, uope nema statikih lanova). DirectoryInfo izvodi iz FileSystem-
Info koja izvodi iz MarshalByRefObject. Klasa FileSystemInfo ima vei broj svojstava i
metoda koje pruaju informacije o datoteci ili mapi.
Tablica 21-1 sadrava popis najvanijih metoda klase Directory a tablica 21-2 popis
najvanijih metoda DirectoryInfo klase, ukljuujui vana svojstva i metode naslje-
ene od FileSystemInfo.

Tablica 21-1. Najvanije metode klase Directory

M e to d a U p o tre b a

CreateDirectory() Stvara sve m ape i podm ape zadane p ara m etrom putanje.

GetCreationTime() Vraa i postavlja vrije m e kad je m apa stvorena.

GetDirectories() U zim a im en ovane m ape.

GetLogicalDrives() Vraa im en a svih logikih diskova u ob liku < d i s k > : \ .

GetFiles() Vraa im en a d atoteka ija im ena odgovaraju uzorku.

GetParent() Vraa roditeljsku m apu za zadanu p u tan ju.

Move() Prem jeta m apu i n jen sadraj na zadanu putan ju.

Tablica 21-2. Najvanije metode i svojstva Directorylnfo klase

M e to d a ili svojstvo U p o tre b a

Attributes Nasljeuje od klase F i l e S y s t e m I n f o . U zim a ili postavlja a trib u te tre n utn e datoteke.

CreationTime Nasljeuje od klase F i l e S y s t e m I n f o . Uzima ili postavlja vrijem e stvaranja trenutne


datoteke.

Exists Javno svojstvo logike vrijednosti koje je t r u e ako m apa postoji.

Extension Javno svojstvo nasljeeno iz F i l e S y s t e m l n f o klase. To je zapravo nastavak im ena datoteke.

FullName Javno svojstvo nasljeeno iz F i l e S y s t e m I n f o klase. T o je zapravo puna putanja


d atoteke ili m ape.

492 | Programiranje C#
Tablica 21-2. Najvanije metode i svojstva Directorylnfo klase (nastavak)

M e to d a ili svojstvo U p o tre b a

L a s t A c c e s s T im e Javno svojstvo nasljeeno od F i l e S y s t e m I n f o klase. Uzima ili postavlja vrijem e


zadnjeg pristupa.

L a s t k l r i t e T im e Javno svojstvo nasljeeno od F i l e S y s t e m I n f o klase. Uzima ili postavlja vrijem e kad |e


trenutna datoteka ili m apa zadnji p ut m ijenjana.

Name Javno svojstvo im ena instance D i r e c t o r y I n f o klase.

P a re n t Javno svojstvo roditeljske m ape zadane mape.

Root Javno svojstvo korijenskog dijela putanje.

C re a te () Javna m etoda koja stvara m apu.

C r e a t e S u b D ir e c t o r y ( ) Javna m etoda koja stvara podm apu na zadanoj putanji.

D e le te () Javna m etod a koja brie D i r e c t o r y I n f o i njene sadraje iz putanje.

G e tD ir e c to r ie s () Javna m etoda koja vraa D i r e c t o r y I n f o polje s podm apama.

G e tF ile s () Javna m etoda koja vraa popis datoteka u mapi.

G e t F ile S y s t e m In f o s ( ) Javna m etoda koja uzim a polje F i l e S y s t e m I n f o objekata.

M o v e T o () Javna m etoda koja prem jeta D i r e c t o r y I n f o i njen sadraj na novu putanju.

R e fre s h () Javna m etoda nasljeena iz F i l e S y s t e m I n f o klase. Obnavlja stanje objekta.

Stvaranje Directorylnfo objekta


Da biste istraili hijerarhiju mapa, morate instancirati DirectoryInfo objekt. Direc-
toryInfo klasa prua metode za uzimanje ne samo imena sadranih datoteka i mapa,
ve i Fileln fo i DirectoryInfo objekat, omoguavajui vam da uronite u hijerarhijsku
strukturu, vadei podmape i pregledavajui ih rekurzivno.
Instancirajte DirectoryInfo objekt s imenom mape koju elite istraiti:
s tr in g p a t h = E n v ir o n m e n t .G e t E n v ir o n m e n t V a r ia b le ( " S y s t e m R o o t " ) ;
D i r e c t o r y I n f o d i r = new D i r e c t o r y I n f o ( p a t h ) ;
M*
r - f & \ --- .
' - Sjetite se da znak @ prije niza znakova stvara doslovan literal niza u
J kojem nije potrebno izostavljati znakove kao to je obrnuta kosa crta.
' l b .1 To je opisano u poglavlju 10.

Od tog DirectoryInfo objekta moete doznati informaciju o njemu, ukljuujui ime,


punu putanju, atribute, vrijeme zadnjeg pristupa tom objektu i tako dalje. Kako biste
hijerarhijski istraili podmape, zatraite od trenutne mape popis njenih podmapa:
D ir e c t o r y In f o [ ] d ir e c t o r ie s = d ir .G e tD ir e c to r ie s ();

To vraa polje DirectoryInfo objekata od kojih svaki predstavlja mapu. Istu metodu
moete ponavljati prosljeujui joj sve DirectoryIivfo objekte:
fo r e a c h ( D ir e c t o r y In f o n e w D ir i n d ir e c to r ie s )
{
d ir C o u n t e r + + ;

Poglavlje 21: Tokovi podataka | 493


ExploreDirectory(newDir);
}
Statika cjelobrojna varijabla lanica dirCounter prati koliko je sve skupa podmapa
pronaeno. Da biste prikaz uinili zanimljivijim dodajte drugu statiku cjelobrojnu
varijablu lanicu indentLevel koja e biti uveana za jedan svaki put kad ponavljajui
metodu uete u neku podmapu, a umanjena za jedan kad izaete iz podmape. To ce
omoguiti da prikaete pomak podmapa u odnosu na roditeljske mape. Kompletan
ispis je dan u primjeru 21-1.

Primjer 21-1. Prolazak kroz podmape


ttregion Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.Text;

#endregion

namespace RecursingDirectories
{
class Tester
{
// Statike varijable lanice za praenje ukupnih vrijednosti
// razina uvlaenja
static int dirCounter = l;
static int indentLevel = -l; // Prvi pomak je = 0

public static void Main()


{
Tester t = new Tester();

// Odabir poetne podmape


string theDirectory = '
Environment .CetEnvironmentVariable( "SystemRoot" );
// Korisnici Monoa i Shared Source CLI-ja pod Linuxom, Unixom ili
// MacOS X-om prethodna dva reda trebaju staviti u komentar
// i izdvojiti iz komentara sljedee:
//string theDirectory = "/tmp";

// Poziva metodu za ispitivanje mape,


// prikazivanje datuma pristupanja i svih
// podmapa.

DirectoryInfo dir = new DirectoryInfo( theDirectory );

t.ExploreDirectory( dir );

// Kompletirano. Ispisuje statistike


Console.WriteLine(

494 | Programiranje C#
Primjer 21-1. Prolazak kroz podmape (nastavak)
"\n\n{o} directories -found.Nn",
dirCounter );
}

// Pokree ju sa DirectoryIrvfo objektom.


// Za svaku mapu koju pronae rekurzivno
// e se pozvati.

private void ExploreDirectory( DirectoryInfo dir )


{
indentLevel++; // Pomak razine mape

// Stvara uvlake za mape


tor ( int i = 0 ; i < indentLevel; i++ )
Console.Write( " " ); // Dva razmaka po razini

// Ispisuje mapu i vrijeme kad joj je zadnji put pristupano


Console.WriteLine( "[{0}] {1} [{2}]\n",
indentLevel, dir.Name, dir.LastAccessTime );

// Uzima sve mape iz tekue mape


// i rekurzivno poziva ovu metodu za svaku
DirectoryInfo[] directories = dir.GetDirectories();
foreach ( DirectoryInfo newDir in directories )
{
dirCounter++; // Poveava broja
ExploreDirectory( newDir );
}
indentLevel--;
}
}
}

Morate dodati using System.I0; na poetku datoteke. Visual Studio


2005 to ne radi automatski.

Program poinje tako da identificira sistemsku korijensku mapu (System Root, obino
C:\WinNT ili C:\Windows) i napravi DirectoryInfo objekt za nju. Zatim poziva Explo-
reDirectory prosljeujui taj DirectoryInfo objekt. ExploreDirectory prikazuje infor-
macije o mapi i onda uzima sve podmape.
Popis svih podmapa trenutne mape se dobija pozivanjem GetDirectories metode. Ona
vraa polje DirectoryInfo objekata. ExploreDirectory je rekurzivna metoda. Svaki
DirectoryInfo objekt je dalje proslijeen u ExploreDirectory. Uinak je da se rekur-
zivno pristupi svakoj podmapi i zatim iz nje izae kako bi se pristupilo ostalim podma-
pama dok ne budu prikazane sve podmape u %SystemRoot%. Kad ExploreDirectory
konano vrati pozivajua metoda ispisuje saetak.

Poglavlje 21: Tokovi podataka I 495


Rad s datotekama
DirectoryInfo objekt moe vratiti i kolekciju svih datoteka u svakoj pronaenoj pod-
mapi. G etFiles() metoda vraa polje F ilelnfo objekata od kojih svaki opisuje datoteku
u toj mapi. F ileln fo i F ile objekti su meusobno povezani, ba kao i Directorylnfo i
Directory. Kao i metode od Directory, tako su i metode od F ile statike. Sve Filelnfo
metode su istovremeno i metode instance.
Tablica 21-3 daje popis najvanijih metoda klase File. Tablica 21-4 daje popis vanih
lanova Fileln fo klase.

Tablica 21-3. Najvanije javne statike metode klase File

M e to d a U p o treb a

A p p e n d T e x t() Stvara S t r e a m V I r i t e r koji dodaje tekst zadanoj datoteci.

C o p y () Kopira postojeu d atoteku u novu datoteku.

C re a te Stvara datoteku na zadanoj putan ji.

C re a te T e x t() Stvara S t r e a m W r i t e r koji upisuje novu tekstualnu d atoteku u zadanu datoteku.

D e le te () Brie zadanu datoteku.

E x is ts () Vraa t r u e ako zadana datoteka postoji.

G e tA t tr ib u te s () , U zim a i postavlja F i l e A t t r i b u t e s zadane datoteke.


S e tA ttr ib u te s ()

G e t C r e a t i o n T im e ( ) , Vraa i postavlja d atu m i vrijem e stvaranja datoteke.


S e t C r e a t io n T im e ( )

G e t L a s t A c c e s s T im e ( ) , Vraa i postavlja vrije m e zadnjeg pristupa datoteci.


S e t L a s t A c c e s s T im e ( )

G e t L a s t W r it e T im e ( ) , Vraa i postavlja vrije m e kad je d atoteka zadnji p u t m ijenjana.


S e t L a s t W r it e T im e ( )

M o v e () Pomie datoteku na novu lokaciju, m oe se koristiti za m ije n jan je im en a datoteke.

O p e n R e a d () Javna statika m etod a koja otvara F i l e S t r e a m na datoteci.

O p e n W r it e () Stvara S t r e a m za itan je i upisivanje na zadanoj p u tan ji.

Tablica 21-4. Metode i svojstva Filelnfo klase

Metoda ili svojstvo Upotreba

A tt r ib u t e s () Nasljeuje iz F i l e S y s t e m I n f o . Uzim a ili postavlja atrib u te tren u tn e datoteke.

C r e a t io n T im e Nasljeuje iz F i l e S y s t e m I n f o . Vraa ili postavlja vrije m e stvaranja datoteke.

D ir e c t o r y Javno svojstvo koje uzim a instancu roditeljske m ape.

E x is t s Javno svojstvo koje vraa t r u e ako m apa postoji.

E x t e n s io n Javno svojstvo nasljeeno iz F i l e S y s t e m I n f o . N astavak im en a d atoteke.

F u llN a m e Javno svojstvo nasljeeno iz F i l e S y s t e m I n f o . Puna putan ja d atote ke ili m ape.

L a s t A c c e s s T im e Javno svojstvo nasljeeno iz F i l e S y s t e m I n f o . Uzim a ili postavlja vrije m e zadnjeg


pristupanja datoteci.

496 | Programiranje C#
Tablica 21-4. Metode i svojstva Filelnfo klase (nastavak)

M e to d a ili svojstvo U potreba

L a s t W r i t e T im e Javno svojstvo nasljeeno iz F i l e S y s t e m I n f o . Uzima ili postavlja vrijem e kada je


u datoteku ili m apu posljednji put upisivano.

L en g th Javno svojstvo koje uzim a veliinu tekue datoteke.

Nam e Javno svojstvo nam e ove d i r e c t o r y i n f o instance.

A p p e n d T e x t() Javna m etoda koja stvara S t r e a m K r i t e r koji dodaje tekst datoteci.


C o p y T o () Javna m etoda koja kopira postojeu datoteku u novu datoteku.

C re a te () Javna m etoda koja stvara novu datoteku.

D e le te () Javna m etoda koja trajno brie datoteku.

M o v e T o () Javna m etoda koja prebacuje datoteku na novu lokaciju. Moe se koristiti za m ijenjanje
im ena datoteke.

O p e n () Javna m etoda koja otvara datoteku sa privilegijam a za itanje, pisanje i dijeljenje.

O p e n R e a d () Javna m etod a koja koja stvara F i l e S l r e a m samo za itanje.

O p e n T e x t() Javna m etoda koja stvara S t r e a m R e a d e r koji ita iz postojee tekstualne datoteke.

O p e n W r it e ( ) Javna m etoda koja stvara F i l e S t r e a m samo za upisivanje.

Primjer 21-2 je modificirani primjer 21-1 u koji je dodan kod koji uzima Filelnfo objekta
za svaku datoteku iz svake podmape. Taj objekt se koristi za prikazivanje imena dato-
teke zajedno s njenom veliinom te datumom i vremenom zadnjeg pristupanja.

Primjer 21-2. Ispitivanje datoteka i podmapa


itregion Using directives

using System;
using System.Collections. Ceneric;
using System.I0;
using System.TeXt;

# endregion

namespace ExploringFilesAndSubdirectories
{
cla ss Tester
{

II Statike v a rija b le lanice za praenje ukupnih


II vrijedn osti i razina uvlaenja
s ta tic int dirCounter = 1 ;
s ta tic int indentLevel = - 1 ; II Prva uvlaka je = 0

s ta tic int fileCounter = 0;

public s ta tic void Main()


{
Tester t = new Tester();

Poglavlje 21: Tokovi podataka | 497


Primjer 21-2. Ispitivanje datoteka i podmapa (nastavak)
// Odabira poetnu podmapu
string theDirectory =
Environment.GetEnvironmentVariable( "SystemRoot" );
// Korisnici Monoa i Shared Source CLI-ja pod linuxom, Unixom ili
// MacOS X-om prethodna dva reda trebaju staviti u komentar
// i izdvojiti iz komentara sljedee:
//string theDirectory = "/tmp";

// Poziva metodu za ispitivanje mape,


// prikazivanje datuma pristupanja i svih
// podmapa.
DirectoryInfo dir = new DirectoryInfo( theDirectory );

t.ExploreDirectory( dir );

// Kraj. Ispisuje statistike.

Console.WriteLine(
"\n\n{0} files in {1} directories found.Nn",
H le C o u n te i , dirCounter ); \
}
// Pokree ju sa directoryInfo objektom.
// Za svaku mapu koju pronae rekurzivno
// e se pozvati.
private void ExploreDirectory( DirectoryInfo dir )
{
indentLevel++; // Pomie razinu mape

// Stvara uvlaku za podmape


tor ( int i = 0; i < indentlevel; i++ )
Console.Write( " " ); // Dva razmaka po razini

// Ispisuje mapu i vrijeme zadnjeg pristupanja


Console.WriteLine( "[{0}] {1} [{2 }]\n",
indentLevel, dir.Name, dir.LastAccessTime );

// Uzima sve datoteke iz mape i


// ispisuje imena, vrijeme zadnjeg pristupa i veliinu
FileInfo[] filesInDir = dir.CetFiles();
foreach ( Filelnfo file in tilesInDir )
{
// lo jedna uvlaka kako bi datoteke
// bile ispod mapa
tor ( int i = 0; i < indentlevel + 1; i++ )
Console.Write( " " ); // Dva razmaka po razini

Console.WriteLine( "{0} [{l}] Sie: {2} bytes",


file.Name,
file.LastWriteTime,
file.Length );
fileCounter-H-;
}

498 | Programiranje C#
Primjer 21-2. Ispitivanje datoteka i podmapa (nastavak)
// Uzima sve mape iz tekue mape
// i rekurzivno poziva ovu metodu za svaku
DirectoryIrrfo[] directories = dir.GetDirectories();
foreach ( DirectoryInfo newDir in directories )
{
dirCounter++; // Poveava broja
ExploreDirectory( newDir );
}
indentLevel--;
}
}
}
Izlaz (odlomak):
[0] WIND0WS [9/4/2004 8:37:13 AH]

0 .LOG [8/30/2004 8:26:05 PM] Sie: 0 bytes


AC3API.INI [1 /1 4 / 1 9 9 9 2:04:06 PM] Sie: 231 bytes
actsetup.log [7/1/2004 11:13:11 AM] Sie: 3 8 4 8 bytes
Blue Lace 16.bmp [8/29/2002 6:00:00 AM] Sie: 1272 bytes
B00TSTAT.DAT [8/30/2004 8 :2 5 : 0 3 PM] Sie: 2 0 4 8 bytes
12630 files in 1 4 4 4 directories found.

Primjer je inicijaliziran imenom SystemRoot mape. Ispisuje informaciju o svim datote-


kama u mapi i onda rekurzivno pretrauje sve podmape i njihove podmape (va izlaz
e se moda razlikovati). Ovaj postupak e moda potrajati jer je SystemRoot mapa
dosta velika (1444 podmapa na mom raunalu, kao to se vidi u izlazu).

Mijenjanje datoteka
Kao to moete vidjeti iz tablica 21-3 i 21-4, mogue je koristiti Filelnfo klasu za stva-
ranje, kopiranje, brisanje i mijenjanje imena datoteka. Sljedei primjer stvara novu
podmapu, kopira datoteke u nju, nekima mijenja imena, neke brie i zatim brie cijelu
mapu.
*4
Da biste postavili ovaj primjer napravite mapu Vest i kopirajte mapu
Media iz W inN T ili Windows mape u nju. Nemojte raditi sa datote-
kama u sistemskoj korijenskoj mapi. Kad radite sa sistemskim datote-
kama morate biti posebno oprezni.

Prvi korak je da napravite DirectoryInfo objekt za testnu mapu (podesite theDirectory


na odgovarajui nain ako koristite Mac OS X , Linux ili Unix sustav):
string theDirectory = @"c:\test\media";
DirectoryInfo dir = new DirectoryInfo(theDirectory);

Zatim napravite podmapu unutar testne mape pozivanjem CreateSubDirectory na


Directorylnfo objektu. Natrag dobijate novi DirectoryInfo objekt koji predstavlja
upravo stvorenu podmapu:

Poglavlje 21: Tokovi podataka I 499


string newDirectory = "newTest";
DirectoryInfo newSubDir =
dir.CreateSubdirectory(newDirectory);

Sad moete iterirati kroz mapu test i kopirati datoteke u napravljenu podmapu:
FileInfo[] filesInDir = dir.GetFiles();
foreach (Filelnfo file in filesInDir)
{
string fullName = newSubDir.FullName +
"\\" + file.Name;
file.CopyTo(fullName);
Console.WriteLine("{0} copied to newTest",
file.FullName);
}
Obratite pozornost na sintaksu metode CopyTo. To je metoda F ile ln fo objekta. Prosli-
jedite joj punu putanju nove datoteke ukljuujui ime i nastavak imena.
Kad ste kopirali datoteke moete dobiti popis datoteka u novoj podmapi i izravno
raditi s njima:
filesInDir = newSubDir.GetFiles();
foreach (Filelnfo file in filesInDir)
{
Napravite obinu cjelobrojnu varijablu counter i upotrijebite ju da promijenite ime
svake druge datoteke:
if (counter++ %2 == 0)
{
file.MoveTo(fullName + ".bak");
Console.WriteLine("{0} renamed to {l}",
fullName,file-FullName);
}
Promijenite ime datoteke tako to ete ju premjestiti u istu mapu, ali sa drugim ime-
nom. Naravno, moete premjestiti datoteku u novu mapu s originalnim imenom. Isto-
vremeno moete premjestiti datoteku i promijeniti joj ime.
Promijenite ime svake druge datoteke i pobriite one kojima niste promijenili imena:
file.DeleteO;
Console,WriteLine("{0} deleted.",
fullName);

Kad ste gotovi s datotekama moete poistiti za sobom tako to ete pobrisati cijelu
podmapu:
newSubDir.Delete(true);

Boolean parametar odreuje da li je ovo rekurzivno brisanje. Ako proslijedite false i


ako mapa ima podmape s datotekama, bit e izbaena iznimka.
Primjer 21-3 daje izvorni kod za cijeli program. Budite oprezni kad ovo izvodite: kad
program zavri podmapa e biti pobrisana. Da biste vidjeli to je preimenovano, a to
pobrisano, ili stavite toku prekida u zadnjem redu ili uklonite zadnji red iz koda.

500 | Programiranje C#
Primjer 21-3. Stvaranje podmape i rad s datotekama
ttregion Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.Text;

ttendregion

namespace CreatingSubdirectoryManipulatingFile
{
cla ss Tester
{
public s ta tic void Main()
{
/ / Stvara instancu i pokree ju
Tester t = new T e ste r();
string theDirectory = @"c:\test\media;
DirectoryInfo dir = new DirectoryInfo( theDirectory );
t.ExploreDirectory( dir );
}

// Pokree ju s imenom mape


private void ExploreDirectory( Directorylnfo dir )

// Stvara novu podmapu


string newDirectory = "newTest";
DirectoryInfo newSubDir =
dir.CreateSubdirectory( newDirectory ) ;

// Uzima sve datoteke iz mape


// i kopira ih u novu mapu
F ile ln fo f] files In D ir = d ir.G e tF ile s();
foreach ( Fileln fo f i l e in filesIn D ir )
{
string fullName = newSubDir.FullName +
"\\" + file.Name;
file.CopyTo( fullName );
Console.WriteLine( " { 0 } copied to newTest",
file.FullName );
}

// Uzima kolekciju kopiranih datoteka


file s In D ir = newSubDir.GetFiles();

// Neke brie a nekima mijenja imena


int counter = 0 ;
foreach ( Fileln fo f i l e in filesIn D ir )

strin g fullName = file.FullName;

i f ( counter++ % 2 == o )

Poglavlje 21: Tokovi podataka I 501


Primjer 21-3. Stvaranje podmape i rad s datotekam a (nastavak)

{
file.MoveTo( fullName + ".bak" );
Console.WriteLine( "{0} renamed to {l}",
fullName, file.FullName );
}
else
{
file.Delete();
Console.WriteLine( "{0} deleted.",
fullName );
}
}

newSubDir.Delete( true ); // Brie podmapu


}
}
}

Izlaz (odlomak):
c:\test\media\Bach's Brandenburg Concerto No. 3.RMI
copied to newTest
c:\test\media\Beethoven's 5th Symphony.RMI copied to newTest
c:\test\media\Beethoven's Fur Elise.RMI copied to newlest
c:\test\media\canyon.mid copied to newTest
c:\test\media\newTest\Bach's Brandenburg Concerto
No. 3.RMI renamed to
c:\test\media\newTest\Bach's Brandenburg Concerto
No. 3.RMI.bak
c:\test\media\newTest\Beethoven's 5th Symphony.RMI deleted.
c:\test\media\newTest\Beethovens Fur Elise.RMI renamed to
c:\test\media\newTest\Beethoven's Fur Elise.RMI.bak
c:\test\media\newTest\canyon.mid deleted.

itanje i upisivanje podataka


itanje i upisivanje podataka se postie preko klase Stream. Sjeate se tokova? Ovo je
poglavlje o njima."
Stream podrava sinkrono i asinkrono itanje i upisivanje. .N ET kostur prua vei broj
klasa izvedenih iz Stream, ukljuujui FileStream, MemoryStream i NetworkStream. Pored
ovih klasa postoji i BufferedStream koja prua ulaz i izlaz preko meuspremnika (engl.
buffer) i moe se koristiti s ostalim klasam a za tokove podataka. Najvanije klase za
rad s ulazom i izlazom navedene su u tablici 21-5.*

* Naklon Arlou Guthrieu.

502 | P r o g r a m i r a n je C#
Tablica 21-5. Najvanije klase .NET kostura za ulaz i izlaz podataka

Klasa U p o tre b a

S tre am Apstraktna klasa koja podrava itanje i upisivanje bajtova.

B in a r y R e a d e r / B in a ry W r ite r ita i pie ifrirane nizove i primitivne tipove podataka iz nizova i u nizove.

F ile ,F ile ln fo , Prua implementadje apstraktnih F ile S y s te m In fo klasa, ukljuujui stvaranje,


D ir e c to r y , D ir e c to r y In fo premjetanje, brisanje i mijenjanje imena datoteka i mapa.
F ile S t re a m Za itanje iz Fi l e objekata. Podrava sluajan pristup datotekama. Podrazumijevano
otvara datoteke sinkrono. Podrava asinkroni pristup datoteci.

T e x t R e a d e r , T e x t W r ite r , T e x tR e a d e r i T e x t W r it e r su apstraktne klase namijenjene za ulaz i izlaz


S t r in g R e a d e r , S t r in g W r i t e r Unicode znakova. S tr in g R e a d e r i S t r i n g k l r i t e r piu u tok ili itaju iz toka,
dozvoljavajui da ulaz i izlaz budu ili tok ili niz znakova.
B u ffe re d S tre a m Tok koji dodaje upoterbu meuspremnika drugom toku kao toje NetvvorkS tream .
B u f fe re d S tre a m s moe poboljati performanse toka kojem je pridruena, ali
zapamtite da File S tr e a m ima ugraen meuspremnik.
M em oryS tream Tok bez meuspremnika iji su uahureni podaci izravno dostupni u memoriji
i najkorisniji je kao privremeni meuspremnik.
N etvvork S tream Tok preko mrene veze.

Binarne datoteke
Ovaj dio poinjemo koritenjem osnovne klase Stream za binarno itanje datoteke.
Izraz binarno itanje (engl. binary read) se koristi za razlikovanje od tekstualnog ita-
nja (engl. text read). Ako niste sigurni da datoteka sadri samo tekst, najsigurnije je
da postupate s njom kao da je tok bajtova, odnosno binarna datoteka.

Klasa Stream prua puno metoda, ali najvanije su Read(),Write(),BeginRead(), Begin-


Write() i Flush( ) . Sve su obraene u nekoliko sljedeih odlomaka.

Da bi izveli binarno itanje ponite sa stvaranjem para Stream objekata - jednog za


itanje i jednog za upisivanje:
S tre a m in p u tS tre a m = F ile .O p e n R e a d (
@"C:\test\source\testl.cs");

S tre a m o u tp u tS tre a m = F ile .O p e n W r ite (


@"C: \ t e s t \ s o u r c e \ t e s t l . b a k " ) ;

Da biste otvorili datoteke za itanje i upisivanje koristite OpenRead() i OpenWrite()


metode klase File. Statiko preoptereivanje ovih metoda uzima putanju datoteke kao
argument, kao to je i prikazano.

Binarno itanje funkcionira preko uitavanja u meuspremnik. Meuspremnik je


samo polje bajtova uitanih Read() metodom.

Proslijedite meuspremnik, pomak u meuspremniku na kojem treba poeti spremati


proitane podatke i broj bajtova koje treba proitati. InputStream. Read uitava bajtove
iz pom onog spremita u meuspremnik i vraa ukupni broj uitanih bajtova.

P o g l a v l je 2 1 : T o k o v i p o d a t a k a | 503
Uitavanje se nastavlja sve dok vie ne bude bajtova za itanje:
while ( (bytesRead =
inputStream.Read(buffer,0,SIZE_BUFF)) > 0 )
{
outputStream.Mrite(buffer,0,bytesRead);
}

Svaki puni meuspremnik bajtova se ispisuje u izlaznu datoteku. Argumenti metode


W rite() su meuspremnik iz kojeg e se itati, pomak u meuspremniku od kojeg se
poinje itati i broj bajtova za upisivanje. Primijetite da upisujete isti broj bajtova koji
ste upravo uitali.

Primjer 2 1 -4 daje cijeli ispis.

P r i m j e r 2 1 - 4 . I m p l e m e n t i r a n j e b in a r n o g i t a n j a i u p is iv a n ja u d a t o t e k u

itregion Using directives

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

#endregion

namespace ImplementingBinaryReadMriteToFile
{
class Tester
{
const int SizeBuff = 1024;

public static void Main()


{
// Stvara instancu i pokree ju
Tester t = new Tester();
t.Run();
}

II Pokretanje s imenom mape


private void Run()
{
// Datoteka za itanje
Stream inputStieam = File.OpenRead(
@"C:\test\sourceVtestl.cs" );

// Datoteka za upisivanje
Stream outputStream = File.OpenWrite(
@"C:Vtest\source\testl.bak" );

II Stvara meuspremnik za uvanje bajtova


byte[] buffer = new Byte[Size8uffj;
int bytesRead;

// Dok metoda vraa bajtove

504 I P r o g r a m i r a n je C #
Primjer 21-4. Implementiranje binarnog itanja i upisivanja u datoteku (nastavak)
I I nastavlja ih zapisivati u izlazni tok
while ( ( bytesRead =
inputStream.Read( buffer, 0 , SizeBuff ) ) > o )
{
outputStream.Write( b u f f e r , 0, bytesRead ) ;
}

I I Posprema prije zavretka


inputStreain.Close();
outputStream.Close() ;
}
}
}

Prije nego pokrenete ovaj program napravite C:\test\source podmapu i


M* 4 . dodajte datoteku (koja sadri izvorni kod ovog programa) testl.cs. Kao
_ Tkv i u prethodnim primjerima, korisnici Unix, Linux i Mac OS sustava
trebaju prilagoditi putanje.

Rezultat izvoenja ovog programa kopija ulazne datoteke (testl.cs ) u istoj mapi s ime-
nom testl.bak. Moete usporediti nastale datoteke koritenjem alata za usporeivanje
datoteka. Datoteke su identine, kao to se vidi na slici 21-1.'

Comparison Results 1
IDENTICALFIlES: C:\test\soufce\tesl1.cs. C:\te$t\soiifce
\test1.bak

EtProgram} CompateNewPatofFies | ShowTheFJesAnyw,

Slika 21-1. Usporedba datoteka pokazuje da su dvije datoteke identine

Tokovi s m e u sp rem n ic im a
U prethodnom primjeru napravili ste meuspremnik u koji e se uitavati podaci. Kad
ste pozvali Read() jedan puni meuspremnik je uitan s diska. Meutim, operacijski
sustav e biti uinkovitiji ako bude uitavao vei (ili manji) broj bajtova odjednom.

Objekt toka s meuspremnikom (engl. buffered stream ) stvara interni meuspremnik


i uitava podatke u pom ono spremite i iz tog spremita u koliinama koje smatra

Meni najdrai alat za usporedbu datoteka je ExamDiff Pro. (h ttp://w w w .p restosoft.com lp s.asp p ag e-ed p
ex am d iffp ro).

P o g la v lje 2 1 : T o k o v i p o d a ta k a | 505
najuinkovitijim. Va meuspremnik ipak e napuniti u koliinama koje zadate, ali iz
meuspremnika u memoriji a ne izravno iz pomonog spremita. Krajnji uinak je da
su ulaz i izlaz uinkovitiji i prema tome bri.

BufferedStream objekt je izgraen na temelju postojeeg Stream objekta kojeg ste ve


napravili. Da biste koristili BufferedStream prvo napravite obinu klasu za tok, kao u
-primjer u.21-4:
Stream input Strea m = File .Ope nRea d(
@ C :\ t e s t \ so ur ce \ f ol de r 3 .c s");

Stream ou tp ut Strea m = File .0pe nWri te(


@ C: \tes t\sourc e\fold er3 .bak") i

Kad imate obian tok proslijedite nejgov objekt konstruktoru toka s meuspremnikom:
Bu ff er ed St re am buffered lnpu t =
new Bu ff er ed Stream(i np ut Strea m) ;

Bu ff er ed St re am buffered Outpu t =
new Bu ff er e d St re am ( o ut pu t S t re am );

Sada moete koristiti BufferedStream kao obian tok, pozivajui Read() i Write () ba kao
to ste prije napravili. Operacijski sustav obrauje pohranjivanje u meuspremnik:
whil e ( (bytesRead =
bu ff er ed ln pu t. Rea d(buffe r,0 ,SIZE_ B UFF)) > 0 )
{
bu f f e r e d O u t p u t .Writ e( bu ff er , 0 , b y t e s R e a d ) ;
}

Ne zaboravite isprazniti meuspremnik kad elite biti sigurni da su podaci upisani u


datoteku:
bu f f e r e d O u t p u t .Fl u s h ( );

Ovo u biti govori meuspremniku u memoriji da isprazni svoj sadraj.

Trebate sami zatvoriti sve tokove iako e ih program za zavravanje


^ ( en g l. f i n a l i z e r ) zatvoriti ako ih pustite da izau iz dosega. Urobustnom
'' iT>.' programu uvijek biste trebali eksplicitno zatvoriti meuspremnik.

Prim jer21-5 sadri ispis cijelog programa.

P r im j e r 2 1 - 5 . lm p l e m e n t i r a n j e u l a z a i i z l a z a p r e k o m e u s p r e m n i k a

nam espace Program ming_ C Sharp


{
using System;
using System.I0;

class Test er
{
const int Si ze Bu ff = 1024;

506 | P r o g r a m i r a n je C#
Primjer 21-5. Implementiranje ulaza i izlaza preko meuspremnika (nastavak)
public static void Main()

// Stvara i pokree instancu


Tester t = new Tester();
t.Run();
}

// Pokree ju s imenom mape


private void Run()
{
// Stvara binarne tokove
Stream inputStream = F ile.O p en R ea d (
@ " C :\test\source\folder 3 .cs");

Stream outputStream = File.OpenWrite(


@ "C:\test\source\folder 3 .bak");

// Dodaje tokove s meuspremnicima na


// poetak binarnih tokova
BufferedStream bufferedlnput =
new BufferedStream(inputStream);

BufferedStream bufferedOutput =
new BufferedStream(outputStream);
byte[] buffer = new Byte[SizeBuff];
int bytesRead;

while ( (bytesRead =
bufferedlnput.Read(buffer,o,SizeBuff)) > o )

bufferedOutput.Write(buffer,0,bytesRead);

bufferedOutput.Flush();
bufferedlnput.Close();
bufferedOutput.Close();

}
}

S veim datotekama ovaj primjer bi se trebao izvoditi bre nego primjer 21-4.

Rad s tekstualnim datotekama


Ako znate da datoteka koju uitavate (ili zapisujete) sadri samo tekst moete koristiti
StreamReader i StrearaWriter klase. One su napravljene da olakaaju rad s tekstom Na
primjer, one podravaju ReadLine() i WriteLine() metode koje itaju i piu red teksta
odjednom. Ve ste koristili WriteLine() s objektima Console.

Da biste napravili StreamReader instancu prvo napravite Filelnfo objekt i onda pozo-
vite OpenText() metodu na njemu;

P o g la v lje 2 1 : T o k o v i p o d a ta k a | 507
Filelnfo theSourceFile =
new Filelnfo (@"C:\test\source\testl.cs");

StreamReader stream = theSourceFile.OpenText();

OpenText () vraa StreamReader za datoteku. Kad imate StreamReader moete itati dato-
teku red po red:
do
{
text = stream.ReadLine();
} while (text != nuli);

ReadLine() uitava po jedan red sve dok ne doe do kraja datoteke. StreamReader e
vratiti nuli kad doe do kraja datoteke.
Da biste napravili StreamWriter klasu pozovite StreamWriter konstruktor prosljeujui
mu puno ime datoteke u koju elite upisivati:
Streamklriter writer = new
StreamWriter((s>"C:\ test\source\folder3.bak", false);

Drugi parametar je logiki argument append. Ako datoteka ve postoji, true e izazvati
da se novi podaci dodaju na kraju datoteke, a false e pisati preko postojee datoteku.
U ovom sluaju proslijedite false tako da pie preko datoteke ako ve postoji.

Sad moete napraviti petlju za ispisivanje sadraja svih redova stare datoteke u novu
datoteku i na konzolu:
do
{
text = reader.ReadLine();
writer.WriteLine(text);
Console.WriteLine(text);
} while (text != nuli);

Primjer 21 -6 sadri kompletan izvorni kod.

Primjer 21-6. Uitavanje iz tekstualne datoteke i ispisivanje u tekstualnu datoteku


ttregion Using directives

using System;
using System.Collections.Ceneric;
using System.I0;
using System.Text;

#endregion

namespace ReadingWritingToTextFile
{
class Tester
{
public static void Main()
{
// Stvara instancu i pokree ju

508 | P r o g r a m i r a n je C#
P r im je r 21-6. Uitavanje iz tekstualne datoteke i ispisivanje u tekstualnu datoteku (nastavak)
Tester t = new Tester();
t .Run();

// Pokree ju s imenom mape


private void Run()
{
// Otvara datoteku
Filelnfo theSourceFile = new FileInfo(
@"C:\test\source\test.cs" );

// Stvara ita teksta za tu datoteku


StreamReader reader = theSourceFile.OpenText();

// Stvara pisa teksta za novu datoteku


StreamUriter uriter = new Strean(Writer(
(?"C:\test\source\test.bak", false );

// Stvara tekstualnu varijablu za uvanje svakog reda


string text;

// Prolazi kroz datoteku i ita svaki red


// Upisuje redove u datoteku
// i upisuje ih u datoteku,
do
{
text = reader.ReadLine();
writer.WriteLine( text );
Console.WriteLine( text );
} while ( text U nuli );

// Pospremanje
reader.Close();
writer.Close();
} '
I }
}

Kad se ovaj program pokrene sadraj originalne datoteke se ispisuje na zaslon i upisuje
u novu datoteku. Pogledajte sintaksu za ispisivanje na konzolu:
Console.WriteLine(text)j

Ona je priblino identina sintaksi koja je koritena za upisivanje u datoteku:


writer.WriteLine(text);

Kljuna razlika je u tome d aje WriteLine() metoda od Console statika, dok je Write-
Line() metoda od StreamWriter, koja je nasljeena od TextWriter, metoda instance i
mora biti pozvana na objektu, a ne na klasi.

P o g l a v l je 2 1 : T o k o v i p o d a t a k a | 509
Asinkroni ulaz i izlaz
Svi programi koje ste dosad razmatrali izvode s i n k r o n i u la z i i z l a z podataka, to znai
da su sve druge aktivnosti zaustavljene dok program uitava ili ispisuje podatke. Pohra-
njivanje podataka u meuspremnik ili uitavanje iz njega moe (relativno govorei)
dugo potrajati, posebno ako je pom ono spremite spor disk ili (jo gore) izvor na
Internetu.

Ako radite s velikim datotekama, ili kad uitavate ili ispisujete preko mree, htjet ete
a s i n k r o n i u l a z i i z l a z p o d a t a k a koji omoguava da zaponete uitavanje i okrenete se
drugim stvarima dok C LR ispunjava va zahtjev. .N ET kostur prua asinkroni U/l s
pomou BeginRead() i BeginWrite() metoda Stream klase.

Redoslijed je da pozovete BeginRead() na datoteci i onda prijeete na drugi posao,


moda u drugoj dretvi. Kad se uitavanje dovri, metoda s povratnim pozivom e vas
0 tome obavijestiti. Tada moete obraditi podatke koji su uitani, pokrenuti drugo
uitavanje i nastaviti s drugim poslom.

Pored tri parametra koje ste koristili kod binarnog uitavanja (meuspremnik, pomak
1broj bajtova koji se uitavaju), BeginRead() trai jo d e l e g a t a i o b j e k t s t a n ja .
a
Ovo je instanca openitijeg asinkronog predloka koji se susree diljem
.N ET kostura (na primjer, asinkroni ulazno/izlazni tok, asinkrone rad-
<' A
it,' nje s pristupnim tokam a, asinkroni poziv delegata itd.).
I

Delegat je opcionalna metoda s povratnim pozivom koja se, ako je pruena, poziva
prilikom uitavanja podataka. Stanje je takoer neobavezno. U ovom primjeru, prosli-
jedite nuli za stanje. Stanje objekta se uva u varijablama lanicama probne klase.

Moete slobodno staviti bilo koji objekt u param etar stanja i moete ga uzeti prilikom
povratnog poziva. Obino (kao to moete pretpostaviti iz imena) sauvate vrijedno-
sti stanja koje ete trebati prilikom vraanja. Projektant programa Parametar stanja
moe koristiti projektantu da spremi stanje poziva (pauzirano, nedovreno, izvodi se,
i tako dalje).

U ovom primjeru, napravite meuspremnik i Stream objekt kao privatne varijable lanice:
public class AsynchIOTester
{
private Stream inputstream;
private byte[] buffer;
corist int BufferSize = 256;

Pored toga, napravite delegat kao privatni lan:


private AsyncCallbacl< myCallBack; U Delegirana m etod a

Delegat je deklariran da bude tipa AsyncCallback, a to je ono to BeginRead() metoda


klase Stream oekuje.

AsyncCallBack delegat je deklariran u imenskom prostoru System kako slijedi:

510 I P r o g r a m i r a n je C#
public delegate void AsyncCallback (IAsyncResult ar);

Prema tome, ovaj delegat moe biti povezan s bilo kojom metodom koja vraa void i
uzima IasyncResult suelje kao parametar. CLR e proslijediti IasyncResult suelje
prilikom izvoenja, kad je metoda pozvana. Vi samo trebate deklarirati metodu:
void OnCompletedRead(IAsyncResult asyncResult)

i onda prikljuiti delegata u konstruktoru:


AsynchIOTester()
{
//...
myCallBack = new AsyncCallback(this.OnCompletedRead);
}

Evo kako to radi, korak po korak. U Main() metodi napravite instancu klase i pokre-
nite ju:
public static void Main()
{
AsynchIOTester theApp = new AsynchIOTester();
theApp.Run();
}

Poziv newpoziva konstruktor. U konstruktoru otvorite datoteku i uzmite Stream objekt.


Onda dodijelite prostor u meuspremniku i spojite mehanizam za povratni poziv:
AsynchIOTester()
{
inputStream = File.OpenRead(@"C:\test\source\AskTim.txt");
buffer = new byte[BufferSize];
myCallBack = new AsyncCallback(this.OnCompletedRead);
}

Za ovaj primjer je potrebna velika tekstualna datoteka. Kopirao sam


lanak koji je napisao Tim 0 Reilly (Ask Tim) s adrese http://www.
oreilly.com u tekstualnu datoteku AskTim.txt. Stavio sam tu datoteku
u podmapu test\source na mom C: disku. Moete koristiti bilo koju
tekstualnu datoteku u bilo kojoj podmapi.

U Run() metodi pozovite BeginRead(), to pokree asinkrono uitavanje datoteke:


inputStream.BeginRead(
buffer, I I Mjesto za smjetanje rezultata
0, // Pomak
buffer.Length, // BufferSize
myCallBack, I I Delegat povratnog poziva
nuli); // Lokalni objekt stanja

Onda radite neto drugo dok uitavanje traje. U ovom sluaju simulirajte koristan rad
brojanjem do 5 0 0 0 0 0 prikazujui napredak nakon svakih 1 00 0 brojeva:
for (long i = 0; i < 500000; i++)
{
if (i%iooo == 0)

P o g la v lje 2 1 : T o k o v i p o d a ta k a | 511
{
Console.WriteLine("i: {0}", i);
}
}

Kad se uitavanje zavri, C LR e pozvati povratnu metodu:


void OnCompletedRead(IAsyncResult asyncResult)
{

Prva stvar koju trebate uiniti kad vam bude javljeno da je uitavanje zavreno je da
odredite koliko je bajtova zapravo uitano. Uinite to pozivanjem metode EndRead() \z
Stream objekta, prosljeujui joj IAsyncResult suelje koje je proslijedio C L R :
int bytesRead = inputStream.EndRead(asyncResult);

EndRead() vraa broj uitanih bajtova. Ako je taj broj vei od nula, pretvorit ete sadr-
aj meuspremnika u niz znakova i ispisati ga na konzolu i onda ponovno pozvati
BeginRead() za novo asinkrono uitavanje:
if (bytesRead > o)
{
String s =
Encoding.ASCII.CetString (buffer, 0, bytesRead);
Console.WriteLine(s);
inputStream.BeginRead(
buffer, 0, buffer.Length,
myCallBack, nuli);
}

Sada moete raditi neto drugo dok se podaci uitavaju, ali moete upravljati uitanim
podacima (u ovom sluaju, ispisivanjem na konzolu) svaki put kad je meuspremnik
pun. Primjer 21-7 sadri kompletan program.

Primjer 21-7. Implementacija asinkronog ulaza i izlaza podataka


#region Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.Text;

#endregion

namespace AsynchronousI0
{
public class AsynchIOTester
{
private Stream inputStream;

// Delegirana metoda
private AsyncCallback myCallBack;

// Meuspremnik za pohranu uitanih podataka


private byte[] buffer;

512 | P r o g r a m i r a n je C #
Primjer 21-7. Implementacija asinkronog ulaza i izlaza podataka (nastavak)
I I Veliina meuspremnika
const int BufferSize = 2 5 6 ;

// Konstruktor
AsynchIOTester()
{
// Otvara ulazni tok
inputStream =
File.OpenRead(
@"C:\test\source\AskTim.txt'' );

// Dodjeljuje meuspremnik
buffer = new byte[BufferSize];

// Dodjeljuje povratni poziv


myCallBack =
new AsyncCallback( this.OnCompletedRead );
}

public static void Main()


{
// Stvara instancu AsynchIOTester
// koja poziva konstruktor
AsynchIOTester theApp =
new AsynchIOTester();

// Poziva instancu metode


theApp.Run();
}

void Run()
{
inputStream.BeginRead(
buffer, // uva rezultate
0, // Pomak
buffer.Length, // (BufferSize)
myCallBack, // Delegat povratnog poziva
nuli ); // Lokalni objekt stanja

// Radi neki posao dok se podaci uitavaju


for ( long i = 0; i < 500000; i++ )
{
if ( i % iooo == o )
{
Console.WriteLine( "i: {o}", i );
}
}
}

// Povratna metoda
void OnCompletedRead( IAsyncResult asyncResult )
{
int bytesRead =

P o g l a v l je 2 1 : T o k o v i p o d a t a k a | 513
Primjer 21-7. Implementacija asinkronog ulaza i izlaza podataka (nastavak)
inpiitStream.EndRead( asyncResult ) ;

// Ako imamo b ajto v e, stvara od n jih niz


// i prikazu je ga. Zatim zapoinje ponovno.
// U suprotnom smo gotov i,
if ( bytesRead > o )
{
S trin g s =
Encoding.A SCII.GetString( b u ffer, 0 , bytesRead ) ;
C onsole.W riteLine( s ) ;
inputStream.BeginRead(
b u ffe r, 0 , b u ffer.Length , myCallBack, n u li ) ;
}
}
}
}

Iz la z (odlomak):
i : 47000
i : 48000
i : 49000
Date: lanuary 2001
From: Dave H eisler
To: Ask Tim
S u b ject: Ouestions About 0 'R e i l ly
Dear Tim,
I 'v e been a programmer fo r about ten y ears. I had heard of
0 ' R eilly b o o k s.th en .. .
Dave,
You might be amazed at how many requ ests fo r help with
school p ro jeets I g e t;
i : 50000
i : 51000
i: 52000

Izlaz otkriva da program radi u dvije dretve istovremeno. Uitavanja se obavljaju u


pozadini dok druga dretva broji i ispisuje broj nakon 1 0 0 0 odbrojanih. Kad se uita-
vanja zavre ispisuju se na konzolu i program nastavlja s brojanjem (skratio sam ispis
da razjasnim izlaz).
U stvarnim aplikacijama mogli biste obraditi korisnike zahtjeve ili proraunavati
vrijednosti dok je asinkroni U /I zauzet uzimanjem ili spremanjem u datoteku ili bazu
podataka.

Ulaz i izlaz podataka preko mree


Zapisivanje u udaljeni objekt na Internetu ne razlikuje se puno od pisanja u datoteku
na lokalnom raunalu. Moda to budete htjeli uiniti ako va program treba spremiti
podatke na raunalu u mrei ili ako stvarate program koji prikazuje podatke na moni-
toru spojenom na drugo raunalo u mrei. Mreni U/I se temelji na upotrebi tokova

514 I P r o g r a m i r a n je C#
podataka stvorenih koritenjem pristupnih toaka. Pristupne toke su vrlo korisne
za aplikacije koje se temelje na pristupu klijent-posluitelj, za aplikacije za izravnu
razmjenu datoteka (engl. peer to peer) i prilikom pozivanja udaljenih procedura. Pri-
stupna toka je objekt koji predstavlja krajnju toku za komunikaciju izmeu procesa
koji komuniciraju preko mree. One mogu raditi s raznim protokolima, ukljuujui
UDP i TCP. U ovom dijelu stvaramo TCP/IP vezu izmeu posluitelja i klijenta. TCP/
IP je konekcijski protokol slian toku podataka koji se koristi za mrenu komunika-
ciju. Konekcijski znai da koritenjem TCP/IP protokola kad je veza uspostavljena dva
procesa mogu komunicirati kao da su povezani izravno telefonskom linijom.
at >
Iako je TCP/IP napravljen za komunikaciju preko mree, moete simuli-
ran mrenu komunikaciju izvoenjem dva procesa na istom raunalu.

Mogue je da vie aplikacija na jednom raunalu komunicira s raznim klijentima isto-


vremeno (na primjer, mogli biste pokrenuti Web posluitelj, FTP posluitelj i program
koji prua podrku za proraune). Prema tome, svaka aplikacija mora imati jedinstveni
identififkator tako da klijent moe rei koju aplikaciju trai. Taj identifikator je poznat
kao ulaz (engl. pori). Zamislite IP adresu kao telefonski broj, a ulaz kao kuni broj.

Posluitelj instancira TcpListener i obavjetava sluatelja da oslukuje veze na odre-


enom ulazu. Konstruktor za TcpListener ima dva parametra, IP adresu i in t koja
predstavlja ulaz na kojem bi sluatelj trebao oslukivati.

Klijentske aplikacije se spajaju na zadanu IP adresu. Na primjer, adresa Yahoo stranica


je 6 6 .9 4 .2 3 4 .1 3 . Takoer, klijenti se moraju spojiti na odreeni ulaz. Podrazumijeva
se da se svi Web preglednici spajaju na ulaz 80. Brojevi ulaza su u rasponu od 0 do
65535 (na primjer, 216). Meutim, neki brojevi su rezervirani.'

Ulazi su podijeljeni u sljedea podruja:

0-1023: dobro poznati ulazi


I 1024-49151: registrirani ulazi
49152-655 35: dinamiki i/ili privatni ulazi
Za popis svih dobro poznatih i registriranih ulaza, pogledajte http://
www.iana.org/assignments/port-numbers.

Kad je sluatelj napravljen pozovite Start() metodu na njemu, javljajui sluatelju da


* pone prihvaati mrene veze. Kad je posluitelj spreman da pone odgovarati na pozive
| : od klijenta, pozovite AcceptSocket (). Dretva u kojoj ste pozvali Accept Socket () uzalud radi
% (ekajui tuno pokraj telefona, stiskajui svoje virtualne ruke, nadajui se pozivu).
i:

Ako program koristite u mrei koja ima vatrozid, pitajte administratora mree koji ulazi su zatvoreni.

P o g la v lje 2 1 : T o k o v i p o d a ta k a | 515
Moete zamisliti da napravite najjednostavnijeg sluatelja na svijetu, koji strpljivo eka
klijentov poziv. Kad primi poziv, meudjeluje s tim klijentom iskljuujui sve druge
klijente. Sljedeih nekoliko klijenata koji pozovu e se spojiti, ali e automatski biti
stavljeni na ekanje. Dok ovi sluaju ugodnu glazbu i dok im se govori da je njihov
poziv vaan i da e biti obraen kad dou na red, oni e uzalud gubiti vrijeme u svo-
jim dretvama. Kad se red za ekanje popuni, sljedei pozivatelji e dobiti isti signal
ili signal da je zauzeto. Moraju onda prekinuti i ekati da naa jednostavna pristupna
toka zavri s trenutnim klijentom. Ovaj model dobro funkcionira za posluitelje koji
primaju jedan ili dva zahtjeva tjedno, ali se ne prilagoava dobro stvarnim situacijama.
Veina posluitelja treba obraivati tisue, ak i desetke tisua zahtjeva u minuti!

Da bi obraivale velik broj veza, aplikacije koriste asinkroni ulaz i izlaz da prihvate
poziv i stvore pristupnu toku s vezom prema klijentu. Originalni sluatelj se onda
vraa oslukivanju, ekajui sljedeeg klijenta. Aplikacija tako moe obraivati mnogo
poziva. Svaki put kad je poziv prihvaen, nova pristupna toka je stvorena.

Klijent nije svjestan ovog trika kojim je nova pristupna toka stvorena. to se tie kli-
jenta, spojio se na IP adresu i port koji je traio. Primjetite da nova pristupna toka uspo-
stavlja vezu s klijentom. To je potpuno drukije nego kod UDP protokola koji koristi
protokol bez veze. Kod TCP/IP protokola, kad je veza uspostavljena, klijent i posluitelj
znaju kako komunicirati meusobno, a da ne moraju ponovno adresirati svaki paket.

Stvaranje posluitelja za mreni tok podataka


Da bi napravili mreni posluitelja za TCP/IP tok podataka prvo napravite TcpListe-
ner objekt da oslukuje na TCP/IP ulazu koji ste odabrali. Ja sam proizvoljno odabrao
ulaz 6 5 0 0 0 meu raspoloivim identifikatorima ulaza:
IPAddress localAddr = IP A d dress.P arse("127 .0 . 0 . 1 " ) ;
Tcp Listener tcp L isten er = new T cp Listener(localA ddr, 65000);

Kad je TcpListener objekt napravljen, moete mu narediti da pone oslukivati;


t c p L is t e n e r .S t a r t ( ) ;

Sad ekajte da klijent zatrai vezu:


Socket so cketForC lien t = tc p L isten er.A ccep tS o ck et();

AcceptSocket metoda od TcpListener objekta vraa Socket objekt koji predstavlja sue-
lje Berkeley pristupne toke (engl. Berkeley socket interface) i koji je povezan na odre-
enu krajnju toku. AcceptSocket() je sinkrona metoda koja nee vratiti dok ne primi
zahtjev za vezu.
<*,
Poto je model iroko prihvaen od strane prodavaa raunala, Berke-
ley pristupne toke ine jednostavnijim zadatak prebacivanja izvornog
f' koda temeljenog na pristupnim tokama iz Windows i U nix okoline.

Kad imate pristupnu toku spremni ste poslati datoteku klijentu. Napravite Net-
morkStream klasu, prosljeujui konstruktoru pristupnu toku:

516 | P r o g r a m i r a n je C #
NetuorkStream networkStream = new NetworkStream (socketForClient);

Onda napravite StreamWriter objekt kao to ste to uinili ranije, osim to ovaj put ne
na datoteci, nego na NetworkStream klasi koju ste upravo napravili:
System .IO.Stream W riter stream klriter = new
System .IO.Stream W riter(networkStream );

Kad ispisujete u ovaj tok podataka, tok se alje preko mree do klijenta. Primjer 21-8
prikazuje cijeli posluitelj (Ogolio sam ovaj posluitelj do sutine. Kod stvarnog poslu-
itelja gotovo sigurno biste izvodili kod za obradu zahtjeva u dretvi i obuhvatili logiku
u tr y blok kako biste obradili mrene probleme).

Primjer 21-8. Implementacija posluitelja za mreni tok podataka


ttregion Using d ire c tiv e s

using System;
using Sy stem .C ollections.G en eric;
using System.Net;
using System .N et.Sockets;
using System .Text;

# endregion

namespace NetworkStreamingServer
{
public c la s s NetuorklOServer
{

pu blic s t a t i c void Main()


{
NetuorklOServer app =
new NetworkIOServer();
app.Run();
}

p riv a te void Run()


{
// Stv ara novi Tcp Listener i poinje
// s lu a t i na ulazu 65000

IPAddress localAddr = IPAddress.Parse( " 1 2 7 . 0 . 0 . 1 " ) ;


TcpListener tcp L isten er = new TcpListener( localAddr, 65000 ) ;
t c p L is t e n e r .S t a r t ( ) ;

//Nastavlja s lu a t i dok ne p o a ljete datoteku


fr ( ; J )
{
// Ako se k l i je n t s p o ji, prihvaa vezu
// i vraa novvu pristupnu toku socketForC lient
// dok tcp L isten er n a sta v lja s lu a t i
Socket socketForC lient =
tcp L isten er.A ccep tS o ck et();
Console.W riteLine( C lien t connected" ) ;

P o g l a v l je 2 1 ; T o k o v i p o d a t a k a | 517
Primjer 21-8. Implementacija posluitelja za mreni tok podataka (nastavak)
// Poziva pomonu metodu za s la n je datoteke
Send FileToC lient( socketForC lient ) ;

Console.W riteLine(
"D isconnecting from c l i e n t . . . " ) ;

// is t i i ide kui
S o ck etF o rC lie n t.C lo se();
C onsole.W riteLine( " E x i t i n g . . . " );
break;
}
}

// Pomona metoda za s la n je datoteke


p riv ate void Send FileToC lient(
Socket socketForC lient )
{
// Stvara mreni tok i pisa toka
// na tom mrenom toku
NetworkStream networkStream =
new NetworkStream( socketForC lient ) ;
System .IO.Stream W riter stream FJriter =
new System .IO.Stream W riter( networkStream ) ;

// Stvara it a toka za datoteku


System.IO.StreamReader streamReader =
new System .IO.Stream Reader(
@"C: \test\source\m yTest. t x t " ) ;

st rin g th e S trin g ;

// I t e r i r a kroz datoteku i a l je
// k lije n tu red po red
do
{
th e S trin g = stream Reader.ReadLine();

i f ( th e S trin g != n u li )
{
C onsole.W riteLine(
"Sendlng { 0 } " , th e S trin g ) ;
stream W riter.W ritel_ine( th e S trin g ) ;
stream W rlter.Flu sh ();
}
}
while ( th e S trin g != n u li ) ;

// Posprema
stream R eader.Close();
networkStream .Close( ) ;
stream W riter.C lose();
}
}
}

518 | P r o g r a m i r a n je C #
Izrada klijenta za mreni tok podataka
Klijent instancira TcpClient klasu koja predstavlja klijentovu TCP/IP vezu s poslui-
teljem:
Tcp Client socketForServer;
socketForServ er = new Tcp C lient("lo calH ost", 65 0 0 0 ) ;

S ovom TcpClient klasom moete napraviti NetworkStream i na njemu napraviti


StreamReader:
NetworkStream networkStream = socketForServ er.CetStream ();
System.IO.StreamReader StreamReader =
new System .IO. StreamReader(networkStream);

Sad uitavajte tok podataka dok god u njemu ima podataka, aljui izlaz na konzolu:
do
{
outputString = StreamReader.ReadLine();

i f ( outputString != n uli )
{
Co nsole.W riteLine(outputString);
}
}
w hile( outputString != n uli ) ;

Primjer 21 -9 prikazuje kompletan klijent.

Primjer 21-9. Implementiranje klijenta za mreni tok podataka


Rregion Using d irectiv e s

using System;
using System .C ollections.G eneric;
using System .Net.Sockets;
using System .Text;

# endregion

namespace NetworkStreamingClient
{
pu blic c la s s Client
{

s t a t i c public void Main( s t rin g [] Args )


{

// Stvara TcpClient za komunikaciju s posluiteljem


TcpClient socketForServer;

try
{
socketForServer =
new TcpClient( "localH ost", 65000 ) ;

P o g l a v l je 2 1 : T o k o v i p o d a t a k a I 519
Primjer 21-9. Implementiranje klijenta za mreni tok podataka (nastavak)
}
catch
{
Console.WriteLine(
"F ailed to connect to serv er at { 0 } :6 5 0 0 0 " ,
" lo c a lh o st " ) ;
retu rn ;
}

// Stv ara NetworkStream StreamReader objekte


NetworkStream networkStream =
socketF o rServ er.C etStream ();
System.IO.StreamReader StreamReader =
new System .IO.Stream Reader( networkStream ) ;

try
{
s t rin g ou tpu tStrin g;

// ita podatke od domaina i prik azu je ih


do
{
ou tpu tString = Stream Reader.ReadLine();

i f ( ou tpu tString != n u li )
{
Console.W riteLine( outputString ) ;
}
}
while ( ou tpu tString != n u li ) ;
}
catch
{
Console.WriteLine(
"Exception reading trom Server" ) ;
}

// Pospremanje
netw orkStream .Close();
}
}
}

Da bih to isprobao napravio sam jednostavnu tekstualnu datoteku myText.txt


This is lin e one
This is lin e two
This is l in e th ree
This is l in e four

Ovdje je izlaz posluitelja i klijenta:

520 | P r o g r a m i r a n je C #
Izlaz posluitelja:

C lien t connected
Sending This is lin e one
Sending This is lin e two
Sending This is lin e three
Sending This is lin e four
Disconnecting trom C l i e n t . . .
E x it in g .. .

Iz la z k l i je n t a :

This is lin e one


This i s lin e two
This is lin e three
This is lin e four
Press any key to continue

Ako ovo isprobavate na jednom raunalu pokrenite klijent i posluitelj


u odvojenim prozorima odzivnika ili u posebnim instancama razvoj-
nog okolia. Prvo trebate pokrenuti posluitelj ili klijent nee uspjeti,
javljajui da se ne moe spojiti. Ako primjer ne izvodite najednom rau-
nalu trebate zamijeniti svugdje gdje se javljaju 127.0.0.1 ilo calh o stsIP
adresom raunala na kojem se izvodi posluitelj. Ako imate Windows
XP Service Pack 2 sa podrazumijevanim postavkama primit ete Win-
dows Security Alert koji vas pita da li elite otvoriti ulaz.

Obrada viestrukih veza


Kao to je ranije reeno, ovaj primjer se ne prilagoava dobro. Svaki klijent zahtijeva
potpunu panju posluitelja. Posluitelj mora prihvatiti vezu i proslijediti ju prema
sustavu za ulaz i izlaz podataka, osiguravajui isto asinkrono rjeenje koje ste ranije
koristili za uitavanje iz datoteke.

Da biste to rijeili napravite novi posluitelj, AsynchNetworkServer, u kojem e se nala-


ziti nova klasa ClientHandler. Kad AsynchNetworkServer prihvati vezu od klijenta, on
instancira ClientHandler i prosljeuje pristupnu toku toj ClientHandler instanci.

ClientHandler konstruktor e napraviti kopiju pristupne toke i meuspremnika iotvo-


riti novi NetworkStream u toj pristupnoj toki. Zatim koristi preklopljeni ulaz/izlaz za
asinkrono uitavanje s pristupne toke i'pisanje u pristupnu toku. U ovom primjeru
jednostavno prosljeuje tekst koji klijent poalje na konzolu i natrag klijentu.

Da bi napravila asinkroni U/I ClientHandler definira dvije metode delegata OnReadCom-


:plete() i OnWriteComplete() koje upravljaju nizovima koje alje klijent.

Tijelo Run() metode za posluitelj je vrlo slino onom koje ste vidjeli u primjeru 21-8.
;Prvo napravite sluatelja i onda pozovite Start (). Zatim napravite beskonanu petlju
i pozovite AcceptSocket(). Kad je pristupna toka spojena, umjesto da obradite vezu
jnapravite novi ClientHandler i pozovite StartRead() na tom objektu.

P o g la v lje 2 1 : T o k o vi p o d a ta k a | 521
Kompletan izvorni kod za posluitelj je prikazan u primjeru 21-10.

Primjer 21-10. Implementiranje asinkronog posluitelja za mreni tok podataka


ffregion Using d ire c tiv e s

using System;
using Sy stem .C ollections.G en eric;
using System.Net;
using System .N et.Sockets;
using System .Text;

ttendregion

namespace AsynchNetworkServer
{
pu blic c la ss AsynchNetworkServer
{

c la s s ClientHandler
{
p riv a te b y te[] b u ffer;
p riv ate Socket Socket;
p riv ate NetworkStream networkStream;
p riv ate AsyncCallback callbackR ead ;
p riv ate AsyncCallback callb ack W rite;

pu blic ClientHandler( Socket socketFo rC lien t )


{
Socket = socketForC lien t;
b u ffer = new b yte[ 2 5 6 ];
networkStream =
new NetworkStream( so cketF o rC lien t ) ;

callbackRead =
new AsyncCallback( this.OnReadComplete ) ;

callback W rite =
new AsyncCallback( this.OnW riteCom plete ) ;
}

// Zapoinje it a n je niza od k l i je n t a
pu blic void StartR ead()
{
networkStream.BeginRead( b
u ffe r, 0, b u ffer.Length ,
callbackRead, n u li ) ;
}

// Kad je pozvan natrag tijekom u ita v a n ja , prikazuje niz


// vraa ga natrag k l i je n t u .
p riv ate void OnReadComplete( IAsyncResult ar )
{
in t bytesRead = networkStream.EndRead( ar ) ;

522 j P r o g r a m i r a n je C #
Primjer 21-10. Implementirale asinkronog posluitelja za mreni tok podataka (nastavak)
i f ( bytesRead > o )
{
st rin g s =
System.Text.Encoding. ASCII. GetString(
b u ffer, o, bytesRead ) ;
Console.W rite(
Received { 0 } bytes from C lien t: { l } 1',
bytesRead, s ) ;
networkStreara.BeginWrite(
b u ffer, o, bytesRead, callbackW rite, nu li V
}
e lse
{
Console.W riteLine( "Read connection dropped ) ;
networkStream .Close();
Socket.Close();
netmorkstream = nuli;
Socket = n u li;
}
}

// Nakon zapisiv anja niza isp isu je poruku i n astav lja it a n je


p riv ate void OnWriteComplete( IAsyncResult ar )

networkStream.EndWrite( ar ) ;
C onsole.W riteLine( "Write complete );
networkStream.BeginRead(
b u ffer, o, bu ffer.Length,
callbackRead, n uli ) ;
}
}

pu blic s t a t i c void Main()


{
AynchNetworkServer app =
new AsynchNetworkServer();
app.Run();
}

p riv ate void Run()


{
// Stvara novi TcpListener i pokree ga
U slu a ju i na ulazu 65000

IPAress localAddr = IPAddress.Parse( " 1 2 7 . 0 .O . 1 " ) ;


Tcp Listener tcp L isten er = new TcpListener( localAddr, 65000 ) ;
t c p L is t e n e r .S t a r t ( ) ;

// N astavlja s lu a t i dok ne p o a lje te datoteku


( ; ; )
{
// Ako se k l i je n t s p o ji, prihvaa vezu
II i vraa novu pristupnu toku socketForC lient

P o g la v lje 2 1 : T o ko v i p o d a ta k a I 523
P r im je r 2 1 - 1 0 . I m p l e m e n t i r a n j e a s in k r o n o g p o s lu i t e l j a z a m r e n i t o k p o d a t a k a ( n a s t a v a k )

I I dok tcpListener i dalje slua


Socket socketForClient =
tcpListener.AcceptSocket();
Console.WriteLine( "Client connected" );
ClientHandler handler =
new ClientHandler( socketForClient );
handler.StartRead();
}
}
}
}

Posluitelj se pokree i poinje oslukivati na ulazu 6 5 0 0 0 . Ako se klijent spoji, poslu-


itelj e instancirati ClientHandler koji e izvoditi ulaz/izlaz s klijentom dok posluitelj
eka sljedeeg klijenta.

U ovom primjeru ispisujete niz znakova primljen od klijenta na kon-


zolu u OnReadComplete() i OnWriteComplete() metodama. Ispisivanje na
i!' konzolu moe zaustaviti dretvu dok se ne zavri. U finalnom programu
ne elite blokiranje u ovim metodama je r koristite dretvu iz ponude
dretvi. Ako doe do zaustavljanja u OnReadComplete() ili u OnWriteCom-
p lete()on da to moe izazvati dodavanje dretvi u ponudu dretvi to je
neuinkovito i naruit e performanse i skalabilnost.

Kod klijenta je vrlo jednostavan. On stvara tcpSocket za ulaz na kojem e poslui-


telj oslukivati ( 6 5 0 0 0 ) i NetworkStream objekt za tu pristupnu toku. Onda ispisuje
poruku u tok podataka i prazni meuspremnik. Klijent stvara StreamReader za uitava-
nje toka podataka i ispisuje ono to dobije na konzolu. Kompletan izvorni kod klijenta
je prikazan u primjeru 21-11.

P r im je r 2 1 - 1 1 . I m p l e m e n t a c i j a k l i j e n t a z a a s i n k r o n i u l a z / i z l a z p o d a t a k a p r e k o m r e e

itregion Using directives

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

#endregion

namespace AsynchNetworkClient
{
public class AsynchNetworkClient
{
private NetworkStream streamToServer;

static public int Main()


{

524 | P r o g r a m i r a n je C#
Primjer 21-11. Implementacija klijenta za asinkroni ulaz!izlaz podataka preko mree (nastavak)
AsynchNetworkClient c l ie n t =
new AsynchNetworkClient();
return C lien t.R u n ();

AsynchNetworkClient()

st rin g serverName = " lo c a lh o st";


Console .IkriteL ine( "Connecting to { o } " , serverName ) ;
cp Client tcpSocket = new TcpClient( serverName, 65000
);
stream toServer = tcp Socket.C etStream ();
}

p riv ate in t Run()


{
st rin g message = "Hello Programming at";
Console.W riteLine(
Sending { 0 } to s e r v e r ." , message ) ;

U Stvara streamWriter i k o r is t i ga
!! za zapisiv an je niza na p o slu ite lj
System .IO.Stream Writer w riter =
new System.IO.Stream W riter( streamToServer )
w riter.W riteLin e( message ) ;
w rite r.F lu sh ();

System .10.StreamReader reader =


new System. 10.StreamReader( streamToServer ) ;
s t rin g strResponse = reader.Read Line();
Console.W riteLine( "Received: { 0 } " , strResponse )
stream ToServ er.C lose();
retu rn 0 ;
}
}

Iz la z ( p o s lu i t e l j) :
C lien t connected
Received 22 bytes from c l ie n t : Hello Programming C#
Write complete
^ Read connection dropped

Izlaz ( k l i je n t ) :
Connecting to lo calh o st
Sending Hello Programming C# to serv er.
Received: Hello Programming Ctt

u ovom primjeru mreni posluitelj se ne blokira dok obrauje veze s klijentima ali
p epusta upravljanje nm vezama instancama ClientHandler. Klijenti ne bi smjeli osje-
titi kanjenje dok posluitelj obrauje njihove veze. J

P o g l a v l je 2 1 : T o k o v i p o d a t a k a | 525
Asinkroni tok datoteka preko mree
Sad moete kombinirati znanja koje ste stekli o asinkronom itanju datoteka i asinkro-
nim mrenim tokovima da biste napisali program koji isporuuje datoteku klijentu
na zahtjev.

Posluitelj e poeti s asinkronim uitavanjem na pristupnoj toki ekajui da uzme


ime datoteke od klijenta. Kad imate ime datoteke, moete poeti s asinkronim uitava-
njem datoteke na posluitelju. Kako svaki puni meuspremnik datoteke postaje dostu-
pan, moete poeti s asinkronim ispisivanjem natrag klijentu. Kad asinkrono ispisi-
vanje klijentu zavri, moeti zapoeti jo jedno uitavanje datoteke. Na ovaj nain
dodajete" se naprijed-natrag punei meuspremnik iz datoteke i ispisujui sadraj
meuspremnika klijentu. Klijent ne treba nita raditi osim uitavati tok podataka koji
mu alje posluitelj. U sljedeem primjeru klijent e ispisivati sadraje datoteke na kon-
zolu, ali biste lako mogli i zapoeti asinkrono zapisivanje u novu datoteku na klijentu
i tako napraviti program za kopiranje datoteka preko mree.

Struktura posluitelja ne razlikuje se od strukture prikazane u primjeru 21-10. Jo jed-


nom ete napraviti ClientHandler klasu, ali ovaj put dodajte AsyncCallBack s imenom
myFileCallBack kojeg ete inicijalizirati u konstruktoru zajedno s povratnim pozivima
za uitavanje i ispisivanje preko mree:
m yFileCallBack =
new A syncC allback(th is. OnFileCompletedRead);

callbackRead =
new AsyncCallback(this.OnReadCom plete);

callback W rite =
new A syncC allback(th is. OnWriteComplete);

Run() metoda uvanjskoj klasi, koja se sada zove AsynchNetworkFileServer je nepromije-


njena. Jo jednom ete napraviti i pokrenuti TcpListener klasu te napraviti beskonanu
petlju u kojoj ete pozvati AcceptSocket(). Ako imate pristupnu toku instancirajte Cli-
entHandler i pozovite StartRead(). Kao u prethodnom primjeru, StartRead() pokree
BeginRead() prosljeujui meuspremnik i delegata u OnReadComplete.

Kad uitavanje iz mrenog toka zavri poziva se metoda OnReadComplete () i uzima


ime datoteke iz meuspremnika. Ako je vraen tekst, OnReadComplete() uzima niz
znakova iz meuspremnika upotrebom statike metode System.Text.Encoding.ASCII.
GetString():
i f ( bytesRead > o )
{
st rin g fileName =
System .Text.Encoding.A SCII.G etString(
b u ffe r, 0 , bytesRead);

Sad imate ime datoteke. S njim moete otvoriti tok podataka u datoteku i koristiti isto
asinkrono uitavanje koje je koriteno u primjeru 21-7:

526 I P r o g r a m i r a n je C#
inputStream =
File.OpenRead(fileName);

inputStream.BeginReadj
buffer, // uva rezultate
0, // Pomak
buffer.Length, // Veliina meduspremnika
myFileCallBack, // Delegat povratnog poziva
nuli); // lokalni objekt stanja

Ovo uitavanje datoteke ima vlastiti povratni poziv koji e biti upuen kad ulazni tok
uita puni meuspremnik iz datoteke s posluiteljevog diska.

Kao sto je maloprije spomenuto, ne biste trebali poduzimati nita u ula-


zno/,2|aznJ metodi to bi moglo jako dugo blokirati dretvu. Pozivza otva-
v ranj e datoteke i poetak uitavanja se obino stavlja u pomonu dretvu
umjesto da se izvodi u metodi OnReadComplete(). Ovo je pojednostavnjeno
za ovaj primjer da bi se izbjeglo skretanje panje s vanih pitanja.

Kad je meuspremnik pun poziva se metoda OnFileCompletedRead() koja provjerava da


lije ista ucitano iz datoteke. Ako jest, poinje asinkrono upisivanje preko mree:
if (bytesRead > o)

// Ispisuje na klijenta
networkStream.BeginWrite(
buffer, o, bytesRead, callbackWrite, nuli);

Ako je OnFileCompletedRead pozvana i nita nije ucitano onda to znai da je cijela dato-
teka poslana. Posluitelj reagira zatvaranjem NetworkStream objekta i pristupne toke
dajui na taj nain do znanja klijentu da je transakcija gotova:
networkStream.Close();
Socket.Close();
networkStream = nuli;
Socket = n u li;

Kad se upisivanje preko mree zavri poziva se OnWriteComplete() metoda to pokree


drugo uitavanje iz datoteke:
private void OnWriteComplete( IAsyncResult ar )

networkStream.EndWrite(ar);
Console.WriteLine( "Write complete");

inputStream.BeginRead(
buffer, // uva rezultate
0, // Pomak
buffer.Length, // (BufferSize)
myFileCallBack, // Delegat povratnog poziva
nuli); // Lokalni objekt stanja

P o g l a v l je 2 1 : T o k o v i p o d a t a k a I 527
Ciklus ponovno poinje s drugim uitavanjem datoteke i nastavlja se sve dok datoteka
ne bude potpuno uitana i poslana klijentu. Klijentov kod jednostavno ispisuje ime
datoteke u mreni tok da pokrene uitavanje datoteke:
s t rin g message = @ "C:\test\source\AskTim .txt";
System .IO.Stream W riter w riter =
new System .IO .Stream W riter(stream ToServer);
w riter.W rite(m essage);
w r it e r .F lu s h ();

Klijent onda zapoinje petlju itajui iz mrenog toka sve dok posluitelj vie ne poa-
lje ni jedan bajt. Kad je posluitelj gotov, mreni tok podataka se zatvara. Ponite inici-
jaliziranjem Boolean vrijednosti na fa lse i stvaranjem meuspremnika za prihvaanje
bajtova koje poalje posluitelj:
bool fO uit = f a l s e ;
while ( ! fO u it)
{
ch ar[] b u ffer = new c h a r[B u ffe r S iz e j;

Sad ste spremni da napravite novi StreamReader iz varijable streamToServer lanice


NetworkStream:
System .IO.Stream Reader reader =
new System. IO. Stream Reader(stream ToServer);

Poziv Read() metode uzima tri parametra: meuspremnik, pomak od kojeg poinje
itanje i veliinu meuspremnika:
in t bytesRead = re a d er.R ea d (b u ffer,0 , B u ffe r S iz e );

Provjerite da lije Read() vratila bajtove. Ako nije, gotovi ste i moete postaviti Boolean
vrijednost fOuit na true i tako prekinuti izvoenje petlje:
i f (bytesRead == 0)
fOu it = tru e ;

Ako ste primili bajtove, moete ih ispisati na konzolu ili u datoteku ili moete uiniti
to god elite s vrijednostima koje je posluitelj poslao.
e ls e
{
s t rin g th e S trin g = new S t rin g ( b u f fe r ) ;
C o n so le.W rite L in e(th e Strin g);
}
}

Kad izaete iz petlje zatvorite NetworkStream:


stream To Serv er.C lose();

Kompletan kod posluitelja s komentarima je prikazan u primjeru 2 1 -1 2 , dok je kod


klijenta dat u primjeru 21-13.

S28 I P r o g r a m i r a n je C #
Primjer 21-12. Implementiranje asinkronog mrenog posluitelja datoteka
# region Using d irectiv e s

using System;
using System. C o llectio n s.C e n e ric;
using System .I0;
using System.Net;
using System .N et.Sockets;
using System .Text;

# endregion

namespace AsynchNetworkFileServer
{
pu blic c la s s AsynchNetworkFileServer
{

c la s s ClientHandler
{
p riv ate const in t Bu fferSize = 2 5 6 ;
p riv ate b yte[] b u ffer;
p riv ate Socket Socket;
p riv ate NetworkStream networkStream;
p riv ate Stream inputStream;
p riv a te AsyncCallback callbackRead;
p riv ate AsyncCallback callback W rite;
p riv ate AsyncCallback m yFileCallBack;

// Konstruktor
pu blic ClientHandler(
Socket socketForC lient )
{
// I n i c i ja l i z i r a n je v a r ija b le la n ice
Socket = socketForC lient;

// I n i c i ja l i z i r a n je meuspremnika
// za uvanje datoteka
bu-ffer = new b y te[2 5 6 ];

// Stvaran je mrenog toka


networkStream =
new NetworkStream( socketForC lient ) ;

// P ostav lja povratni poziv datoteke


// za it a n je datoteke
m yFileCallBack =
new AsyncCallback( this.OnFileCompletedRead ) ;

II P ostav lja povratni poziv za it a n je


II iz mrenog toka
callbackRead =
new AsyncCallback( this.OnReadComplete ) ;

II P ostav lja povratni poziv za zapisivan je

P o g la v lje 2 1 : T o k o v i p o d a ta k a | 529
Primjer 21-12. hnplementiranje asinkronog mrenog posluitelja datoteka (nastavak)

I I u mreni tok
callbackNrite =
new AsyncCallback( this.OnWriteComplete );

// Zapoinje itanje niza od klijenta


public void StartRead()
{
// ita s mree
// i uzima ime datoteke
networkStream.BeginRead(
buffer, o, buffer.Length,
callbackRead, nuli );
}

// Kad je pozvan natrag od strane itaa prikazuje niz


// i vraa ga natrag klijentu
private void OnReadComplete( IAsyncResult ar )

int bytesRead = networkStream.EndRead( ar );

// Ako dobije niz


if ( bytesRead > 0 )
{
// pretvara ga u ime datoteke
string fileName =
System.Text.Encoding.ASCII.GetString(
buffer, o, bytesRead );

// Osvjeava konzolu
Console.Write(
"Opening file {o}", fileName );

// Otvara ulazni tok datoteke


inputStream =
File.OpenRead( fileName );

// Zapoinje itanje datoteke


inputStream.BeginRead(
buffer, // uva rezultat
o, // Pomak
buffer.Length, // Veliina meuspremnika
myFileCallBack, // Delegat povratnog poziva
nuli ); // Lokalni objekt stanja

}
else
{
Console.WriteLine( "Read connection dropped" );
networkStream.Close();
Socket.Close();
networkStream = nuli;
Socket = nuli;

5B0 | P r o g r a m i r a n je C #
Primjer 21-12. Implementiranje asinkronog mrenog posluitelja datoteka (nastavak)
}
}

// Kad imate pun meuspremnik datoteke


void OnFileCompletedRead( IAsyncResult asyncResult )
{
int bytesRead =
inputStream.EndRead( asyncResult );

// Ako je uitan dio datoteke


if ( bytesRead > 0 )
{
I I ispisuje ga klijentu
networkStream.BeginWrite(
buffer, o, bytesRead, callbackNrite, nuli );
}
else
{
Console.WriteLine( "Finished." );
networkStream.Close();
Socket.Close();
networkStream = nuli;
Socket = nuli;
}
}

// Nakon upisivanja niza uzima drugi dio datoteke


private void OnWriteComplete( IAsyncResult ar )
{
networkStream.EndWrite( ar );
Console.WriteLine( "Write complete );

// Zapoinje uitavanji : ostatka datoteke


inputStream.BeginRead(
buffer, // uva rezultate
o, // Pomak
buffer.Length, U (BufferSize)
myFileCallBack, // Delegat povratnog poziva
nuli ); // Lokalni objekt stanja
>
}

public static v o id Main()


{
AsynchNetworkFileServer app =
new AsynchNetworkFileServer();
app.Run();
}

private void Run()


{
I I Stvara novi TcpListener i zapoinje
U sluanje na ulazu 6 S0 0 0

P o g l a v l je 2 1 ; T o k o v i p o d a t a k a I 531
Primjer 21-12. Implementiranje asinkronog mrenog posluitelja datoteka (nastavak)
IPAddress localAddr = IPAddress.Parse( "127.0.0.1" );
TcpListener tcpListener = new TcpListener( localAddr, 65000 );
tcpListener.Start();

// Nastavlja sluanje dok aljete datoteku


for ( ; ; )
{
// Ako se klijent povee, prihvaa vezu
// i vraa novu pristupnu toku socketForClient
II dok tcpListener nastavlja sluati
Socket socketForClient =
tcpListener.AcceptSocket();
if ( socketForClient.Connected )

Console.WriteLine( "Client connected" );


ClientHandler handler =
new ClientHandler( socketForClient );
handler.StartRead();
}
}
}
}
}

Primjer 21-13. Implementiranje klijenta asinkronog mrenog posluitelja datoteka


using System;
using System.Net.Socket's;
using System.Threading;
using System.Text;

public class AsynchNetworkClient


{
private const int BufferSize = 2 5 6 ;
private NetworkStream streamToServer;

static public int Main()


{

AsynchNetworkClient Client =
new AsynchNetworkClient();
return Client.Run();

AsynchNetworkClient()
{
string serverName = "localhost";
Console.WriteLine(Connecting to {0}", serverName);
TcpClient tcpSocket = new TcpClient(serverName, 65000);
streamToServer = tcpSocket.CetStreain();
}

private int Run()

532 | P r o g r a m i r a n je C #
Primjer 21-13. Implementirale klijenta asinkronog mrenog posluitelja datoteka (nastavak)
{
string message = l"C:\test\source\AskTim.txt ;
Console.Write(
"Sending {0 } to server.", message);

// Stvara streamWriter i koristi ga za


/ / pisanje niza na posluitelju
System.IO.StreamWriter writer =
new System.I O .StreamWriter(streamToServer);
writer.Write(message);
writer.Flush();

bool fOuit = false;

/ / Dok podaci stiu sa


// posluitelja, nastavalja itati
while (!fOuit)
{
// Meuspremnik za uvanje odgovora
char[] buffer = new char[BufferSize];

// Odgovor itanja
System.IO.StreamReader reader =
new System.IO.StreamReader(streamToServer);

// Provjerava koliko j e bajtova


// smjeteno u meuspremnik
int bytesRead =
reader.Read(buffer,0,ButterSize);
if (bytesRead == 0 ) // Ni jedan? Izlaz
fOuit = true;
else // ima neki?
{
// Prikazuje ga kao niz
string theString = new String(buffer);
Console.WriteLine(theString);
}
}
streamToServer.Close(); // Posprema
return 0;
}
}

Kombiniranjem asinkronog itanja datoteka s asinkronim mrenim itanjem, napravili


ste prilagodljivu aplikaciju koja moe obraivati zahtjeve od veeg broja klijenata.

Web tokovi
Umjesto itanja iz toka podataka koji prua posluitelj, moete jednako lako itati s
bilo koje Web stranice na Internetu.

P o g l a v l je 2 1 : T o k o v i p o d a t a k a I 533
WebRequest je objekt koji zahtijeva resurs koji se identificira s pomou URI adrese, to
je slino U RL adresi Web stranice. Moete koristiti WebRequest objekt da napravite
WebResponse objekt koji e uahuriti objekt na koji pokazuje URI. To jest, moete
pozvati GetResponse() na WebRequest objektu da bi dobili pristup objektu na koji poka-
zuje URI. Ono to se vraa je uahureno u WebResponse objekt. Onda od WebResponse
objekta moete traiti Stream objekt pozivanjem metode GetResponseStream(). GetRe-
sponseStream() vraa tok podataka koji uahuruje sadraj Web objekta (na primjer,
toka podataka s Web stranicom).

Sljedei primjer vraa sadraj Web stranice kao tok podataka. Da bi pribavili Web stra-
nicu, trebat ete koristiti HttpWebRequest. HttpWebRequest izvodi iz WebRequest i prua
dodatnu podrku za rad s H T T P protokolom.

Da biste napravili HttpWebRequest, pretvorite tip WebRequest objekta koji ste dobili od
statike C reate() metode iz WebRequestFactory:
HttpWebRequest WebRequest =
(HttpWebRequest) WebRequest.Create
("http://www.libertyassociates.com/book_edit.htm'1);

C reate() je statika metoda objekta WebRequest. Kad joj proslijedite U RI, stvara se
instanca HttpWebRequest.

Metoda je preoptereena na tip parametra. Vraa razne izvedene tipove


ovisno o tome to je proslijeeno u nju. Na primjer, ako proslijedite
URI stvara se objekt tipa HttpWebRequest. Povratni tip je WebRequest pa
zato morate pretvoriti vraenu vrijednost u HttpWebRequest.

Stvaranje HttpWebRequest objekta uspostavlja vezu prema Web stranici. Ono to dobi-
jete natrag od posluitelja je uahureno u HttpWebResponse objekt koji je H TTP pot-
klasa openitije WebResponse klase:
HttpWebResponse WebResponse =
(HttpNebResponse) WebRequest.GetResponse();

Sad moete otvoriti StreamReader na toj stranici pozivanjem GetResponseStream()


metode WebResponse objekta:
StreamReader StreamReader = new StreamReader(
WebResponse,GetResponseStream(), Ertcoding.ASCII);

Moete itati iz tog toka podataka ba kao to ste itali iz mrenog toka podataka.
Primjer 21 -14 sadri kompletan ispis.

P rim je r 21-14. itanje Web stranice kao to ka H T M L podataka


ttregion Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.Net;

534 | Programiranje C#
P r im je r 21-14. itanje Web stranice kao toka H T M L podataka (nastavak)
using System.Net.SocketS;
using System.Text;

Sendregion

namespace ReadingWebPageAsHTML

public class Client


{

static public void Main( string[] Args )

// Stvara WebRequest z a o d r e e n u s t r a n i c u
HttpWebRequest WebRequest =
( HttpWebRequest ) WebRequest.Create
( ''http://www.libertyassociates.com/");

// alje Web zahtjev za WebResponse koji uahurava


// tu stranicu
HttpMebResponse WebResponse =
( HttpMebResponse ) WebRequest.GetResponse();

// U z i m a streamReader od odgovora
StreamReader streamReader = new StreamReader(
MebResponse.GetResponseStream(), Encoding.ASCII );

try
{
string outputstring;
outputstring = streamReader.ReadToEnd();
Console.WriteLine( outputstring );

catch
{
Co"sole.WriteLine( "Exception reading trom Web page" );

streamReader.Close();
}
}
}

Izlaz (odlomak):
<html>
<head>
<title>Liberty Associates</title>
<meta http-equiv="Content-Type content="text/html; charset=iso-8 8 5 9 -i>
<scnpt language="lavaScript>
<!--
isNS=(navigator.appName=="Netscape");
activeMenu="";
activelndex=-l;
activelmg="

Poglavlje 21: Tokovi podataka | 535


P rim je r 21-14. itanje Web stranice kao toka H T M L p od a ta ka (nastavak)

window.onError = nuli;

function setImage(imgName,index) {
if(activeImg==imgName)
return true;
document.imagesfimgName].src = rolloverIirig[index] .src;
-return true;
}

rolloverImg=new Array();

Izlaz pokazuje da se kroz tok podataka alje H TM L stranica koju ste traili. Mogli
biste koristiti ovu mogunost za analizu stranica: uitavanje stranice s Weba u meu-
spremnik i izvlaenje sam o nekih potrebnih informacija.
*4
Svi primjeri analize Web stranica u ovoj knjizi pretpostavljaju da
podatke uitavate uz dozvolu autora.

Serijalizacija
Kad se objekt ispisuje na disk njegovi podaci moraju biti serijalizirani (engl. serialized)
- to jest, ispisani u tok podataka kao niz bajtova. Objekt e takoer biti serijaliziran
kod spremanja u datoteku ili prilikom rasporeivanja u drugi kontekst, aplikacijsku
domenu, proces ili preko granica izmeu raunala.

CLR prua podrku za serijalizanje objekta i svih podataka lanova objekta. Kao to
je istaknuto u poglavlju 19, tipovi se ne mogu serijalizirati. Da biste mogli serijaliziran
objekt morate ga eksplicitno oznaiti [S e ria liz a b le ] atributom.

CLR e obaviti serijalizaciju objekta za vas. Poto C L R zna kako serijalizirati sve pri-
mitivne tipove, ako se objekt sastoji samo od njih, spremni ste za serijalizaciju. Ako se
objekt sastoji i od drugih tipova (klasa) koje je definirao korisnik, morate osigurati da
se i ti tipovi mogu serijalizirati. CLR e pokuati erijatiirati svaki objekt koji se nalazi
unutar vaeg objekta (i sve njihove objekte), ali ovi objekti moraju biti ili osnovni tipovi
ili tipovi koje je mogue serijalizirati.
Ovo je takoer bilo evidentno u poglavlju 19 kad ste rasporeivali Shape objekt koji
je sadravao P o in t objekt kao podatak lan. P o in t objekt se sastojao od primitivnih
podataka. Da biste serijalizirali (i rasporedil) Shape objekt, njegov sastavni dio, Point
objekt, je takoer trebao biti oznaen tako da se moe serijalizirati.

Kad se objekt rasporeuje, bilo po vrijednosti ili po referenci, mora


biti serijaliziran. Razlika je jedino u tome da li se pravi kopija ili se kli-
4
Tkv jentu prua posrednik. O bjekti oznaeni [Se ria liz a b le ] atributom se
rasporeuju po vrijednosti, a oni koji izvode iz MarshalByRefObject po
referenci, ali u oba sluaja se serijalizirani. Pogledajte poglavlje 19 za
vie informacija.

536 | Programiranje C#
Upotreba formatera
Kad su podaci serijalizirani vjerojatno e ih netko itati, bilo isti program ili neki drugi
program na istom ili drugom raunalu. U svakom sluaju, kod koji ita podatke oe-
kuje da podaci budu u odreenom formatu. U veini .N ET aplikacija oekivani format
je ili binarni format ili SOAP format.

SOAP je jednostavan protokol za razmjenu podataka preko Weba teme-


. ^ lien na XM L-u. SOAP je vrlo modularan i proiriv te koristi postojee
i.*,' Internet tehnologije kao to su HTTP i SMTP.

Kad su podaci serijalizirani, formater odreuje format serijalizacije. U poglavlju 19


koristili ste formater s kanalima prilikom komuniciranja s udaljenim objektom. Klase
formatera implementiraju suelje IFormatter. Moete napraviti vlastiti formater, iako
e vrlo malo programera to ikad trebati ili htjeti! CLR prua i SoapFormatter za Web
usluge te BinaryFormatter koji je koristan za brzo lokalno pohranjivanje ili rad na
daljinu.

M oete instancirati ove objekte s njihovim podrazumijevanim konstruktorima:


BinaryFormatter binaryFormatter =
new BinaryFormatter();

Kad imate instancu formatera moete pozvati metodu S e ria liz e f) prosljeujui joj tok
podataka i objekt za serijalizaciju. U sljedeem primjeru vidjet ete kako se to radi.

Upotreba serijalizacije
Da biste vidjeli kako radi serijalizacija trebate pokusnu klasu koju moete serijalizirati
i zatim deserijalizirati. Moete poeti stvaranjem klase SumOf. Ona ima tri varijable
lanice klase:
private int startNumber = l;
private int endNumber;
private int[] theSums;

Polje theSums predstavlja vrijednost zbrojeva svih brojeva od startNumber do endNumber.


Prema tome, ako je startNumber 1, a endNumber 10, polje e sadravati vrijednosti:
1,3,6,10,15,21,28,36,45,55
Svaka vrijednost je zbroj prethodne vrijednosti i sljedeeg broja u nizu. Prema tome,
a k o jen iz l, 2, 3, 4, prva vrijednost u theSums e biti l. Druga vrijednost je prethodna
vrijednost (l) zbrojena sa sljedeim brojem u nizu (2). Prema tome, theSums [ l] e sadr-
avati vrijednost 3. Na isti nain, trea vrijednost je prethodna vrijednost (3) zbrojena
sa sljedeim brojem u nizu pa je theSums [2] jednak 6. Konano, etvrta vrijednost u t h e -
Sums je prethodna vrijednost (6) zbrojena sa sljedeim brojem u nizu (4) i iznosi 10.

Konstruktor za SumOf objekt uzima dvije cjelobrojne vrijednosti: poetni broj i zavrni
broj. On ih dodjeljuje lokalnim vrijednostima i poziva pomonu metodu da izrauna
sadraj polja:

Poglavlje 21:Tokovi podataka I 537


public SumOf(int start, int end)
{
startNumber = start;
endNumber = end;
ComputeSums();
Pomona metoda ComputeSums popunjava polje raunajui zbrojeve u nizu od startNum-
ber do endNumber:
private void ComputeSums()
{
int count = endNumber - startNumber + l;
theSums = new intfcountj;
theSums[o] = startNumber;
tor (int i=l,j=startNumber + l;i<count;i++,}++)
{
theSums[i] = j + theSums[i-l];
}
}
Moete pokazati sadraj polja bilo kad koritenjem toreach petlje:

private void DisplaySums()


{
foreach(int i in theSums)
{
Console.WriteLine("{o}, ",i);
}
}

Serijaliziranje objekta
Sad oznaite klasu za serijaliziranje atributom [S e ria liz a b le ]:
[Serializable]
class SumOf
Da biste pokrenuli serijalizaciju prvo trebate fileStream objekt u kojeg ete serijalizirati
SumOf objekt:
FileStream fileStream =
new FileStream("DoSum.out",FileMode.Create);

Sad ste spremni da pozovete metodu S e ria liz a b le () formatera, prosljeujui tok poda-
taka i objekt za serijalizaciju. Budui da se to izvodi u metodi SumOf, moete proslijediti
th is objekt koji ukazuje na trenutni objekt:
binaryFormatter.Serialize(fileStream,this);

To serijalizira SumOf objekt na disk.

Deserijalizacija objekta
Da biste ponovno sloili objekt, otvorite datoteku i zatraite da ju binarni formater
deserijalizira metodom D e S erialize():

538 | Programiranje C#
public static SumOf DeSerialize()
{
FileStream fileStream =
new FileStream("DoSum.out",FileMode.Open);
BinaryFormatter binaryFormatter =
new BinaryFormatter();
SumOf retVal = (SumOf) binaryFormatter.Deserialize(fileStream);
fileStream.Close();
return retVal;
}

Da biste bili sigurni da sve radi, prvo instanci raj te novi objekt tipa SumOf i zatraite da
se serijalizira. Zatim stvorite novu instancu tipa SumOf pozivanjem statikog deserijali-
zatora i zatraite da prikae svoje vrijednosti:
public static void Main()
{
Console.WriteLine("Creating first one with new...");
SumOf app = new SumOf(l,1 0 );

Console.WriteLine(
"Creating second one with deserialize...
SumOf newlnstance = SumOf.DeSerialize();
newInstance.DisplaySums();
}

Primjer 21-15 sadri kompletan izvorni kod za prikaz serijalizacije i deserijalizacije.

P rim je r 21 -15 . S erijaliziran je i deserijaliziranje objekta


tlregion Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

ttendregion

namespace SerializingDeserialingAnObject

[Serializable]
class SumOf
{
private int startNumber = l;
private int endNumber;
private int[] theSums;

public static void Main()


{
Console.WriteLine( "Creating first one with new..." );
SumOf app = new SumOf( l, 1 0 );

Poglavlje 21: Tokovi podataka I 539


Primjer 21-15. Serijaliziranje i deserijahziranje objekta (nastavak)
Console.Writel_ine( "Creating second one with deserialize
SumOf newlnstance = SumOf.DeSerialize();
newInstance.DisplaySums();
}

public SumOf( int start, int end )


{
startNumber = start;
endNumber = end;
ComputeSums();
DisplaySums();
Serialize();

private void ComputeSums()

int count = endNumber - startNumber + 1,


theSums = new int[count];
theSums[o] = startNumber;
for ( int i = 1, j = startNumber + l; i < count; i++, ]
{ r. .
theSums[i] = j + theSums[i - l];
}
}

private void DisplaySums()

^ foreach ( int i in theSums )

Console.WriteLine( "{o}, ", i );


}
}

private void Serialize()

Console.Write( "Serializing..." );
// Stvara tok datoteke za zapisivanje datoteke
FileStream fileStream =
new FileStream( "OoSum.out", FileMode.Create );
U Koristi binarni formater CLR-a
BinaryFormatter binaryFormatter =
new BinaryFormatter();
// Serijaliziranje na disk
binaryFormatter.Serialize( fileStream, this ),
Console.WriteLine( "...completed" );
fileStream.Ciose();
}

public static SumOf DeSerialize()


{
FileStream fileStream =
new FileStream( "DoSum.out", FileMode.Open );

540 | Programiranje C#
Primjer 21-15. Serijaliziranje i deserijaliziranje objekta (nastavak)
BinaryFormatter binaryFormatter =
new BinaryFormatter();
SumOf retVal = ( SuraO-f ) binaryFormatter.Oeserialize( fileStieam
fileStream.Close();
return retVal:

Izlaz:
Creating first one with new...
l,
1,
6,
10,
15,
21,
28,
36,
45,
55,
Serializing.... completed
Creating second one with deserialize...
1,
3,
6,
10,
15,
21,
28,
36,
45,
55,
Izlaz prikazuje d aje objekt napravljen, prikazan i zatim serijaliziran. Objekt je nakon
toga deserijaliziran i ponovno prikazan, bez gubitka podataka.

Obrada privremenih podataka


Pristup serijalizaciji prikazan u primjeru 21-15 je, na neki nain, vrlo rasipan. Poto
moete izraunati vrijednosti polja ako imate poetni i zavrni broj, onda nema raz-
loga da spremate njegove elemente na disk. Iako ovaj postupak ne predstavlja veliki
problem za malo polje, mogao bi predstavljati jako velik troak kod velikih polja.

$ Neke podatke moete oznaiti [NonSerialized] atributom tako da se ne serijaliziraju:


[NonSerialized] private int[] theSums;

Meutim, ako ne serijalizirate polje, objekt kojeg napravite nee biti valjan kad ga dese-
| rijalizirate. Polje e biti prazno. Zapamtite, kad deserijalizirate objekt, jednostavno ga
itate iz serijaliziranog oblika i ne koriste se metode.

Poglavlje 21: Tokovi podataka I 541


Da biste popravili objekt prije nego to ga vratite, implementirajte ID eserialization-
Callback suelje:
[Serializable]
class SumOf : IDeserializationCallback

Takoer implementirajte jednu metodu ovog suelja: O n D eserialization(). CLR obe-


aje da e, ako implementirate ovo suelje, metoda OnDeserialization() iz vae klase
biti pozvana kad cijeli objekt i njegovi podaci budu deserijalizirani. To je ba ono to
elite: CLR e ponovno sastaviti ono to ste serijalizirali i onda ete moi popraviti
dijelove koji nisu bili serijalizirani.

Ova implementacija moe biti vrlo jednostavna. Samo zatraite od objekta da ponovno
izrauna nizove:
public virtual void OnDeserialization (Object sender)
{
ComputeSums();
}
Ovo je klasian ustupak izmeu vremena i prostora na disku. Ako ne serijalizirate
polje deserijalizacija e biti neto sporija (jer morate ponovno izraunati polje), ali e
datoteka biti neto manja. Da bih provjerio jesmo li bez serijaliziranja polja postigli
kakav uinak, pokrenuo sam program s brojevima od 1 do 5 0 0 0 . Prije nego to sam
postavio atribut [NonSerialized] za polje, serijalizirana datoteka je imala 20K. Nakon
postavljanja atributa [NonSerialized], datoteka je imala 1K. Nije loe. Primjer 21-16
prikazuje izvorni kod koji koristi brojeve od jedan do pet kao ulaz (da pojednostav-
nimo izlaz).

P rim je r 21-16. R ad sa ne se rija liz iran im objektom


ifregion Using directives

using System;
using System.Collectlons.Generic;
using System.I0;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

#endregion

namespace WorkingWithNonSerializedObject
{
[Serializable]
class SumOf : IDeserializationCallback
{
private int startNumber = l;
private int endNumber;
[NonSerialized]
private int[] theSums;

public static void Main()

542 | Programiranje C#
P rim je r 21-16. R ad sa ne se rija lizira nim objektom {nastavak)
{
Console.WriteLine("Creating first one with new.
SumOf app = new Sum0f(i,5); );

Console.WriteLine("Creating se c o n d one with deserialize...


SumOf newlnstance = SumOf. DeSerializeM-
new!nstance.DisplaySums();
}

public SumOf(int start, int end)

startNumber = start;
endNumber = end;
ComputeSums();
DisplaySums();
Serialize();

private void ComputeSums()

int count = endNumber - startNumber + i-


theSums = new int[countj;
theSums[o] = startNumber;
for (int 1=1,j=startNumber + l;i<count;i++,j++)

theSums[i] = j + theSums[i-i];

private void DisplaySums()

foreach(int i in theSums)

Console.WriteLine("{o}, ",i);

private void Serialize()

C onsole.W rite("Serializing...
// Stvara t o k za zapisivanje datoteke
FileStream fileStream =
new FileStream("DoSum.out",FileMode.Create);
// Koristi binarni formater CLR-a
8inaryFormatter binaryFormatter =
new BinaryFormatter();
H Serijaliziranje na disk
binaryForm atter.Serialize(fileStream ,this)-
C o n s o l e . WriteLine(..completed);
^ fileStream.Close();

public static SumOf DeSerialize()

Poglavlje 21: Tokovi podataka


Primjer 21-16. Rad sa neserijaliziranim objektom (nastavak)

{
FileStream fileStream =
new FileStream( "DoSum.out",FileHode.Open);
BinaryFormatter binaryFormatter =
new BinaryFormatter();
SumOf retVal = (SumOf) binaryFormatter.Deserialize(fileStream);
fileStream .Close.O;
return retVal;
}

// Popravlja neserijalizirane podatke

public Virtual void OnDeserialization


(Object sender)
{
ComputeSums();
}
}
}

Output:
Creating first one with new...
1,
3,
6,
10,
15,
Serializing.... completed
Creating second one with deserialize...
1,
3,
6,
10,
15,
Moete vidjeti u izlazu da su podaci uspjeno serijalizirani na disk i zatim ponovno
sastavljeni deserijalizacijom. Ustupak izmeu vremena i prostora na disku nema puno
smisla s pet vrijednosti, ali ima ako koristite pet milijuna vrijednosti.

Dosad ste tok podataka slali na disk da biste ga pohranili i preko mree da biste
lake komunicirali s udaljenim programim a. Postoji jo jedan sluaj kad biste mogli
napraviti tok podataka: da biste spremili stalnu konfiguraciju i podatke o statusu za
pojedinane korisnike. .N E T kostur za ovu svrhu prua izolirano spremite (engl. iso-
lated storage).

Izolirano spremite
.N E T C L R prua izolirano spremite da bi om oguio projektantima aplikacija da
pohrane podatke o korisnicima. Izolirano spremite prua veinu funkcionalnosti
uobiajenih .ini datoteka ili novijeg H K EY _C U R R E N T_U SE R kljua u Registryju.

544 | Programiranje Cif


Aplikacije spremaju podatke u jedinstveni odjeljak za podatke (engl. data compart-
ment) koji im je pridruen. CLR implementira odjeljak za podatke sa spremitem za
podatke, a to je obino mapa u datotenom sustavu.
Adm inistratori slobodno mogu ograniiti koliko pojedina aplikacija moe koristiti
zasebno spremite. Takoer mogu koristiti sigurnosni sustav tako da manje pouzdan
kod ne moe pozvati vie pouzdan kod za pisanje u izolirano spremite.

Ono to je vano kod izoliranog spremita je da CLR prua standardno mjesto za


pohranu podataka aplikacije, ali ne namee (ili podrava) neki odreeni raspored ili
sintaksu podataka. Ukratko, u izolirano spremite moete pohraniti sve to elite.

Najee ete pohraniti tekst, esto u obliku parova ime-vrijednost. Izolirano spre-
mite je dobar mehanizam za spremanje informacija o korisniku kao to su korisniko
ime, poloaj raznih prozora i programa te drugi podaci specifini za aplikaciju i kori-
snika. Podaci se pohranjuju u zasebnu datoteku za svakog korisnika, ali mogu biti
izolirane jo vie tako da se dijele prema raznim aspektima identiteta koda (po sklopu
ili po aplikacijskoj domeni kojoj pripadaju).

Upotreba izoliranog spremita je prilino jednostavna. Da biste pisali u izolirano spre-


mite, napravite instancu Is o la te d S to r a g e F ile S tre a m koju ete inicijalizirati s imenom
i reimom datoteke (izradi, dodaj itd).
IsolatedStorageFileStream configFile =
new IsolatedStorageFileStream
("Tester.cfg",FileMode.Create);

Sada napravite Stream W riter na toj datoteci:


StreamHriter writer =
new StreamWriter(configFile);

Zatim upisujte u taj tok kao to biste u bilo koji drugi. Primjer 21-17 to prikazuje.

P rim je r 21-17. Pisanje u izoliran o sprem ite


ttregion Using directives

using System;
using System.Collections.Generic;
using System.I0;
using System.IO.IsolatedStorage;
using System.Text;

#endregion

namespace HritingToIsolatedStorage
{
public class Tester
{

public static void Main()


{
Tester app = new Tester();

Poglavlje 21: Tokovi podataka I 545


Primjer 21-17. Pisanje u izolirano spremite (nastavak)
app.Run();
}

private void Run()

// Stvara tok konfiguracijske datoteke


IsolatedStorageFileStream configFile =
new IsolatedStorageFileStream
( "Tester.cfg", FileMode.Create ),

// Stvara pica koji e pisati u tok


StreamWriter writer =
new StreamWriter( configFile );

// Upisuje podatke u konfiguracijsku datoteku


String output;
System.DateTime currentlime = System.DateTime.Now;
output = "Last access: " + currentTime.ToStringO;
writer.WriteLine( output );
output = "Last position = 27,35";
writer.WriteLine( output );

// Prazni meduspremnik i posprema


writer.Close();
configFile.Close();
}
}
}
Nakon izvoenja koda potraite datoteku Tester.cfg na tvrdom disku. Na mom rau-
nalu, ona se nalazi u:
C:\Documents and Settings\3esse\Local SettingsNApplication Data\IsolatedStorage\
mipjwcsz.iir\2hzvpjcc.pOy\StrongName.mwoxzllzqpx3uOtaclpldtillkpddwyo\Ur .
a2f4v2g3ytucslmvlpt2wmdxhrhqglpz\F iles

Moete ju otvoriti s Notepadom ako ste upisali samo tekst.


Last access: 5/2/2001 10:00:57 AM
Last position = 27,35
Ili, moete programski pristupiti ovim podacima. Da biste to uinili, ponovno otvorite
datoteku:
IsolatedStorageFileStream configFile =
new IsolatedStorageFileStream
("Tester.cfg",FileMode.Open);

Napravite StreamReader objekt:


StreamReader reader =
new StreamReader(configFile);

Koristite standardni idiom toka podataka za itanje unutar datoteke.

string theEntry;
do

546 | Programiranje C#
{
theEntry = reader.ReadLine();
Console.WriteLine(theEntry);
} while (theEntry != nuli);
Console.WriteLine(theEntry);

Izolirano spremite je ogranieno sklopom (tako da, ako zatvorite program i kasnije
ga ponovno pokrenete, moi ete proitati konfiguraciju datoteke koju ste napravili,
ali neete moi itati konfiguraciju ni jednog drugog sklopa). Primjer 21-18 prua
metodu potrebnu za itanje datoteke. Zamijenite Run() metodu u prethodnom pri-
mjeru, ponovno prevedite i pokrenite program (ali nemojte mijenjati ime jer nee moi
pristupiti izoliranom spremitu koje ste prethodno napravili).

P rim je r 21-18. itanje iz izoliran og sprem ita


private void Run()
{
// Otvara tok konfiguracijske datoteke
IsolatedStorageFileStream configFile =
new IsolatedStorageFileStream
("Tester.cfg",FileMode.Open);

// Stvara standarni ita toka


StreamReader reader =
new StreamReader(configFile);

// ita datoteku i prikazuje


string theEntry;
do
{
theEntry = reader.ReadLine();
Console.WriteLine(theEntry);
} while (theEntry != nuli);

reader.Close();
i ' configFile.Close();
J: }

| Output:
I Last access: 5/2/2001 10:00:57 AM
Last position = 27,35

Poglavlje 21: Tokovi podataka | 547


POGLAVLJE 22
.NET i COM programiranje

Programeri vole imati istu situaciju. Iako bi bilo zgodno kad bi mogli odbaciti sav kod
koji smo ikad napisali i poeti iznova, to nije prihvatljiva opcija za veinu kompanija.
Tijekom proteklog desetljee mnoge razvojne tvrtke su puno investirale u razvoj i kup-
nju COM komponenata i ActiveX kontrola. M icrosoft se obvezao da e osigurati da
se ove zastarjele komponente mogu koristiti unutar .N ET aplikacija i (moda manje
vano) da se .N E T komponente mogu jednostavno pozivati iz COM objekata.

Ovo poglavlje opisuje podrku koju .N E T prua za koritenje ActiveX kontrola i


COM komponenata u aplikacijama, za izlaganje .N E T klasa aplikacijama temeljnim
na COM-u te za izravne pozive prema W in 32 API-jima. Takoer ete nauiti o C #
pokazivaima i kljunim rijeima za izravan pristup memoriji, to moe biti kljuno
u nekim aplikacijama.

Uvoenje ActiveX kontrola


A ctiveX kontrole su CO M komponente koje se obino smjetaju na obrazac i koje
mogu, ali ne moraju, imati korisniko suelje. Kad je M icrosoft razvio O C X standard
koji je omoguio projektantima da naprave ActiveX kontrole u VB jeziku i koriste ih
s pomou C + + jezika (i obrnuto), revolucija ActiveX kontrola je poela. Tijekom zad-
njih nekoliko godina, tisue takvih kontrola su razvijene, prodane i koritene. One
su male, lako se koriste i predstavljaju dobar primjer ponovnog koritenja binarnih
datoteka.

Uvoenje ActiveX kontrola u .N E T je iznenaujue lako uzimajui u obzir koliko su


CO M objekti drukiji od .N ET objekata. Visual Studio 2 0 0 5 moe automatski uvoziti
ActiveX kontrole. Kao alternativu za Visual Studio, M icrosoft je razvio pom oni pro-
gram Axlmp koji se poziva iz naredbenog reda i pravi sklopove potrebne da za upotrebu
ActiveX kontrola u .N E T aplikaciji.

548
Stvaranje ActiveX kontrole
Da biste prikazali mogunost koritenja klasine ActiveX kontrole u .N ET aplikaciji
)
prvo napravite jednostavan kalkulator s etiri funkcije kao Act,veX kontrolu i zatim
I T v , ACtln tr? ' U iZ C # aPlikacje- NaPiite kontrolu na VB6 jeziku i ispi-
tajte ju u VB6 aplikaciji. Ako nemate VB6 ili se ne elite gnjaviti stvaranjem kontrole,
zete preuzeti kontrolu s moje Web stranice (http://www.LibcrtyAssodates.com ).
i
I Kad kontrola bude radila u standardnom Windows okoliu uvest ete ju u vau apli-
kaciju temeljenu na Windows Forms obrascima.

nroibik ? M
apravili k0ntr,lu r ite VB6 1 0daberite A ct'eX Control kao tip novog

i p rojek t^ Napravite: projektni obrazac to je mogue manji jer ova kontrola nee imati
isnicko suelje. Pritisnite desnom tipkom mia na UserControll i odaberite Proper-
zon, p T ! r teiJ J ' me U Calculat0r u Prozoru Properties. Odaberite Project u pro-
. J Explorer, a u Properties prozoru promijenite ime u CalcControl. Odmah
snimite projekt pa datoteku i projekt nazovite CalcControl, kao na slici 22-1.
\

.
3

S lika 22 -1. Iz rada VB A c tiv e X kontrole

Sad moete dodati etiri funkcije kalkulatora tako to ete desnom tipkom mia priti-
snuti CalcControl obrazac, odabrati View Code s kontekstnog izbornika i upisati VB
kod prikazan u primjeru 22-1.

Poglavlje 22: .NET i COM programiranje | 549


Primjer 22-1. Implementiranje ActiveX kontrole CalcControl
Public Function
Add(left As Double, right As Double)
As Double
Add = left + right
End Function

Public Function
Subtract(left As Double, right As Double)
As Double
Subtract = left - right
End Function

Public Function
Multiply(left As Double, right As Double)
As Double
Multiply = left * right
End Function

Public Function
Divide(left As Double, right As Double)
As Double
. Divide = left / right
End Function

Ovo je sav kod za kotrolu. Prevedite ga u CalcControl.ocx datoteku tako to ete oda -
brati File - Make C alcControl.ocx s izbornika.

Zatim otvorite drugi VB projekt kao standardni (E X E ). Nazovite obrazac TestForm


a projekt CalcTest. Spremite datoteku i projekt kao CalcTest.

Dodajte ActiveX kontrolu kao komponentu pritiskanjem Ctrl-T i odabirom CalcCon -


trol na kartici Controls, kao to je prikazano na slici 2 2 -2 .

Cortiobjoeaflrs| InMttafcfeOtocoj
: - ) VldeoSoft vs F lex 3 C ontrols
O A i v e Setup C ontroi Llb ra r/
A d o b e SVG Vtow er Type L t o rv y 2 .0
AxBrowse
C:lW ZNN T\System 32lm sconf.cS
G C :\W lN N T \S ys tem 32 \tdc .oo:
Bal
C d lg
CJ C D T od 1.0 Type U b ra r/
c ertm ap OLE C ontroi m odule
C e rtW te Activ eK C ontroi m odule
G ctc 1 . 0 T y p e L to e ry
enfgprts OLE C ontroi m odde
ii P. SeietedhemsOniy
refcontrd
. Idcatiori: C{\..;\22-!..\Adi^CekCon^.(>cx

S lika 22 -2 . D odavanje C a lc C o n tro l ko n tro le u V B 6 pa le tu s a la tim a

550 | Programiranje C#
To stavlja novu kontrolu na paletu s alatima, kao to je prikazano na slici 22 -3 (zao
krueni dio).

S lika 22 -3 . C a lc C on trol k ontro la na VB 6 p a le ti s a la tim a

Povucite novu kontrolu na obrazac TestForm i nazovite ju CalcControl. Primjetite da


nova kontrola nee biti vidljiva jer nema korisnikog suelja. Dodajte dva polja za
tekst, etiri gumba i jedan natpis, kao to je prikazano na slici 22-4.

S lika 2 2 -4 . Iz ra d a korisnikog suelja TestForm

Dajte gumbima imena btnAdd, b tn S u b tra ct, btn M ultiply i btn D ivide. Sve to preostaje
je da implementirate metode za obradu pritiska na gumbe kalkulatora. Svaki put kad
je gumb pritisnut elite uzeti vrijednosti iz dva polja za tekst, prebaciti ih u double
(kako zahtijeva CalcControl) koristei VB6 CDbl metodu, pozvati C alcC ontrol metodu
i ispisati rezultate u natpisu.

Primjer 2 2 -2 sadri kompletan izvorni kod.

P rim je r 22 -2 . K oritenje A c tive X kontro le C a lc C o n tro l u VB program u (TestForm)


Private Sub btnAdd_Click()
Labell.Caption =
CalcControl.Add(CDbl(Text1.Text),
CDbl(Text2 .Text))

Poglavlje 22: .NET i COM programiranje I 551


P r im je r 22 -2 . K orite nje A c tiv e X k o n trole C a lc C o n tro l u VB pro gra m u (TestForm ) (nastavak)

End Sub

Private Sub btnDivide_Click()


Labell.Caption =
calcControl.Divide(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Private Sub btnMultiply_Click()


Labell.Caption =
calcControl.Multiply(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Private Sub btnSubtract_Click()


Labell.Caption =
calcControl.Subtract(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Uvoenje kontrole u .NET


Kad ste se uvjerili da ActiveX kontrola CalcControl radi, moete kopirati CalcCon-
trol.ocx datoteku u razvojni okoli. Kad ste to uinili, zapamtite da CalcControl.ocx
datoteka zahtijeva da ju registrirate koritenjem programa Regsvr32 . Sad ste spremni
napraviti probni program u .NET-u koji e koristiti kalkulator:
Regsvr32 CalcControl.ocx

Da biste poeli, napravite Visual C # W indows aplikaciju koristei Visaual Studio


2 0 0 5 (pogledajte trinaesto poglavlje), nazovite aplikaciju In te ro p T e st i napravite obra-
zac (kao stoje TestForm kojeg ste napravili u VB jeziku u prethodnom dijelu) povlaei
kontrole i isputajui ih na obrazac. Nazovite obrazac TestForm . itav predloak obra-
sca je prikazan na slici 22 -5 .

/'Forml.cs^'totml.cs[Design]*XConsolej .L ."//

S lika 22 -5 . Iz ra d a W in do w s obrasca za testiranje A c tiv e X k o n tro le C a lc C o n tro l

552 | Programiranje C#
Uvoenje kontrola
Postoje dva naina za uvoenje ActiveX kontrolu u Visual Studio 2 0 0 5 razvojni oko-
li: moete koristiti Visual Studio alate ili moete uvesti kontrolu runo s pomou
aximp pomonog programa koji se isporuuje s .N ET SDK kosturom. Da biste koristili
Visual Studio 2 0 0 5 odaberite Tools - Choose ToolboxItems s izbornika. Otvara se
dijaloki okvir. Na kartici COM Components naite CalcControl.Calculator objekt
koji ste upravo registrirali (slika 2 2 -6 ).

Choose Toolbox Items m


j ,NETFramewotk Corrponents fOMComponentT]

Name J Patl i Lfcrary '


'&
AAlstVlevvCtri Class C:\WO(3tA~l\NORTC~l\NAVComUl. NAVComUI 1.0T... -m
& CalcControl.Calculator C:\Documents and Setthg$\Jesse\My... CalcControl
O Calendar Control 11.0 C:\PfOQramFtes\Mlciosoft Office\CfFlC. . Microsoft CaJenda...
U CDDBControl Class C:\ProgramFies\Oeative\Shared F#es\.. CDOBContrd 1.0 ...
CDOBControl Class C:\ProgramFtes\Real\RealOne Player\C..
U CDD8RoxioControl Class C:\WlhDOWS\System32\CDOBControl... CDDBControl(Rox...
CDToolC(rl Class C:\WM50WS\System32\cdtool.cB C D T o d 1.0 Type...
U CotorBvr Class C:\WlNDOWS\System32\lmrt.cfl
COMNSVievvOass C:\WMJOWS\System32\comsnap.d8 ComSnap 1.0Ty...
Ct Behavtor Factory c.\w\NDOWS\system32\hirt.tfi
1.1 Crystal ActiveXReport Vievver Contr . C:\PfogramFtes\CommonFtes\Crystal.. Crystal ActhreXR...
SI .i~.
; :-) VkleoSoft fte*Array Conftol....... -
Lanouage: Language Neutral
Verstan: 3,0

. Slika 2 2 - 6 . Uvoenje A c tiv e X kontro le C alc C on trol

; Kako je CalcControl registrirana na vaem ,N ET raunalu, dijaloki okvir Choose


; Toolbox Item moe ju prepoznati. Kad odaberete kontrolu u ovom dijalokom okviru,
i; ona se uvozi u aplikaciju. Visual Studio se brine za detalje ukljuujui dodavanje kon-
itrole na alatnu vrpcu.

j Runo uvoenje kontrole


fAlternativno, moete otvoriti prozor odzivnika i runo uvesti kontrolu koritenjem
pom onog programa aximp.exe, kao to je prikazano na slici 22-7.

C:\W INNT\System 32\cmd.exe IH0E3

G : \ > a .x in ji c a .I o o o n t r u 1 . oc>
r a t od ft a r o n it J y : Ci: \(/ c Criii t l'n 1 . d l 1
(io n e r a t ori ft r a v n i ) I y * G \ft> ioCun t.ro l.d J 1

mm
flife a 2 2 -7 . P rogram axim p

Poglavlje 22: .NET i COM programiranje | 553


Program aximp.exe uzima jedan argument, ActiveX kontrolu koju elite uvesti (Calc-
Control.ocx). Program stvara tri datoteke:
AxCalcControl.dll
.N ET Windows kontrola

CalcControl.dll
Posrednika .N ET biblioteka klasa

AxCalcControl.pdb
Datoteka za otkrivanje pogreaka

Kad je to uinjeno moete se vratiti u Choose Toolbox Items prozor, ali ovaj put oda-
berite .N ET Framework Components. Sad moete potraiti lokaciju na kojoj je .NET
Windows kontrola AxCalcControl.dll stvorena i uvesi tu datoteku na paletu s alatima,
kao to je prikazano na slici 2 2 -8 .

Choose Toolfoox Item s

.NET Framework Components ] COM Components |

" 1 Namespace^-: AssemblvName , lD lre c to ^ |


ACC8,f^a^l>*'Airn .1:<artKn
nActioM JKS-tsf
0 ACtlV Lbokln: VB6
adoi,
adoi Q ComCalculator.dlt
0AdRe. J ComCalcufatorDU.NET.dN

. 0 A d R c- Desfcop : CakControi.dll
0 Appe
0 Asse
I H _
HyProjects
F fte r
. pAccesst

My Computer

Ftenarpe. p
3 I ~ QP i-' .1
, J l f e o f t y p e [Executables 3 Cancd L

S lika 2 2 -8 . O d a b ir k ontro le za uvoz

Dodavanje kontrole na obrazac


Kad je uvezena, kontrola se pojavljuje u paleti s alatima (slika 2 2 -9 ). Kontrola se moi
pojaviti na dnu palete s alatima.

554 | Programiranje C#
% VScrollBar
WebBrowser
8 CalcCoritrol.Calculator

S lika 22 -9 . Pogled na A x C a lc C o n tro l k a lku la to ra nakon u vo ie n ja na paletu s alatim a

Sad moete povui ovu kontrolu na Windows Forms obrazac i koristiti njene metode,
ba kao to ste radili u VB6 primjeru.

Dodajte metode za obradu dogaaja za svaki od etiri gumba. One e delegirati svoj
posao ActiveX kontroli koju ste napisali u VB6 i uvezli u .NET.

Izvorni kod metode za obradu dogaaja je prikazan u primjeru 22-3.

P rim je r 22 -3 . Im plem entiranje metoda za obradu dogaaja za pro bn i W indow s Forms obrazac.
private void btnAdd_Click(object sender, System.EventArgs e)
{
double left = double.Parse(textBoxl.Text);
double right = double.Parse(textBox2 .Text);
labell.Text = axCalculatorl.Add( ref left, ref right).ToStringO;

private void btnDivide_Click(object sender, System.EventArgs e)


{
double left = double.Parse(textBoxl.Text);
double right = double.Parse(textBox2 .Text);
labell.Text = axCalculatorl.Divide(ref left, ref right) .ToStringO;

private void btnMultiply_Click(object sender, System.EventArgs e)


{
double left = double.Parse(textBoxl.Text);
double right = double.Parse(textBox2.Text);
labell.Text = axCalculatorl.Multiply(ref left, ref right).ToStringO;

private void btnSubtract_Click(object sender, System.EventArgs e)


{
double left = double.Parse(textBoxl.Text);
double right = double.Parse(textBox2.Text);
labell.Text = axCalculatorl.Subtract(ref left, ref right).ToStringO;
}
Svaka metoda dobija vrijednosti preko polja za tekst, pretvara ih u realne varijable
koritenjem statike metode double.Parse() i prosljeuje ih metodama kalkulatora.
Rezultati se pretvaraju natrag u niz i umeu u natpis kao to je prikazano na slici
22 - 10.

Poglavlje 22: .NET i COM programiranje j 555


Slika 22 -10 . Izvoenje A c tiv e X k o n tro le uvezene u W indow s obrazac

Uvoenje COM komponenata


Ispada da je uvoenje A ctiveX kontrola vrlo jednostavno. Mnoge CO M kompnente
koje tvrtke razvijaju nisu A ctiveX kontrole, ve standardne C O M D LL datoteke.
Da biste vidjeli kako koristiti te komponente pod .N ET-om , u V B 6 jeziku napravite
poslovni objekt koji e se ponaati kao komponenta iz prethodnog dijela.

Prvi korak je da napravite novi ActiveX DLL projekt. To je nain na koji VB6 stvara
standardne CO M DLL-ove. Nazovite klasu Com Calc, a projekt Com Calculator. Spre-
mite datoteku i projekt. Kopirajte metode iz primjera 2 2 - 4 u prozor s kodom.

P rim je r 2 2 -4 . Im p le m e n tira n je m etoda iz C om C alc


Public Function
Add(left As Oouble, right As Double)
As Double
Add = left + right
End Function

Public Function
Subtract(left As Double, right As Double)
As Double
Subtract = left - right
End Function

Public Function
Multiply(left As Double, right As Double)
As Double
Multiply = left * right
End Function

Public Function
Divide(left As Double, right As Double)
As Double
Divide = left / right
End Function

Napravite D LL odabirom opcije izbornika File -* Make Com Calculator.dll. Moete


to testirati vraanjem na raniji probni program i uklanjanjem C alculator kontrole iz
obrasca. Dodajte novi D LL tako to ete otvoriti prozor s programskim referencama i
oznaiti ComCalculator, kao na slici 22-11.

556 | Programiranje C#
t o, * 05
Avsfebte Refefebiest

CMProps 1.0 Type Ubrary


COIBCAT 1.0Typelibrary
COM + 1.0 Admfn Type Ubrary
D COM MakeCab 1.0 Type Llbrary
COM+ Services Tvpe Ubran

ComExp 1.0 Type Ubrary


COMMSIGen 1.0 Type Ubrary "Prlority
CornPlus 1.0 Catalog Replication Type Ubrary Help
C l COMWin 1.0 Type Library
Control Wizard 1.0 Type Library '"ii 1
cryptext 1.0 TypeUbrary
cSSEd
ncSSEd Utilities
ii
rOjnCalculator-
Locaflon C sDocumentsand Settlngs\A(mterator\MyDocuments\Wor
Language: Standard ' / (

S lika 22 -11. D odavanje reference do C o m C a lcu la to r.dll

Pisanje COMTestForm programa


Kod za COM komponentu je vrlo slian ranijem primjeru. Meutim, ovaj put instanci-
rate ComCalc objekt i pozivate njegove metode, kao to je prikazano u primjeru 22-5.

P rim je r 22 -5. U pra vljaki program za C om C a lc.dll


Private Sub btnAdd_Click()
Dim theCalc As New ComCalc
Labell.Caption = _
theCalc.Add(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Private Sub btnDivide_Click()


Dim theCalc As New ComCalc
Labell.Caption =
theCalc.Divide(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Private Sub btnMultiply_Click()


Dim theCalc As New ComCalc
Labell.Caption =
theCalc.Multiply(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Poglavlje 22: .NET i COM programiranje | 557


P rim je r 22 -5 . U p ravlja ki program za C o m C a lc .d ll (nastavak)
Private Sub btnSubtract_Click()
Dim theCalc As New ComCalc
Labell.Caption =
theCalc.Subtract(CDbl(Textl.Text),
CDbl(Text2.Text))
End Sub

Uvoenje COM DLL-a u .NET


Sad Com Calc D LL moete uvesti u .NET. Prije nego to to uinite morate se odluiti
za rano ili kasno povezivanja. Kad klijent pozove metodu na posluitelju adresa poslu-
iteljske metode u memoriji mora biti pronaena. Ovaj proces se zove povezivanje
(engl. binding).

Kod ranog povezivanja (engl. early binding) pronalaenje adrese metode na posluitelju
se odvija prilikom prevoenja projekta klijenta, kad se metapodaci dodaju u klijentov
modul. Kod kasnog povezivanja (engl. late binding) adresa se ne pronalazi sve do izvo-
enja, kada COM ispituje posluitelj da li podrava metodu.

Rano povezivanje ima mnogo prednosti. Najvanija prednost su bolje preformanse.


Rano povezane metode se bre pozivaju nego kasno povezane metode. Da bi prevo-
ditelj izveo rano povezivanje, mora ispitati COM objekt. Ako prevoditelj eli ispitati
posluiteljsku biblioteku tipova, onda je prvo treba uvesti u .NET.

Uvoenje biblioteke tipova


COM DLL napisan u jeziku VB 6 ima u sebi biblioteku tipova, ali .N E T aplikacije ne
mogu koristiti format COM biblioteke tipova. Da biste rijeili ovaj problem morate
uvesti COM biblioteku tipova u sklop. Ponovno imate dva naina da to uinite: moete
dozvoliti da IDE uveze klasu registriranjem komponente kao to je prikazano u sljede-
em dijelu, ili moete runo uvesti bibilioteku tipova koritenjem pom one aplikacije
Tlblmp.exe.
Tlblmp.exe e napraviti interoperabilni sklop. Objekt iz .N E T kostura koji umata
COM objekt se naziva Runtime Callable Wrapper (R C W ). .N E T klijent e koristiti
RCW za povezivanje s metodama iz COM objekta, kao to je prikazano u sljedeem
dijelu.

Runo uvoenje
Zaponite kopiranjem ComCalculator.dll datoteke u .N E T okoli i registriranjem s
pomou Regsvr32. Tada ste spremni za uvoenje CO M objekta u .N E T s pomou
programa Tlblmp.exe. Sintaksa ovog postupka je da unesete ime CO M komponente,
a nakon njega opcionalno ime rezultirajue datoteke, kao to je prikazano na slici
22 - 12.

558 ] Programiranje C#
Slika 22 -12 . Program T lblm p.exe

| Izrada testnog programa


| Sad je vrijeme da napravite upravljaki program za testiranje COM objekta koji ete
jf nazvati COMDllTest.

Ako odluite ne uvoziti runo biblioteku, uvezite je kroz IDE. Da biste to uinili,
odaberite COM karticu u dijalokom okviru Add Reference i zatim registrirani COM
objekt, kao to je prikazano na slici 22-13.
'i

.hET COM |Projecte)Promse] Recent|


Ccmponeni:Harne T jm & V e r ., IPath
coloader 1.0Typelihrary i.o C:\ProgramRles\CommonFil..
COM+l.QAdminTypeUbrary 1.0 C:\WINNT\5ystem32\Com\co.
COMMateCeb1.0TypeUbrary I.o C:\WlNNT\System32\catsrvu,
COM+ServicesTypelforary i.0 C:\WHWT\Syrtem32\COM5V
ComExp1.0Typelibrary C:\WINNT\System32\catsrvu.
CommonLanguageRuntlmeE... C:\WlNNT\Microsoft.NET\Fra.
COMMSIGen1.0TypeLbrary C;^WlNNT\$ysteJ32\catsrvu.
ComPlus1.0CatalogReplicati...
CRDesigner9TypeLibrary C:\WINNT\System32\comrepl.
C;\
C:\W
Pro gram
lflW T\Fsy
deste
s\m
M 3ic2ro
\asy
oftV
ptaxi..
cryptext 1.0T
CrystalM ypap
tvexR elibraryfewer...
ortV C:\Proyafrirtes\CommonFd..
CrystalClientDocumentUbra... C:\ProgramFtes\CommonFd..
CrystalPrmtControfTypeL4>r...
CrvstalOu?ryEnqfne C:\Progratnfifes\C Coom m m
moonnFd.. .-i
C:\froqrarnFdesKK oomfnttnFd.^'.
, V
f sp* i
V*\ u ' X W

S lika 22 -13 . D odavanje reference na C om C a lcula tor

Time e Tlblmp biti pozvan za vas i kopirat e rezultirajui RCW u mapu C:\Docu-
ments and Settings\Administrator\Application Data\Microsoft\VisualStudio\RCW.
Meutim, morat ete biti oprezni jer DLL koji je nastao ima isto ime kao COM DLL.

Ako upotrijebite Tlblmp.exe moete dodati referencu s kartice Projects. Pronaite


mapu u kojoj je napravljen ComCalculatorDLLNET.dll i dodajte ga meu reference.

U svakom sluaju moete napraviti korisniko suelje slino onom koritenom za ispra-
bavanje ActiveX kontrole (slika 22-14).

Poglavlje 22: .NETi COM programiranje | 559


labtfl

't'lrSifliact { |. tvldei Vi %
,fceW ( ^ v#v . v
* i- >

S lika 22 -14 . O brazac za testiranje C O M objekta

Sve to je preostalo jest da napiete metode za obradu dogaaja za etir. gumba, kao

u primjeru 2 2 -6 .

r , , 2 2 -6 , m * . a ** ia gaia,a za V E 6 C O M D L L , . obzazaa

ttregion Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Oata;
using System.Drawing;
using System.Windows.Forms;

ttendregion

namespace ComDLLTest
{
partial class Formi : Form

public Forml()

InitializeComponent();
}

private void btnAdd_Click(


object sender, System.EventArgs e )

^ Double left, right, result;


Xeft = Double.Parse( textBoxl.Text );
tight = Double.Parse( textBox2.Text );

ComCalculatorDLLNET.ComCalc theCalc
new ComCalculatorDLLNET.ComCalc();
result = theCalc.Add( ref left, ref right );
labell.Text = result. ToStringO;

private void btnSubtract_Click(


object sender, System.EventArgs e )
{

560 | Programiranje C#
P r im je r 2 2 -6 . lm p lem e ntiranje metoda za obradu dogaaja za VB6 C O M D L L testni obrazac
(nastavak)
Double left, right, result;
left = Double.Parse(textBoxl.Text);
right = Double.Parse(textBox2.Text);
ComCalculatorDLLNET.ComCalc theCalc =
new ComCalculatorDLLNET.Comalc();
result = theCalc.Subtract(ref left, ref right);
labell.Text = result.ToString();
}

private void btnMultiply_Click(


object sender, 5ystem.EventArgs e )
{
Double left, right, result;
left = Double.Parse( textBoxl.Text );
right = Double.Parse( textBox2.Text );
ComCalculatorDLLNET.ComCalc theCalc =
new ComCalculatorDLLNET.ComCalc();
result = theCalc.Multiply( ref left, ref right );
labell.Text = result.ToStringO;
}

private void btnDivide_Click(


object sender, System.EventArgs e )
{
Double left, right, result;
left = Double.Parse( textBoxl.Text );
right = Double.Parse( textBox2.Text );
ComCalculatorDLLNET.ComCalc theCalc =
new ComCalculatorDLLNET.ComCalc();
result = theCalc.Divide( ref left, ref right );
labell.Text = result.ToStringO;
}
}
}
Umjesto referenciranja na ActiveX kontrolu koja se nalazi na obrascu, morate instanci-
rati ComCalculator.ComCalc objekt. COM objekt je tada dostupan za koritenje kao da
je napravljen u .N ET sklopu, a program radi prema oekivanjima (slika 22-15).

S lik a 2 2 - 1 5 . P robni up ravlja ki program u a k ciji

Poglavlje 22: .NETi COM programiranje | 561


Upotreba kasnog povezivanja i refleksije
Ako nemate datoteku s bibliotekom tipova za COM objekt m orate koristiti kasno
povezivanje s refleksijom. U poglavlju 18 vidjeli ste kako dinamiki pozivati metode u
,N ET sklopovima. Taj postupak s COM objektima nije puno drugaiji.

Da biste vidjeli kako to izvesti, ponite s aplikacijom prikazanom u primjeru 22 -6 ,


ali uklonite referencu prema uvezenoj biblioteci. Metode za etiri gumba sad moraju
biti ponovno napisane. Ne moete vie instancirati ComCalculator.comCalc objekt pa
njegove metode morate pozvati dinamiki.

Ba kao to ste vidjeli u poglavlju 18, poinjete tako da napravite Type objekt koji e
sadravati informacije o tipu comCalc:
Type comCalcType;
comCalcType = Type.GetTypeFromProgID("ComCalculator.ComCalc );

Poziv metode GetTypeFromProgID govori .N E T kosturu da otvori registrirani COM


DLL i uzme potrebne informacije o tipu za zadani objekt. To je ekvivalentno pozivanju
GetType, kako ste uinili u poglavlju 18:
Type theMathType = Type.GetType("System.Math");

Sad moete nastaviti kao kada biste pozivali ovu metodu na klasi opisanoj u .NET
sklopu. Ponite pozivom Createlnstance da biste uzeli instancu comCalc objekta:
object comCalcObject = Activator.CreateInstance(comCalcType);

Zatim napravite polje za uvanje argumenata i pozovite metodu koritenjem InvokeMem-


ber, prosljeujui kao niz znakova metodu koju elite pozvati, zastavicu spone, nuli
sponu, objekt koji je vratila Createlnstance i polje ulaznog argumenata:
object[] inputArguments = {left, right };
result = (Double) comCalcType.InvokeMember(
"Subtract", // Metoda koju treba pozvati
BindingFlags.InvokeMethod, // Nain povezivanja
nuli, // Spona
comCalcObject, // COM objekt
inputArguments); // Argumenti metode

Rezultati ovog poziva se pretvaraju u Double i spremaju u lokalnu varijablu result.


Rezultat moete prikazati u korisnikom suelju, kao na slici 22-16.

S lika 22 -16 . La te B in din g Test

562 | Programiranje C#
osto sve etiri metode za obradu moraju ponoviti ovaj posao, s razlikom jedino u
imenu metode koju pozivaju, faktorirat ete zajedniki kod u privatnu pomonu
metodu Invoke, kako stoje prikazano u primjeru 22-7. Trebate dodati using deklara-
ciju za System. Reflection u izvornom kodu.

P rim je r 22 -7. Kasno povezivanje C O M objekata


ttregion Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

ftendregion

namespace LateBinding
{
partial class Formi : Form
{
public Forml()
{
InitializeComponent();
}

private void btnAdd_Click(


object sender, System.EventArgs e )

Invoke( "Add" );
}

private void btnSubtract_Click(


object sender, System.EventArgs e )

Invoke( "Subtract" );
}

private void btnHultiply_Click(


object sender, System.EventArgs e )

Invoke( Hultiply" )
}

private void btnDivide_Click(


object sender, System.EventArgs e )

Invoke( "Divide" );
}

private void Invoke( string whichMethod )

Poglavlje 22: ,NET i COM programiranje | 563


Primjer 22-7. Kasno povezivanje COM objekata (nastavak)

{
Double left, right, result;
left = Double.Parse( textBoxl.Text );
right = Double.Parse( textBox2.Text );

// Stvara Type objekt za uvanje informacija o tipu


Type comCalcType;

// Polje za argumente
object[] inputArguments =
{ left, right };

// Uzima podatke o tipu iz COM objekta


comCalcType =
Type.GetTypeFromProgID(
"ComCalculator.ComCalc" );

// Stvara instancu
object comCalcObject =
Activator.CreateInstance( comCalcType );

// Dinamiki poziva metodu i


// pretvara rezultat u Double
result = ( Double ) comCalcType.InvokeMember(
whichMethod, // Metoda koju treba pozvati
BindingFlags.InvokeMethod, // Nain povezivanja
n u li, I I Spona
comCalcObject, // COM objekt
inputArguments ); II argumenti metode

labell.Text = result.ToStringO;
}
}
}

Izvoenje .NET komponenata


M oete izvesti .N E T klasu za upotrebu s postojeim CO M komponentama. Regasm
alat e registrirati metapodatke komponente u bazi Registry sustava.

Pozovite Regasm s imenom D LL-a koji m ora biti postavljen u GAC (pogledajte poglavlje
17). Na primjer:
Regasm myAssembly.dll
To e izvesti metapodatke komponente u Registry. Na primjer, moete napraviti novi
C # DLL projekt u kojem ete ponovno napraviti kalkulator s etiri funkcije, kao to
je prikazano u primjeru 2 2 -8 .

564 | Programiranje C#
P rim je r 2 2 -8 . K a lk u la to r s e tiri fu n kc ije u D L L -u
using System;
using System.Reflection;

[assembly: AssemblyKeyFile(test.key")]
namespace Programming_CSharp
{
public class Calculator
{
public Calculator()
{

}
public Double Add (Double left, Double right)

return left + right;


}
public Double Subtract (Double left, Double right)

return left - right;


}
public Double Multiply (Double left, Double right)

return left * right;


}
public Double Divide (Double left, Double right)

return left / right;


}
}
}

Spremite ovaj kod u datoteku C a l c u l a t o r . c s u projektu ProgrammingCSharpDLL. Da


biste napravili jako ime, odaberite Project - ProgrammingCSharpDll Properties. O da-
berite karticu Signing i oznaite sklop, kao to je prikazano na slici 22-17.

Oal-* renote] v. . n
Appfcation Confkurattan: (LstoJO) i
Syiing ------------------------

ReferencePaths 0Slgn theassembty


Biid Events KeySetectkn ................ .............
BuU <S)VseateY flei
Debuo (!a5ttoy.a* TS ] i Ctengepassmrd... i
Resouces OUsetHs provide:
/ Settrgs Providename; :
/ Contaher: 1
/ /
QOeJayslgnorty

S lika 22-17. Stvaranje k lju a u n u ta r V isual Studio aplikacije

Poglavlje 22: .NET i COM programiranje I 565


Otvorit e se dijaloki okvir Create Key (slika 22-18).

Give the key a name:


testKev

Protect m y key file with a password


Enter password:______________

Confitm passwotd:

| OK ~] | Cancel | [ Help |

Slika 22-18. Stvaranje kljua jakog imena

Dodajte program u GAC i registrirajte ga:


gacutil /i ProgrammingCSharpDLL.dll
Regasm ProgrammingCSharpDLL.dll

Sada moete pozvati kalkulator s etiri funkcije kao COM objekt koristei standardni
VBScript. Na primjer, moete napisati kratku Windows skriptu, poput one u primjeru
22-9.

Primjer 22-9. Pozivanje COM objekta kalkulatora s pomou skripte


dim calc
dim msg
dim result
set calc = CreateObject("Programming_CSharp.Calculator'')
result = calc.Multiply(7,3)
msg = " 7 * 3 =" & result &
Call MsgBox(msg)

Kad pokrenete skriptu pojavljuje se dijaloki okvir koji potvruje da je objekt napra-
vljen i pozvan (slika 22-19).

Slika 22-19. Kasno povezivanje preko COM-a

566 | P ro g ra m ira n je C#
Stvaranje biblioteke tipova
Ako elite koristiti rano povezivanje s .NET DLL-om, obino ete napraviti biblio-
teku tipova. To moete uiniti koritenjem pomonog programa TlbExp (Type Library
Export) tako to ete napisati:
TlbExp ProgrammingCSharpDLL.dll /out:Calc.tlb

Rezultat je biblioteka tipova koju moete pregledati u pregledniku OLE/COM obje-


kata (slika 22-20).

Kad imate biblioteku tipova, moete uvesti klasu kalkulatora u bilo koji COM okoli.

P/Invoke
Iz jezika C # mogue je pozvati neupravljani kod (engl. unmanaged code). Obino ete
to raditi kada budete trebali postii neto to ne moete kroz FCL. Inaica 2 .0 NET-a
ini upotrebu P/Invoke jo rjeom.

Znaajka P/Invoke je predviena samo da prui pristup WindowsAPI-ju, ali ju moete


koristiti za pozivanje metoda iz bilo kojeg DLL-a.

| - tT ypc
yp cl rb Viewcr h e j d i
'Ffe ::'Vtew V " . > "
- :' y v i '.-':.(
:.( t.'V
t.'V-
'V-

sfb Generoted .IDL file (by the OLE/COMObject Viever)


codas
dass Cai
Caiculetor
0 < _Caiciiet<x
typelib fiienae: <eouldnot dotermine lilenae>
: 8 fijM e th o*
ft) TcSlitng
t u\iid(OFE043E7-338F-339B-BCA8-S47EFF7B9BA4>.
ft) Cquah
uah version(l.0).
ft) GetHashCo
shCod
Code eusto*(9O003FOS-3D28-llD2-0F17-OOAOC9A6186D.
ft) GetTyp
Type
m Add
m subt
ubtratt
library ProgramaingCShaxpDLI
ft) /im
/ pTolirbtiib:('sc/o/rlT ibli.btlb:*):i {8ED7F4EA-1A96-UD2-0FO0-OOAOC9A6106D}
pTolribtiib:(*OsLE
tdAoule
to2m .taltbion); : <00020430-0000-0000-c000-000000000046)
: ft) Drride
Si f Irteftedlnterfac
faces
< Objed
B V ckiprterfac
erface_C#iaiator
SI
SIIM eth od*
od*
ft) ToStrtog
ft) Equeb uuid(902B73B7-2SC6-3FCF-ASCF-93721SA45D2F).
m GetHoi
HoihCod
Code custom(OF21F3S9-AB04-41E0-9A78-36DllOE6D2F9. ProgrammingCSharpDlL.Calculator)
m GetTyp
Type
coclass Calculator <
m
m
Add
Sub
Subtrert
[default] interface _Calculator;
interfeca _0bject;
Dl M
Jtiply
m O
a f Wier
Wie ivkfeInLerfa
red rfaces odi.
9 intfrf
frface Caloiatcv
uuid(FF0B7383-C3C4-3DFl-B09B-2A0C63A5C403).
hidden,
dual.
DOnextensible.
oleautomation.
custom(OF21F3S9-AB84-41E0-9A78-36DUOE6D2F9. PrograaaingCSharpDLL.Calculator)
inter[fiadc(e00
_0Ca0
00lc0u
0)lat
.oprrop:gIeD
t]ispatch { _......... ..................J

Slika 22-20. Pregledavanje sadraja biblioteke tipova

Da biste vidjeli kako radi, pogledajte ponovno primjer 21-3. Sjetit ete se da ste koristili
Filelnfo klasu da promijenite ime datoteke pozivanjem MoveTo() metode:

P o g lavlje 22 : .NET i COM p ro g ra m iran je | 56 7


file.MoveTo(fullName + ".bak");

Moete postii isto upotrebom kernel32.dll u Windowsima i pozivanjem MoveFile


metode.' Da biste to uinili trebate deklarirati metodu kao static extern i upotrijebiti
Dlllmport atribut:
[Dlllmport("kernel32.dll", EntryPoint="MoveFile",
ExactSpelling=false, ChaiSet=ChaiSet.Unicode,
SetLastErrorrtrue)]
static extern bool MoveFile(
string sourceFile, string destinationFile);

Dlllmport atribut se koristi da pokae da e kroz P/Invoke biti pozvana neupravljana


metoda. Parametri su sljedei:

DLL Name
Ime DLL-a kojeg pozivate.
EntryPoint
Pokazuje ime ulazne toke (metode) DLL-a koju se poziva.
ExactSpelling
Omoguava da CLR pronae odgovarujue metode ali s malo drugaijim ime-
nima na temelju CLR-ovog poznavanja pravila imenovanja.

CharSet
Govori kako bi znakovni argumenti metoda trebali biti rasporeeni.

SetLastError
Ako ovaj parametar postavite na true moi ete pozvati Marshal.GetLastWin32
Error i provjeriti je li dolo do pogreke prilikom pozivanja metode.

O statak koda je praktino isti, osim samog poziva metode MoveFile(). Primjetite da
je MoveFile() deklarirana da bude statika metoda klase, pa koristite semantiku sta-
tike metode:
Tester.MoveFile(file.FullName,file.FullName + ".bak");

Proslijedite izvorno i novo ime datoteke i ona e biti promijenjena, ba kao i kada ste
pozvali file.MoveTo(). U ovom primjeru nema prednosti (i postoji znatna teta) kori-
tenja P/Invoke. Napustili ste upravljani kod, a tako i sigurnost tipa pa kod vie nee
raditi u situacijama djelomine pouzdanosti*1. Primjer 22-10 prikazuje kompletan
izvorni kod za koritenje P/Invoke za mijenjanje imena datoteka.

Primjer 22-10. Upotreba P/Invoke za pozivanje Win32 API metode


ttregion Using diiectives

using System;
using System.Collections.Generic;
using System.I0;

* T o je o n o to Fileinfo.Move() inae radi.

568 | P ro g ra m ira n je C#
P r i m j e r 2 2 - 1 0 . U p o t r e b a P l ln v o k e z a p o z i v a n je W in 3 2 A P l m e t o d e ( n a s ta v a k )

using System.Runtime.InteropServices;
using System.Text;

#endregion

namespace UsingPIrvoke
{
class Tester
{

// Deklarira WinAPI metodu koju elite koristiti s P/Invoke


[DllImport( "kernel 3 2 .dll", EntryPoint = "MoveFile",
ExactSpelling = false, CharSet = CharSet.Unicode,
SetLastError = true )]
static extern bool MoveFile(
string sourceFile, string destinationFile );

public static void Main()


{
I I Stvara instancu i pokree ju
Tester t = new Tester();
string theDirectory = @"c:\test\media";
DirectoryInfo dir =
new DirectoryInto( theDirectory );
t.ExploreDirectory( dir );
}

// Pokree ju s imenom mape


private void ExploreDirectory( DirectoryInfo dir )
{

// Pravi novu podmapu


string newDirectory = "newTest'';
DirectoryInfo newSubDir =
dir.CreateSubdirectory( newDirectory );

// Uzima sve datoteke u mapi


// i kopira ih u novu mapu
FileInfo[] filesInDir = dir.CetFiles();
foreach ( Filelnto file in filesInDir )
{
string fullName = newSubDir.FullName +
"\\" + file.Name;
file.CopyTo( fullName );
Console.WriteLine( "{0 } copied to newTest'',
file.FullName );
}

// Uzima kolekciju kopiranih datoteka


filesInDir = newSubDir.GetFiles();

// Neke brie, a ostalima mijenja imena


int counter = 0;

P oglavlje 2 2 : .NET i COM p ro g ra m ira n je | 569


P r im je r 2 2 - 1 0 . U p o t r e b a P / l n v o k e z a p o z i v a n j e W in 3 2 A P l m e t o d e ( n a s t a v a k )

foreach ( Filelnfo file in filesInDir )


{
string fullName = file.FullName;

if ( counter++ % 2 == 0 )
{
// P/Invoke Windows API
Tester.MoveFile( fullName, fullName + ".bak" );

Console.WriteLine( "{0} renamed to {l}",


fullName, file.FullName );
}
e lse
{
file.D e le teO ;
Console.WriteLine( "{0} deleted.",
fullName );
}
}
// Brie podmapu
newSubDir.Delete( true );
}
}
}

Pokazivai
U knjizi jo nismo koristili pokazivae u stilu C/C++ jezika. Ova tema pojavljuje se
samo ovdje, u zadnjim odlomcima i na zadnjim stranicama knjige, iako su pokazivai
temeljni dio C obitelji jezika. U C # jeziku pokazivai su namijenjenu nestandardnom
i naprednom programiranju. Obino se koriste samo s P/Invoke.
C # podrava uobiajene C pokazivake operatore koji su opisani u tablici 22-1.

Tablica 22-1. C # p o k a z i v a k i o p e r a t o r i

Operator Znaenje
& Operator adresa od" vraa pokaziva na adresu vrijednosti.
* Ovaj operator vraa vrijednost na adresi pokazivaa.
-> Operator koji se koristi za pristupanje lanovima tipa.

Upotreba pokazivaa gotovo nikad se ne zahtijeva i ne preporuuje. Ako ipak koristite


pokazivae, kod morate oznaiti s oznakom unsafe. Kod je tako oznaen jer s pomou
pokazivaa moete izravno manipulirati memorijskim lokacijama. To je mogunost
koje inae nije pruena unutar C # programa. U nepouzdanom kodu moete izravno
pristupiti memoriji, pretvarati izmeu pokazivaa i integralnih tipova, uzimati adrese
varijabli i tako dalje. Zauzvrat, odriete se skupljanja otpada i zatite protiv neinicijali-
ziranih varijabli, viseih pokazivaa i pristupa memoriji izvan granica polja. Drugim

570 | P ro g ra m ira n je C#
rijeima, nepouzdani kod stvara otok C++ koda unutar inae sigurne C # aplikacije,
pa kod nee raditi u situacijama djelominog povjerenja.
Da biste vidjeli primjer kada bi ovo moglo biti korisno, itajte datoteku na konzolu
upuivanjem dva W in32 API poziva: CreateFile i ReadFile. ReadFile uzima kao svoj
drugi parametar pokaziva na meuspremnik. Deklaracije dviju uvezenih metoda nije
drukija od onih u primjeru 22-11.

P r i m j e r 2 2 - 1 !. D e k l a r i r a n j e W in 3 2 A P I m e t o d a z a u v o e n je u C# p rog ra m

[011Import(''kernel32", SetLastError=true)j
static extern unsafe int CreateFile(
string filename,
uint desiredAccess,
uint shareMode,
uint attributes,
uint creationDisposition,
uint flagsAndAttributes,
uint templateFile);

[Dl1 Import("kernel32", SetLastError=true)]


static extern unsafe bool ReadFile(
int hFile,
void* lpBuffer,
int nBytesToRead,
int* nBytesRead,
int overlapped);

Napravit ete novu klasu, APIFileReader, iji e konstruktor pozvati CreateFile()


metodu. Konstruktor uzima ime datoteke kao parametar i prosljeuje ga CreateFile()
metodi:
public APIFileReader(string f i l e n a m e )
{
fileHandle = CreateFile(
filename, // Ime datoteke
GenericRead, I I desiredAccess
UseDefault, // shareMode
UseDefault, // atributi
OpenExisting, I I creationDisposition
UseDefault, // flagsAndAttributes
UseDefault); // templateFile
}
APIFileReader klasa implementira samo jednu metodu, Read(), koja poziva ReadFile().
Ona prosljeuje identifikator datoteke napravljen u konstruktoru klase, zajedno s
pokazivaem na meuspremnik, brojem bajtova za uzimanje i referencom na varija-
blu koja e sadravati broj uitanih bajtova. Nama je ovdje zanimljiv pokaziva na
meuspremnik. Da biste uputili API pozive morate koristiti pokazivae.
Poto ete mu pristupiti preko pokazivaa, meuspremnik treba biti privren (pin-
ned) u memoriji. Ne smije se dozvoliti .N ET kosturu da pomie meuspremnik tije-
kom sakupljanja otpada. Da biste to postigli upotrijebite fixed kljunu rije C # jezika.

P oglavlje 22 : .NET i COM p ro g ra m iran je | 571


Ona omoguava da uzmete pokaziva na dio memorije koji koristi meuspremnik i
oznaite tu instancu tako da ju sakuplja otpada ne pomie.
Blok iskaza nakon kljune rijei fixed stvara doseg unutar kojeg e memorija biti
privrena. Na kraju tog bloka, oznaka instance e biti uklonjena tako da se moe
pomicati. Ovo je poznato pod imenom deklarativno privrivanje (engl. declarative
pinning):
public unsafe int Read(byte[] buffer, int index, int count)
{
int bytesRead = 0;
fixed (byte* bytePointer = buffer)
{
ReadFile(
fileHandle,
bytePointer + index,
count ,
&bytesRead, 0);
1
return bytesRead;
}
Metoda mora biti oznaena s kljunom rijei unsafe. Ona stvara nepouzdani kontekst
i omoguava da napravite pokazivae. Da biste ovakav kod preveli, morate koristiti /
unsafe opciju prevoditelja. Najlaki nain da to uinite je da otvorite svojstva projekta,
pritisnete karticu Build i potvrdite polje Allow Unsafe Code, kao na slici 22-21.

O stng P o in te rs j usngPomters.es [ ConsdeJ______ ______________________ _________________ ---------------- - >--------- ---------------

Appteatttn
Corflguration: [Actrve (Oebug)
Stgnlng

Reference Paths Cnrritlonal ComDteaon Symbols: [BUG;TRACE ]


B u U e re nte P ta tk rm T a o e t:

Secultv ^ ^ 0 A lo w unsafe C o d e ^ )
U O pom lz e Code
Pubfch
............................. -J Eftors and V V arrtngs...................................
Build
W anm g Level: |3 3 )
Debug
Sunvess warrings: | 1
Resouces
,-Treat W artngs as & rors ...................................- - - - - -
S etthgs
N one
O s ie d f lc Warrincis | ........................................................................................................... i

O ab

-O u tput

O u tout Path: jbm\Debug\ . J

i n HA. O o c uren U ttan Fte: 1 _!


Regbter far COM Irrterop

S lik a 2 2 -2 1 . D o z v o lja v a n je u p o tr e b e n e p o u z d a n o g k o d a

572 | P ro g ra m ira n je C#
Probni program instancira APIFileReader i ASCIIEncoding objekte. Prosljeuje ime dato-
teke konstruktoru APIFileReader i zatim stvara petlju da ponavljanjem napuni meu-
spremnik pozivanjem Read() metode, koja upuuje AP1 poziv ReadFile. Vraeno je
polje bajtova i zatim pretvoreno u niz koritenjem GetString() metode ASCIIEncoding
objekta. Taj niz je prosljeen Console.Write() metodi za ispisivanje na konzoli. Kom-
pletan izvorni kod je prikazan u primjeru 22 -12 .

P r im je r 2 2 -1 2 . K o riten je p o k a z iv a a u C # p rog ra m u

(fregion Using directives

using System;
us in g System.Colle ct io ns .G eneri c;
us i n g S y s t e m .R u n t i m e .I n te r o p S er vi c e s;
us in g System.Text;

flendregion

na me sp ac e Usin gP ointer s
{
class API FileRe ader
{
const uint GenericRead = 0x80000000;
const uint 0penExisting = 3;
const uint UseDefault = 0;
int fileHandle;

[DllImport( "kernel32", SetLastError = true )]


static extern unsafe int CreateFile(
string filename,
uint desiredAccess,
uint shareMode,
uint attributes,
uint creationDisposition,
. uint flag sAnd Attr ibut es,
uint template Fil e );

[DllImport( "kernel32", SetLastError = true )]


static extern unsafe bool ReadFile(
int hFile,
void* lpBuffer,
int nBytesToReadJ
int* nBytesRead,
int overlapped );

// Kons truk tor otvara post oj eu da toteku


//i po st av lja lan za identi fika tor dato teke
public APIFileReader( stri ng f ile name )
{
file Han dle = Crea teFi lej
filename, // Ime da tote ke
Gene ricRead, // desi redA ccess
UseDefault, // sh areMode

P oglavlje 22 ; .NET i COM p ro g ram iran je | 57 3


primjer 22-12. Koritenje pokazivaa u C# programu (nastavak)
UseDefault, U Atributi
OpenExisting, // creationDisposition
UseDefault, // flagsAndAttnbutes
UseDefault ); // templateFile
}
int count )
public unsafe int Read( byte[] butfer, int index:
{
int bytesRead = 0;
fixed ( byte* bytePointer = buffer )
{
ReadFile(
fileHandle, // hfile
bytePointer + index, // lpBuffer
// nBytesToRead
count,
&bytesRead, // nBytesRead
// overlapped
o )i

}
return bytesRead;
}
}
class Test

public static void Main()

// Stvara instancu APIFileReader,


// i prosljeuje ime postojee datoteke
APIFileReader fileReader =
new APIFileReader( "myTestFile.txt" );

// Stvara meduspremnik i ASCII koder


const int Buf-fSize = 128;
bvtefl buffer = new byte[BuffSize];
ASCIIEncoding asciiEncoder = new ASCIIEncodingO;

H Uitava datoteku u meduspremnik i prikazuje konzolu


while ( fileReader.Read( buffer, 0, BuffSize ) .= 0 )

^ Console.Write( "{0}", asciiEncoder.GetString( buffer ) );


}
}
}
k i fiksirate meusprem-
Kljuni dio koda u kojem stvarate pokaziva na meuspremnt
. Ovdje trebate koristiti
nik u memoriji koritenjem kljune rijei fixed je podebljan
pokazivae jer to zahtijeva AP1 poziv.

574 j P ro g ra m ira n je C#
DODATAK

C# kljune rijei

abstract
Moifikator klase koji oznaava da se iz klase mora izvoditi da bi bila instancirana.
as
Pretvara lijevi operand u tip odreen desnim operanom i vraa nuli umjesto da
izbaci iznimku ako ne uspije.
base
Varijabla koja ima isto znaenje kao i th is osim to pristupa implementaciji
osnovne klase lana.
bool
Logiki tip podataka koji moe biti true ili false.
break
Iskaz za preskakanje koji izlazi iz petlje ili bloka switch iskaza.
byte
Integralni tip podataka bez predznaka duljine jedan bajt.
case
Iskaz odabira koja definira odreeni izbor u switch iskazu,
catch
Dio try iskaza koji hvata iznimke tipa zadanog u catch.
char
Unicode znakovni tip podataka duljine dva bajta.
checked
Iskaz ili operator koji namee provjeravanje aritmetikih granica izraza ili bloka
iskaza.
cla ss
Proiriv referentni tip koji kombinira podatke i funkcionalnost u jednu jedinicu.

575
Modifikator lokalne varijable ili deklaracije polja koji pokazuje da je vrijednost
konstanta. Const se evaluira prilikom prevoenja i moe biti samo unaprijed defi-
niranog tipa.

continue
Iskaz koji preskae preostale iskaze u bloku i nastavlja prema sljedeem ponavlja-
nju petlje,
decimal
16-bitni precizni decimalni tip podataka.

default
Oznaka u switch iskazu koja odreuje koju akciju izvesti ako nijedan case iskaz
ne odgovara switch iskazu.

delegate
Tip za definiranje potpisa metode tako da instance delegata mogu drati i pozivati
metodu ili popis metoda koje odgovaraju njegovom potpisu.

Iskaz petlje za iteriranje kroz blok iskaza sve dok iskaz na dnu petlje ne evaluira
fa lse.

double
Tip podataka za realne brojeve duljine osam bajtova.

else
Uvjetni iskaz koji definira akciju koju treba poduzeti kad prethodni i f izraz eva-
luira fa lse.

enum
Vrijednosni tip koji definira skupinu imenovanih brojanih konstanti.

event . . .
Modifikator lana za polje delegata ili svojstvo koje zadaje da se moe pristupiti
samo metodama += i -= delegata.

e x p licit
Operator koji definira eksplicitno pretvaranje.

extern .
Modifikator metode koji oznaava da je metoda implementirana sa neupravljamm
kodom.
fa lse
Logiki literal.
finally
Dio iskaza try koji se izvodi uvijek kada kontrola napusti doseg bloka try.

576 | P ro g ra m ira n je C#
fixed

Iskaz za privrivanje referentnog tipa tako da ga sakuplja otpada ne pomie


tijekom aritmetikih operacija s pokazivaem.
float
Tip podataka s pominim zarezom duljine etiri bajta.
for
Iskaz petlje koji kombinira iskaz inicijaliziranja, uvjet zaustavljanja i iterativni
iskaz u jedan iskaz.
foreach
Iskaz petlje koji iterira kroz kolekcije koje implementiraju IEnumerable.
get
Ime pristupnika koji vraa vrijednost svojstva.
goto
Iskaz za preskakanje koji odlazi do oznake unutar iste metode i istog dosega u
kojem se nalazi toka preskakanja.
if
Uvjetni iskaz koji izvodi svoj blok iskaza ako njegov izraz evaluira true.
im plicit
Operator koji definira implicitno pretvaranje,
in
Operator izmeu tipa i IEnumerable u foreach iskazu,
int
Cjelobrojni tip podataka sa predznakom duljine etiri bajta.
interface
Ugovor koji odreuje lanove koje klasa ili struktura mogu implementirati da
primi generike usluge za taj tip.
Internal
Modifikator pristupa koji pokazuje da je tip ili lan tipa pristupaan samo drugim
tipovima u tom istom sklopu.
is
Relacijski operator koji evaluira true ako tip lijevog operanda odgovara, izveden
je iz, ili implementira tip zadan desnim operandom.
lock
Iskaz koji postavlja lokot na objekt referentnog tipa kako bi omoguio suradnju
vie dretvi.

lng
Integralni tip podataka sa predznakom duljine osam bajtova.

Dodatak: C#kljune rijei ) 577


namespace
Preslikava skup tipova u zajedniko ime.

new
Operator kojt poziva konstruktor na tipu, alocirajui novi objekt na gomilu ako
je taj tip referentni ili inicijalizirajui objekt ako je to vrijednosni tip vrijednosti.
Kljuna rije je preoptereena kako bi sakrila naslijeenog lana.

nuli
Literal referentnog tipa koji pokazuje da nijedan objekt nije referenciran.

object
Tip iz kojeg izvode svi drugi tipovi,

operator
Modifikator metode koji preoptereava operatore.

Ut Modifikator parametra koji pokazuje da se parametar prosljeuje po referenci i


mora ga dodijeliti metoda koju se poziva.

override
Modifikator metode koji pokazuje da metoda klase premouje virtualnu metodu
klase ili suelja.

Modifikator parametra koji zadaje da zadnji parametar metode moe prihvatiti


vie parametara istog tipa.

private . .
Modifikator pristupa koji pokazuje da samo sadravajui tip moe pristupiti
lanu.
publi-Lc. ,
Modifikator pristupa koji zadaje da je tip ili lan tipa dostupan svim drugim tipo-
vima
readonly . .
Modifikator polja koji zadaje da polje moe biti dodijeljeno samo jednom u svojoj
deklaraciji ili konstruktoru svog sadravajueg tipa.

ref
Modifikator parametra koji zadaje da se parametar prosljeuje po referenci i dodje-
ljuje prije prosljeivanja metodi.

return
Iskaz za preskakanje koji izlazi iz metode i zadaje povratnu vrijednost ako metoda
vraa vrijednost.

sbyte
Integralni tip podataka sa predznakom duljine jedan bajt.

578 | P ro g ra m ira n je C#
sealed
Modifikator klase koji pokazuje da se iz klase ne moe izvoditi,
set
Ime pristupnika koji postavlja vrijednost svojstva,
short
Integralni tip podataka s predznakom duljine dva bajta.
sizeof
Operator koji vraa veliinu strukture (u bajtovima).
stackalloc
Operator koji vraa pokaziva na zadani broj vrijednosnih tipova alociranih na
stogu.
static

Modifikator lana tipa koji pokazuje da se lan primjenjuje na tip, a ne na instancu


tipa.
string
Unaprijed definirani referentni tip koji predstavlja nepromjenljiv niz Unicode zna-
kova.
struct
Vrijednosni tip koji kombinira podatke i funkcionalnost u jednoj jedinici.
switch
Iskaz odabira koja omoguava odabir izmeu vie mogunosti ovisno o vrijedno-
sti unaprijed definiranog tipa.
this
Varijabla koja referencira trenutnu instancu klase ili strukturu.
throw
Iskaz za preskakanje koja izbacuje iznimku kad nastupi nenormalno stanje.
true
Logiki literal.
try
Iskaz koji prua mogunost obrade iznimke ili prerani izlazak u bloku iskaza.
typeof
Operator koji vraa tip objekta kao System.Type objekt.
uint
Integralni tip podataka s predznakom duine etiri bajta.
unchecked
Iskaz ili operator koji sprjeava provjeru aritmetikih granica.

Dodatak: C#kljune rijei | 579


unsafe
Modifikator metode ili iskaza koji dozvoljava izvoenje pokazivake aritmetike
unutar odreenog bloka.

ushort
Integralni tip podataka bez predznaka duine dva bajta.

using
Zadaje da se tipovi iz odreenog imenskog prostora mogu koristiti bez upotrebe
punih imena tipova. Iskaz using definira doseg na ijem kraju se objekt odbacuje.

value
Ime implicitne varijable postavljene od strane set pristupnika svojstvu.

Virtual
Modifikator metode klase koji govori da metoda moe biti premoena izvedenom
klasom.

void
Kljuna rije koja se koristi umjesto tipa za metode koje nemaju povratnu vrijed-
nost.
volatile
Pokazuje da polje moe biti promijenjeno od strane operativnog sustava ili druge
dretve.
while
Iskaz petlje za iteriranje kroz blok iskaza sve dok izraz na poetku svake iteracije
ne evaluira false.

580 | P ro g ra m ira n je C#
Kazalo

Sim boli & & (I), operator, 57, 59


{ } (vitiaste zagrade) , (zarez), u deklaracijama polja
bijeli prostor i, 50 ' (mnoenje), operator, 52
definiciranje svojstava i ponaanja klasa '= (mnoenje i pridruivanje), operator, 54
u, 10 + (zbrajanje), operator, 52
smjetanje blokova iskaza u, 40 preoptereivanje, 120
() (zagrade) ++ (poveanje), operator, 54
grupiranje u regularnim izrazima, 241 += (zbrajanje i pridruivanje), operator, 55
metaznakovi regularnih izraza, 235 /(dijeljenje), operator, 52
ugnjeivanje za pravilan redoslijed /= (dijeljenje i pridruivanje), operator, 55
operacija, 59 :: (odvajanje dosega), operator, C++, 12
(pomicanje), operator, 58 ; (toka zarez)
I (okomita crta) definicije C# klasa i, 65
metaznak regularnog izraza, 235 napomena za C++ programere, 10
II (uvjetni ILI), operator, 59 zavravanje C# iskaza s, 37
~ (valovita crta), u deklaracijama -= (oduzimanje i pridruivanje), operator, 54
konstruktora, 81 = (pridruivanje), operator, 52
< (manje od), operator, 56 >= (vee ili jednako), operator, 56
- (oduzimanje), operator, 52 <= manje ili jednako operator, 56
. (toka), operator, 13 == (jednakost), operator, 56
pozivanje metode, 12 operator pridruivanja (=) u usporedbi s, 41
pristup lanovima i bijeli prostor, 13 preoptereivanje, 122
> (vee od), operator, 56 provjeravanje jednakosti nizova, 228
- - (smanjenje), operator, 55 ? (uvjetni), operator, 320
1(NE), operator, 57 ?: (ternarni), operator, 59
!= (nejednakost), operator, 56 @ (at) simbol u Iiteralima niza, 222, 239
# (ljestve), u direktivama predprocesora, 60 ' DirectoryInfo, objekt, stvaranje, 493
# define, iskaz, 60 A (logiko ekskluzivno ILI), operator, 59
# elif, iskaz, 62 -> (pristup lanovima), operator, 570
# else, iskaz, 61-62 [] (uglate zagrade)
# endif, iskaz, 61-62 deklaracije polja, 168
# endregion, iskaz, 62 pristup lanovima polja, 179
# if, iskaz, 61 [] (indeks), operator, 169
# region iskaz, 62 pronalaenje odreenog znaka u nizu, 228
# undef, iskaz, 61
% (modulo), operator, 52 A
%= (modulo i pridruivanje), operator, 54
& (logiko I), operator Abort(), metoda (Tliread), 476
kao adresa C++ operatora, 570 AcceptSocket(), metoda, 516, 521
(TcpListener), 516

Kazalo | 581
AppendFormatO, metoda (StringBuilder), 234
Access, baza podataka, primjer, 346 (File), 496
Activator, klasa, 445 (Filelnfo), 497
ActiveX, kontrole Application, klasa, DoEvcntsO metoda iz, 324
stvaranje, 549 apstraktne klase, 109, 575
uvoenje, 548 ogranienja, 112
u .NET, 552 primjer, 109-110
Add(), metoda, 386, 418 suelja u usporedbi s, 136, 152
(Dictionary), 217 apstraktne metode, 109
(List), 201 primjer, 109-110
AddRangeO, metoda (List), 201 ArgumentException, 261
ADO.NET argumenti, 68
objektni model, 341-343 dogaaja, 359
DataAdapter, 342 ArithmeticException, 253
DataReader, 343 aritmetiki operatori, 52
DataRelations, 342 as operator, 150-151, 575
DataTables i DataColumns, 342 is operator u usporedbi s, 151
DBCommand i DBConnection, 342
asinkroni U/I, 491, 510, 516
osnovne klase, 341 ASP kontrole, 367
Rows kolekcija, Data! able, 342 ASP.NET, 356-388
OLE DB Managed Provider, 346-348 C# programiranje i, 356
poetak rada s, 343-346 DataGri u, implementiranje (primjer),
povezivanje podataka, kontrole za, 349-353
404 -406
podeavanje DataSet, 353-355 ^ datoteke s kodom, 362-364
programsko popunjavanje mree
kontrole, dodavanje na Web Forms obrasce,
podataka, 352-353 365-367
pregled, 337 metode za obradu dogaaja, 359
adrcsa-od (Sr), operator, 570 posluiteljske kontrole, 365
Amazon Web usluge posluiteljske kontrole, 367
klijentska aplikacija (primjer), 383 povezivanje podataka, kontrole za, 367-377
skup alata za programere, 391 stanje posluiteljskih kontrola, 359
Amazon.com, 389 stranice s pozadinskim kodom u inaici
analiza Web stranica, 536
2 .0 ,3 5 8
analiza Web stranica, aplikacija za, 390 pregled, 357-361
analizator stranice (regularni izrazi), 233 stvaranje, 361-364
anonimne metode, 6, 293 Web kontrole, 358
aplikacije Web stranice, stvaranje (primjer), 399
klijent Web usluga ivotni ciklus, 360
izrada, 389 ASP.NET 1.1, napomena za programere
pretraivanje prema kategoriji, 408
aspx.es datoteka, 377
prikaz rezultata, 399 pozadinski kod, model, 362
konzolne, 12 aspabel, kontrola, 402
sastavljanje u cjelinu, 389 ,aspx, nastavak imena datoteke, 361-362
uvoenje ActiveX kontrola i LOM, pohranjivanje stranica korisnikog suelja
komponenata u, 548
u, 358
Windows (pogledajte Windows Forms)
Assembly, cilj atributa, 432
aplikacijske domene, 448 Assenrbly.Load(), statika metoda, 440
dogaaji, 450 AssemblyInfo.cs, datoteka, 418
dretve u usporedbi s, 451 AssemblyLoad, dogaaj, 450
konteksti, 459 AssemblyResolve, dogaaj, 450
metode i svojstva, 450 AssemblyResolver, uitavanje sklopova s
primjer, 455-458 pomou, 422
rasporeivanje objekata preko granica, 453
Atributes, svojstvo
stvaranje i upotreba, 451
(Directorylnfo), 492
AppDomain klasa, 450 (Filelnfo), 496
CreateDomainO, metoda, 451 atributi, 7, 431-447
append, argument, 508
ciljevi, 432
Append(), metoda (StringBuilder), 233
definirani, 431
prilagoeni, 433 destruktori, 81
deklariranje, 434 enum tipovi, 35
imenovanje, 434 gcnerici (C# ) u usporedbi s C++
konstruiranje, 434 predlocima, 193
primjer, 43.5 imenski prostori, 12
upotreba, 435 indekseri, 185
primjena, 433 iskaz case, 44
Autoeomplete, svojstvo tehnologije iznimke, izbacivanje, 246
Intellisense, 15 kljuna rije implicit, 123
AutoPostBack, svojstvo, 359 konstruktor za kopiranje, 75
autorska Weh stranica, 389 logiki vrijednosni tip, 25
AWSProuctData, objekt, 397 metoda Main(), 10
Axlmp, naredbeni usluni program, 548, 553 nasljeivanje, 113
auriranje baze podataka (klijent Web usluge), nepridruene varijable, 30
399 nizovi, 220, 221
pretprocesori, 61
preoptereivanja operatora, logiki parovi,
B 122
base, kljuna rije, 575 privatno ili zatieno nasljeivanje, 102
baze podataka referentni parametri, 90
definirane, 337 referentni tipovi, 24
relacijske, 337-341 strukture, 129
13eginRead(), metoda, 5 1 1 toka zarez, 10, 65
(Stream), 503, 510 uvjetni izrazi, 38
BeginWrite(), metoda (Stream), 503, 510 virtualne metode, premoivanje, 106
Berkeley suelje pristupne toke, 516 C#
beskonane petlje, 521 definicije klase, 7
bezuvjetno grananje, 37, 38 jezik
biblioteke tipova, 558 ASP.NET i, 356
bijeli prostor, 36, 50 osnove, 23-63
binarne datoteke, 503-505 pregled, 6
binarni formater, 453 kljune rijei, 575-580
binarni operatori, 121 lock, iskaz, 480
binarno itanje datoteke, 503 naredbeni prevoditelj, prevoenje Hello
BinaryFormatter, 537 World programa s pomou, 19
BinaryReader, klasa, 503 vrlo tipizirane klase kolekcija, 24
BinarySeareh(), metoda Web Forms obrasci, upotreba, 357
(List), 201 Capacity, svojstvo (List), 201, 203
(System.Array), 167 CaptureCollection, klasa, 242-244
BinaryWriter, klasa, 503 case, iskaz, 575
BindingFlags, parametar, 444 napomena za C i C++ programere, 44
bool, tipovi, 575 napomena za VB6 programere, 44
Boolean izrazi, 38 catch, iskaz, 248-253, 575
break, iskaz, 38, 50-52, 575 namjenski iskaz catch, stvaranje, 251-253
upotreba sa svvitch iskazima, 42 odmotavanje stoga poziva, 250
brisanje, operator za, napomena za C i C++ - postupak korektivnih akcija, 249
programere, 26 ChannelServices, klasa, 463
brisanje datoteka, 500 char, tip, 27, 575
brzi razvoj aplikacija, 356 Chars, polje
BufferedStream, klasa, 503, 505 (String), 224
byte, tip, 575 (StringBuilder), 233
CharSet, parametar, 568
c checked, operator, 575
ciljevi atributa, 432
C i C++ programeri, napomena za cjelobrojne vrijednosti
apstraktne klase, ogranienja u C# , 112 dijeljenje, 52
binarni operatori, 121 pretvaranje razlomaka u/iz, 126
brisanje, operator za, 26 veliine (short, int ili long), 26

K azalo ] 583
Class, cilj atributa, 432 ContainsKey(), metoda (Dictionary), 218
class, kljuna rije, 10 ContainsValue(), metoda (Dictionary), 218
Clear, obrada pritisaka 11a gumb (primjer), 320 ContextBoundObject, 461
C lear(), metoda continue, iskaz, 38, 50-52, 576
(Dictionary), 2)7 C.ontrol, klasa
(List), 201 apstraktna, stvaranje, 112
(Queue), 212 Dra\vWindow(), metoda, oznaavanje
(Stack), 214 polimorlizmom, 103
(System.Array), 167 stvaranje polja Control objekata, 103
Clone(), metoda, 221, 228 Copy, impiementiranje dogaaja gumba
(Stack), 214 (primjer), 321-324
(String), 222 sortiranje popisa odabranih datoteka, 322-324
suelje ICloneable, 73 uzimanje odabranih datoteka, 321
Close(), metoda, 83 Copy(), metoda
CLR (Common Language Runtime), 4, 5 (File), 496
asinkroni U/l, 510 (String), 223, 227
dijeljeni sklopovi i, 425 (Systein.Array), 167
glavna ili prva metoda klase, 10 CopyTo(), metoda
podrka za dretve, 471 (Filelnfo), 497, 500
pokretanje statikih konstruktora, 78 (List), 202
rasporeivanje po referenci i, 454 (Queue), 212
serijaliziranje objekata, 536 (Stack), 215, 217
CLS (Common Language Specification), 4 (String), 224
Collections, imenski prostor, 13 C o s(), metoda, 445
Columns, kolekcija (DataTable), 342 dinamiko povezivanje, 445
COM (Component Object Model) Count, svojstvo
programiranje, 548-574 (Dictionary), 217
uvoenje COM komponenata, 556 (List), 201
biblioteka tipova u .NET, 558 (Queue), 212
COM DLL-a u .NET, 558 (Stack), 214
kasno povezivanje i refleksija, 562 CreateO, metoda
testni program, stvaranje, 559 (Directorylnfo), 493
commandString, parametar, 343 (File), 496
Common Language Runtime (pogledajte CLR) (Filelnfo), 497
Common Language Specification (CLS), 4 (WebRequest), 534
Common Type System (CTS), 4 CreateChildControls(), metoda, 360-361
Compare(), metoda (String), 223, 226, 227, CreateComlnstanceFrom(), metoda
271 (Activator), 445
CompareOrdinal(), metoda (String), 223 CreateDirectory(), metoda (Directory), 492
(String), 224 CreateDomain(), metoda
CompareTo(), metoda, 200, 203, 207 (AppDomain), 450, 451
prilagoena inaica (primjer), 208 CreateFile(), metoda, 571
Component Object Model (pogledajte COM) Createlnstance(), metoda, 179, 451, 453
Concat(), metoda (String), 223, 227 (System.Array), 167
Configuration, imenski prostor, 13 Createlnstance(), metoda (Activator), 445
connectionString, parametar, 343 CreatelnstanceFrom(), metoda
Console, klasa (Activator), 445
W rite(), metoda, 49, 573 CreateSubDirectory(), metoda (Directorylnfo),
W riteLine(), metoda, 28, 509 4 9 3 ,4 9 9
Console, objekt CreateText(), metoda (File), 496
ispis teksta na monitor, 12 CreationTime, svojstvo
operator toka i, 13 (Directorylnfo), 492
const, kljuna rije, 576 (Filelnfo), 496
Constructor, cilj atributa, 432 CTS (Common Type System), 4
(List), 201 CurrentDomain, svojstvo (AppDomain), 450
(Queue), 212
(Stack), 214

584 | Kazalo
klase ThreaStart, 472
arobnjak Data Sovirce Configurator, 349 koje zahtijeva BeginReaj), 510
lanovi instance, 77 metode instance i, 276
lanovi klase, 65 pozivanje metoda delegata (primjer),
lanovi instance ili statiki lanovi, 77 272-275
statiki, 276
vieodredini, 278-281
D vieoredini, dohvat vrijednosti iz, 295-302
Data, imenski prostor, 13 asinkrono pozivanje dogaaja, 299-302
DataAdapter, objekt, 342 metode pozivanja, 299-302
DataColumn, objekti, 342 zadavanje tijekom izvedbe, 267-275
DataColumnCollection, objekti, 342 delegirane metode, runo pozivanje (primjer),
DataGrid 297-299
implementiranje (primjer aplikacije) Deletc, obrada dogaaja gumba (primjer),
404-406 324-334
ltem_Bound, metoda, 407 DeleteO, metoda
programsko popunjavanje, 352-353 (Directorylnfo), 493
DataReader, objekti, 343 (File), 496
DataRelation, objekti, 341 (Filelnfo), 497
DataRotv, objekti, 338 Dequeue(), metoda (Queue), 212
DataSet, klasa, 338, 341 Deserialize(), metoda (SoapFormatter), 470
podeavanje, 353-355 deserijalizacija, 538, 542
stvaranje DataSet, 343 primjer, 539
svojstvo Relations, 342 destruktori, 81-83
DataTable, objekti, 338, 342 dispose u usporedbi s, 82
kolekcija Rows, 342 koje ne podravaju strukture, 129
DataTableCollection, 341 deva zapis (nain imenovanja), 13, 35
datoteke, 492-502 digitalni potpisi, 426
binarne, itanje, 503-505 dijeljeni resursi, simuliranje, 480
mijenjanje, 499-502 dijeljeni sklopovi, 424
rad s, 496-499 DLL pakao i, 425
klasa Filelnfo, 496-497 inaice, 425
metode klase File, 496 izgradnja, 427
tekstualne, itanje i pisanje, 507 stvaranje jakog imena, 428
DBCommand, objekt, 342 ostali potrebni sklopovi, 430
DBConnection, objekt, 342 s vie modula, 416
decimalni tip podataka, 26, 576 dijeljenje (/), operator, 52
napomena za Java programere, 25 Dim i New, kljune rijei (VB6), 70
Declarative Referential lntegrity (DRI), 339 dinamiki nizovi, 233-234
Decrement(), metoda (lnterlocked), 483 ogranienja graninika, 234
default, kljuna rije, 576 dinamiko povezivanje, 452
DefineDynamicAssembly(), metoda dirCounter, varijabla, 493
(AppDomain), 450 Directory, klasa, 492, 503
deklarativni jezici, 339 metode, 492
deklarativno privrivanje, 572 Directory, svojstvo (Filelnfo), 496
deklarativno Web programiranje, 357 DirectoryInfo, klasa, 492, 501
Delegate, cilj atributa, 432 GetFiles(), metoda, 496
delegate, kljuna rije, 267, 576 metode, 492
(pogledajte takoer dogaaji) stvaranje instance, 493-495, 499
delegati, 7, 267-281 Directorylnfo, objekti, 314
anonimne metode, koritenje, 294 dirSub.Attributes, svojstvo, 315
dogaaji i, 282-290 Dispose(), metoda, 82, 361
implementiranje dogaaja s delegatima pozvana metodom CIose(), 83
286-289 pozvana s pomou iskaza, 38-84
rjeavanje problema s delegatima s Distributed interNet Applications (DNA),
pomou dogaaja, 289 arhitektura, 4
kao svojstva, 277 Div(), metoda, 379
DivideByZeroException, 253

K azalo | 5 85
spajanje, 475
dizajniranje aplikacija, 389
stanje natjecanja, 489-490
DLL datoteke zastoj, 489-490
COM komponente, uvaenje, 556
dijeljeni sklopovi i, 425 DRI (Declarative Referential lntegrity), 339
pozivanje funkcija s pomou P/Invoke, 567 duboka kopija, 75
dvodimenzionalna polja
sklopovi i, 414
deklariranje, 174
sklopovi s vie modula i, 416
DNA (Distributed interNet Applications), inicijali ziranje, 175-176
arhitektura, 4 pravokutno polje (primjer), 174
zupasto polje cjelobrojnih vrijednosti,
do, iskaz, 37, 576
177-179
do-whi!e, petlje, 47
dobro poznati posltiiteljski objekti, 461
registriranje, 463 E
automatsko, 54
eksplicitna implementacija suelja, 156-165
indekseri i, 188
pristup zapeaenim klasama i
redoslijed operatora, 59
s vrijednosnim tipovima, 160-165
DoEvents(), metoda (Application), 324
sakrivanje lana, 159
(pogledajte takoer delegati)
dogaaji, 281-294 selektivno izlaganje metoda, 159
eksplicitno pretvaranje, 27, 123
aplikacijske domene, 450
konverzija izmeu enum i cjelobrojnog tipa,
asinkrono pozivanje, 299 35
delegati i, 282-290
implementiranje dogaaja s pomou tipovi za raspakiravanje, 115
delegata, 286-289 element predloka stupca, 402
rjeavanje problema delegata, 289 elementi polja, 168
Delete, metoda za obradu dogaaja else, iskaz, 576
(primjer), 324-334 Emacs, ureivanje programa s, 16
dodavanje Web Forms obrascima, 372-377 Empty, polje (String), 223
event, upotreba kljune rijei s, 290-294 EndRead(), metoda, 512
EndsWith(), metoda (String), 224, 228
implementiranje dogaaja gumba Copy
Enqueue(), metoda (Queue), 212
(primjer), 321-324
klijenta Web usluge (primjer), 396 Enter(), metoda (Monitor), 485
EntryPoint, parametar, 568
objavljivanje i pretplaivanje na, 281
Enum, ciljevi atributa, 432
obrada dogaaja kontrole TreeView
(primjer), 317-320 enum, kljuna rije, 33
OnRorvDataBound, 402 enumeracije, 31, 33-35
deklariranje, 33
RorvDataBound, 407
enum, iskaz, 33
Web Forms obrasca, 358
povratni u usporedbi s nepovratnim, 359 enum tipovi, 576
pretvaranje izmeu enum i cjelobrojnog
doseg, 13
sklopovi kao granica za sadrane tipove, tipa, 35
enumeratori, popis, 33
415
varijable u petlji, 49 Environment, klasa, 314
Equals(), metoda, 113, 200
double, tip, 26, 576
kljuni objekt rjenika, 218
DrawWindow(), metoda, 22
premoivanje virtualne, 122
klase Control, oznaivanje kao virtualne,
(String), 223, 228
103
pozivanje polja objekata Control, 103 Event, cilj atributa, 432
event, kljuna rije, 576
dretve, 472-479
aplikacijske domene u usporedbi s, 451 EventArgs, klasa, 282-283
ExactSpelling, parametar, 568
pokretanje, 472-475
EXE (izvedbena datoteka)
prekidanje, 476
J1T kompilacija i, 19
sinkronizacija, 480-489
sklopovi i, 414
stanje natjecanja, 489-490
sklopovi s vie modula i, 416
upotreba lnterlocked, 482-484
upotreba lokota, 484-485 ExecuteAssembly(), metoda
(AppDomain), 450
upotreba monitora, 485-489
zastoj, 490
!

Exists(), metoda
formatirani nizovi, dodavanje, 235
(File), 496
1raction, definiranje konverzija i operatora za
(List), 202
klasu, 123-128
Exists, svojstvo Framervork Class Libra ry (FCL), 4
(DirectoryInfo), 492 imenski prostori i, 12
(Filelnfo), 496
klase Web Forms obrazaca, 358
E x it(), metoda (Monitor), 486, 488
FriendlyName, svojstvo (AppDoinain), 450
Extension, svojstvo FullName, svojstvo
(Directorylnfo), 316, 493
F (Filelnfo), 496
funkcije, 10
false, kljuna rije, 576 funkcije lanice, 10
FCL (Framework Class Library), 4
imenski prostori i, 12
klase Web Forms obrazaca, 358 G
Field, cilj atributa, 432
GAC (Global Assembly Cache), 425, 427
File, klasa, 492, 503 generalizacija, 99-101
metode, 496 generici, 6
OpenRead() i OpenWrite(), 503 klasa List
FileAttributes, klasa, 315
IComparable, iinplementiranje, 204-207
FileCopier, aplikacija (primjer), 310-334 IComparer, implementiranje, 207-212
implementira nje dogaaja gumba Copv, ogranienja upotrebe, 196-201
321- 324
suelje lEnumerable, 193-196
sortiranje popisa odabranih datoteka get, kljuna rije, 577
322- 324 get, pristupnik, 95
uzimanje odabranih datoteka, 321 get(), metoda i indekseri, 187-188
Delete, metoda za obradu dogaaja GetAttributes(), metoda (File), 496
324-334
GetCheckedFiles(), metoda, 321
osnovni obrazac korisnikog suelja, GetCreationTimesO, metoda
stvaranje, 312 (Directory), 492
TreeVie\v, obrada dogaaja, 317-320 (File), 496
TreeVierv, popunjavanje kontrole, 313-317 GetCurrentThreadlD(), metoda
Filelnfo, klasa, 503 (AppDomain), 450
CopyTo(), metoda, 500 GetData(), metoda (AppDomain), 450
metode i svojstva, 496-497 GetDirectories(), metoda
Filelnfo, objekti, 316 (Directory), 492
FileStream, klasa, 503 (DirectoryInfo), 314, 493
FileSystemlnfo, klasa, 492 GetEnumerator(), metoda
F ill(), metoda (DataAdapter), 342 (Dictionary), 218
FillDirectoryTree(), metoda, 313, 315 (lEnumerable), 194
FilterName, polje (Type), 443-444 (List), 202
Finalize(), metoda, 113 (Queue), 212
finally, iskaz, 253-255, 576 (Stack), 214
finalna klasa (Java), 112 (System.Array), 167
Fin d(), metoda (List), 202 GetFileList(), metoda, 321
FindAU(), metoda (List), 202 GetFilesO, metoda
FindMembers(), metoda (Type), 443 (Directory), 492
fixed, iskaz, 574
(Directorylnfo), 316, 493, 496
fixed, kljuna rije, 572
GetFileSystemlnfos(), metoda (DirectoryInfo)
float, tip podataka, 26, 577 493
Flush(), metoda (Stream), 503 GetHashCode(), metoda, 113, 218
for, petlje, 37, 48-50, 577
GetLastAccessTime(), metoda (File), 496
foreach, iskaz, 37, 50-52, 171-184, 441, 577 GetLastWriteTime(), metoda (File), 496
upotreba s lEnumerable, 195-196, 221 GetLength(), metoda (System.Array), 167
Form at(), metoda (String), 224 GetLocalDrives(), metoda
formateri, 449, 453, 470 (Directory), 492
podrazumijevani, 463 (Environment), 314
upotreba za serijaliziranje podataka, 537

K azala | 587
GetLowerBound(), metoda stvaranje tri GridVietv kontrole, 402
(System.Array), 167 tokovi podataka, itanje Web stranice kao,
GetMembers(), metoda (Type), 442 534
GetMethod(), 446 WSDL ugovor, pregled, 382
GetMethods(), metoda (Typc), 443 HTTP sesije, 359
GetO bjecr(), metoda (Activator), 445 HTTPChannel, tip, 463
GetO bjectData(), metoda (Dictionary), 218 HttpWebRequest, 534
GetParent(), metoda (Directory), 492 HttpWebResponse, objekt, 534
GetParentS'tring(), metoda, 32 0 -3 2 L
GetRange(), metoda (List), 202
GetResponse(), metoda, 534 I
GetResponseStream(), metoda I, operator (&&r), 57, 59
(NVebResponse), 534 IAsyncResult, suelje, 511
GetString(), metoda, 573 ICalc, suelje, 462
GetSubDirectoryNodes(), metoda, 315-316 ICloneable, suelje, 75
(Type), 445 ICloneable objekti, nizovi i, 221
GetType(), metoda, 113, 442 ICollection, suelje, 193
GetUpperBound(), metoda (System.Array), [Comparable, suelje, 193, 196-201
167 implementiranje, 204-207
.gif datoteke, 414 nizovi i, 221
Global Assembly Cache (GAC), 425, 427 IComparer, suelje, 193,322
globalne metode, 77 implementiranje, 207-212
gomila ogranienja, 212
alokacija elemenata polja, 168 lConvertible, klase, 222
definirana, 26 IDataReader, suelje, 338
goto, iskaz, 38, 45, 577 IDE (Integrated Development Enviroment), 16
iskaz switch, upotreba s, 42, 44 identifikatori, 35
grafiko korisniko suelje (GUI) definiranje, 60
alati za izradu, 12 ponitavanje definicije, 61
grananje, kljune rijei za IDeserializationCallback, suelje, 542
bezuvjetno grananje, 38 lDictionary, suelje, 193, 218
uvjetno grananje, 38 IDisposable, suelje, 82
GridLayout reim, dodavanje kontrola na Web IDL (Interface Definition Language), 7
Forms obrasce, 365 lEnumerable, suelje, 193-196, 214
GridViews, stvaranje, 402 nizovi i, 221
Group, klasa (Regex), 239-242 IEnumerator, suelje, 193, 214
GUI (grafiko korisniko suelje) alati za if, iskaz, 577
izradu, 12 ugnijeeni, 40-42
gumbi iskaz switch kao zamjena, 42
povezivanje podataka i, 367 if...else, iskaz, 38
IFormatter, suelje, 537
lLDasm, alat, 415
H ILI, operator (|), 59
Hejlsberg, Anders, 7 ILI, operator (j|), 57, 59
Hello World, program, 9-22 IList, suelje, 193
klase, objekti i tipovi, 9-15 imenovanje, naini, 15
prevoenje i pokretanje, 18-19 ,,deva i Pascalov zapis, 35
razvoj, 16 maarski zapis, 35
ureivanje, 16 imenski prostori, 12-13
uzorak koda, 9 ispisivanje cijelih, 15
HelpLink, svojstvo (Exception), 255 napomena za C++ programere, 12
HTML napomena za Java programere, 12
dodavanje Web Forms obrascima, 363 stvaranje u Visual Studiju .NET, 17
kontrole, dodavanje Web Forms obrascima, System.Text.RegularExpressions, 236
365 u primjerima koda, 29
posluiteljske kontrole, 367 XM L, za WSDL dokumente, 380

588 | Kazalo
IMessage, suelje, 483
nnplicit, operator, 577 IsBackground, svojstvo (Thread) 477
implicitno pretvaranje, 27 123 lsFixedSize, svojstvo (System.Array), 167
pakiranje, 115 iskazi, 37,60-63
in, iskaz, 37 bezuvjetno grananje, 38
in, operator, 577 bijeli prostor u, 36
inacica, broj za dijeljene sklopove 425 iteracija, 45-52
(pogledajte takoer sklopovi) continueibreak, 50-52
inaica, praenje, 414 do-while, petlja, 47
apstraktne klase u usporedbi sa sueljima, for, petlja, 48-50
foreach, 50
goto, 45
s pomou kljunih rijei new i override, 107
Increment(), metoda (Interlocked) 483 while, petlja, 46
indekser, deklariranje svojstva, 184 izrazi, 35
indekseri, 184-192 uvjetno grananje, 38
definirani, 184 if...else, 38
dodjeljivanje i, 188 svvitch, 42-45
get(), metoda i, 187-188 ugnijeeni if iskaz, 40-42
iskazi, blokovi, 40
preoptereivanje indeksera, 189-192
ser(), metoda i, 187-188 IsReadOnly, svojstvo (System.Array), 167
sintaksa, 187 s5ynchromzed, svojstvo (System.Array), 167
tliis, kljuna rije, 184 tem, element za klase kolekcije 202
IndexOf(), metoda Item, objekt, 397, 413
(List), 202 Item, svojstvo(IDictionary), 218
(String), 229 Item(), metoda
(System.Array), 167 (Dictionary), 217
inicijalizatori, 73-75 (List), 201
ImtializeO, metoda (System.Array), 167 ItemLookup, objekt, 397
lnnerException, svojstvo (Exception), 261 ItemLookupRequest, objekt, 397
JnputMream, klasa, metoda Readf) 503 ItemSearch, objekt, 413
lnsert(), metoda ItemSearchRequest, objekt, 413
(List), 202 ItemSearchResponse, objekt, 413
(String), 224, 229 Item_Bound, metoda (DataGrid), 407
(StringBuilder), 233 iteracija, iskazi za, 37 , 45-52
lnsertRange(), metoda (List), 202 continue i break, 50-52
instance, 9 do-while, petlja, 47
for, petlja, 48-50
brojanje s pomou statikih polja, 80
razlika izmeu klase i, 65 foreach, 50
instanceof (Java), 147 goto, 45
int, tip, 26, 578 while, petlja, 46
izdavai, 281
Intellisense, svojstvo Autocomplete 15
Interface, cilj atributa, 432 izlaz na zaslon, ispisivanje 28
interface, kljuna rije, 137 iznimke, 245-265
Interface Definition Language (1DL), 7 definirane, 245
Interlock, klasa, 480 Exception, objekti, 255-258
Interlocked, klasa, 482-483 izbacivanje i hvatanje, 246 -255
internal, kljuna rije, 107 catch, iskaz, 248-253
interna!, modifikator pristupa, 68, 577 finally, iskaz, 253-255
internal protected, kljuna rije, 107 throw, iskaz, 246-248
InternalErrorException, 261 metode za obradu iznimaka, definirane, 245
Internet, pozivanje Web usluga preko, 377 ponovno izbacivanje, 261-265
Internet Information Server(IlS) 450 prilagoene, 258-261
lnterruptf), metoda (Thread) 476 izolirano spremite, 5 44
IP adrese, 515 itanje iz, 545
is, operator, 147-150, 577 pisanje u, 545
u usporedbi s operatorom as, 151 izravan pristup memoriji, 8
izravna razmjena datoteka, 515

K azalo | 589
izrazi, 35, 48 definiranje, 65-70
Boolean, 38 argumenti metode, 68-70
regularni (pogledajte regularni izrazi) modifikatori pristupa, 67
uvjetni, 59 Time, klasa (primjer), 66
izvedene klase, 102, 113 definiranje tipova, 10
apstraktne klase kao osnovna klasa, 109 implementiranje vie suelja, 140, 157
premoivanje virtualne metode osnovne implemetiranje suelja, 153
klase, 108 instanca klase u usporedbi s, 65
izvoenje .NET lcomponenara, 564 ja v n e ,107
klasa Object kao korijenska klasa, 113
kolekcije unutar, pristup {pogledajte
J indekseri)
jaka imena za sklopove, 426 metode, 10
java, napomena za programere odnos izmeu, UML dijagrami, 100
decimalni tip, 25 pregled, 64
imenski prostori, 12 razlika izmeu struktura i, 129, 131
konstante lanice, 136 refleksija, 439
M ain(), metoda, 10 statike, 79
pravokutna polja, 174 stvaranje i imenovanje u Visual Studiju
referentni parametri, 85 .NET, 17
statike metode, pozivanje , 78 ugnjeivanje, 117
statiki konstruktori, 79 zapeaene,112
ugnijeene klase, 118 klijenti
zapeaena klasa, 112 mrenog toka podataka, stvaranje, 519
javna svojstva rad na daljinu, 469-470
AppDomain klase, 450 izgradnja, 465
Filelnfo klasa i, 496-497 za asinkrone mrene tokove, 521
javne statike metode, prekidanje dretvi, 476 klijentska podrka, .NET Web uslugama, 378
javni kljuevi, 426 klijentski aktivirani posluiteljski objekti, 461
oznake za, 428 klju i vrijednost, veze izmeu, 217-219
javni pristup, modifikator za, 68 ,107 , 578 referentni tip kao klju, 219
jedan modul, sklop s, 416 kljune rijei u C# , 575-580
jedan poziv, objekti s, 461 kod
jednakost, operator (==), 56 odvajanje korisnikog suelja od, 358
operator pridruivanja (=) u usporedbi s, 41 podruje, saimanje u Visual Studiju, 62
premoivanje, 122 ponovna upotreba, 102
jednostavni tipovi smjernice za stil (Microsoft), 15
popis, 25 kolekcije
JIT (Just In Time), prevoditelj, 6, 19 klasa List, 201-212
Jo in (), metoda implementiranje iComparable, 204-207
(String), 224 implementiranje lComparer, 207-212
(Thread), 490 klase, 166
klase vrlo tipiziranih, 24
MatchCollection, 238-239
K redovi, 212-214
kanali, 449, 453 rjenici, 217-219
registriranje na klijentu, 465 stogovi, 214-217
stvaranje, 463 suelja, 193-196
kasno povezivanje, 107, 445, 558 IComparable, 196-201
refleksija i, 562 lEnumerable, 193-196
KeepAlive, zastavica, 476 ureivanje Listltems, 365
Keys, svojstvo (Dictionary), 217 komentari, 11
KeywordRequest, objekt, 412 u stilu jezika C (/*... 7 ), 11
klasa, lanovi, 65 u vie redova, 11
lanovi instance ili statiki lanovi, 77 XM L, 334-336
klase za dokumentaciju, 334-336
apstraktne (pogledajte apstraktne klase)
C# , podrka za definiranje i rad s, 7

590 \ Kazalo
komponentno orijcntii ' i i no programiranje, 7
konstante, 31 L
enumeracije kao zamjena za, 3.3 LastAccessTime, svojstvo
enumerirane, 33 (Direaorylnfo), 493
inieijaliziranje, 32 (Filelnfo), 496
ponovno inieijaliziranje za vrijeme LascindexOf(), metoda
prevoenja, 32 (List), 202
n simboline, 31 (String), 224
konsrrukrorza kopiranje, 75 (System.Array), 167
konstruktori, 70-72 LastlVritelime, svojstvo
i deklariranje, 71 (Directorylnfo), 493
osnovne klase, pozivanje, 106 (Filelnfo), 497
podrazumijevani, 107 Length, polje
preoptereeni, definirani, 73 (String), 228
preoptereivanje, 90-93 (StringButlder), 233
statiki, 78 Length, svojstvo
strukture, 131 (Capture), 242
konteksti, 448, 459 (Filelnfo), 497
kontekstno povezani i okretni objekti, 459 (String), 228
rasporeivanje preko granica, 459 (Systeni.Arrny), 167, 169
kontekstno vezani objekti, 459 Listltem Collection Editor, 365
kontrole literati, 31
ActiveX literali (regularni izraz), 235
stvaranje, 549 literati niza, 222, 226
u .NET-u, 552 Directory info, objekt, stvaranje, 493
uvoenje, 548 oznaeni simbolom 239
aspdabel, kontrola, 402 Load, dogaaj, 360
$ dodavanje na Web Forms obrasce. 365-367 Load(), metoda
posluiteljske, 367 (AppDomain), 450
I (Assembly), 440
povezane s podacima, ADO.NET, 349-355
DataGri, programsko popunjavanje, LoadPostData(), metoda, 360
352-353 LoadViewSrate(), metoda, 360
podeavanje DataSet, 353-355 lock, iskaz, 577
povezivanje podataka s, 367-377 lock, kljuna rije, 484
TreeVie\v logiko ekskluzivno ILI, operator H , 59
obrada dogaaja, 317-320 logiko ILI (|), operator, 59
popunjavanje, 313-317 logiki operatori, 57
Web, 357 logiki vrijednosni tip, napomena za C i C++
posluiteljske, 363 programere, 25
uobiajene, 27 lokalne varijable, prikaz vrijednosti u programu
konzola za uklanjanje pogreaka, 21
aplikacije, 12 lokoti, sinkroniziranje dretvi, 480, 484-485
ispisivanje na long, tip, 26, 577
asinkroni posluitelj za mrene tokove
podataka, 521 M
klijent mrenog toka podataka, 519
korijenska klasa, 113 maarski zapis, 35
korisnika konfiguracija, informacije o, 544 M ain(), metoda, 10, 78
korisniki definirani tipovi, 23 asinkroni U/I i, 511
serijalizacija, 536 izgradnja posluitelja koji koristi, 462
korisniko suelje, 358 konzolne aplikacije i, 12
kraj reda (napomena za Visual Basic napomena za C++ programere, 10
programere), 37 napomena za Java programere, 11
kultura, 430 SingleCall, upotreba, 467
static kljuna rije i, 16
zavrne toke i, 469-470

K azalo | 591
makefile datoteka za sklop s vie modula, 418 statike, 15, 77
manifesti, 415 pozivanje, 77-78
dijeljeni sklop (primjer), 428 pristup statikim poljima, 81
sklopa svie modula (primjer), 421 suelja (p o g le d a jt e suelja)
manje ili jednako (<=), operator, 56 virtualne (p o g le d a jt e metode, polimorfne)
manje od, operator (<), 56 zadavanje tijekom izvedbe, 267-275
mape, 492 metode instance, delegati i, 276
Directorylnfo, objekt, stvaranje, 493-495 metode za obradu dogaaja, 359
rekurzija kroz.podmape, 493-495 definirane, 282
rad s, 492 Microsoft
rekurzija kroz podnrape, 315 naini imenovanja, 35
irenje, 318 smjernice za pisanje koda, 15
MarshaK ), metoda (RemotingServices), 467, Microsoft SQL Server Desktop Engine
469-470 (MSDE), 343
MarshalByRefObject, 454, 462 mjeavine, 137
Match, klasa, 238-239, 242 mnoenje ('), operator za, 52
kolekcije Grotips, 239 modifikatori pristupa, 67, 107
MatchCollection, klasa, 238-239 metode suelja i, 139
matematiki operatori, 52-54 statiki konstruktori i, 79
Matli klasa, C o s(), metoda, 445 svojstava, 96
meuspremnik, 503 modulo, operator (%), 52
privren u memoriji, 572 Module, cilj atributa, 432
MemberFilter, parametar, 443-444 moduli, 414
MemberTypes, parametar, 443-444 mogunosti, klase, 137
MemberwiseClone(), metoda, 113 Monitor, klasa, 480
memorija, izravan pristup s pomou monitori, sinkroniziranje dretvi, 485-489
pokazivaa, 8 Monitor, objekt (primjer), 486-489
MemoryStream, klasa, 503 Mono, prevoditelj, 19
Message, svojstvo (Exception), 255 Move(), metoda
MessageBox, statika Show() metoda, 324 (Directory), 492
(pogledajte takoer atributi; refleksija) (File), 496
metapodaci, 7, 414 MoveFile(), metoda, 568
datoteke Assemblylnfo.cs, 418 MoveFirst(), metoda, 338
definirani, 431 MoveTo(), metoda
refleksija i, 438 (Directorylnfo), 493
metaznakovi (regularni izraz), 235 (Filelnfo), 497
Method, cilj atributa, 432 P/lnvoke i, 568
Methodlnfo, objekt, 446 mscorlib, sklop, 415
metode MSDE (Microsoft SQL Server Desktop Engine),
anonimne, 294 343
apstraktne, 109 MS1L (Microsoft lntermediate Language)
primjer, 109-110 datoteke
argumenti, 68-70 JIT prevoenje i, 19
definirane, 10 prevoenje i, 6
definiranje i deklariranje, 67 M u lt(), metoda, 379
deklariranje, 11
klase u usporedbi s globalnim, 77
klase Appdomain, 450 N
klase Object, 113 Name, svojstvo
modifikatori pristupa, 67 (DirectoryInfo), 316, 493
neizravno pozivanje, 7 (Filelnfo), 316, 497
polimorfne, 103 namespace, kljuna rije, 578
povratni pozivi, 299-302 namijenjena samo itanju, polja, 97
pozivanje s pomou operatora . (toka), 12 nasljeivanje, 102-119
preoptereivanje, 90-93 implementiranje, 102
rasporeivanje, zadavanje, 454 klasa C# , 7

refleksija i, 438 koje ne podravaju strukture, 129

592 | Kazalo
napomena za C i C++ programere, J 13 pronalaenje podnizova, 229-231
od klase Object, 113 prvoklasni tip u C# , 220
regularnih izraza, 242 rad sa, 223-229
sprjeavanje s pomou zapeaenih klasa, kopiranje nizova, 227
J 12 metode i polja klase String, 223
nasumini brojevi, 219 nastavljanje nizova, 227
navodni znaci u nizovima, 399 provjere jednakosti, 227
navodnici, nizovi pod, 222 traenje podniza, 229
NE, operator (!), 57
umetanje podniza, 229
neinicijalizirane varijable, 31 usporedba nizova, 226
neizravno pozivanje metoda, 7 stvaranje, 222
nejednaka polja, 177-179 koristei ToString(), 223
nejednakost, operator za (!=), 56 umake, program, 419
nepovezani podaci, arhitektura, 337 Nodes, svojstvo (TreeVietv), 314
nepovratni dogaaji, 359 NonSerialize, atribut, 542
.NET
normalizacija, 339
izvoz komponenata Notepad
biblioteka tipova, stvaranje, 567 ureivanje programa s, 16
komponente, izvoenje, 564 Web obrasci, stvaranje, 358
kontrole! uvoenje, 552 nuli, referenca, 578
P/Invoke (usluga za pozivanje platforme), NullReferenceException, 261
567
programiranje, 548-574
sigurno pisanje koda, informacije o, 371
uvoenje COM DLL-a u, 558 Object, klasa, 113
biblioteka tipova, 558 metode, 113
kasno povezivanje i refleksija, upotreba, nasljeivanje od, 113
562 Object, parametri, 444
probni program, stvaranje, 559 object, tip, 578
.NET kostur, 4-5 objekti
arhitektura, 5 ADO.NET (pogledajte ADO.NET, objektni
klase za U/l, 503 model)
tokovi podataka i, 491 definirani, 10
.NET platforma, 3 indenttfikatori, 452
.NET Web usluge, 357, 377 iznimaka, 255-258
izgradnja kao referentni i vrijednosni tipovi, 132
kalkulatora (primjer), 380 klasa Object kao korijen svih klasa, 112
pregledavanje WSDL saetka, 382 kontekstno povezani i okretni, 459
izrada Web usluge, 378-383 na stogu i gomili, 26
klijentska podrka, 378 proirenje u razvojnom okoliu programa za
posrednik, stvaranje, 383-388 pronalaenje pogreaka, 21
testiranje usluge, 386 rad na daljinu, 461
testiranje, 381 tipovi posluiteljskih objekata, 461
NetvvorkStream, klasa, 503, 517, 519 rasporeivanje (pogledajte rasporeivanje)
new, kljuna rije, 70, 578 serijalizacija (pogledajte serijalizacija)
instanciranje polja, 168 sinkronizacija, 460
praenje inaica s pomou, 107 sinkronizacija dretvi i, 480
stvaranje struktura, 132 stvaranje, 70-75
stvaranje struktura bez upotrebe, 133-135 inicijalizatori, 73-75
niz, literali, 226 kljuna rije this, 75
niz znakova za povezivanje, 396 konstruktori, 70-72
nizovi, 35, 221-234 suelja lCloneable, 75
definirani, 12 stvaranje s pomou metoda klase Activator,
dijeljenje, 231-233 445
dinamiki, 233-234 tokovi s meuspremnicima, 505
ogranienja graninika, 234 unitavanje, 81-84
primjena regularnih izraza na (pogledajte C# destruktori, 81
regularni izrazi) Close(), 83

Kazalo | 5 93
destruktori u usporedbi s metodom pridruivanje (=), 52
dispose, 82 smanjenje i poveanje, 54
iskaz tising, 83 ternarni (?:), 59
objektno orijentirano programiranje osnovne klase, 102
napomena za VB6 programere, 99 apstraktne klase kao, 109
klasa Object kao korijen svih klasa, 112
stvaranje novih tipova, 9
konstruktori, pozivanje, 106
ObjRef, objekt, 469-470
obrada poslanih podataka, 360 virtualne metode, premoivanje, 103
Observer (Pubiish/Subscribe), predloak, 282 osnovni tip (pozadinski tip) za eiuimeracije, 33
ostatak pri dijeljenju, 52
OCX standard, 548
odmotnvanje stoga poziva, 250 otkrivanje (tipova), 439-440
out, modifikator, 578
odnos, operatori, 56 out, parametar, 84, 87
oduzimanje (-), operator za, 52
override, kljuna rije, 103,578
odvajanje stanja klase i metode, 93
praenje inaica s pomou, 107
odvodi, 453
oznaka javnog kljua, 428
definirani, 449 oznake, 45
rasporeivanje s posrednicima, 453
stvaranje lanca posluiteljskih odvoda, 463 aspabel kontrole, 402
ogranienja, 212
veza izmeu razliitih tablica u bazama P
podataka, 339
P/Invoke (usluga za pozivanje platforme), 567
okretni objekti, 459 pozivanje Win32 API metode, 568
OLE DB upravljani izvor podataka, 346-348 padajui popisi, 66
OnDeserialization(), metoda, 542
PadLeftO, metoda (String), 224
O nLoadl), metoda, 360 PadRighc(), metoda (String), 224
OnPreRenderO, metoda, 361
OnReadComplete(), metoda, 521 pageLayout, svojstvo, 365
pakiranje tipova, 25 ,115
OnRotvDataBound, dogaaj, 402
reference suelja, 160, 163
OnWriteComplete(), metoda, 521
strukture, 132
O pen(), metoda (Filelnfo), 497
Parameter, cilj atributa, 432
OpenReadf), metoda parametri, 68
(File), 496, 503
prosljeivanje, 84-90
(Filelnfo), 497 po referenci, 85-87
otvaranje binarnih datoteka, 503
s definitivnim pridruivanjem, 87-90
OpenText(), metoda (Filelnfo), 497
OpenWrite(), metoda vraanje vrijednosti, 85
params, kljuna rije, 172-173, 187
(File), 496 upotreba sa Splir() metodom (String), 232
otvaranje binarnih datoteka, 503
(Filelnfo), 497 params, modifikator parametra, 578
Patent, svojstvo (Directorylnfo), 493
operator, kljuna rije, 118, 578
Pascalov zapis, 15, 35
operator pridruivanja (=), 52
PE (Portable Executable) datoteke, 414
operator jednakosti (==) u usporedbi s, 41
operator za pretvaranje tipova, mijenjanje Peek(), metoda
(Queue), 212
tipova s pomou, 28 (Stack), 215
operatori, 52-60
Perl 5 regexp, 236
logiki, 56
petlja, iskaz za stavljanje u, 37
matematiki, 52-54 petlja, varijable za stvaranje, napomena za VB6
odnosni, 56 programere, 49
operator as, 150-151
pisanje izlaza na zaslon, 28
operator is, 147-150
plitka kopija, 75
pokazivai, 570 podaci
prednost, 57-59
itanje i upisivanje, 502-509
preoptereivanje, 120-128
privremeni, obrada, 541
jednakost (==), operatora, 122
podmape
logiki parovi, 122
operator, upotreba kljune rijei, 120 rad sa, primjer, 500-502
rekurzija kroz, 315, 493-495
operatora za pretvaranje, 123-128
zahtijevanje popisa od trenutne podmape, 493
stvaranje korisnih operatora, 121

594 | K azalo
podnizovi, smjetanje unutar nizova, 229 zadavanje preko suelja, 461
podrka na strani posluitelja za .NET Web posluiteljske kontrole, 363
usluge, 378
dodavanje Weh Forms obrascima, 365
pogreke, definirane, 245
tipovi u Web Forms obrascima, 367
Point, klasa, 455
pokaziva, tipovi, 24 posluiiteljski objekti koji podravaju rad na
daljinu, 461
pokazivai, 8, 570
posrednici, 378
referenca this u usporedbi s, 75
klasa posrednika za klijenta Web usluga
upotreba u C# programu (primjer), 573-574 391
polimorfizam, 102-109
definicija, 99 rasporeivanje preko granica konteksta, 459
rasporeivanje s, 453
metoda Equals(), premoivanje, 122 stvaranje, 383-388
praenje inaica s pomou kljune rijei testiranje Web usluge, 386
ovem'de, 107 posrfix, operatori, 55
stvaranje polimorfnih metoda, 103 potpis metode, 90
stvaranje polimorfnih tipova, 103 potpisi (digitalni), 426
polja, 166-184
deklariranje, 168 potpisivanje sklopa, 426
poveanje i smanjenje, operatori za, 55-56
granice, 179
povezivanje, 452
inicijaliiranje elemenata, 172
kasno (pogledajte kasno povezivanje)
iteriranje krozs pomou iskaza foreach, 171 uvoenje COM DLL-a u .NET, 558
konverzija, 180-182
List klasa, 201-212 povezivanje podataka, 367-377
konfiguracija izvora podataka, 368
metoda Sorr(), 182-184
kontrole i dogaaji, dodavanje Web Forms
namijenjena samo itanju, 97
objekata, 103 obrascima, 372-377
odabir izvora podataka, 368
podrazumijevane vrijednosti, 169 pozivanje polja s kontrolom radio-gumba
primjer, 169
(primjer), 371
pristup lanovima s pomou operatora testiranje upita, 370
indeksa, 103 povratni dogaaji, 359
pristup elementima, 169 Pow(), metoda, 379, 387
sortiranje, 182-184
pozadinske dretve, 477
primjer, 205-207, 208-212
pozadinski kod, datoteke, 358,362-364
statika, 80
veliina, 201 prikaz izlaza za klijenta Web usluge
(primjer), 404-406
viedimenzionalna, 174-179 razlike u odnosu na ASP.NET 1.x, 362
inicijaliiranje dvodimenzionalnih, 175-176 pozadinski kod, stranice s, 356
pravokutna, 174-177
pozicijski parametri, za konstruiranje atributa
zupasta, 177-179 434
ponovno izbacivanje iznimaka, 261-265 RegisterWellKnownServiceType(),
Pop(), metoda (Stack), 215 pravokutna polja, 174-177
popisi
pranjenje meuspremnika, 505
List, klasa, 201-212 preaci na tipkovnici, 18
implementiranje IComparable, 204-207 predloci, C++, 6
implementiranje IComparer, 207-212 predmemorija, 491
metode i svojstva, 201-202 prednost operatora, 57-59
svojstvo Capacity, 204
pretprocesorske direktive
sortiranje popisa odabranih datoteka # define iskaz, 60
(primjer), 322-324 # elif, # else i # endif, 62
Portable Executable (PE) datoteke, 414 # region, 62
poslani podaci, obrada, 360 # undef, 61
posluitelj, kontrole (ASP.NET), 367 pretprocesor, napomena za C i C++
posluitelji
programere, 61
asinkroni posluitelj mrenog toka prefiks operatori, 54
podataka, 521 preglednici
izgradnja, 463 IP adrese i, 515
posluitelj mrenog toka podataka, Web Forms obrasci pokrenuti na, 358
stvaranje, 516
preimenovanje datoteka, 500

K azalo | 595
SingleCall objekt, upotreba, 467
prekidanje dretvi, 476-479
tipovi posluiteljskib objekata, 461
premoivanje zadavanje posluitelja s pomou suelja,
implementacije suelja, 153-156
metode klase Object, 115 462
zavrne toke, 469-470
preoptereeni konstruktor, definiran, 73
RaisePostDataChangedEvent(), metoda, 360
preoptereivanje
Rank, svojstvo (System.Array), 167
metoda i konstruktora, 90-93
rano povezivanje
operatora, 120-128
koji podravaju druge .NET jezike, 121 definirano, 558
Rapid Application Development (RAD), 6
pretprocesorski iskazi, 60-63
raspakiranje, tipovi za, 115
pretplatnici, 281 rasporeivanje, 448
pretraivanje prema kategoriji, Web usluge, 408
definirano, 448
pretvaranje tipova, 27
objekta bez poznate zavrne toke, 469-470
pretvaranje ugraenih tipova, 27 po vrijednosti, 448, 453
pretvaranje, operatori za, 123-128
po vrijednosti ili po referenci, 453
prevoditelj u odzivniku, 19
preko granica aplikacijske domene, 454
prevoenje
primjer, 455-458
MSIL datoteke i, 6
s posrednicima, 453
normalizacija i, 339 preko granica konteksta, 459
pokretanje programa Hello World, 18
razlikovanje velikih i malih slova, 15
privrivanje meuspremnitta, 572
ientifikatori, 35
prijateljsko ime, 451 usporedbe nizova, 226
prilagoene iznimke, 258-261
usporedbe nizova unutar DacaTable
prilagoeni atributi, 433
objekata, 353
primjer, 435 RCW (Runtime Class Wrapper), 558
primarni kljuevi, 338 (lnputStream), 503
podrazumijevane vrijednosti, 70-71
(Stream), 503
serijalizacija, 536
Read(), metoda, 573
PrintValues(), metoda, 214
ReadFile(), metoda, 571
pristup lanovima (->), operator za, 570
ReadLine(), metoda (klasa StreamReader i
private, kljuna rije, 107 StreamWriter), 507
private, modifikatori, 578
readonly, modifikator polja, 578
privatni kljuevi, 426 RealProxy, klasa, 453
privatni sklopovi, 424
redovi, 212-214
privremeni podaci, 542
primjer, 213-214
proceduralni jezici, 339
Queue klasa, metode i svojstva, 212
procesi ref, modifikator, 578
aplikacijske domene za, 450
ref, parametri, 84, 87
definirani, 448 reference na vrijednosne tipove, 160-165
ProcessExit, dogaaj, 450
ReferenceEquals(), metoda, 113
ProgCS, imenski prostor, 418 referentni parametri, 85
programiranje temeljeno na dogaajima, 266
propadanje do sljedeeg iskaza case, 44 referentni tipovi, 24
alokacija na gomilu, 26
Property, cilj atributa, 432
prosljeivanje parametara prema referenci, 85-87 klase kao, 129
objekti kao, 132
proirenje osnovnih klasa kostura, 6
odnosi izmeu kljua i vrijednosti i, 219
proirenje suelja, 140
Publish/Subscribe (Observer), predloak, 282 pakiranje i raspakiravanje, 115
Pulsef), metoda (Monitor), 485, 488 polja, 169
polja kao, 168
Push(), metoda (Stack), 215
refleksija, 438
definirana, 431
R kasno povezivanje i, 445, 562
rad na daljinu, 460 otkrivanje tipa, 440
izgradnja klijenta, 465 pregledavanje metapodaraka, 439
izgradnja posluitelja, 463 tipa, 442
RegisterWellKnownServiceType(), metoda, Refresh(), metoda (Directorylnfo), 493

467 Regasm, alat, izvoenje .NET komponenata, 564

596 ( Kazalo
Regex, klasa, 235-237
Split(), metoda, 237 Select Case, iskaz (VB6), 44
Serializable, atribut, 454, 461
RegistetChanneK), metoda (ChannelServices) Seria!ize(), metoda, 537
463
serijalizacija, 454, 536
RegisterWellKnovvnServiceType() metoda descrijalizacija objekta, 538
463, 467
regularni izrazi, 220, 235-237 formateri, upotreba, 537
temeljeni na Perl 5 regexp, 236 objekti, stvaranje tokova podataka, 491
obrada privremenih podataka, 542
CaptureCollection, koristei, 242-244
definirani, 235 rad s neserijaliziranim objektom, 542
rasporeivanje i, 537
literali i metaznakovi, 235
serijaliziranje i deserijaliziranje objekta
MatchCollection i Match klase, 238-239
(primjer), 538
nasljeivanje, 242
serijaliziranje objekta, 538
Regex klasa, upotrebom, 235-237
upotreba, 537
rekurzija kroz podmape, 493
relacijske baze podataka, 337-341 Service Oriented Architecture Protocol
definirane, 337 (,pogledajte SOAP)
sesije, HTTP, 359
normalizacija, 339
SQL, 339 set, pristupnik, 96, 579
tablice, zapisi i stupci, 338 se t(), metoda i indekseri, 187-188
Relations, svojstvo (DataSet), 342 SetAppDomainPo!icy(), metoda, 450
SetAttributes(), metoda (File), 496
RemotingConfiguration, klasa, 463
SetCheckO, metoda, 320
RemotingServices, klasa, MarshaK), metoda
467, 469-470 SetCreationTime(), metoda (File), 496
Remove(), metoda SetData(), metoda (AppDomain), 450
(Dictionary), 218 SetLastAccessTime(), metoda (File), 496
(List), 202 SetLastError, parametar, 568
(String), 224 SetLastWriteTime(), metoda
(File), 496
(StringBuilder), 233
RemoveAt(), metoda (List), 202 SetValue(), metoda (System,Array), 167
Shape, klasa, 451, 453
RemoveRange(), metoda (List), 202
Shared Source CL1 prevoditelj, 19
Rep!ace(), metoda (StringBuilder), 233 short, tip, 26, 579
ResourceResolve, dogaaj, 450 Show(), metoda, 313
resursi, simuliranje dijeljenih, 480
return, iskaz, 38, 578 (MessageBox), 324
ShowModal(), metoda, 313
ReturnValue, cilj atributa, 432
Reverse(), metoda, 182-184 sigurne za tipove, kolekcije (pogledajte generici)
(List), 202 sigurnosne granice, 415
sigurnost
(System.Array), 167, 182-184
rjenici, 217-219 aplikacijske domene i, 451
suelje IDictionary, 218 parametarizirani upiti, upotreba, 399
Root, svojstvo (DirectoryInfo), 493 sigurno pisanje koda u.NET-u, 371
simbolike konstante, 31
Rows, kolekcija (DataTable), 342
R u n(), metoda, 521 Simple Object Access Protocol (pogledajte
SOAP)
asinkroni U/l i, 511
SingleCall, objekti, 467
runat=server, atribut, 363
Singleton, objekti, 467
Runtime Class Wrapper (RCW), 558
sinkronizacija, 461, 480-489
Interlocked, upotreba klase, 482-484
s lokoti, upotreba, 484-485
sadrana klasa, 118 monitori, upotreba, 485-489
sakriveni bitovi, 315 stanje natjecanja, 489-490
sakriveni lanovi suelja, 159 zastoj, 489-490

sakupljanje otpada, 25 sinkronizirani U/I, 510


sizeof, operator, 579
SaveViewState(), metoda, 361
sbyte, tip, 578 sklopovi, 107, 414
sealed, modifikator, 578 definirani, 7

Searchlndex, svojstvo, 413 dijeljeni (pogledajte dijeljeni sklopovi)


dinam iko povezivanje za vrijeme StartReacK), m etoda, 521
izvoenja, 452 StartsW ich{), metoda (String), 224
m anifesti. 415 static, kljuna rije, 16, 579
m etapodaci, 414 napomena za VB 6 programere, 77
moduli, 414 statiki lanovi, 7 7 -8 1
m scorlib, 415 pozivanje statikih m etoda, 77-78
PE (Portable Exchange) datoteke, 414 statika polja, 80
privatni, 424 pristup s pom ou statikih metoda, 81
refleksija, 440 statike klase, 79
s vise modula, 416 statiki konstruktori, 78
izgradnja, 416 stil kom entara, C + + , 11
sigurnosne granice, 415 stilske sm jernice za kod, 15
skraena provjera, 57 stog poziva, 256
slanje prom jena, dogaaji, 3 60 stogovi, 214-217
S lee p (), metoda (Thread), 476 definirani, 26
sm anjenje i p o v e a n j e , operatori za, 5 5 - 5 6 o d m o t a v a l e p o z iv a , 2 5 0
sm rtonosni zagrljaj (zastoj), 490 primjer, 215-217
SOAP, 377, 398 , 537 Stack klasa, metode i svojstva, 214
formater, 453 , 463 stream , klasa, 503
SoapForm atter, 470, 537 Begin R ead() i Begin W rite(), metode, 510
SoapH ttpClientProtocol, klasa, 386 binarno itanje datoteke, 503
S o r t (), metoda Stream Reader, klasa, 507
(List), 202 StreamWritei\ klasa, 507
(System.Avray), 167, 182-184 W rite L in e(), m etoda, 5 08
sortira nje String, klasa
polja, 182-184 C om pare{), m etoda, 271
prim jer, 205 -207 , 208 -2 1 2 deklaracija, 221
popis odabranih datoteka (primjer), 322-324 jednakost nizova, testiranje, 228
spajanje dretvi, 475 m etode i polja, 223
specijalizacija, 99-101 preoptereeni konstruktori, 223
implementacija sa sueljem, 102 string, tip, 579
{ p o g led a jt e t a k o e r nasljeivanje) StringBuilder, klasa, 2 3 3 -2 3 4
S p lit(), metoda metode, 233
(Regex), 237 primjer, 233
(String), 224 , 231-233 StringReader, klasa, 503
param s, upotreba kljune rijei, 232 StringW riter, klasa, 503
spoj, SQL., 340 Stru ct, cilj atributa, 432
spone, 452 stru ct, kljuna rije, 579
SQ L (Structured Query Language) Structured Query Language (p o g le d a jt e SQL)
k la se ,5 strukture, 129-135
Managed Provider, 348 definicija, 7, 129
pregled, 339 definiranje, 129-135
relacijske baze podataka i, 337-341 kao vrijednosni tipovi, 132
SQ L Server, instaliranje, 343 nasljeivanje i, 131
SQL Server baza podataka (primjer), 389 razlike izmeu klasa i, 129, 131
SQ L Server Managed Provider, 3 46 stvaranje, 132-135
SqlCom m and, objekti, 354 , 396 bez operatora new, 133-135
SqlCon nection, objekti, 353 , 396 stupci, baza podataka, 338
SqlDataAdapter, objekt, 354 klijentska aplikacija Web usluge (primjer),
stackalloc, operator, 5 79 402
StackTrace, svojstvo (Exception), 2 5 6 -2 5 8 stvarni tip, zam ijenjen opim tipom, 193
standardni izlaz, 12 S u b (), m etoda, 379
stanje objekata, 510 Su b strin gf), metoda (String), 224 , 229-231
sranje prije generiranja Web Forms obrazaca, 361 suelja, 7, 136-165
stanje W eb aplikacija, 359 apstraktne klase u usporedbi s, 136, 152
S t a rt (), metoda, 515 lanice, konstante, 136
objekt TcpListener, 516 definicija, 136
(Thread), 472

598 | K a z a lo
definiranje, 137
eksplicitna implementacija, 1 5 6 - 1 6 5
metode, selektivno izlaganje 1 5 9 tablice baza podataka, 338
Pi'istup zapeaenim klasama i ogranienja veza izmeu, 339
vrijednosnim tipovima, 160-16.5
sakrivanje lanova, 159 TCM P 1! 1 bijeli prostor)
implementiranjc (jednostavan primjer) T r / P kiHla b" 1'lm i formi ter, 45.3
138-139 rCP/lP veze, 515
tokovi podataka, stvaranje, 519
implemenr/ranje vie, 140 IcpC/ient, klasa, 519
instanciju nje, direktno, 1 4 6 TcpLisrener, objekt, 516
izgradnja posluitelja kroz, 462
tehnologije ifriranja za jaka imena, 426
kolekcija (pogledajte kolekcije, suelja) tekstualne datoteke, rad s, 507
kombiniranje, 141
tekstualno itanje (datoteke), 503
metode, pristup, 145-1.52
ternarni operator (?:), 5 9 , 320
operator as, 150-151 Test.cs datoteka, 421
operator is, 147-150 7extReaer, klasa, 503
pretvaranje u suelje, 146 TextWriter, klasa, 503
modifikatori pristupa i, 96 tfiis, kljuna rije, 75, 5 7 9
piemosctvanje implementacija, 1 5 3 - 1 5 6 indekseri i, 184
produivale i kombiniranje (primjer), Thread, klasa, 472
Abort(), metoda, 476
proirivanje, 140
InterruptO, metoda, 476
stvaranje instance, 146
Sleep(), metoda, 475-476
svrha, 137
T iteadAbortException, iznimka, 476
zadavanje posluitelja preko, 461 I hreadSrart, klasa, 472
svi ciljevi atributa, 432
thrmv, iskaz, 38, 246-248, 264 579
svojstva, 93-96 tijek programa,3 7
definiranje u C# klasi, 10 Time, primjer klase, 6 6
deklariranje, 95 tip, otkrivanje, 440
delegati kao, 277
tip povratne vrijednost, odreen u deklaraciji
modifikatori pristupa, 96
metode, 11 J
oprez pri upotrebi, 135 tipovi, 9, 23-28
polja, 169
biblioteke, 558
prisrupnik get, 95
enumeracije, 33-35
pristupnik set, 96
pakiranje i raspakiravanje, 115
refleksija i, 438
upotreba (primjer), 9 3 (p ^ da/te ugraeni tipovi)
tipovi, biblio teka, 567
svvitch, iskaz, 42-45, 5 7 9
Tlblmp.exe, uvoenje biblioteke tipova 558
SyncRoot, svojstvo (System.Array), 1 6 7 ToArray(), metoda
oystem, imenski prostor, 12 (List), 202
upotreba kljune rijei i, 13 (Queue), 212
System.Array, klasa, 182-184 (Stack), 215
metode i svojstva, 167 toke prekida
ToStringO, metoda, 182 postavljanje, 20
System.HnterpriseServices.Synchronization, T pokretanje programa, 20
atribut klase, 460 loCharArray(), metoda (String) 224
System.Exception, objekti, 2 5 5 , 2 6 4 tokovi podataka, 491-547
System.Int32, klasa, 204
asinkroni U/I, 510-514
System.Reflectton, imenski prostor, 4 3 3 4 3 9 binarne datoteke, itanje, 503-505
System.String, klasa, 2 2 0
deklaracija, 221
itanje 1 upisivanje podataka, 502
definirani, 491
System.Text.RegularExpressions, imenski izolirano spremite, 544
prostor, 236 itanje iz, 5 4 5
System.Text.StringBuiJder, klasa, 233 pisanje u, 545
5ystem.Threadtng, imenski prostor, 4 7 1 klase .NET kostura, 503
5ystem.Web, imenski prostor, 356, 358 krajnje toke, 491
System.Web Services, imenski prostor, 378 s meuspremnicima, 505
System.Web.UI, imenski prostor, 356, 358 serijalizacija objekata, 536

Kazalo / 599
postavljanje toaka prekida, 20
deserijalizacija objekta, 538 klijent m renog toka podataka, stvaranje,
fornvateri, upotreba, 53/
519 , ,
obrada privremenih podataka, poslu itelj m renog toka po data ka,
spriializaciia objekta, j j o , . . . stvaranje, 516
serijalizacija/deserijalizacija (primjer), viestruke veze, obrada, 521
( p o g le d a jt e t a k o e r tokovi)
tekstua5l3n8e datoteke, wd. sa, 5= 0A77 ulaz/izlaz (U/l)
klijent mrenog toka p odat, , ,
asinkroni, 4 9 2 , 510 , 516
stvaranje, 519 glavne klase .N ET kostura za, 503
posluitelj za mreni tok podata . ,
stvaranje, 516 ^ t e S i z l a z a , klijent W eb usluga, 399
viestruke veze, obrada, 52 datoteka s pozadinskim kodom,
ulaz i izlaz preko mree, 514 4 0 4 -4 0 6
standardni izlaz, 12
W 1 u 2 t w e b stranica kao HTML tokova
ulazi, 515 . ric
podataka, 534 ulazi (identifikatori aplikacije), 513
tokovi s m eusprem nicim a, 505 ulaz i izlaz preko m ree, 514 _
ToLower(), metoda (Stnng), 22 UM L (Unified M odeling Language), 100
(System.Array), 182 unchecked, operator, 579
T o Strin g (), metoda, 113, 223
T o U pper(), metoda (Stnng), 22 Unified M odeling Language (pogledajte U M U
TP (transparentni posrednik), Uiiiforin Resource Identifier (U R I), 534
TreeNodeCollection, objekt, unikatni objekti, 461
TteeVietv, kontrole (P "m j ) U n load (), m etoda (AppDom ain), 4 50
obrada dogaaja, 317-320 unsafe, kljuna rije, 572
popunjavanje, 313-317 unsafe, m odifikator, 5 7 1 ,5 7 9
T rim (), metoda (Stnng), unutarnje iznim ke, ponovno izbacivanje
TrimHndO, metoda ( S m n g ) ,^ (prim jer), 261-265
T rim S ta rt(), metoda (Stnng)- 22 unutarnji podaci, 21
T rim To Size(), metoda (List), 202 unutarnji spoj (S Q U , 340
true, kljuna rije, 579 U nw rap(), m etoda (ObjectH andle), 452
try iskaz/blok, 2 4 8 ,5 7 9 upiti, 3 4 0 _ ,
Type, klasa param etatizirani radi sigurnosti, 399
FilterName, polja, 443-444
FindMembers(), mecoda 4 upravljani izvori za A D O .N ET, 3 4 6 -34 8
GetMembers( ) , mecoda 442
GetMethodsO, metoda 442 jjr j(U nifo^m Ilesourc e ld em?fier), 4 6 5 ,5 3 4
GecType(), metoda 445 ushort, tip, 5 8 0 , 1Q
typeof, operator, 439, 4 6 3 , 579 using, direktiva (u prim jerim a koda), 2y
using, iskaz, 14, 83 , 5 80
atributi sklopa nakon, 433
usklaivanje uzoraka (.p og led ajte regularni
uahurivanje podataka sa svojstvima, 93-96 izrazi)
udaljeni objekti, z?P'S'va"Je u 51 uvjetno 1, operator (& & ), 59
ugnijeeni iskaz u, 40- uvjetno ILI, operator (II), 59
iskaz switch kao zamjena, 42
uvjetni izrazi, 59
ugnjeivanje napom ena za C i C++ program ere, 38
iznim aka, 261 uvjetni operator (?), 320
klasa, 118 uvjetno grananje, 37, 38
kom entara, 12 if...else iskaz, 38
ugraeni tipovi, 24-28 switch iskaz, 42 -45
char, 27 ugnijeeni if iskaz, 4 0 -4 2
odabir, 26 uvoenje
ugraeni tipovi, 25 COM kom ponenata, 5 56
uint, tip ,5 7 9 71-22 biblioteka tipova u .N ET, 558
uklanjanje pogreaka, Pr S r 2* C O M D LL-a u .N ET, 558
pokretanje programa bez uklanjanja
nosreaka, 18

600 | K a z a lo
kasno povezivanje i refleksija, 562 void, kljuna rije, U , 580
testni program, stvaranje, 559 volatile, kljuna rije, 580
kontrole u .NET, 552 vrijednosni parametri, 87
biblioteka tipova, stvaranje, 567 vrijednosni tipovi, 23
alokacija na stogu, 26
neinijalizirani, 72
V objekti kao, 132
valne, kljuna rije, 580 pakiranje i raspakiravanje, 115
Values, svojstvo (Dictionary), 217 polja, 169
vanjska klasa, 117 pristup, 160-165
vanjski kljuevi, 338 prosljeivanje u metode, 88
vanjski spoj (SQL), 340 strukture kao, 129, 132
varijable, 28-31 ugraeni, popis, 25
definirane, 28 vrijednosti, vraanje u parametrima, 85
inicijaliziranje i pridruivanje vrijednosti, vrlo tipizirani jezici, 23
29
konstante i, 31
napomena za VB6 programere, 29 w
neinicijalizirane, 30 W ait(), metoda (Monitor), 485, 490
pridruivanje bez inijaliziranja, 31 \Veb aplikacije
VB napomena za programere, kraj reda, 37 dogaaji, 359
VB6 CDbl, funkcija, 551 metode brzog razvoja primijenjene na, 356
VB6 napomena za programere prednosti, 306, 356
vee ili jednako(>=), operator, 56 stanje, 359
vee od(>), operator, 56 Web tokovi podataka, 533
veliina polja, 201 Web Forms obrasci, 6
veze (viestruke), obrada od strane mrenog dodavanje kontrola na, 365-367
posluitelja za tokove podataka, 521 posluiteljskih, 367
vidljivost klase i njenih lanova (pogledajte povezivanje podataka s kontrolama,
modifikatori pristupa) 367-377
VievvState, svojstvo, 360 pregled, 357-361
virtual, kljuna rije, 103, 580 dogaaji, 358
virtualne metode, 103 podjela korisnikog suelja, 358
klase Object, 113 ivotni ciklus, 360
premoivanje, 103, 107 stvaranje, 361-364
virtualni stroj, .NET CLR-a, 5 datoteke s kodom, 362-364
vie modula, sklopovi s, 416 (pogledajte takoer ASP kontrole)
izgradnja, 416 Web kontrole, 367
' ispitivanje, 421 Web Service Description Language (pogledajte
makefile datoteka, upotreba, 418 WSDL)
uitavanje sklopa, 422 Web usluge, 5
viedimenzionalna polja, 174-179 izgradnja .NET usluge
granice dimenzija, 179 kalkulator (primjer), 380
vieodredisni delegati, dohvat vrijednosti iz, pregledavanje WSDL saetka, 382
295-302 testiranje, 381
Visual Studio .NET izrada .NET usluga, 378-383
imenski prostor, stvaranje, 17 klijentska aplikacija (primjer), 389
kontrole ActiveX, uvoenje, 548, 552 izrada klijenta, 389
prednosti za razvoj softvera, 16 pretraivanje prema kategoriji, 408
programi za uklanjanje pogreaka, prikaz izlaza, 399
upotreba, 21-22 posrednik, stvaranje, 383-388
stvaranje konzolnih aplikacija, 17-18 testiranje usluge, 386
uvoenje ActiveX kontrole u, 552 Web.config, datoteka, 364
Web Forms obrasci, stvaranje, 358 WebRequest, objekt, 534
Visual Studio .NET Designer WebRequestFactory, klasa, 534
alatni okvir, 307 WebResponse, objekt, 534
izrada Windo-w Forms obrazaca, 306-310 GetResponseStrearo(), metoda, 534
prozor Properties, 308

K a z a lo | 601
\vhile, iskaz, 37 ugovor, pregled, 382
while, petlja, 46 , 580 X M L imenski prostor za W SD L dokum ente,
W iltam uth, Scott, 7 380

W in 32 API .wsdl datoteka, 391


deklariranje metoda za uvoenje u C#
program, 571
X
pozivanje metode s P/Invoke, 568
Windo\v klasa, metoda D raw W indow (), 22 X M L (Extensible Markup Language)
Windo\vs Forms obrasci, 6 imenski prostor za W SDL dokum ent, 38 0
osnovni obrazac za korisniko suelje, klase u .N ET kosturu, 5

312 kom entari za dokum entaciju, 334 -336


aplikacije, stvaranje, 310-334 SOAP, prednosti temeljenja na, 378
dogaaj gumba Delete, 324 -334
im plementiranje dogaaja gumba Copy, Y
321-324
yield, kljuna rije, 194
kontrole TreeView, 313-317
metoda za obradu dogaaja TreeVievv, 319
obrada dogaaja TreeVievv, 317-320 z
rekurzija kroz podmape, 315
zapeaene klase, 112
uzim anje datoteka iz mape, 316
pristup, 160-165
dodavanje kontrola na, 554
zapisi u bazi podataka, 338
izrada jednostavnih, 306-310
zaslon, ispisivanje izlaza na, 28
Visual Studio Designer, upotreba,
zastoj kod sinkronizacije dretvi, 490
306 -310
zavrne toke, 4 6 3 , 4 67
Windo\vs.Forms imenski prostor, 310 razum ijevanje, 4 6 9 -4 7 0
W rite(), metoda runo povezivanje usluge s, 4 67
(Console), 49 toka, 491
(Stream), 503
ulazi kao, 515
W rite L in e(), metoda, 12, 13
zbrajanje, operator za (+), 52
(Console), 509
p reo p tereiv ale, 120
pisanje izlaza na zaslon, 28
znakovi, tipovi u regularnim izrazim a, 235
(StreamReader), 50 7
(StreamWriter), 507, 509
wsdl, alat, 382 1
W SDL (Web Service Description Language), 377 ivotni ciklus Web Form s obrasca, 360
datoteke, stvaranje posrednika, 382

602 | K a z a lo

You might also like