Programiranje C# 2.0 4th Liberty Knjiga Hr

March 23, 2017 | Author: Mirko Mirkovic | Category: N/A
Share Embed Donate


Short Description

C# 2.0 Programiranje Liberty Knjiga na hrvatskom...

Description

PRIJEVOD ČETVRTOG IZDANJA

Programiranje C#

Jesse Liberty Prijevod: Ana Antić Marko Grgić

IT

E x p e rt 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: Zagreb

Denona,

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 uložen veliki trud kako bi se izbjegle pogreške, autor i izdavač ne preuzimaju odgovornost za pogreške ili propuste niti za štetu koja bi mogla nastati upotrebom informacija iz ove knjige.

CIP - Katalogizacija u publikaciji Nacionalna i sveučilišna knjižnica 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 -Priručnik 451124026 L ISBN 953-95207-1-1 3 2 1

J

Sadržaj

Predgovor ........................................................................ ix Dio I 1.

JezikC# ProgramskijezikC#i.NETkostur .......................................... 3 Platforma .NET .NET kostur Prevoñenje i MSIL Jezik C#

2.

Početak:„HelloVVorld" ............................................................. 9 Klase, objekti i tipovi Razvoj programa „Hello World“ Korištenje programa za ispravljanje pogrešaka

3.

9 16 20

Osnove programskog jezika C # ............................................... 23 Tipovi Varijable i konstante Izrazi Bijeli prostor Iskazi Operatori Naredbe za pretprocesor

4.

3 4 6 6

23 28 35 36 37 52 60

Klase i objekti ............................................................... 64 Definiranje klasa Stvaranje objekata Korištenje statičkih članova

65 70 77

Uništavanje objekata Prosljeñivanje parametara Preopterećivanje metoda i konstruktora Učahurivanje podataka sa svojstvima Polja readonly 5.

Nasljeñivanje i polimorfizam ............................................. 99 Specijalizacija i generalizacija Nasljeñivanje Apstraktne klase Korijen svih klasa: Object Pakiranje i raspakiravanje tipova Ugnježñivanje klasa

6.

|

137 145 153 156

Polja, indekseri i kolekcije .................................................... 166 Polja Iskaz foreach Inicijaliz'iranje elemenata polja Indekseri Sučelja kolekcija Ograničenja

vi

129 132

Sučelja ........................................................................ 136 Definiranje i implementiranje sučelja Pristupanje metodama sučelja Premošćivanje implementacija sučelja Eksplicitna implementacija sučelja

9.

120 121 121 122 122 123

Strukture ............................................................................129 Definiranje struktura Stvaranje struktura

8.

99 102 109 113 115 117

Preopterećivanje operatora ................................................120 Korištenje ključne riječi operator Podrška ostalim .NET jezicima Stvaranje korisnih operatora Logički parovi Operator jednakosti Operatori pretvaranja

7.

81 84 90 93 97

Sadržaj

166 171 172 184 193 196

List Redovi Stogovi Rječnici

201 212 214 217

10.

Nizovi i regularni izrazi ........................................................ 220 Nizovi 221 Regularni izrazi 235

11.

Obrada izn im k i ........................................................................ 245 Izbacivanje i hvatanje iznimki Objekti Exception Prilagoñene iznimke Ponovno izbacivanje iznimki

12.

246 255 258 261

Delegati i dogañaji ........................................................ 266 Delegati Dogañaji Korištenje anonimnih metoda Dohvat vrijednosti iz višeodredišnih delegata

267 281 294 295

Dio II Programiranje na jeziku C# 13.

Programiranje Windows aplikacija ................................ 305 Izrada jednostavnog Windows Forms obrasca Izrada Windows Forms aplikacija XML komentari za dokumentaciju

14.

306 310 334

PristuppodacimakrozADO.NET ....................................... 337 Relacijske baze podataka i SQL Objektni model ADO.NET-a Početak rada s ADO.NET-om Korištenje OLE DB upravljanih izvora podataka Rad s kontrolama za podatke 15.

337 341 343 346 349

Programiranje ASP.NET aplikacija i Web usluga

Razumijevanje Web Forms obrazaca Izrada Web Forms obrasca Dodavanje kontrola

356 357 361 365

Sadržaj

|

vii

Povezivanje podataka Web usluge SOAP, WSDL i otkrivanje Izrada Web usluge Stvaranje posrednika 16.

Sastavljanje u cjelinu .......................................................... 389 Ukupni dizajn Izrada klijenta Web usluge Prikazivanje rezultata Pretraživanje po kategorijama

Dio III. 17.

Sklopovi i rad s inačicama .................................................415

19.

20.

431 438

Rasporeñivanje i rad na daljinu ...................................... 448

Aplikacijske domene Kontekst Rad na daljinu

449 458 460

Dretve i sinkronizacija .................................................. 471

Dretve Sinkronizacija Stanja natjecanja i zastoji

|

415 415 416 416 417 425 425 428 428

Atributi i refleksija ........................................................... 431 Atributi Refleksija

viii

389 389 399 408

CLRi.NET kostur

PE datoteke Metapodaci Sigurnosna granica Manifesti Sklopovi s više modula Privatni sklopovi Dijeljeni sklopovi Global Assembly Cache Izrada dijeljenog sklopa 18.

367 377 377 378 383

Sadržaj

472 480 489

21.

Tokovi p o d a ta k a ............................................... 491

Datoteke i mape Čitanje i upisivanje podataka Asinkroni ulaz i izlaz Ulaz i izlaz podataka preko mreže Web tokovi Serijalizacija Izolirano spremište 22.

492 502 510 514 533 536 544

.NET i COM programiranje .............................. 548

Uvoženje ActiveX kontrola Uvoženje COM komponenata Izvoženje .NET komponenata P/Invoke Pokazivači

548 556 564 567 570

Dodatak: C# ključne riječi.......................................................... 575 Kazalo ....................................................................................... 581

Sadržaj

|

ix

Predgovor

Otprilike svakih deset godina pojavi se nova tehnologija koja promijeni naš stav prema razvoju aplikacija. Početkom 1980-ih pojavio se operacioni sustav Unix koji se mogao pokrenuti na stolnom računalu, a tvrtka AT&T razvila je moćan novi programski jezik C. Početkom 1990-ih pojavio se operativni sustav Windows i jezik C++. Svaka nova tehnologija predstavljala je veliku promjenu u pristupu programiranju. 2000. godine uslijedio 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 potroši milijarde dolara i promijeni cjelokupnu strukturu poduzeća kako bi podržavala novu platformu, to ne ostaje nezapaženo meñu programerima. .NET će ustvari promijeniti vaš stav prema programiranju. Ukratko, radi se o novoj razvojnoj platformi čija je svrha olakšati objektno orijentiran razvoj za Internet. Programski jezik koji je izabran za ovu platformu je C# , smišljen na temelju ranijeg iskustva sa jezicima C (odlične performanse), C++ (objektno orijentirana struktura), Java™ (sakupljanje otpada, visoka razina sigurnosti) i Visual Basic (brz razvoj), s namjerom stvaranja novog jezika koji će u potpunosti odgovarati višeslojnim Web aplikacijama temeljenim na komponentama. C# 2.0, jezik odabran za .NET 2005, sadrži ažurirane alate i novi snažni razvojni okoliš. On predstavlja najveće dostignuće Microsoftovog ulaganja u istraživanje i razvoj. Jednostavno je sjajan.

0 knjizi Ova knjiga je priručnik za C# i pisanje .NET aplikacija s pomoću jezika C# . Ako ste programer koji se dobro služi jezikom C# 1.1 i samo želite saznati koje su nove značajke jezika C# 2 .2 , odložite ovu knjigu i kupite Visual C# 2005: A Developer’s Notebook (u izdanju 0 ’Reilly Media, Ine.). Ako, s druge strane, želite poboljšati svoje vještine programiranja ili se dobro služite 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 korištenje ove knjige Od beta izdanja programa Visual Studio Whidbey (2005) pristup radovima tvrtke Microsoft mnogo je jednostavniji. Dostupno vam je nekoliko mogućnosti: baš kao što Visual Studio ima mnogo inačica, tako su i demo inačice .NET-a 2 .0 i Visual Studija 2005 dostupne u različitim oblicima:

Preuzimanje SDK Beta SDK, zajedno s prevoditeljima za pokretanje u naredbenom redu, dokumentacijom i drugim alatima, može se besplatno preuzeti na adresi http://msdn.micmsoft.com/netframework/downloads/updates/default.aspx. Datoteka za preuzimanje nije velika, ali bit će vam potreban vlastiti program za ureñivanje koda (možete koristiti bilo koji, od Notepada do SharpDevelopera). Express izdanja Microsoft je izdao i ogoljene inačice platforme Visual Studio koje su male pa ih lako možete preuzeti s Internetra i pokrenuti. Express inačice možete preuzeti na adresi http://lab.msdn.microsoft.com/vs2005. Za većinu 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 Server Express ili MSDE (Microsoft Data Engine).

Beta i Community Tech Preview Pune inačice platforme Visual Studio 2005 dostupne su za preuzimanje u dva formata: Community Technology Preview (CTP) koji djeluje pomalo nedovršeno 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 inačica Visual Studija Professional takoñer je dostupna MSDN pretplatnicima, a ostali plaćaju samo troškove poštarine. Više informacija potražite 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 može se koristiti na operativnim sustavima Linux, Mac OS X i drugima. Iako je trenutna inačica namijenjena za .NET 1.1, možete ju instalirati s podrškom za neke .NET 2 .0 značajke. Više informacija potražite na http://www.mono-project.com/about/index.html. Četvrto izdanje Programiranja C# možete koristiti za sve navedene okoliše. Slike zaslona mogu se, meñutim, razlikovati jer je svaki od ovih okoliša u drugoj inačici.

Kako je knjiga organizirana Prvi dio knjige bavi se pojedinostima jezika, u drugom dijelu možete naučiti kako se pišu .NET programi, a u trećem je dijelu opisan način korištenja 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, Početak: „Hello World“ opisuje jednostavan program kao uvod u sljedeća poglavlja te vam predstavlja Visual Studio IDE i razne koncepte jezika C# . U Poglavlju 3, Osnove programskog jezika C# , opisane su osnove jezika, od ugrañenih tipova podataka do ključnih riječi. Klase definiraju nove tipove i programeru omogućuju proširivanje jezika kako bi bolje mogao oblikovati problem koji pokušava riješiti. U Poglavlju 4, Klase i objekti, opisane su komponente koje čine bit jezika C# . Klase mogu biti kompleksna predočenja i apstrakcije pojava iz stvarnog svijeta. Poglavlje 5, Nasljeñivanje i polimorfizam, bavi se odnosima izmeñu klasa. U Poglavlju 6, Preopterećivanje operatora, možete naučiti kako svojim korisnički definiranim tipovima možete dodati operatore. Poglavlja 7 i 8 su uvod u Strukture i Sučelja, kategorije srodne klasama. Strukture su jednostavni objekti koji su nešto ograničeniji od klasa, a manje su zahtjevni za operativni sustav i memoriju. Sučelja su poput ugovora - opisuju kako će klasa funkcionirati tako da drugi programeri mogu koristiti vaše objekte na dobro definiran način. Objektno orijentirani programi mogu stvoriti veliki broj različitih objekata. Često ih je prikladno grupirati i s njima zajednički raditi, a C# pruža opsežnu podršku za kolekcije. U Poglavlju 9, Polja, indekseri i kolekcije, opisane su klase kolekcija koje pruža Framevvork Class Library, nove generičke kolekcije i način na koji programeri mogu stvarati vlastite tipove kolekcija s pomoću generika. Poglavlje 10, Nizovi i regularni izrazi, opisuje način upotrebe jezika C# za rad s tekstualnim nizovima i regularnim izrazima. Većina Windows i Web programa komunicira s korisnikom, a nizovi imaju ključnu ulogu u korisničkom sučelju. U Poglavlju 11, Obrada iznimki, objašnjeni su postupci obrade iznimki koji pružaju objektno orijentiran mehanizam za rješavanje mogućih „hitnih slučajeva". I Windows i Web aplikacije reagiraju na dogañaje. U jeziku C# dogañaji su važni članovi jezika. Poglavlje 12, Delegati i dogañaji posvećeno je načinu obrade dogañaja i korištenju delegata (objektno orijentiranih mehanizama povratnih poziva koji su sigurni za tip) kao podrške za obradu dogañaja.

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 s bazama podataka i način stvaranja Web usluga.

Predgovor

rad

|

xiii

Na vrhu .NET infrastrukture nalazi se apstrakcija operativnog sustava čija je svrha da olakša razvoj objektno orijentiranog softvera. U ovaj gornji sloj pripadaju i ASP. NET i Windows Forms. ASP.NET uključuje Web Forms za brzi razvoj Web aplikacija i Web usluge za izradu Web objekata bez korisničkog sučelja. Web usluga je distribuirana aplikacija koja pruža funkcionalnost preko standardnih Web protokola, najčešće XML-a i HTTP-a. C# pruža model brzog razvoja aplikacija koji je dosad postojao samo u jeziku Visual Basic. Poglavlje 13, Programiranje Windows aplikacija, opisuje način korištenja RAD modela za stvaranje Windows programa profesionalne kvalitete s pomoću razvojnog okoliša Windows Forms. Bez obzira jesu li namijenjene za Web ili klasični operacioni sustav, većina aplikacija se temelji na obradi velike količine podataka. Poglavlje 14, Pristup podacima kroz ADO. NET, objašnjava ADO.NET sloj u .NET kosturu i način interakcije s Microsoft SQL poslužiteljem 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 vještina obrañenih u drugom dijelu kako biste naučili 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 sadrži sustav tipizacije podataka koji se primjenjuje na cijeloj platformi i koji je zajednički svim jezicima koje.NET podržava. CLR je odgovoran za postupke poput upravljanja memorijom i brojanja referenci objekata. Druga ključna značajka .NET CLR-a je sakupljanje otpada. Za razliku od tradicionalnog C/C++ programiranja, u jeziku C# programer nije odgovoran za uništavanje objekata. Beskrajni sati provedeni u traženju objekata koji se više ne koriste sada su stvar prošlosti. Kada se objekti više ne koriste, CLR će za vama obaviti čišćenje. Metoda sakupljanja otpada u CLR-u provjerava ima li u gomili objekata bez referenci i oslobaña memoriju koju ti objekti zauzimaju. Platforma .NET i biblioteka klasa proširuju se prema višim razinama sve do platforme srednje razine na kojoj se nalazi infrastruktura klasa za podršku, zajedno s tipovima za komunikciju izmeñu procesa, XM L, rad s dretvama, ulaz i izlaz podataka, zaštitu, dijagnostiku itd. Srednji sloj sadrži i komponente za pristup podacima koje se skupno nazivaju ADO.NET. U trećem dijelu ove knjige obrañen je odnos izmeñu jezika C# , CLR-a i biblioteke klasa kostura.

xiv

|

Predgovor

U Poglavlju 17, Sklopovi i rad s inačicama, opisane su razlike izmeñu privatnih i javnih sklopova te način 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 inačicama, zaštitu i primjenu. .NET sklopovi sadrže iscrpne metapodatke o klasama, metodama, svojstvima, dogañajima i ostalim elementima. Oni se prevode u program i automatski dohvaćaju kroz refleksiju. U Poglavlju 18, Atributi i refleksija, objašnjeno je dodavanje metapodataka u kod, stvaranje prilagoñenih atributa i pristup metapodacima s pomoću refleksije. U ovom poglavlju možete pronaći i informacije o dinamičkom pozivanju pri kojem se metode pozivaju sa kasnim povezivanjem. .NET kostur projektiran je kako bi pružio podršku distribuiranim aplikacijama i aplikacijama temeljenim na Webu. Komponente napisane na jeziku C# mogu se nalaziti unutar drugih procesa na istom stroju ili na drugim umreženim strojevima ili na Internetu. Rasporeñivanje je tehnika interakcije s objektima koji su u biti negdje drugdje, a rad na daljinu predstavlja tehniku komunikacije s takvim objektima. Te tehnike objašnjene su u Poglavlju 19, Rasporeñivanje i rad na daljinu. FCL pruža opsežnu podršku za asinkroni ulaz i izlaz podataka i druge klase koje eksplicitnu manipulaciju dretvama čine nepotrebnom. C# , meñutim, pruža podršku za dretve i sinkronizaciju, o čemu govori poglavlje 20. U poglavlju 21 objašnjeni su Tokovi podataka, mehanizam koji ne služi samo za interakciju s korisnikom, već i za uzimanje podataka s Interneta. Ovo poglavlje sadrži i potpuni pregled C# podrške za serijalizaciju - mogućnost zapisivanja objekata na disk i njihovog ponovnog čitanja. Poglavlje 22, .NET i COM programiranje, bavi se interoperabilnošću, mogućnošću interakcije sa COM komponentama koje su stvorene izvan upravljanog okoliša .NET kostura. Moguće je pozivanje komponenata iz C# aplikacija u COM i pozivanje COM komponenata u C# . Ti su postupci objašnjeni u poglavlju 22. Na kraju knjige nalazi se dodatak A s ključnim riječima 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 programiranju s jezicima C++, Java ili Visual Basic (VB). Drugi čitatelji možda imaju iskustva s drugim programerskim jezicima, dok neki možda uopće nemaju programerskog iskustva, ali su radili s HTML-om i ostalim Web tehnologijama. Ova knjiga namijenjena je svima vama, no možda će vam biti nešto teža za razumijevanje ako nemate nikakvog iskustva u programiranju.

Predgovor

|

xv

Ako ste prethodno radili s jezicima C, C++, VB 6 ili Java, u sljedećim odjeljcima možete pronaći 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 okolišu i .NET kosturu mnogo toga se promijenilo od inačice 1.1. Sve su učinjene kako bi se smanjila količina koda koji morate napisati te kako biste se lakši fokusirali na izradu robusnih aplikacija. Ova knjiga obuhvaća i te promjene, no ona nije napisana kao vodič za programera koji se bavi jezikom C# 1.1 i samo želi saznati više o promjenama koje su uvedene u inačici 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 riječima Georgea Orvvella, neki jezici sujednakiji od drugih. C# je izvrstan jezik za .NET razvoj. Primijetit ćete kako je riječ o svestranom, robusnom i dobro projektiranom jeziku. C# je i jezik koji se trenutno najviše koristi u člancima i vodičima za .NET programiranje. Moguće 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 težak 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 veće mogućnosti zarade od VB programera. VB programeri nikad zapravo nisu dobili pažnju i naknadu koju zaslužuju, a C# im pruža jedinstvenu mogućnost za potencijalno unosan prijelaz. U svakom slučaju, ako imate iskustva s VB jezikom, odabrali ste pravu knjigu jer ovdje možete pronaći upute koje će vam olakšati prijelaz s jednog jezika na drugi.

C# u usporedbi s Javom Java programeri će na C# vjerojatno gledati s mješavinom osjećaja strepnje, veselja i ogorčenosti. Postoji mišljenje da je C# „kopija" jezika Java. Ne bih htio komentirati vjerski rat izmeñu Microsofta i onih koji mu nisu naklonjeni. Jedino ću napomenuti kako je jezik C# mnogo toga naučio od jezika Java, ali je i Java mnogo toga naučila od C++, koji je svoju sintaksu preuzeo od jezika C, a C je izgrañen po uzoru na starije programske jezike. Svi mi stojimo na ramenima divova. C# Java programerima nudi jednostavan prijelaz: sintaksa je vrlo slična, a semantika je poznata i jednostavna. Java programeri će se za učinkovito korištenje jezika C# vjerojatno morati usredotočiti na razlike izmeñu ta dva jezika. U knjizi sam te razlike pokušao 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 moguće, no nije jednostavno niti prirodno. Iskreno, deset sam godina radio kao C++ programer, napisao sam desetak knjiga o njemu i radije bih otišao zubaru nego radio s upravljanim C++. Možda se radi samo o tome da je C# mnogo jednostavniji. U svakom slučaju, nakon što sam počeo koristiti C# , više se nisam osvrtao. Meñutim, budite pažljivi: postoji mnogo malih zamki koje sam nastojao vidljivo označiti.

Pravila označavanja korištena u ovoj knjizi U knjizi se koriste sljedeće 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 ključne riječi u primjerima programa, uključujući nazive metoda, varijabli 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 pažnju obratite na napomene koje su od teksta odvojene ovim sličicama: Ovo je savjet. On sadrži korisne dodatne informacije o odreñenoj temi.

Ovo je upozorenje. Ono vam pomaže u rješavanju i izbjegavanju problema.

Predgovor

|

xvii

Podrška Kao autor, pruža m stalnu podršku za svoje knjige na Web stranici čija je adresa:

http://www.LibertyAssociates.com Na toj stranici možete preuzeti i izvorni kod za sve primjere iz knjige Programiranje C#. Tamo ćete moći pristupiti forumu o knjizi s posebnim dijelom za pitanja u vezi s jezikom C#. Prije postavlja nja pitanja pročitajte FAQ i datoteke s ispravka ma. Ako i na kon što pročitate te dokumente još uvijek imate pitanje, postavite ga na forumu. Najučinkovitiji način za dobiva nje pomoći je postavlja nje vrlo preciznog pitanja ili čak pisa nje kratkog progra ma koji ilustrira područje koje vas zanima ili zabrinjava. Korisno je iprovjeriti različite diskusijske grupe i forume na Internetu. Microsof t nudi širok raspon diskusijskih grupa, a DevelopMentor (http://discuss.develo p.com) održava iznimno korisan e-mail foru m.

Zahvale Prije nego što nastavim, moram se posebno zahvaliti lanu Griffithsu koji je temeljito recenzirao tekst i dao stručne savjete. Ubraja se u skupinu ugodnijih i pametnijih ljudi s kojima surađujem. Ovo je četvrto izdanje knjige i previše mi je prijatelja i čitatelja pomoglo unaprijediti ju da bi ih mogao sve nabrojati. Ali, posebno mora m spomenuti sljedeće 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 omogućio mi je suradnju s izdavačkom kućom O'Reilly, za što ću mu uvijek biti zahvalan. Valerie Quercia, Claire Cloutier i Tatiana Diaz napravile su velik posao na prethodnim inačica ma, a nadogradnju na C# 2.0 predvodio je Brian Jepson. Rob Romano je izradio brojne nove ilustracije i poboljšao stare. Tim O'Reilly je pružio podršku i resurse i za to sam mu zahvalan. Javili su mi se m nogi čitatelji i obavijestili nas o pravopisnim i manjim pogreškama u prva tri izda nja. Vrlo smo im zahvalni, a posebno bi htio spomenuti sljedeće čitatelje: Peter Adams, Sol Bick, Brian Cassel, Steve Charbonneau, Ronal Chu, John Corner, 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 Martyrossian, 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 pogreške. Pomno smo pregledali knjigu kako bi se uvjerili da se nisu pojavile nove pogreške i da se sav kod prevodi i ispravno pokreće u Visual Studiju 2005. Ako pronañete kakvu pogrešku, provjerite datoteke s popisanim pogreškama na Web stranici (http://www.LibertyAssociates.com) i ako ste otkrili novu pogrešku slobodno pošaljite 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 desetljeća. Kao što u djeci možete vidjeti osobine njihovih roditelja, baka i djedova, u jeziku C# lako možete zapaziti utjecaj jezika Java, C++, Visual Basic (VB) i drugih ali i lekcije naučene 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 okolišu Visual Studio.NET 2005 (potpuna ili Express Edition inačica). Mnogi programi u ovoj knjizi pisani su kao konzolne aplikacije (a ne kao Windows ili Web aplikacije) kako bi se lakše usredotočili na značajke jezika bez nepotrebnih pojedinosti o korisničkom sučelju. Ako koristite Mono ili neku drugu inačicu jezika C # koju nije proizvela tvrtka Microsoft, vjerojatno ćete uvidjeti kako svi programi iz ove knjige ispravno funkcioniraju, iako su testirani samo na odobrenoj Microsoft inačici.

Ovo je poglavlje uvod u jezik C# i platformu .NET, uključujući .NET kostur.

Platforma .NET Kada je u srpnju 2000 . Microsoft najavio C# , njegova premijera bila je dio mnogo većeg dogañaja: najave platforme .NET. C# 2.0 predstavlja zrelu fazu jezika i podudara se s izdanjem nove generacije alata za .NET. Platforma .NET je razvojni kostur koji daje novi API za usluge i API-je starijih inačica operativnog sustava Windows, a istovremeno kombinira brojne različite tehnologije koje je Microsoft razvio tijekom kasnih 1990-ih. To uključuje komponentne usluge

3

COM+, posvećenost XML-u i objektno orijentiranom dizajnu, podršku za nove protokole 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 tehnologija. Dosadašnji rezultati tog rada uistinu su impresivni. Ako ništa drugo, .NET je ogromnog raspona. Platforma se sastoji od tri grupe proizvoda: • Skupa jezika, koji uključuje C# i VB, skupa razvojnih alata u kojem se nalazi i Visual Studio .NET, iscrpna biblioteka klasa za izradu Web usluga te Web i Windows aplikacija, kao i Common Language Runtime (CLR) za izvoñenje objekata izrañenih unutar ovog kostura. • Dvije generacije poslužitelja .NET Enterprise: već objavljenih poslužitelja i onih koji trebaju biti objavljeni u sljedeće dvije ili tri godine • Novih ureñaja koji nisu kućna računala, a podržavaju .NET

.NET kostur Microsoft .NET podržava ne samo neovisnost jezika, već i integraciju jezika. To znači da možete nasljeñivati iz klasa, hvatati iznimke i iskoristiti prednosti polimorfizma u različitim jezicima. .NET kostur vam to omogućava kroz specifikaciju Common Type System (CTS) koju sve .NET komponente moraju poštivati. Na primjer, u .NET-u je sve objekt odreñene klase koja izvodi iz korijenske klase System.Object. CTS podržava opći koncept klasa, sučelja i delegata (koji podržavaju povratne pozive). Uz to, .NET sadrži i Common Language Specification koja pruža niz osnovnih pravila potrebnih za integraciju jezika. CLS postavlja minimalne zahtjeve koje jezik mora ispunjavati da bi bio.NET jezik. Prevoditelji koji su u skladu s CLS-om stvaraju objekte koji mogu meñusobno djelovati. Frameivork Class Library (FCL) može u potpunosti koristiti bilo koji jezik koji je u skladu s CLS-om. .NET kostur se nalazi iznad operativnog sustava, a to može biti bilo koja inačica Windowsa‘ i sastoji se od različitih komponenata koje trenutno uključuju: • Pet službenih jezika: C# , VB, Visual C++, Visual J# i JScript.N ET • CLR, objektno orijentiranu platformu za Windows i Web razvoj zajedničku svim navedenim jezicima • Različite povezane biblioteke klasa koje se zajednički nazivaju Framework Class Library Na slici 1-1 prikazane su komponente .NET kostura.

Zbog arhitekture CLR-a, operacioni sustav može biti bilo koja inačica 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, mreže, sigurnost, dretve, tekst, refleksija, kolekcije, itd.)

Common Language Runtime (pronalaženje pogrešaka, iznimke, provjera tipova, JITprevoditelji)

Windows

Slika 1-1. Arhitektura .NET kostura

Najvažnija komponenta u .NET kosturu je CLR koja pruža okruženje za izvoñenje programa. CLR sadrži virtualni stroj koji je u mnogo čemu sličan Java virtualnom stroju. Na višoj razini CLR aktivira objekte, na njima izvodi sigurnosne provjere, pohranjuje ih u memoriju, izvodi ih i odlaže u otpad (Common Type System takoñer je dio CLR-a). Na slici 1-1 sloj iznad CLR-a je skup klasa kostura, iza kojeg slijedi dodatni sloj podatkovnih i X M L klasa, zatim još jedan sloj klasa namijenjenih Web uslugama te Web i Windows obrascima. Ove klase zajednički čine FCL, jednu od najvećih biblioteka klasa u povijesti koja pruža objektno orijentiran API za sve funkcionalnosti učahurene u platformi .NET. Sa preko 4000 klasa, FCL omogućava brzi razvoj stolnih aplikacija, klijent/poslužitelj aplikacija te drugih Web usluga i apliakcija. Skup osnovnih klasa, odnosno najniža razina FCL-a, sličan je skupu klasa u jeziku Java. Te klase podržavaju ulaz i izlaz, rad s nizovima, upravljanje sustavom zaštite, mrežnu komunikaciju, upravljanje dretvama, rad s tekstom, refleksiju, kolekcije itd. Iznad te razine nalazi se sloj klasa koje proširuju osnovne klase tako da podržavaju rad s podacima i XML-om. Podatkovne klase podržavaju rad s podacima koji se čuvaju u bazama podataka. Te klase uključuju Structured Query Language (SQL) klase koje omogućavaju rad s podacima kroz standardno SQL sučelje. .NET kostur podržava i brojne druge klase koje omogućavaju rad s X M L podacima te njihovo pretraživanja i prevoñenje. Kao proširenje osnovnih klasa kostura te podatkovnih i XML klasa, postoji i sloj klasa koje su namijenjene izradi aplikacija s pomoću tri različite tehnologije: Web Services, Web Forms i Windows Forms. Web Services uključuju velik broj klasa koje podržavaju razvoj jednostavnih distribuiranih komponenata koje će raditi čak i uz upotrebu vatrozida i NAT poslužitelja. Budući da Web Services kao temeljne komunikacijske

Poglavlje 1: Programski jezikC# i.NET kostur

|

5

protokole koriste HTTP i SOAP, ove komponente u kiberprostoru podržavaju metodu Plug and Play. Web Forms i Windows Forms omogućavaju primjenu tehnika za brzi razvoj aplikacija za izradu Web i Windows aplikacija. Jednostavno povucite i ispustite kontrole na obrazac, dvaput pritisnite kontrolu i upišite kod koji će odgovoriti na dogañaj. Iscrpniji opis .NET kostura potražite u knjizi .NET Erameu/ork Essentials u izdanju0 ’Reilly Media.

Prevoñenje i MSI L Na platformi .NET programi se ne prevode u izvedbene datoteke, oni se prevode u sklopove koji sadrže 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 identične IL datotekama koje proizvode ostali jezici podržani u .NET platformi. Ključna osobina CLR-a je da je on zajednički: isti izvedbeni okoliš podržava 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 može izvesti. Standardni se JIT prevoditelj pokreće na zahtjev. Kada se metoda pozove, JIT prevoditelj analizira IL i proizvodi iznimno učinkovit strojni kod koji se vrlo brzo izvodi. Dok se program izvodi prevoñenje 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 brže jer se koristi već prevedeni kod. CLS znači da svi .NET jezici proizvode sličan IL kod. To omogućava da se objektima stvorenim u jednom jeziku može pristupiti i iz drugog jezika. Stoga je moguće osnovnu klasu stvoriti u jeziku VB.NET, a iz nje izvoditi u C# .

Jezik C# Jezik C# je iznenañujuće jednostavan. Sadrži oko 80 ključnih riječi i desetak ugrañenih tipova, ali je vrlo izražajan kada je riječ o implementaciji modernih programerskih koncepata. C # pruža svu podršku potrebnu za strukturirano, objektno orijentirano programiranje temeljeno na komponentama kakvo biste i očekivali od modernog jezika sagrañenog na temeljima jezika C++ i Java. U inačici 2 .0 dodani su mu i mnogi važni elementi koji su ranije nedostajali, poput generika i anonimnih metoda. C++ programeri obratite pažnju: generici u C # su ekvivalent predlošcima. Iako su C # generici nešto jednostavniji i učinkovitiji od C++ predložaka. Oni smanjuju količinu koda ponovnom upotrebom zajedničkog koda tijekom izvoñenja, ali je njihova fleksibilnost nešto manja od fleksibilnosti C++ predložaka.

6

|

Programiranje C#

Jezik C# razvio je mali tim koji predvode dva istaknuta Microsoftova inženjera, Anders Hejlsberg i Scott Wiltamuth. Hejlsberg je poznat i kao autor jezika Turbo Pascal koji je bio popularan na kućnim računalima te kao voña tima koji je razvio Borland Delphi, jedan od prvih uspješnih integriranih razvojnih okoliša za klijent/ poslužitelj programiranje. U središtu svakog objektno orijentiranog jezika je njegova podrška za definiranje klasa i rad s klasama. Klase definiraju nove tipove, što omogućava proširivanje jezika kako biste bolje modelirali problem koji pokušavate riješiti. C# sadrži ključne riječi za deklariranje novih klasa, njihovih metoda i svojstava te za implementaciju učahurivanja, nasljeñivanja i polimorfizma - tri stupa objektno orijentiranog programiranja. U C# , sve što je potrebno za deklariranje klase nalazi se u samoj deklaraciji. Definicije C# klasa ne zahtijevaju posebne datoteke zaglavlja niti IDL (Interface definition Language) datoteke. Štoviše, C # podržava novi XML stil dokumentiranja koji pojednostavljuje izradu dokumentacije. C# podržava i sučelja, koja su poput ugovora s klasom o pružanju usluga koje sučelje zahtijeva. U C# klasa može nasljeñivati samo iz jedne roditeljske klase, ali može implementirati više sučelja. Kada implementira sučelje, C# klasa ustvari obećava pružiti funkcionalnosti koje sučelje zahtijeva. C# pruža i podršku za strukture (engl. structs), koncept čije se značenje značajno promijenilo u odnosu na C++. U C# struktura je ograničen, jednostavan tip koji prilikom instanciranja manje opterećuje operacioni sustav i memoriju od obične klase. Struktura ne može nasljeñivati iz klase niti se iz nje može nasljeñivati, ali struktura može implementirati sučelje. C# pruža potpunu podršku za delegate (engl. delegate) kako bi se omogućilo neizravno pozivanje metoda. U drugim jezicima, poput C++, možete pronaći sličnu funkcionalnost (npr. u pokazivačima na metode članice), ali delegati su referentni tipovi koji učahuravaju metode s posebnim potpisima i povratnim tipovima. C# nudi komponentno orijentirane značajke, poput svojstava, dogañaja i deklarativnih konstrukcija (kao što su atributi). Komponentno orijentirano programiranje podržava spremište metapodataka u kojem se nalazi kod za klasu. Metapodaci opisuju klasu, zajedno s njenim metodama i svojstvima, kao i njene sigurnosne potrebe i druge atribute, poput mogućnosti serijalizacije. Prevedena klasa je, dakle, samostojna jedinica. Stoga, okruženju koje zna pročitati metapodatke i kod klase nisu potrebne druge informacije za korištenje klase. Ako se koristi C# i CLR, klasi se mogu dodati prilagoñeni metapodaci stvaranjem prilagoñenih atributa. Isto tako, metapodaci klase se mogu pročitati s pomoću CLR tipova koji podržavaju refleksiju. Prevoñenjem koda zapravo stvarate sklop (engl. assembly). Sklop je kolekcija datoteka koju programer vidi kao jednu dinamički povezanu biblioteka (DLL) ili izvršnu datoteku (EXE). Na .NET platformi sklop je osnovna jedinica za ponovnu upotrebu, praćenje inačica, zaštitu i razmještaj.

Poglavlje 1: Programski jezike# i.NET kostur

| 7

Naposljetku treba napomenuti kako C# daje podršku i za: • Izravan pristup memoriji s pomoću pokazivača u stilu jezika C++ • Ključne riječi za odvajanje takvih operacija kao opasnih • Upozoravanje CLR sakupljača otpada da ne sakuplja objekte na koje pokazuju pokazivači dok se ne oslobode

8

|

Programiranje C#

POGLAVLJE 2

Početak: „Hello World"

Knjige o programiranju se tradicionalno započinju 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 najvažnije značajke 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");

}

}

Prevoñenjem i pokretanjem ovog koda u konzoli će se ispisati riječi „Hello W orld“. Prije hego što ga prevedemo i pokrenemo, pogledajmo pažljivije 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 nešto opipljivija, poput gumba u prozoru. Tip definira opća svojstva i ponašanje stvari. Ako program u prozoru koristi tri instance tipa gumba - na primjer, gumbe OK, Cancel i Help - svaki gumb može imati svoju veličinu. Slično tome, svi gumbi će se jednako ponašati, iako se način na koji oni implementiraju ta ponašanja može razlikovati. Dakle, pojedini gumbi se mogu razlikovati iako svi pripadaju istom tipu.

9

Kao što je to slučaj u većini objektno orijentiranih programskih jezika, tip je u C# definiran klasom, a pojedinačne instance te klase nazivaju se objektima. U kasnijim poglavljima objašnjeno 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 usredotočiti na klase. Program „Hello World“ deklarira jedan tip: klasu Hello. Ako u C # želite definirati tip, deklarirate ga kao klasu koristeći ključnu riječ cla ss, date mu naziv - u ovom slučaju ,,Hello“ - i zatim definirate njegova svojstva i ponašanja. Definicije svojstava i ponašanja klase u C # moraju se nalaziti u vitičastim zagradama ({}). C++ programeri obratite pažnju: iza zatvorene zagrade ne stoji točka

Metode Klasa ima svojstva i ponašanja. Ponašanja su definirana s metodama članicama, a svojstva su opisana u poglavlju 3.

Metoda )t funkcija u vlasništvu klase. Zapravo, metode se ponekad i nazivaju funkcijama članicama. Metode članice definiraju što klasa može učiniti ili kako se ponaša. Metodama se obično daju nazivi akcija koje izvode, na primjer WriteLine() ili AddNumbers(). Meñutim, 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 pažnju: Main() se u C # piše velikim početnim slovom i mora biti članica klase, a ne globalna članica. Main() može vratiti in t ili void.

CLR prilikom pokretanja programa poziva Main(). Main() je ulazna točka programa i svaki C# program je mora imati.' Deklaracije metoda su zapravo ugovori izmeñu autora i korisnika metode. Autor i korisnik metode će vjerojatno biti isti programer, ali to ne mora uvijek biti slučaj. Može se dogoditi da jedan član razvojnog tima napiše metodu, a da je drugi koristi.

a* Napomena za jav a programere: Main() je ulazna točka svakog C # programa, što je u odreñenoj mjeri slično metodi run() u Java apletu ili metodi Main() u Java programu.

Tehnički je u C# moguće imati nekoliko Main() metoda. U tom slučaju morate upotrijebiti preklopnik /main u odzivniku da biste zadali klasu u kojoj se nalazi Main() metoda koja će služiti kao ulazna točka za program.

10

|

Programiranje C#

Za deklariranje metode trebate zadati tip povratne vrijednosti i iza njega navesti ime. deklaracijama metoda obavezne su i zagrade, bez obzira na to prihvaća li metoda parametre ili ne. Na primjer: U

int myMethod(int siže)

deklarira metodu myMethod() koja ima jedan parametar - cjelobrojnu vrijednost koja će se unutar metode koristit kao siže. Ova metoda vraća cjelobrojnu vrijednost. Tip povratne vrijednosti korisniku metode govori koju će vrstu podataka metoda vratiti kad završi. Neke metode uopće ne vraćaju vrijednosti. Za takve se metode kaže da vraćaju void, što se posebno definira ključnom riječju void. Na primjer: void myVoidMethod();

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

Komentari C# program može sadržati i komentare. Pogledajte prvi red iza otvorene vitičaste zagrade ranije navedene glavne metode: // Koristi objekt konzole sustava

Tekst počinje s dvije kose crte (//). One označavaju komentar. Komentar je napomena za programera koja ne utječe na izvoñenje programa. C# podržava tri vrste komentara. Prva upravo prikazana vrsta govori da se sav tekst s desne strane oznake za komentar treba smatrati komentarom, sve do završetka tog reda. Ta se vrsta naziva komentarom

u C++ stilu. Druga vrsta komentara naziva se komentar u C stilu i počinje otvorenom oznakom za komentar (/*) a završava zatvorenom oznakom za komentar (*/). Na taj se način komentari mogu prostirati u više redova i nije potrebno umetati znakove // na početku svakog reda, kao što je pokazano u primjeru 2-2. Primjer 2-2. Komentari u više redova namespace Helloklorld

{ class Helloklorld

{ static void Main()

{ /* Koristi objekt konzole sustava kao što je objašnjeno u tekstu */ System.Console.IVriteLine("Hello klorid");

}

}

}

Poglavlje 2: Početalc:„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 moguće, a komentari u C stilu samo za privremeno izdvajanje dijelova koda iz projekta. Treća, i posljednja, vrsta komentara koju C# podržava koristi se kako bi se kodu pridružila vanjska XM L dokumentacija. Ta vrsta komentara objašnjena je u poglavlju 13.

Konzolne aplikacije „Hello W orld“ je primjer konzolne aplikacije (engl. console applicatiori). Konzolna aplikacija obično nema grafičkog korisničkog sučelja - ne postoje izbornici, gumbi, prozori i slično. Tekst se unosi i prikazuje u standardnoj konzoli (najčešće je to DOS prozor pod Windowsima). Na početku knjige bavit ćemo se samo konzolnim aplikacijama kako bi se lakše usredotočili na sam jezik. Kasnija poglavlja obrañuju i Windows i Web aplikacije te ćemo se tamo usredotočiti na alate za izradu grafičkog korisničkog sučelja koje nudi Visual Studio .NET. Metoda Main() iz našeg primjera jednostavno ispisuje tekst „Hello W orld" na standardni 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 računala pojavit će se riječi „Hello World“. Metoda se poziva operatorom točka (.). Stoga, kako biste pozvali metodu WriteLine() objekta Console, napišite Console.W riteL ine(...) i u zagrade upišite 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 sadrži na tisuće naziva, kao što su ArrayList, Hashtable, FileDialog, DataException, EventArgs itd. Postoje stotine, tisuće, čak i desetine tisuća naziva. To predstavlja problem. Nijedan razvojni inženjer ne može 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 dobavljača i zatim otkrijete da je ona u sukobu s klasom Hashtable koju pruža platforma .NET? Upamtite, u C # svaka klasa mora imati jedinstven naziv a nazive gotovih klasa koje ste kupili obično ne možete promijeniti. Rješenje ovog problema leži u upotrebi imenskih prostora. Imenski prostor (engl. namespace) ograničava doseg naziva, ograničavajući njegovu smislenost samo na definirani imenski prostor.* * '

Napomena za C++ programere: imenski prostori u C++ odvajaju su operatorom za odvajanje dosega ( ::), dok se u C # koristi operator točka (.).

--------- Napomena za Java programere: imenski prostori pružaju brojne pogodnosti paketa. 12

|

Programiranje C#

Recimo da je Jim inženjer. Riječ „inženjer11 može značiti mnogo toga i biti prilično zbunjujuća. Projektira li on zgrade? Piše programe? Izrañuje vlakove? Izraz možemo pojasniti tako da kažemo „on je znanstvenik" ili „on je strojarski inženjer". C # programer može objasniti kako je Jim Science.engineer, a ne train. engineer. Imenski prostor (u ovom slučaju Science i train) ograničava doseg sljedeće riječi. On stvara „prostor" unutar kojeg riječ ima značenje. Nadalje, Jim možda nije bilo kakav science.engineer. Možda je Jim diplomirao na sveučilištu M IT i ima titulu softverskog inženjera, a ne inženjera grañevinarstva. To znači da se objekt 3 im može odreñenije definirati kao Science. software.engineer. Ova klasifikacija ukazuje kako je imenski prostor software smislen unutar imenskog prostora science, a engineer je u ovom kontekstu smislen unutar imenskog prostora software. Ako kasnije saznate kako je Charlotte transportation.train.engineer, odmah će vam biti jasno kojoj ona vrsti inženjera pripada. Istovremeno mogu postojati dva objekta engineer, svaki unutar svog imenskog prostora. Slično 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 takoñer stvorili klasu Hashtable, neće doći do sukoba jer svaka klasa postoji unutar svog imenskog prostora. U primjeru 2-1 naznačeno je da se klasa Console nalazi unutar imenskog prostora System s pomoću sljedećeg koda: Systera. Console. Write Line();

Operator točka (.) U primjeru 2-1 operator točka (.) koristi se za pristup metodi (i podacima) unutar klase (u ovom slučaju metodi WriteLine()) te za ograničenje naziva klase na odreñen imenski prostor (u ovom slučaju Console se nalazi unutar imenskog prostora System). To funkcionira jer u oba slučaja „kopamo" kako bismo pronašli točno ono što tražimo. Najviša razina je imenski prostor System (u kojem se nalaze svi objekti System koje pruža FCL). Tip Console postoji unutar tog imenskog prostora, a metoda WriteLine() je metoda članica tipa Console. U većini slučajeva imenski se prostori dijele na potprostore. Na primjer, imenski prostor System sadrži više podreñenih imenskih prostora kao što su Data, Configuration, Collections i tako dalje, dok je sam imenski prostor Collections podijeljen na mnogo podreñenih imenskih prostora. Imenski prostori mogu pomoći u organizaciji i razvrstavanju tipova. Kada pišete kompleksni C # program, korisno je stvoriti vlastitu hijerarhiju imenskih prostora, koja može biti vrlo duboka. Svrha imenskih prostora je da vam pomognu da metodom „podijeli i vladaj" lakše koristite složenu hijerarhiju objekata.

Poglavlje 2: Pofetak:„Hello World"

|

13

Ključna riječ using Umjesto da riječ System napišete ispred riječi Console, možete navesti da ćete koristiti tipove iz imenskog prostora System tako da direktivu: using System;

napišete na početku popisa, kako je prikazano u primjeru 2-3. Primjer 2-3. Korištenje ključne riječi using using System; class Hello { static void Main()

{ //Console iz imenskog prostora System Console.WriteLine("Hello Horld");

}

}

Možda 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 uključuje tri iskaza using (System, System.Collections.Generic i System.Text).

Iako možete izjaviti da koristite imenski prostor System, za razliku od nekih drugih jezika, ne možete izjaviti da koristite objekt System.Console. Primjer 2-4 se neće prevesti. Primjer 2-4. Kod koji se ne m ože prevesti (nije dopušten u C# ) using System.Console; class Hello { static void Main()

{ //Console iz imenskog prostora System WriteLine("Hello Horld");

}

}

Generira se pogreška u prevoñenju: 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 pogrešku, jer kad unesete using System i iza toga stavite točku, Visual Studio .N ET 2005 će navesti popis valjanih imenskih prostora na kojem se ne nalazi Console.

Korištenje ključne riječi using može smanjiti količinu koda koji trebate napisati, ali može i poništiti prednosti imenskih prostora jer zagañuje doseg brojnim nazivima koji

14

|

Programiranje C#

se preklapaju. Najčešće rješenje je korištenje ključne riječi using uz ugrañene imenske prostore i one koje ste sami stvorili, ali ne i uz komponente koje ste nabavili od neke treće strane. Neki programeri uvijek ispisuju cijelu putanju kroz imenske prostore do objekta (npr. System.Console.WriteLine(), a ne samo Consoie.UriteLine()) i to koriste kao dodatak dokumentaciji. To je neupotrebljiv pristup ako se koriste složene hijerarhije imenskih prostora.

Razlikovanje velikih i malih slova U C# razlikovanje velikih i malih slova je obavezno, što znači da writeLine nije isto što i WriteLine, a oba su različita od WRITELINE. Nažalost, za razliku od VB-a, C# razvojni okoliš neće ispraviti pogrešno unesena velika i mala slova. Ako istu riječ jednom unesete malim, a drugi put velikim slovima, unijet ćete pogrešku u program koju ćete kasnije teško pronaći. Koristan trik je označiti naziv u kojem su pogrešno napisana jedino mala i velika slova i zatim pritisnuti Ctrl-Space. Značajka Intellisense će umjesto vas ispraviti pogrešno napisana slova.

Kako biste spriječili ovakve pogreške na kojima se nepotrebno gube vrijeme i energija, trebate sastaviti pravilnik za imenovanje varijabli, metoda, konstanti itd. U ovoj se knjizi varijable imenuju ,,deva“ zapisom (engl. camel notation) (npr. imeNekeVarijable), a metode, konstante i svojstva Pascalovim zapisom (npr. NekaMetoda). Pascalov zapis se od ,,deva“ zapisa razlikuje samo po tome što u njemu nazivi počinju velikim slovom. Microsoft je razvio smjernice za pisanje koda koje je korisno pogledati (one su često sve što vam je zapravo potrebno). Možete ih preuzeti sa adrese:

http://msdn.microsoft.com/library/default.aspturfc/library/en-us/ cpgenref/html/cpconNETFrameworkDesignGuidelines.asp

Ključna riječ static Metoda Main() prikazana u primjeru 2-1 ima još jedno odreñenje. Odmah ispred deklaracije povratnog tipa void (koji, sjećate se, označava da metoda ne vraća nikakvu vrijednost) možete pronaći ključnu riječ static: static void Main()

Ključna riječ static govori kako Main() možete pozvati bez prethodnog stvaranja objekta tipa Hello. Ova pomalo kompleksna tema bit će detaljnije obrañena u kasnijim poglavljima. Jedan od problema koji se javljaju prilikom učenja novog programskog jezika je potreba za korištenjem složenijih značajki prije njihova potpuna razumijevanja. Za sada deklaraciju metode Main() možete smatrati čarobnom formulom.

Poglavlje 2: Početak:„Hello World"

|

15

Razvoj programa „Hello World" Postoje najmanje dva načina na koje možete napisati, prevesti i pokrenuti programe iz ove knjige: s pomoću Visual Studio .NET IDE-a ili s pomoću programa za ureñivanje teksta i prevoditelja koji se pokreće u odzivniku. Iako programe možete razvijati izvan Visual Studia .NET, IDE vam pruža razne prednosti. One uključuju podršku za uvlačenje redova, Intellisense, označavanje bojama i integraciju s datotekama za pomoć. Najvažnije od svega, IDE sadrži moćan alat za ispravljanje pogrešaka i mnoštvo drugih alata. U ovoj knjizi je pretpostavljeno da ćete koristiti Visual Studio .NET. Meñutim, upute su više fokusirane na jezik i platformu nego na alate. Sve navedene primjere možete kopirati u program za ureñivanje teksta poput Notepada ili Emacsa, spremiti ih kao tekstualne datoteke s nastavkom imena .cs i prevesti s pomoću 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 možete ručno upisati u Notepad ako želite ići težim putem.

Ureñivanje programa „Hello World" Za izradu programa „Hello World“ u IDE-u, odaberite Visual Studio .N ET s izbornika Start ili odgovarajuću ikonu na radnoj površini, zatim odaberite File -* New -* Project. Time ćete otvoriti prozor New Project (slika 2-1). Ako prvi put koristite Visual Studio, prozor New Project možda će se sam pojaviti.

*

r $ffHeBoWoHd| J u f* 2

S o W lo n N ® e

.7. ..

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 lH e lk M o H d

m

-----

P , 0 « to B f« to ry fo r S o U io n

vi'*-

iSr • • ■

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

J

Programiranje C#

i

v sfV i

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 možete upisati naziv projekta i odabrati mapu u koji ćete spremati datoteke. Pritisnite OK i otvorit će se novi prozor u koji možete 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 £*

&fflSjCv-> c t-bFile Mame $ Full Path

Program.es ' C:\Docijmonts and 5

s

s „

r *-■.\ _________

gpeisw lW K££M f .*> & ?. "A »i

Slika 2-2. Editor u koji možete 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 možete preimenovati. Kada klasi mijenjate naziv, dobro je promijeniti i naziv datoteke (Classl.cs). Ako promijenite naziv datoteke, Visual Studio će automatski umjesto vas promijeniti naziv klase. Za reprodukciju primjera 2-1 promijenite ime datoteke Program.es (nalazi se u prozoru 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 lakše započeli s radom. Za reprodukciju primjera 2-1 uklonite argumente (string[ ] args) iz metode Main(). Zatim sljedeća dva reda kopirajte u tijelo metode Main():

Gradska knjižnica

Poglavlje 2: Početak:„Hello World"

|

17

// 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.

Prevoñenje i pokretanje programa„Hello World" Visual Studio pruža mnogo načina za prevoñenje i pokretanje programa „Hello World“. Skoro svaki zadatak možete obaviti pritiskom na gumb na alatnoj vrpci Visual Studija, odabirom opcije izbornika ili u mnogim slučajevima upotrebom prečaca na tipkovnici. Prečaci na tipkovnici mogu se postaviti odabirom opcije izbornika Tools -* Options -* Keyboard. U ovoj knjizi pretpostavljeno je da koristite zadane postavke.

Za prevoñenje programa „Hello W orld“ možete, na primjer, pritisnuti Ctrl-Shift-B ili odabrati opciju izbornika Build -> Build Solution. Možete i pritisnuti gumb Build na alatnoj vrpci Build (da biste je vidjeli možda ćete morati desnom tipkom miša pritisnuti 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 pogrešaka možete na tipkovnici 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 24 (možda ćete morati prilagoditi svoju alatnu vrpcu kako bi ovaj gumb bio dostupan). Program možete pokrenuti bez da ga prethodno prevedete (ovisno o postavkama pod Tools -* Options) - IDE će spremiti datoteku, prevesti je i pokrenuti (pritom će možda od vas zatražiti dopuštenje za svaki korak). » Slika 2-4. Gufnb Start Without Debugging

U prvom redu preporučam da proučite razvojni okoliš Visual Studija 2005. To je za vas, kao .N ET razvojnog inženjera, osnovni alat i vrlo je važno da se njime znate dobro služiti. Vrijeme koje uložite u upoznavanje okoliša Visual Studia višestruko će vam se isplatiti u sljedećim mjesecima.

18

|

Programiranje C#

Pravovrem eno prevoñenje Prevoñenjem program a hello.es koristeći 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 napišete u V B.N ET -u ili bilo kojem drugom jeziku sukladnom s .N E T C L S-om , dobit ćete više-m anje isti MS1L. Razlike izmeñu IL ko d a stvorenog u razlićitim jezicim a praktički ne postoje. Uz to što stvara IL kod, prevoditelj stvara i segm ćnt ,exe datoteke sam o za čitanje u koji um eće standardno W in 3 2 izvedbeno zaglavlje. Prevoditelj zadaje ulaznu točku unutar segm enta sam o za čita nje. Program za učitavanje prelazi na tu točku kada pokrenete program , baš kao i kod svakog drugog programa za W indowse. O peracioni sustav, m eñutim , ne može izvesti IL kod i ta ulazna točka služi samo za prijelaz do .N ET J1 T prevoditelja (koji je takoñer opisan u poglavlju 1). J1 T prevoditelj generira originalne instrukcije za procesor kakve se mogu pronaći u običnim ,exe datotekam a. K ljučno svojstvo J1 T prevoditelja zapravo je da se metode prevode samo prilikom korištenja, kad su na redu za izvoñenje.

Za prevoñenje i pokretanje programa „Hello World“ s pomoću 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 sljedeće 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 nešto što će vam prikazati odzivnik ljuske. 3. U naredbenom redu zadajte sljedeću naredbu ako koristite .NET ili Shared Source CLI C # prevoditelja: esc /debug hello.es

Ako koristite Mono, zadajte sljedeću naredbu: mes -debug hello.es

Ovim korakom pravi se EXE datoteka. Ako program sadrži pogreške, prevoditelj će o njima izvijestiti u prozoru odzivnika. Preklopnik /debug u kod umeće simbole tako da EXE datoteku možete pokrenuti pod programom za ispravljanje pogrešaka ili pogledati brojeve redova koda u tragovima stoga (trag stoga ćete dobiti ako program generira pogrešku koju ne obrañujete). 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 riječi „Hello World“. Poglavlje 2: Početak: „Hello World"

|

19

Korištenje programa za ispravljanje pogrešaka Alat za ispravljanje pogrešaka vjerojatno je najvažniji alat u svakom razvojnom okolišu. Visual Studio ima vrlo moćan alat za ispravljanje pogrešaka i vrijeme koje potrošite na njegovo svladavanje višestruko će vam se isplatiti. Osnove ispravljanja pogrešaka vrlo su jednostavne. Tri osnovne vještine su: * Kako postaviti točku prekida te kako doći do nje * Kako ući u poziv metode i prijeći preko njega * Kako provjeriti i promijeniti vrijednost varijable, podatka člana itd. Ovo poglavlje ne ponavlja kompletnu dokumentaciju alata za ispravljanje pogrešaka, ali ove su vještine toliko važne da se jednostavno moraju ukratko objasniti. Alat za ispravljanje pogrešaka isti cilj može se koristiti na različite načine, obično preko opcija izbornika, gumba itd. Najjednostavniji način postavljanja točke prekida je pritiskom na lijevu marginu programskog koda. IDE točku prekida označava crvenom bojom, kao na slici 2-5. 1 for ( int i = 0; i < 3; i++ ) {

1 •

) > )

Slika 2-5. Točka prekida

rW r

Za objašnjenje alata za ispravljanje pogrešaka 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 pogrešaka možete odabrati Debug -> Start ili jednostavno pritisnuti F5. Program se zatim prevodi i izvodi do točke prekida, na njoj se zaustavlja, a žuta strelica označava sljedeći iskaz za izvoñenje, kao što je prikazano na slici 2-6.

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

0

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

Slika 2-6. Dosegnuta točka prekida

20

|

Programiranje C#

Nakon što ste došli do točke prekida, provjera vrijednosti različitih objekata je jednostavna. Na primjer, vrijednost varijable i vidjet ćete ako iznad nje postavite pokazivač miša i pričekate nekoliko trenutaka (slika 2-7).

Slika 2-7. Prikaz vrijednosti objekta

U IDE alatu za ispravljanje pogrešaka 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 w in

0 {V irtu a lM e th o d s.W in d o w }

in t VirtualM ethods.VVindow

Gfl



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

*

{Dimensions:[33^

VirtualMethods.Vtfnñovvl]

w inArray

] A u to s j ®

L o c a ls J | p W a tch 1

Slika 2-8. Prozor Locals

Ugrañeni tipovi, poput cjelobrojnih vrijednosti, prikazuju vrijednost, ali objekti prikazuju sv,oj tip i uz njih je prikazan znak plus (+). Te objekte možete proširiti kako biste vidjeli njihove unutarnje podatke (slika 2-9). Više o objektima i njihovim unutarnjim podacima možete saznati u poglavljima koja slijede.

a a

^ w ln A rra y

M S

0 [0 ]

{V irtu alM etho ds.B utto n).

V irtua lM ethod s.B utton

{D im e n sio n s:{3 ))

V irtudlM ethods.W indo(v{)

{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: Početak:,,Hello World"

|

21

U sljedeću metodu moiete ući pritiskom na F l l . U o.om slućaju sljedeća 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, 0«a

W ntto*

.v:BL

& i *9. • .0*. ; ' Vliliiriini'thnfir f f

}•

tScfcjbon&rploror-So... '



tSSSSSSS iS :

v'-

~ .... VŠ; «a«.vW)

j

i

5i tB 'MProperi** Jl

Ca n ao l e.M r l te L ln e( "Bindons drauing » indov ' top,

11* SokAton'VktUftMetho'K( { 3 VjrtoalMcthod«

t h i s . c o p - to p ; th is .le t t -

// c v w il-» t« :s th e v tn d ov p'ufclic V i r t u a l v o id Draw«indOW| ']I

ffl iaSI Referent«*

viituaM ethods

, (Ol, (1)”,

leit );

Slika 2-10. Vlazka u metodu

Možete primijetiti kako jesljedeći iskaz na redu za izvoñenjeWriteLine()uDrawWindow(). Prozor Autos se ažurirao i u njemu je prikazano trenutno stanje objekata. O alatu za ispravljanje pogrešaka treba naučiti još mnogo toga, ali ovaj kratki uvod trebao bi vam biti dovoljan za početak. Mnogo programerskih pitanja može se riješiti tako d : „ ;” e” kratke probu« p r o g ™ * , pregleda,« ih u alatu aa ? p r » i . £ Dobar alat za ispravljanje pogrešaka je, na neki nacin, najmoćniji 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 složen da smo morali preskočiti nekoliko važnih pojedinosti. U ovom poglavlju te su pojedinosti objašnjene detaljnijim prikazom sintakse i strukture samog jezika C# . Ovo poglavlje opisuje sustav tipova u jeziku C# , praveći razliku izmeñu ugrañenih tipova (int, bool itd.) i korisnički definiranih tipova (tipova koje sami stvarate u obliku klasa i sučelja). U njemu su objašnjene i osnove programiranja poput stvaranja i korištenja varijabli i konstanti. Nadalje, tu su objašnjene enumeracije, nizovi, identifikatori, izrazi i iskazi. U drugom dijelu ovog poglavlja možete pronaći objašnjenja i primjere korištenja iskaza za kontrolu tijeka i f , switch, while, do.. .while, for i foreach. Objašnjeni su i operatori, uključujući logičke, relacijske, matematičke operatore i operatore pridruživanja. 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 početi s osnovnim dijelovima: elementima od kojih se objekti stvaraju. U te se elemente ubrajaju ugrañeni tipovi koji su temeljni dio jezika C# , kao i sintaktički 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 pomičnim zarezom, nizova, prozora, gumba itd.), a prevoditelj će spriječiti pojavu pogrešaka tako što će inzistirati da objektima budu pridruženi samo podaci odgovarajućeg tipa. Tip objekta govori prevoditelju koliko je objekt velik (npr. int označava objekt od 4 bajta) i koje su njegove mogućnosti (npr. gumbi se mogu povući, pritisnuti i tako dalje).

23

Napomena za C # 1.1 programere: sve do inačice 2 platforma .N ET bila je vrlo tipizirana u svemu osim u kolekcijama. Uvoñenjem 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: ugrañene (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 izmeñu vrijednosnih i referentnih tipova je način spremanja njihovih vrijednosti 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 pokazatelj da je objekt referentnog tipa (tj. ne koristi se operator &). Takoñer, u C # obično se ne koriste pokazivači (iznimku ovog pravila potražite u poglavlju 22).

Ako imate vrlo velik objekt, njegovo smještanje na gomilu ima mnogo prednosti. U poglavlju 4 objašnjene su prednosti i nedostaci rada s referentnim tipovima. Ovo se poglavlje bavi ugrañenim vrijednosnim tipovima koji su dostupni u C# . U C # veličina i format pohrane za različite ugrañene tipove (npr. int) ne ovise o platformi i jednaki su u svim .N ET jezicima.

C # podržava tipove p o k a z iv a č a u C++ stilu, ali oni se koriste samo kod rada s neupravljanim kodom. To je kod koji nije stvoren na platformi .NET, na primjer, COM objekti. (Rad s COM objektima objašnjen je u poglavlju 22.)

Rad s ugrañenim tipovima C # nudi izobilje ugrañenih tipova koji se očekuju od modernog programskog jezika, i svaki od njih preslikava se u temeljni tip kojeg podržava .NET CLS. Preslikavanje C # primitivnih tipova u temeljni .N ET tip osigurava da se objekt stvoren u C # može koristiti naizmjenično s objektima napravljenim u bilo kojem drugom jeziku koji je u skladu s .N ET CLS-om , primjerice u VB.NET-u.

Svi ugrañeni tipovi, osim Object (koji je detaljnije opisan u poglavlju 5) i String (opisan u poglavlju 10) su vrijednosni tipovi. Svi korisnički 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 izračune.

Svaki tip ima odreñenu i nepromjenjivu veličinu. Za razliku od C++, veličina int tipa u C# je uvijek 4 bajta jer se on preslikava u Int32 u .NET CLS-u. U tablici 3-1 popisani su ugrañeni vrijednosni tipovi dostupni u jeziku C# . Tablica 3-1. Vrijednosni tipovi u C#

Tip

Veličina (u 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

Brojspomičnim zarezom. Čuva vrijednostiod otprilike+/- 1.5*1(HSdo otprilike3.4‘IĐ38sa sedam značajnih znamenki.

Double

8

Double

Brojspomičnim zarezom dvostruke preciznosti. Čuva vrijednosti od otprilike+/- 5.0*10 w do otprilike+/-1.8*10308sa 15-16 značajnih znamenki.

decimal

16

Decimal

Stalna preciznostdo 28 znamenki ipoložajem decimalnog zareza. Koristise u financijskimizračunima izahtijeva prefiks,,m“ili,,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 može biti samo true ili false. Cjelobrojne vrijednosti se ne izjednačavaju sa Boolean vrijednostima u C # i ne postoji implicitno pretvaranje.

Uz ove primitivne tipove C # ima još dva vrijednosna tipa: enum (objašnjen kasnije u ovom poglavlju) i struct (objašnjen u poglavlju 4). U poglavlju 4 objašnjene 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 nasljeñuju.

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 načelu „posljednji unutra prvi van“ (poput gom ile tanjura u restoranu). Stog se odnosi na područje memorije koje podržava procesor i u koje se sprem aju lokalne varijable. U C # vrijednosni tipovi (npr. cjelob rojn e vrijednosti) se alociraju na stog. Za njihovu vrijednost se rezervira podru čje u m em oriji i na to se podru čje referira imenom varijable. Referentni tipovi (npr. objekti) rasporeñuju se na gom ilu. G om ila (engl. heap) je područje memorije koje se koristi za alociran je prostora ob jektim a. Kad se objekt alocira na gomilu, vrača se njegova adresa i zatim pridružuje referenci. O bjekti na stogu se uništavaju kad izañu iz dosega. O kvir stoga je obično definiran metodom. Ako lokalnu varijablu deklarirate unutar m etode (kao što je objašnjeno kasnije u ovom poglavlju), objekti koje stavite na stog unutar te m etode bit će uništeni kad metoda završi. O bjekti na gomili se sakupljaju u otpad kratko nakon uništavanja posljednje reference koja ukazuje na njih.

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

Odabir ugrañenog tipa Odluku o tome koju ćete vrstu cjelobrojne vrijednosti (short, int ili long) koristiti obično donosite na temelju veličine vrijednosti koju želite spremiti. Na primjer, ushort može sadržati samo vrijednosti od 0 do 65,535 , dok uint može sadržati vrijednosti od 0 do 4,294,967,295. Memorija je razmjerno jeftina, dok je vrijeme jednog programera sve skuplje. Većinu vremena ćete varijable deklarirati kao in t, osim ako ne postoji dobar razlog da učinite drukčije.

a Cjelobrojne vrijednosti su često brže od manjih tipova jer su moderni procesori optimizirani za rad s njima.

Float, double i decimal nude različite stupnjeve veličine i preciznosti. Float je dobar za većinu malih necjelobrojnih vrijednosti. Uzmite u obzir da prevoditelj pretpostavlja da je svaki broj s decimalnim zarezom double ako ne kažete drukčije. Za doslovnu dodjelu tipa float iza broja dodajte slovo f (pojedinosti pridruživanja vrijednosti literalima objašnjene 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 eñu 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 ičaje n i k o n tro ln i znakovi navedeni su u ta blici 3 - 2 .

Tablica 3-2. Uobičajeni kontrolni znakovi Znak

Značenje

V

Apostrof

\"

Navodnik

\\

Obrnuta kosa crta

\0

Nuli

\a

Upozorenje

\b

Brisanjeunatrag

\f

Nova stranica

\n

Novi red

\r

Prijelazu sljedeći red

\t

Vodoravni tabulator

\v

Okomiti tabulator

Pretvaranje ugrañenih tipova Objekti jednog tipa mogu se implicitno ili eksplicitno pretvoriti u objekte drugog tipa. Implicitne se pretvorbe dogañaju automatski - prevoditelj sve radi umjesto vas. Eksplicitne se pretvorbe dogañaju kad vrijednost „pretvorite" u drugi tip. Semantika eksplicitne pretvorbe je „Hej! prevoditelju, znam što radim!" To se ponekad naziva „udarac velikim čekićem" i može 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 znakovni tip mogu lako pomiješati. 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 izmeñu apostrofa. Uzmite u obzir i činjenicu da VB 6 metode za pretvorbu izmeñu znaka i njegove odgovarajuće ASCII vrijednosti (Chr() i Asc()) ne postoje u C# . Kako biste char konvertirali u odgovarajuću 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 dogañaju automatski i u njima se informacije ne mogu izgubiti. Na primjer, short in t (koji je veličine dva bajta) možete jednostavno pretvoriti u int (koji je veličine četiri bajta). Bez obzira na to koja se vrijednost nalazi u short, ona se neće izgubiti prilikom pretvorbe u in t: short x = 5; int y = x; // Implicitna pretvorba

Ako provedete obrnutu pretvorbu, može lako doći do gubitka informacija. Ako je vrijednost u in t veća od 32 767 njen dio se može izgubiti prilikom pretvorbe. Prevoditelj neće izvesti implicitnu pretvorbu iz in t u short: short x; int y = 5 0 0 ; x = y;

// Neće se prevesti

Morate izvesti eksplicitnu pretvorbu koristeći operator: short x; int y = 5 0 0 ; x = (short) y;

// 0K

Svi ugrañeni tipovi sami definiraju vlastita pravila pretvorbe. Ponekad je zgodno definirati pravila pretvorbe za korisnički definirane tipove, a to je objašnjeno 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 pridružiti vrijednosti koji se mogu programski promijeniti.

WriteLine() .N E T kostu r sadrži korisnu metodu za ispis na zaslon. Pojedinosti m etode System. C onsole.W riteLine() bit će pojašnjene kasnije u knjizi, ali osnove su jednostavne. Pozovite m etodu kako je prikazano u prim jeru 3 -1 , unoseći niz koji želite ispisati na konzoli i param etre koji će se zam ijeniti. U sljedećem prim jeru: System.Console.WriteLine("Aiter assignmervt, mylnt: {0}", mylnt);

niz „A fter assignment, mylnt“ se ispisuje kao takav, iza čega slijedi v rijednost varijable mylnt. Lokacija param etra zam jene { 0 } zadaje gdje će se prikazati vrijednost prve izlazne varijable mylnt - u ovom slučaju, na kraju niza. U sljedećim poglavljim a saznat ćete m nogo više o WriteLine().

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

28

|

Programiranje C#

primjer 3-1- Inicijalizacija varijable i pridruživanje 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; Syštem.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 područje upotrebe). Kako bi se uštedilo na prostoru, to je izostavljeno u većini primjera koda, iako je uključeno u primjere koje možete preuzeti s prezentacija 0 ’Reilly ili LibertyAssociates.com.

Ovdje se varijabla mylnt inicijalizira sa vrijednosti 7, ta se vrijednost prikazuje, varijabli se dodjeljuje vrijednosti 5 koja se zatim ponovno prikazuje. Napomena za VB6 programere: u C # tip podatka dolazi ispred naziva

varijable.

Obavezno pridruživanje U jeziku C # pridruživanje je obavezno: to jest, varijable se moraju inicijalizirati ili im se mora pridružiti vrijednost prije korištenja. Kako biste isprobali ovo pravilo, promijenite 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 pridružiti konačna vrijednost.

Primjer 3-2. Korištenje 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 pokušate prevesti ovaj ispis, C # prevoditelj će prikazati poruku o pogrešci prikazanu na slici 3-1. J

■ 1

D escrlpUon

.



__ ^

U se o f u nassigned local v a riab le 'm y ln t'

;

■| . . F fe .

.

-

U n in itia lized V ariable.es

i

• C olum n 17

I

33

Slika 3-1. Poruka o pogrešci nastaloj zbog korištenja varijable kojoj nije dodijeljena vrijednost

Ako dvaput pritisnete poruku o pogrešci razvojni okoliš će postaviti kursor na mjestu pogreške u kodu. U C# nije dopušteno korištenje varijabli koje nisu inicijalizirane. Znači li to da svaku varijablu u programu morate inicijalizirati? Zapravo, ne. Ne morate stvarno inicijalizirati varijablu, ali joj prije korištenja morate dodijeliti vrijednost. U primjeru 3-3 prikazana je ispravna inačica programa.

30

|

Programiranje C#

Primjer 3-3. Pridruživanje 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 može promijeniti. Varijable predstavljaju moćan alat, ali ponekad je potrebna točno odreñena vrijednost, vrijednost za koju želite biti sigurni da će ostati nepromijenjena. Na primjer, u programu za simuliranje kemijskog eksperimenta trebate koristiti točke vrelišta i ledišta na Fahrenheitovoj skali. Program će biti jasniji ako varijable u koje se vrijednosti spremaju nazovete FreezingPoint i BoilingPoint, ali ne želite dopustiti da se njihove vrijednosti promijene. Kako to spriječiti? Rješenje je korištenje konstanti.

Postoje tri vrste konstanti: literali (engl. literals ), sim boličke 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 možete dodijeliti drugu vrijednost. Koliko god se trudili ne možete postići da 32 predstavlja vrijednost 99. Simboličke konstante pridružuju naziv konstantnoj vrijednosti. Simboličlytkonstantu možete deklarirati s pomoću ključne riječi const i sljedeće sintakse;/ const tip identifikator=vrijednost;

Konstanta se prilikom deklariranja mora inicijalizirati, a kad je jednom inicijalizirana, ne možete 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 simbolička konstanta tipa int. U primjeru 3 -4 prikazana je upotreba simboličkih konstanti. Primjer 3-4. Upotreba simboličkih 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 simboličke cjelobrojne konstante; FreezingPoint i BoilingPoint. 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 točke ledišta i vrelišta ali, budući da konstante imaju nazive, one prenose mnogo više značenja. Ako odlučite program prebaciti na Celzijusovu skalu, konstante možete ponovno inicijalizirati prilikom prevoñenja na 0 i 100. Ostatak koda trebao bi normalno funkcionirati. Kako biste se uvjerili da se konstanti ne može ponovno dodijeliti vrijednost, pokušajte ukloniti oznaku komentar iz posljednjeg reda u kodu (prikazan podebljanim slovima). Prilikom ponovnog prevoñenja trebala bi se javiti pogreška prikazana na slici 3-2. !

Q

| Description 1

The left-hand side of an assignment must be a variable, property or indexer

i File SymbolicConstants.cs

j Une 23

' Cojumn 3

Slika 3-2. Upozorenje koje se pojavljuje kada konstanti pokušate ponovno dodijeliti vrijednost

32

|

Programiranje C#

i

Enumeracije Enumeracije (engl. enumerations) su iznimno korisna alternativa konstantama. Enumeracija 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 ožete 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 izmeñu navedenih konstanti ne postoji logička veza. Za rješavanje 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 može biti bilo koji tip cjelobrojne vrijednosti (integer, short, long itd.) osim char. Tehnička definicija enumeracije je: [atributi] [modifikatori] enum identifikator [:osnovni tip] {enumeratorski popis};

Neobavezni modifikatori i atributi opisani su kasnije u knjizi. Zasad ćemo se usredotočiti na ostatak ove deklaracije. Enumeracija počinje ključnom riječi enum iza koje obično slijedi identifikator, na primjer: enum Temperatures

Osnovni tip je temeljni tip enumeracije. Ako izostavite ovu neobaveznu vrijednost (što se često dogaña), ona će poprimiti vrijednost int, ali slobodno možete koristiti bilo koji tip cjelobrojne vrijednosti (npr. ushort, long) osim char. Sljedeći odlomak, na primjer, deklarira enumeraciju cjelobrojnih vrijednosti bez predznaka (uint): enum ServingSizes :uint

{ Small = 1 , Regular = 2, Large = 3

} Obratite pažnju na enumeratorski popis kojim završava deklaracija. On sadrži 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 korištenjem 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 možete primijetiti, enum se mora kvalificirati tipom enumeracije (npr. Temperatures. WickedCold). Vrijednost enumeracije se podrazumijevano prikazuje s pomoću simboličkog naziva (kao što su BoilingPoint i FreezingPoint). Kada želite prikazati vrijednost enumerirane konstante, konstantu morate pretvoriti u njen temeljni tip (int). Cjelobrojna vrijednost se prosljeñuje u WriteLine i prikazuje. Svakoj konstanti u enumeraciji odgovara brojčana vrijednost - u ovom slučaju cijeli broj. Ako je ne postavite drugačije, enumeracija počinje nulom, a svaka sljedeća vrijednost je za jedan veća od prethodne. Ako stvorite sljedeću 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 drugačiji način nego u C++, tako d a je pridruživanje tipu enumeracije iz cjelobrojne vrijednosti ograničeno, ali omogućava unaprjeñenje enumeracije u cjelobrojnu vrijednost za dodjeljivanje enumeracije cjelobrojnoj vrijednosti.

Nizovi znakova Gotovo je nemoguće napisati program u jeziku C # bez upotrebe nizova. U objektu niza nalazi se niz znakova. Varijabla niza se deklarira ključnom riječi 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 obično 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 počinjati slovom ili donjom crtom. Standardna Microsoft praksa imenovanja predlaže ,,deva“ zapis (malo početno slovo, npr. nekolme) za nazive varijabli i Pascalov zapis (veliko početno slovo, npr. NekoDrugoIme) za nazive metoda i većinu drugih identifikatora. Microsoft više ne preporuča korištenje mañarskog zapisa (npr. iNekilnteger) i donjih crta (npr. Neka Vrijednost).

U identifikatorima se velika i mala slova razlikuju pa tako C# myVariable i MyVariable tretira kao dva različita imena varijabli.

Izrazi Iskazi koji su jednaki nekoj vrijednosti zovu se izrazi (engl. expressions). Iznenañujuće velik broj iskaza je jednak nekoj vrijednosti. Na primjer, pridruživanje:

Poglavlje 3: Osnove programskog jezika C#

|

35

myVariable = 57; I

je izraz. Jednako je dodijeljenoj vrijednosti koja, u ovom slučaju, iznosi 57. Obratite pažnju na prethodni iskaz koji vrijednost 57 pridružuje varijabli myVariable. Operator pridruživanja (=) ne provjerava jednakost. Njime se ono stoje na desnoj strani (57) dodjeljuje onome Stoje na lijevoj strani (myVariable). Svi C # operatori (uključujući one za pridruživanje i jednakost) objašnjeni su kasnije u ovom poglavlju. Budući da je myVariable = 57 izraz koji iznosi 57, može se koristiti kao dio drugog operatora pridruživanja, npr: mySecond\/ariable = myVariable = 57;

U ovom se iskazu vrijednost literala 57 pridružuje varijabli myVariable. Vrijednost tog pridruživanja (57) se zatim dodjeljuje drugoj varijabli, mySecondVariable. Vrijednost 5 7 je, dakle, pridružena objema varijablama. Dakle, jednim iskazom možete inicijalizirati beskonačan broj varijabli i pridružiti 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. Možete 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 napišete: Console.WriteLine("Hello World")

svaki razmak izmeñu „Hello" i ,,World“ bit će tretiran kao znak u nizu. Korištenje 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 pridruživanja suvišan, ali

36

|

Programiranje C#

rostor izmeñu deklaracije tipa in t i naziva varijable x nije suvišan, već je obavezan. To nije iznenañujuće: bijeli prostor prevoditelju omogućuje razlikovanje ključne riječi in t i nekog nepoznatog termina in tx . Izmeñu i n t i x možete dodati koliko god želite bjelina, ali mora postojati najmanje jedna (obično razmak ili tabulator). Napomena za VB programere: završetak reda u C # nema posebnog značenja. Iskazi završavaju točkom 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 završavati točkom zarez (;). Na primjer: int x; // iskaz x = 23; // još jedan iskaz int y = x; // i još jedan iskaz

C# iskazi se procjenjuju redom. Prevoditelj počinje na vrhu popisa iskaza i kreće se prema kraju. To bi bilo prilično jednostavno i ograničavajuće kad ne bi postojalo grananje. U C# programu postoje dvije vrste grananja: bezu vjetno g ran anje (engl. unconditional bran ch in g ) i uvjetno g rananje (engl. cond ition al branching). Na tijek programa utječu i petlje i iteracijski iskazi koji se označavaju ključnim riječima for, while, do, in i foreach. Iteracija je objašnjena kasnije u ovom poglavlju. Prvo ćemo objasniti neke osnovnije metode uvjetnog i bezuvjetnog grananja.

Iskazi bezuvjetnog grananja Bezuvjetno grananje se može postići na dva načina. Prvi je pozivanjem metode. Kad prevoditelj naiñe na naziv metode, on zaustavlja izvoñenje tekuće metode i prelazi na novu „pozvanu" metodu. Kada ta metoda vrati vrijednost, izvoñenje originalne metode se nastavlja točno 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 počine s Main() i nastavlja se do poziva SomeMethod(). U toj se točki program grana na drugu metodu. Kad se ona završi, program nastavlja s redom iza poziva te metode. Drugi način bezuvjetnog grananja je s pomoću jedne od ključnih riječi za bezuvjetno grananje: goto, break, continue, return ili throw. Dodatne informacije o prva tri iskaza navedene su kasnije u ovom poglavlju. Iskaz return vraća kontrolu pozivajućoj metodi. Posljednji iskaz, throw, objašnjen je u poglavlju 11.

Iskazi uvjetnog grananja Uvjetno grananje stvara se uvjetnim iskazom na koji upućuju ključne riječi i f , else i switch i do njega može doći 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 može koristiti kao uvjetni, u C # svi uvjetni izrazi moraju se usporeñivati sa Boolean vrijednostima.

Iskazi if...else Iskazi i f . . .e lse označavaju grananje temeljeno na uvjetu. Uvjet je izraz koji se testira na početku iskaza i f . Ako je uvjet istinit, izvodi se iskaz (ili blok iskaza) u glavnom dijelu i f iskaza. i f iskazi mogu sadržati dodatni iskaz else. On se izvodi samo ako je izraz na početku iskaza i f neistinit: if (izraz) iskazi [else iskaz2]

Ovakav opis iskaza i f možete pronaći u dokumentaciji prevoditelja. On pokazuje da i f iskaz uzima Boolean izraz (izraz koji može biti istinit ili neistinit) unutar zagrada i izvodi iskazi ako je izraz istinit. Upamtite da iskazi može biti i blok iskaza unutar vitičastih zagrada.

38

|

Programiranje C#

M ožete p rim ije titi i da je isk az e l s e n e obav ezan, bu dući 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 višlje if ( valueOne > valueTwo )

{ valueTwo = valueOne++; Console.WriteLine("\nSetting valueTwo to valueOne value, "); Console.WriteLine("and incrementing ValueOne.\n''); Console.WriteLine("ValueOne: {o} valueOne, valueTwo);

ValueTwo: {l}",

} else

{ valueOne = valueTwo; Console.WriteLine("Setting them equal. ”); Console.WriteLine("ValueOne: ,{o} valueOne, valueTwo);

}

}

ValueTwo: {i}",

}

U primjeru 3-7 prvim se iskazom if testira je li valueOne veće od valueTwo. Relacijski operatori poput veće od (>), manje od ()vraća istinitu vrijednost ako je vrijednost s lijeve strane operatora veća od vrijednosti s desne strane. Stoga 5>2 vraća true, a 2>5 vraća -false. Relacijski operatori jezika C # prikazani su u tablici 2-3. Ona pretpostavlja dvije varijable: 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 bigValue == 8 0

true false

bigValue != 1O0

false

bigValue != 80

true

Nijejednako

f=

Veće od

>

bigValue > smallValue

true

Veće ilijednako

>=

bigValue >= smallValue smallValue >= bigValue

true false

Manje od

<

bigValue < smallValue

false

Manje ilijednako

< = > = isas

Relacijski jednakost Logičko I

&

Logičkoekskluzivno ILI

A

Logičko ILI Uvjetno I

&&

Uvjetno ILI

II

Uvjetni Pridruživanje

= *= / = + = -= « = & =

a

= |=

U nekim složenim jednadžbama možda ć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 potroše 20 minuta uz kavu i 10 minuta čitajući novine. Djeca na ljenčarenje potroše 30 minuta, a na svañu 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, teško ga je pročitati i ispravno sastaviti. Sljedeći način korištenja meñuvarijabli 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 više varijabli, ali je taj primjer mnogo lakše pročitati i razumjeti te (najvažnije od svega) u njemu ispraviti pogreške. Prilikom pregledavanja ovog programa alatom za ispravljanje pogrešaka možete lako uočiti meñuvrijednosti i provjeriti jesu li one ispravne.

Ternarni operator Iako većina 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 vraća vrijednost tipa bool), a zatim poziva iz razi ako je uvjetni izraz vratio istinitu vrijednost ili izraz 2 ako je vraćena neistinita vrijednost. Logika je „ako je ovo istinito, učini prvo, u suprotnom učini 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 veće od valueTwo. Ako je to slučaj, 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, meñutim, potrebno prevesti samo dijelove programa - na primjer, ovisno o tome ispravljate li pogreške ili prevodite finalni kod. Prije nego što je kod preveden, pokreće se drugi program koji se naziva pretprocesor i priprema program za prevoditelj. Pretprocesor provjerava postoje li u kodu posebne naredbe za pretprocesor koje uvijek počinju znakom ljestve (# ). Ove naredbe omogućavaju zadavanje identifikatora i testiranje njihova postojanja.

Definiranje identifikatora Naredba #define DEBUG definira identifikator pretprocesora DEBUG. Iako se druge direktive za pretprocesor mogu nalaziti bilo gdje unutar koda, identifikatori se moraju definirati prije svakog drugog koda, uključujući 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 podržava m a k r o naredbe.

Možete p ro v jeriti je li DEBUG d efin ira n s iskazo m # i f . D a kle, m ožete napisati: #define DEBUG 1 1 ..

. normalni kod na koji pretprocesor ne djeluje

#if DEBUG // kod koji će biti uključen u uklanjanje pogrešaka #else #endif°d ^ 1 1 ..

^

UkljUČen ^

ne radi isPr^ l ja nj e pogrešaka

. 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 ilježiti id en tifika to r DEBUG P retprocesor p resk a če u o b iča 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

»meñu « f , » , 1 « prevodi „ p,„gram - ali se kod lzmeñu

, , ej di ' J

J

Taj se kod uopće ne pojavljuje u sklopu - kao da ga uopće 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 eñu # i f i # e ls e se ne bi prevodio , za razliku od koda izm eñu # i f i ifen dif. **

r*&-

Pretprocesor ne utječe na kod koji se ne nalazi izmeñu #if/#endif i on

t „ se prevodi u program.

ik '

Poništavanje definiranih identifikatora k o ^ "‘rani ‘dentifikator možete Por,ištiti s pomoću #undef. Pretprocesor prolazi kroz #undef illdo3 Prema, dnU’ St° ga S£ identifikator definira od iskaza #define do iskaza #undef ih do završetka programa. Stoga, ako napišete: #define DEBUG #if DEBUG // ovaj će se kod prevesti #endif #undef DEBUG # if DEBUG // ovaj kod se neće 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 pružaju veliku prilagodljivost. Naredba # e lif omogućava logiku „u suprotnom-ako“ tj. „ako DEBUG onda prva akcija, u suprotnom ako TEST onda druga akcija, inače treća 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 izmeñu #if i #elif, a ostatak koda sve do #endif se neće prevoditi. Ako (i samo ako) identifikator DEBUGnije definiran, pretprocesor će provjeriti je li definiran TEST. Pretprocesor će postojanje identifikatora TEST provjeriti samo ako DEBUG nije definiran. Ako je TEST definiran, prevodit će se kod izmeñu direktiva #elif i #else. Ako se ustanovi kako ni TEST ni DEBUG nisu definirani, prevodit će se kod izmeñu iskaza #else i #endif.

#region Naredba itregion polje teksta označava komentarom. Ona se u prvom redu upotrebljava da bi se alatima kao sto je Visual Studio .N ET omogućilo izdvajanje odreñenog dijela koda i njegovo sažimanje u programu za ureñivanje tako da se vidi samo naredba itregion i njen komentar. Kad, na primjer, pišete Windows aplikaciju (sto je objašnjeno u poglavlju 13), Visual Studio stvara područje za kod koji će upisati razvojni inženjer. Kad se područje proširi, ono izgleda poput područja prikazanog na slici 3-3 (napomena: na slici je radi lakšeg snalaženja područje istaknuto i označeno četverokutom). Možete vidjeti područje označeno naredbama itregion i ifendregion. Meñutim, kad područje sažmete, vidjet ćete samo komentar područja (Windows Form Designer generated 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

j»r. 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. Proširivanje područja 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. Sažeto područje 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 mogućnost stvaranja novih, složenih tipova koje definira sam programer, a koji jasno preslikavaju objekte od kojih se sastoji problem koji pokušavate riješiti. Upravo je ova mogućnost stvaranja novih tipova glavna karakteristika objektno orijentiranih jezika. Nove tipove u jeziku C # zadajete deklariranjem i definiranjem klasa. Tipove možete definirati i s pomoću sučelja (engl. interfaces ), što je detaljnije objašnjeno u poglavlju 8. Instance klase nazivaju se objekti (engl. objects). Objekti se stvaraju u memoriji prilikom izvoñenja programa. Razlika izmeñu klase i objekta jednaka je razlici izmeñu koncepta psa i stvarnog psa koji vam možda sjedi kraj noge dok ovo čitate. Definiciji psa ne možete baciti štap, to možete učiniti samo s instancom. Klasa Dog opisuje kakvi su psi: oni imaju svoju težinu, visinu, boju očiju, boju dlake, narav i tako dalje. Karakteriziraju ih i akcije koje mogu izvesti, na primjer, mogu jesti, hodati, lajati i spavati. Odreñeni pas (npr. moj pas Milo) ima odreñenu težinu (31 kg) i visinu (55 cm), boju očiju (crna), boju dlake (žuta), narav (pravi anñeo) 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 učahurene (engl. encapsulated ) u jedinstvenoj, samostojnoj i samoodrživoj jedinici koda. Kada, na primjer, želite sortirati sadržaj instance padajućeg popisa, samo popisu kažete da se sortira. Kako će to učiniti nije bitno - sve što je bitno je da je sortiranje izvedeno. Učahurivanje, zajedno s polimorfizmom (engl. polymorphism) i nasljeñivanjem (engl. inheritance), je jedan od tri osnovna načela objektno orijentiranog programiranja. Stari programerski vic kaže: „Koliko je objektno orijentiranih programera potrebno da bi se promijenila žarulja? Odgovor: nijedan, žarulji jednostavno kažete da se sama promijeni (drugi odgovor: nijedan, Microsoft je standard promijenio na mrak)“.

64

I

U ovom su poglavlju opisane značajke jezika C # koje se koriste za zadavanje novih klasa. Elementi klase - ponašanja (engl. behaviors) i svojstva (engl. properties) - zajednički se nazivaju članovima klase (engl. class members). U ovom poglavlju opisat ćemo kako se metode koriste za definiranje ponašanja klase i kako se stanje klase održava u varijablama članicama (koje se često nazivaju polja (engl.fields)). Pored toga, poglavlje se bavi i svojstvima koja razvojnim inženjerima izgledaju poput metoda, a klijentima klase kao polja.

Definiranje klasa Za definiranje novog tipa ili klase prvo treba deklarirati, a zatim definirati pripadajuće metode i polja. Klasa se deklarira s pomoću ključne riječi class. Potpuna sintaksa glasi: [atributi] [modifikator pristupa] class identifikator [:osnovna klasa [,sučelja(e)]] (tijelo klase}

Atributi su objašnjeni u poglavlju 8; modifikatori pristupa su objašnjeni u sljedećem odjeljku (kao modifikator pristupa klase obično se koristi ključna riječ public). identifikator je naziv klase koji unesete. Neobavezna osnovna klasa objašnjena je u poglavlju 5. Definicije članova od kojih se sastoji tijelo klase pišu se izmeñu vitičastih zagrada ({})• Napomena za C i C++ programere: u C # definicija klase ne završava točkom-zarez, no ako je slučajno dodate, program će se svejedno prevesti.

U C# se sve dogaña unutar klase. Dosad, meñutim, nismo instancirali nijednu instancu klase; to jest, nismo stvorili nijedan objekt. Koja je razlika izmeñu klase i instance te klase? Kako bismo odgovorili na to pitanje, krenut ćemo od razlike izmeñu tipa in t i varijable tipa int. Možete, na primjer, napisati: int mylnteger = 5;

no ne možete napisati: int = 5;

Vrijednost se ne može dodijeliti tipu. Ona se dodjeljuje objektu koji pripada tom tipu (u ovom slučaju, varijabli tipa int). Prilikom deklariranja nove klase programer definira svojstva svih objekata koji pripadaju toj klasi, kao i njihova ponašanja. 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 padajući popis koji prikazuje niz opcija i korisniku omogućava da odabere neke od njih.

Poglavlje 4: Klase i objekti

|

65

Padajući popisi imaju različite karakteristike - na primjer, visinu, širinu, položaj i boju teksta. Programeri od padajućih popisa obično očekuju i odreñena ponašanja: oni se mogu otvoriti, zatvoriti, sortirati i tako dalje. Objektno orijentirano programiranje dozvoljava stvaranje novog tipa, ListBox, u kojem su učahurene sve ove karakteristike i mogućnosti. Takva klasa kao članove može imati varijable height, width, location i text_ color te metode so rt(), add(), remove() itd. Tipu ListBox ne možete dodijeliti podatke. Prvo morate stvoriti objekt tog tipa, kao u sljedećem odlomku koda: ListBox myListBox;

Kad ste stvorili instancu ListBox, njenim poljima možete dodijeliti podatke. Uzmimo za primjer klasu koja služi za praćenje i prikaz vremena. Unutarnje stanje klase mora moći prikazati tekuću godinu, mjesec, datum, sat, minutu i sekundu. Vjerojatno ćete htjeti da klasa vrijeme prikazuje u različitim formatima. Takvu klasu možete 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 int int int int

Month; Date; Hour; Minute; 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();

-f4 > •fs>" Month, Date, Year, Hour, Minute, Second ); 1 ' ’

Poglavlje 4: Klase i objekti

|

73

Primjer 4-4. Korištenje 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 pridruživanje

} 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 proslijeñena vrijednost, ona će se prilikom stvaranja t2 postaviti na

30:

Time t2 = new Time(2005,n,l8,ll>45); t 2 .DisplayCurrentTime();

74

|

Programiranje Ctt

jVfeñutim, ako je Second dodijeljena vrijednost, kao što je to učinjeno u konstruktoru (koji prihvaća objekt DateTime, podebljan u ispisu), ta će vrijednost zamijeniti inicijaliziranu 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 aća 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, izričito p o stav ljam o v rijem e na 1 1 :4 5 (b ez seku ndi) i dalje preuzim a in icijalizator.

Da u programu nije bilo inicijalizatora i da se za Second vrijednost nije dodijelila na neki drugi način, CLR bi vrijednost inicijalizirao s 0. Napomena za C++ programere: u C # nema konstruktora za kopiranje, pa se semantika kopiranja postiže implementacijom sučelja ICloneable.

Sučelje ICloneable .N E T kostur definira sučelje IC lo n ea b le radi podrške konceptu konstruktora za kopiranje (sučelja su detaljnije obrañena u poglavlju 8). Ovo sučelje definira jednu jedinu metodu: C lo n e (). Klase koje podržavaju zamisao konstruktora za kopiranje trebale bi implementirati IC lo n ea b le, a zatim implementirati ili plitku kopiju (pozivanje MemberwiseClone) ili duboku kopiju (npr. pozivajući konstruktora za kopiranje i ručno kopirajući sve članove). class SomeType: ICloneable

{

public Object Clone()

{

return MemberwiseClone(); // plitka kopija

} }•

Ključna riječ this Ključna riječ th is pokazuje na trenutnu instancu objekta. Referenca this (ponekad se naziva i pokazivač this') je skrivena referenca koja se prosljeñuje svim metodama klase koje nisu statičke. Preko reference this’ svaka metoda može referirati druge metode i varijable objekta. Referenca th is koristi se na razne načine. Prvi je kvalifikacija članova instance koji su inače skriveni parametrima, kao što je to slučaj u sljedećem primjeru:

Pokazivač je varijabla koja sadrži adresu objekta u memoriji. C# ne koristi pokazivače s upravljivim objektima. 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 () prihvaća parametar (hour) s istim nazivom kakvog ima varijabla članica klase. Referenca t h i s se koristi za rješavanje višeznačnosti 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. Loša je strana korištenje istog naziva za parametar i varijablu članicu, što ponekad može biti zbunjujuće. Drugi način upotrebe reference th is je prosljeñivanje 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 prosljeñuje joj se referenca do tekuće instance myClass. To metodi Bar omogućava rad s javnim metodama i članovima tekuće instance myClass. Treći način upotrebe reference th is je s indekserima, što je objašnjeno u poglavlju 9. Četvrti način na koji se referenca th is može 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 pomoću ključne riječi th is poziva preopterećeni konstruktor koji uzima cjelobrojnu vrijednost. Naposljetku, ključna riječ th is može se koristiti i za eksplicitno pozivanje metoda i članova klase, kao oblik dokumentacije: public void MyMethod(int y)

{

' int x = 0; pridružuje lokalnoj varijabli

X = 7 ;

//

y = 8;

// pridružuje parametru

this.z = 5;

// pridružuje metodi članici

this.Draw(); // poziva metodu članicu

}

76

|

Programiranje C#

U navedenim primjerima korištenje reference this je suvišno, ali može dodatno pojasniti namjeru programera, a da pritom ne učini nikakvu štetu (osim mogućeg opterećenja koda).

Korištenje statičkih članova Članovi klase (varijable, metode, dogañaji, indeksi itd.) mogu biti ili članovi instance ili statički članovi. Članovi instance povezani su s instancama tipa, dok se statički članovi smatraju dijelom klase. Statičkom članu možete pristupiti s pomoću 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 statičku članicu SomeMethod( ) . Za pristup statičkoj metodi napišite: Button.SomeMethod();

a ne: btnUpdate.SomeMethod();

U C# nije dopušten pristup statičkoj metodi i varijabli članici putem instance i to će generirati pogrešku prevoditelja (C++ programeri posebno pripazite na to). Neki jezici prave razliku izmeñu metoda klase i drugih (globalnih) metoda koje su dostupne izvan konteksta bilo koje klase. U C # ne postoje globalne metode samo metode klase, ali odgovarajući rezultat možete postići definiranjem statičkih metoda unutar klase. ''

Napomena za VB6 programere: ključna riječ static u C# nije isto što i J ^ ključna riječ Static uVB6 i VB.NET. U VB, ključna riječ Static deklaV*' rira vari!ablu koje j e dostupna samo metodi u kojoj je deklarirana. Drugim riječima, varijablu Static ne dijele različiti objekti iz njene klase (tj. svaka instanca varijable Static ima svoju vrijednost). Ova varijabla, meñutim, postoji tijekom trajanja programa koji njenoj vrijednosti dopušta da traje od jednog poziva metode do drugog.

I

U C# ključna riječ static označava člana klase. Odgovarajuća ključna riječ u VB je Shared. Statičke 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 ožete 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 iče n na klasu u k o jo j se p o ja v ljuje, stoga ne dolazi do p re op terećen ja globalnog im en sko g p ro stora v elikim b ro je m naziva m etoda. T o m ože b iti k o risn o za upravljanje složenim p ro g ra m im a , a naziv k la se se uvelike ponaša kao im en ski pro stor za statičke m eto de k o je se n a la z e u n u ta r klase.

’ .'T ^ e n o ; btnUPdate 1 btnDelete «> »Pravo varijable koje referiraju na neimenovane f ” S‘° ’ 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, statičkim metodama se kao parametri mogu proslijediti članovi instance (ili same mogu stvoriti takve instance unutar statičke metode). Kako im doseg nije globalan, već je ograničen na klasu, one imaju pristup privatnim članovima instance. Odupritese želji da u programu napravite jednu klasu u koju ćete sm jestiti sve metode. To je moguće, ali nije preporučljivo i protivi se načelu učahurivanja u objektno orijentiranom programiranju.

Pozivanje statičkih metoda Metoda Main() je statička. Kaže se da statičke 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 dopušteno pozivanje statičkih metoda kroz varijable instanci.

Statičke metode ne mogu izravno pristupiti članovima koji nisu statički. Kako bi metoda Main() pozvala metodu koja nije statička, ona mora instancirati objekt. Pogledajte ranije navedeni primjer 4-2. SomeMethod() je nestatička metoda MyClass. Kako bi Main() pristupila toj metodi, ona prvo mora instancirati objekt tipa MyClass i zatim pozvati metodu kroz taj objekt.

Korištenje statičkih konstruktora Ako klasa deklarira statički konstruktor možete biti sigurni da će se on pokrenuti prije stvaranja bilo koje instance klase.' Trenutak pokretanja statičkog konstruktora ne možete točno znati, no to će se dogoditi nakon pokretanja programa i prije stvaranja prve instance. Zbog toga se ne može pretpostaviti (niti utvrditi) da je instanca stvorena.

Na primjer, klasi Time iz primjera 4 -4 možete dodati sljedeći statički konstruktor: static Time()

{ Name = "Time";

}

.

Zapravo, CLR jamči da će pokrenuti statički konstruktor prije b ilo k a k v e operacije s klasom. Dalje, on samo jamči da će p okren u ti izvoñenje konstruktora, ali ne i da će zav ršiti njegovo izvoñenje. Moguće je zamisliti primjer u kojem su dvije klase ovisne jedna o drugoj. Umjesto da uñe u slijepu ulicu, CLR može pokrenuti konstruktore u različitim dretvama tako da ispuni jamstvo da će bar započeti izvoñenje konstruktora 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 rište 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 tičko 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 tička v arijabla: private static string Name;

Posljednja p ro m jen a je d odav anje sljedećeg 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 sljedeći: 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 statičkog konstruktora nije obavezno za postizanje ovog cilja. Umjesto toga možete koristiti incijalizator: private static string Name = "Time";

kojim se dobija isti rezultat. Statički su konstruktori, meñutim, korisni za pripremu koja se ne može postići inicijalizatorom, a treba se izvesti samo jednom. •>’ ®

Napomena za Java programere: u C # se statički konstruktor koristi na j , onim N i t i m a na kojima se u jeziku Java koristi statički inicijalizator.

Pretpostavimo, na primjer da u starom DLL-u postoji dio neupravljivog koda. Za taj kod želite napraviti omotač klase. Možete pozvati LoadLibrary u statičkom konstruktoru i inicijalizirati tablicu preskakanja (engl. jump table) unutar statičkog konstruktora. Obrada starog koda i rad s neupravljivim kodom objašnjen je u poglavlju 22.

Statičke klase U C# ne postoje globalne metode ili konstante. Možda ćete morati stvarati male pomoćne klase čija je jedina svrha da sadržavaju statičke članove. Zanemarimo li posljedice tog postupka, ako uistinu stvorite takvu klasu, trebat ćete izbjeći stvaranje instanci. Klasu označite sa S t a t i c kako biste osigurali da se neće stvoriti nijedna instanca klase. Statičke su klase zapečaćene i stoga se ne mogu stvoriti izvedeni tipovi klase S t a t i c . Upamtite kako statičke klase ne moraju sadržavati nestatičke članove ili imati konstruktor.

Poglavlje 4: Klase i objekti

|

79

Korištenje statičkih polja Uobičajen način za prikaz upotrebe statičkih varijabli članica je praćenje broja instanci koje trenutno postoje za klasu. To je prikazano u primjeru 4-5. Primjer 4-5. Korištenje statičkih 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 statička varijabla članica instances i inicijalizirana je s 0. Ako pogledate primjer, vidjet ćete kako se statički član smatra dijelom klase, a ne članom instance te ga prevoditelj ne može inicijalizirati prilikom stvaranja instance. Stoga, ako želite incijalizirati statički član, morate osigurati eksplicitni inicijalizator. Prilikom stvaranja dodatnih instanci Cats (unutar konstruktora) broj se povećava.

80

|

Programiranje C#

Statičke metode za pristup statičkim poljima Podatke članove nije dobro označiti s public. To se odnosi i na statičke varijable članice. Jedn o je rješenje označiti statičkog člana s p rivate, kao što smo to m aloprije učinili s instartces. Stvorena je javna metoda HowManyCats() koja omogućava pristup ovom privatnom članu.

Uništavanje objekata Budući da C # osigurava sakupljanje otpada, objekte ne morate sami uništavati. Meñutim, ako objekt kontrolira neupravljive resurse, te resurse ćete morati eksplicitno osloboditi kad vam više nisu potrebni. Implicitnu kontrolu neupravljivih resursa omogućava destruktor kojeg sakupljač otpada poziva kada je objekt uništen. -**r----

4,

Napomena za C i C++ programere: destruktor se ne pozva obavezno nakon što objekt izañe iz dosega, već kad bude sakupljen kao otpad

TL-*. (što se može dogoditi mnogo kasnije). To se naziva i nedeterministička 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 trebate, i ne biste smjeli, implementirati destruktor. On je potreban samo za rukovanje neupravljivim resursima. Budući da korištenje destruktora ima svoju cijenu, njegova implementacija se preporuča samo u metodama u kojima je obavezan (tj. metodama koje troše vrijedne neupravljive resurse). Destruktor objekta se ne može izravno pozvati. To će učiniti sakupljač otpada.

Kako destruktori funkcioniraju Sakupljač otpada ima popis objekata koji imaju destruktora. Taj se popis ažurira prilikom svakog stvaranja ili uništavanja takvog objekta. Kad se objekt s popisa sakupi kao otpad, on se stavlja u red zajedno s drugim objektima koji čekaju uništavanje. Sakupljač otpada će nakon izvoñenja destruktora sakupiti objekt i ažurirati red, kao i odgovarajući popis objekata za uništavanje.

C# destruktor Destruktor jezika C # sintaktički nalikuje C++ destruktoru, no ponaša se drugačije. C# destruktor deklarirajte znakom tilda, na sljedeći način: ~MyClass(){}

Poglavlje 4: Klase i objekti

|

81

U C # ova sintaksa predstavlja prečacza deklarairanje metode Finalize() koja se povezuje sa svojom osnovnom klasom. Stoga, ako napišete: ~MyClass()

{ // obavlja posao

} C# prevoditelj će to prevesti kao: protected override void Finalize()

{ try

{ // obavlja posao

} finally

{ base.Finalize();

} }

Razlika izmeñu uništavanja i odlaganja Eksplicitno pozivanje destruktora nije dopušteno. Destruktora će pozvati sakupljač otpada. Ako koristite vrijedne neupravljane resurse (poput identifikatora datoteka) koje želite što prije zatvoriti i odložiti, trebate implementirati sučelje IDisposable' (više o sučeljima možete naučiti u poglavlju 8). Prilikom implementacije sučelja IDisposable() trebate obavezno definirati metodu Dispose() koja će obavljati sva važna čišćenja. Dostupnost metode Dispose() klijentima omogućava da rade na načelu „Ne čekaj na pozivanje destruktora, već očisti odmah“. Ako pružite metodu Dispose() trebate spriječiti da sakupljač otpada pozove destruktor objekta. To možete učiniti pozivanjem metode GC.SuppressFinalize() prosljeñujući pokazivač th is objekta. Destruktor tada može pozvati metodu Dispose(). Možete, na primjer, napisati sljedeće: 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(

Većinu vremena nećete trebati izravno stvarati klase koje služe za rad s neupravljivim resursima poput sirovih identifikatora. Možda ćete, meñutim, koristiti klase omotače poput FileStream ili Socket, ali te klase ne implementiraju IDisposable pa u tom slučaju 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 čišćenje za ovaj objekt Console.WriteLine("Disposing..

}

this.is_disposed = true;

} public void Dispose()

{

Dispose(true); // govori sakupljaču otpada da ne finalizira GC.SuppressFinalize(this);

} ~Testing()

{ Dispose(false); Console.WriteLine(”In destructor.”);

} }

Implementiranje metode CloseO Za neke objekte možda ćete radije omogućiti klijentu da pozove metodu Close(). Na primjer, ta metoda vjerojatno ima više smisla za objekte datoteka nego metoda Dispose(). Metodu Close() možete implementirati stvaranjem privatne metode Dispose() i javne metode Close() koju ćete napisati tako da poziva Dispose().

Iskaz using Kako bi klijenti lakše ispravno odložili objekte, u C# je dostupan iskaz using koji osigurava najranije moguće pozivanje metode Dispose(). Trebate deklarirati objekte koje koristite i zatim napraviti doseg za njih s pomoću vitičastih zagrada. Kada se dosegne zatvorena vitičasta 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 using using using

System; System.Collections.Generic; System.Drawing; 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 završi, za objekt Font pozvat će se metoda Dispose(). U drugom dijelu primjera objekt Font je stvoren izvan iskaza using. Kada odlučimo upotrijebiti upravo to pismo stavljamo ga unutar iskaza using. Kad iskaz završi, ponovno se poziva Dispose (). Drugi je pristup prilično opasan. Ako se izbaci iznimka nakon stvaranja objekta, no prije početka bloka using, objekt se neće odložiti. Drugo, varijabla ostaje u dosegu i nakon završetka bloka using, no ona neće uspjeti ako joj se pristupi. Iskaz using štiti i od neočekivanih iznimki. Dispose() se poziva bez obzira na to kako kontrola napušta iskaz using. Stvara se implicitni blok try-finally (pogledajte poglavlje 11 za više informacija).

Prosljeñivanje parametara Prema zadanim postavkama vrijednosni tipovi se u metode prosljeñuju po vrijednosti. To znači da se, prilikom prosljeñivanja objekta vrijednosti metodi, unutar te metode stvara privremena kopija objekta. Kad metoda završi, kopija se uništava. Iako je prosljeñivanje po vrijednosti uobičajen postupak, ponekad se objekti prosljeñuju i po referenci. C # za prosljeñivanje objekata vrijednosti u metodu po referenci nudi parametar ref, a za prosljeñivanje varijable ref bez prethodne inicijalizacije postoji modifikator out. C # podržava i modifikator params koji metodi dopušta prihvaćanje promjenjivog broja parametara. Ključna riječ params pobliže je objašnjena u poglavlju 9.

84

|

Programiranje C#

Prosljeñivanje 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 vrijednosti). 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 omotači. 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. Vraćanje 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 private private private private

int int int int int

Month; Date; Hour; Minute; 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. Vraćanje 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 pokušaj očito nije dao željene rezultate. Problem je u parametrima. U GetTime() se prosljeñuju tri cjelobrojna parametra, 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 znači da se prosljeñuju 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 označavaju 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 označavaju ključnom riječi ref, prevoditelj će javiti da se argument ne može pretvoriti iz int u ref int. Novi rezultat pokazuje točno vrijeme. Deklariranjem parametara kao ref parametara prevoditelju dajete uputu da ih proslijedi po referenci. Umjesto stvaranja kopije, parametar 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 pridruživanja s pomoću parametara out C# nameće definitivno pridruživanje (engl. definite assignment) koje zahtjeva da se prije upotrebe svim varijablama dodijeli vrijednost. Ako u primjeru 4-7 ne inicijalizirate theHour, theMinute i theSecond prije nego što ih kao parametre proslijedite GetTime(), prevoditelj će javiti pogrešku. Meñutim, sprovedena inicijalizacija samo postavlja 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 prosljeñujete GetTime gdje će se promijeniti. No, ako to ne učinite, javit će se sljedeće pogreške 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 inicijalizacijom 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 označimo 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 deklaracije 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 prosljeñuju po vrijednosti. Parametri re f se koriste za prosljeñivanje vrijednosnih tipova po referenci. To omogućava uzimanje njihove modificirane vrijednosti u pozivnoj metodi. Parametri out se koriste samo za vraćanje informacija iz metode. Primjer 4-7 je modificiran u primjeru 4 -8 gdje se koriste sva tri parametra. Primjer 4-8. Korištenje 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 )

{ • // // // if

ako je proslijeđeno vrijeme >= 30 povećava minute i postavlja sekunde na o u suprotnom ne mijenja ništa ( sec >= 30 )

{ Minute++; Second = 0;

}

88

|

Programiranje C#

Primjer 4-8. Korištenje parametara

in , o u t

i r e f (nastavak)

Hour = hr; // postavlja na proslijeđenu vrijednost // prosljeđuje 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 prosljeñuje kao parametar vrijednosti. Njegova jedina funkcija je postavljanje varijable članice Hour i s pomoću tog se parametra ne vraća nikakva vrijednost. ref parametar theSecond se koristi za postavljanje vrijednosti u metodi. Ako je theSecond veći ili jednak 30 , varijabla članica Second se ponovno postavlja na nulu a varijabla

članica Minute se povećava.

Poglavlje 4: Klase i objekti

|

89

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

Naposljetku, parametar theMinute se u metodu prenosi samo da bi se vratila vrijednost varijable članice Minute, pa je stoga označen kao parametar out. Potpuno je razumljivo da se theHour i theSecond moraju inicijalizirati. Njihove su vrijednosti potrebne i koriste se. theMinute nije potrebno inicijalizirati jer se radi o parametru out koji služi samo za vraćanje vrijednosti. Pravila koja su se isprva činila arbitrarnim i mušičavim sada napokon imaju smisla. Vrijednosti se trebaju inicijalizirati samo kada njihova početna vrijednost ima smisla.

Preopterećivanje metoda i konstruktora Često će vam biti potrebne dvije različite metode ali sa istim imenom. Najčešći primjer takvog imenovanja je kad imate više konstruktora. Konstruktori iz dosad navedenih primjera imali su po jedan parametar: objekt DateTime. Bilo bi korisno kada bismo objekte Time mogli postaviti na arbitrarno vrijeme prosljeñivanjem vrijednosti za godinu, mjesec, datum, sat, minuti i sekundu. Još bi korisnije bilo kada bi neki klijenti koristili jedan konstruktor, a ostali neke druge konstruktore. Preopterećivanje metode omogućava upravo to.

Potpis (engl. signature) metode definiran je njenim nazivom i popisom parametara. Dvije se metode razlikuju po svojim potpisima ako imaju različita imena ili različite popise parametara. Prva se metoda u sljedećem kodu od druge razlikuje prema broju parametara, a druga se od treće razlikuje po tipu parametara: void myMethod(int pl); void myMethod(int pl, int p 2 ); void myMethod(int pl, string si);

Klasa može imati neograničen broj metoda, ali se njihovi potpisi moraju meñusobno razlikovati. U primjeru 4-9 prikazana je klasa Time sa dva konstruktora: jednim koji prihvaća objekt DateTime i drugim koji prihvaća šest cjelobrojnih vrijednosti. Primjer 4-9. Preopterećivanje 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 private int private int private int private int private int private int

varijable članice Year; Month; Date; Hour; Minute; 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 možete 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. Budući da se u potpisu nalaze i tipovi argumenata metode, prevoditelj može poziv konstruktora za t i uskladiti s konstruktorom čiji potpis zahtjeva objekt DateTime. Isto tako, prevoditelj može poziv konstruktora za t 2 povezati s metodom konstruktora u čijem je potpisu navedeno šest argumenata. Prilikom preopterećivanja metode morate promijeniti njen potpis (tj. naziv, broj i tip parametara). Možete promijeniti i povratni tip, no to nije obavezno. Ako promijenite samo povratni tip, preopterećenje metode se neće izvesti, a stvaranje dvije metode s istim potpisom i različitim povratnim tipovima generirat će pogrešku prevoditelja (pogledajte primjer 4-10). Primjer 4-10. P reop terećivale metode s različitim 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} long lx = 1 0 ; ' long ly = Triple( lx ); System.Console.WriteLine( "lx: {o}

} static void Main()

{ Tester t = new Tester();

92

|

Programiranje C#

y: {l}", x, y );

ly: {l}", lx, ly );

*

Primjer 4-10. Preopterećivanje metode s različitim povratnim tipovima (nastavak) t.Testf);

} }

}

u ovom primjeru klasa Tester preopterećuje metodu T riple(), tako da jedna prihvaća cjelobrojnu vrijednost, a druga long. Dvije metode Triple() imaju različite povratne tipove. Iako to nije obavezno, u ovom slučaju je vrlo korisno.

Učahurivanje podataka sa svojstvima Svojstva dopuštaju klijentima da pristupe stanju klase kao da izravno pristupaju poljima članovima, dok zapravo implementiraju pristup kroz metodu klase. Taj je postupak savršen. Klijentu treba izravan pristup stanju objekta, a želi izbjeći metode. Dizajner klase, meñutim, želi u članovima klase sakriti unutarnje stanje i osigurati izravan pristup putem metode. Odvajanjem stanja klase od metode koja tom stanju pristupa, dizajner može prema potrebi promijeniti unutarnje stanje objekta. Prilikom prvog stvaranja klase Time, vrijednost Hour mogla bi se spremiti kao varijabla članica. Kad se klasa prepravi, vrijednost Hour može se izračunati ili preuzeti iz baze podataka. Ako klijent ima izravan pristup izvornoj varijabli članici Hour, prebacivanje na izračun vrijednosti prekinulo bi njegov rad. Odvajanjem i prisiljavanjem klijenta da proñe kroz metodu (ili svojstvo) klasa Time može promijeniti način na koji upravljanja internim stanjem bez narušavanja koda klijenta. Svojstva zadovoljavaju oba uvjeta: klijentu pružaju jednostavno sučelje jer izgledaju kao varijable članice. Meñutim, implementiraju se kao metode, omogućavajući sakrivanje podataka koje zahtijeva dobar objektno orijentiran dizajn, kao što je prikazano u primjeru 4-11. Primjer 4-11. Korištenje 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. Korištenje 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( th eHour );

94

|

Programiranje C#

"\ nRetr i eved the hour: {0}\n

primjer 4-11. Korištenje svojstva (nastavak) theHour++; t.Hour = theHour; System.Console.WriteLine( "Updated the hour: {0}\n", theHour );

} } } 2a deklariranje svojstva napišite tip i naziv svojstva i iza njih stavite par vitičastih zagrada. Unutar zagrada možete deklarirati pristupnike (engl. accessors) get i set, Nijedan od njih nema eksplicitne parametre, iako metoda se t() ima implicitni parametar value, kao što je prikazano u sljedećem 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 može biti pohranjena u bazi podataka (u tom će slučaju glavni dio pristupnika učiniti ono što je potrebno za interakciju s bazom podataka) ili može biti pohranjena u privatnoj varijabli članici; private int hour;

Pristupnik get Tijelo pristupnika get slično je metodi klase koja vraća objekt tipa svojstva. Svojstvo Hour iz navedenog primjera slično je metodi koja vraća in t. Ono vraća vrijednost privatne varijable članice u kojoj je pohranjena vrijednost svojstva: get

{ return hour;

} U ovom primjeru vraća se lokalna varijabla članica int, ali mogli biste isto tako uzeti cjelobrojnu vrijednost iz baze podataka ili je izračunati. 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 vraća 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 sličan je metodi koja vraća void. Prilikom definiranja pristupnika set morate koristiti ključnu riječ value koja će predstavljati argument čija se vrijednost prosljeñuje i koju svojstvo sprema: set

{ hour = value;

} U ovom se primjeru ponovno za spremanje vrijednosti svojstva koristi privatna varijabla članica, no pristupnik set može prema potrebi zapisivati u bazu podataka ili ažurirati druge varijable članice. Kada svojstvu dodijelite vrijednost, automatski se poziva pristupnik set te se implicitni parametar value postavlja na vrijednost koju dodijelite: theHour++; t.Hour = theHour;

Dvije glavne prednosti ovog pristupa su što klijent može izravno raditi sa svojstvima, bez žrtvovanja neprikosnovenosti sakrivanja podataka i učahurivanja u dobrom objektno orijentiranom programiranju, te mogućnost da autor svojstva osigura valjanost podataka.

M o d ifik a to ri p ristu p a svojstvu Modifikator pristupa (protected, internal, private) može se postaviti i za reguliranje pristupa pristupnicima get ili set. Da biste to učinili, svojstvo mora sadržati i pristupnik set i pristupnik get, a modificirati možete 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 možete dodati protected): public strirg MyStrirg

{ protected get { return myString; } set { myString = value; }

} U ovom je primjeru pristup pristupniku get ograničen na metode ove klase i klasa koje su izvedene iz nje, dok je pristupnik set javan. Modifikator pristupa ne možete primijeniti na sučelje (pogledajte poglavlje 8) niti u eksplicitnu implementaciju člana sučelja. Uz to, ako prem ošćujete virtualno svojstvo ili indeks (što je objašnjeno u sljedećem odjeljku), modifikator pristupa mora odgovarati modifikatoru pristupa osnovnog svojstva.

96

| Programiranje C#

Polja readonly , vi Možda ćete trebati stvoriti inačicu klase Time koja će pružati javne statičke vrijednosti d--"' kkojeo j e će pred r stavljati tekuće 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 možete vidjeti u primjeru, vrijednost RightNow. Year se može promijeniti u, na primjer, 2 0 0 6 . Očito ne želimo da se to dogodi. Bilo bi dobro statičke vrijednosti označiti kao konstante, ali to nije moguće jer se one inicijaliziraju tek kad se izvede statički konstruktor. U C# za ovu svrhu postoji ključna riječ readonly. Ako deklaracije varijabli članica klase promijenite na sljedeći način: public public public public public public

static readonly readonly readonly readonly readonly readonly

static static static static static

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

int int int int int

i zatim u komentar izdvojite ponovno pridruživanje u Main(): // RightNow.Year = 2006; // pogreška!

program će se ispravno prevesti i izvesti.

98

I

Programiranje C#

POGLAVLJE 5

Nasljeñivanje i polimorfizam

U prethodnom smo poglavlju pokazali kako se novi tipovi mogu stvoriti deklariranjem klasa. Ovo poglavlje bavi se odnosom izmeñu objekata iz stvarnog svijeta i modeliranjem tih odnosa u kodu. Glavna tema ovog poglavlja je specijalizacija koja se u jeziku C# implementira s pomoću nasljeñivanja. U ovom je poglavlju objašnjeno i kako se instance više specijaliziranih klasa mogu tretirati kao da je riječ o instancama općih klasa, a taj se postupak naziva polimorfizmom (engl. polymorphism). Poglavlje završava objašnjenjem zapečaćenih 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# pruža potpuno

objektno orijentiranu tehnologiju, uključujući nasljeñivanje, polimorfizam i učahurivanje. Te su teme relativna nepoznanica VB6 programerima. Trebate ih pažljivo proučiti jer one utječu na klase i dizajn aplikacije.

Specijalizacija i generalizacija Klase i njihove instance (objekti) ne postoje u vakuumu, već u mreži meñusobne ovisnosti i odnosa, kao što mi, društvene životinje, živimo u svijetu odnosa i kategorija. Odnos to je je jedna vrsta specijalizacije. Kada kažemo kako je pas sisavac, to znači kako je pas specijalizirana vrsta sisavca. On ima sve osobine sisavca (raña žive mlade, doji ih mlijekom, ima dlaku), ali su te osobine specijalizirane na poznate osobine porodice canine domesticus. Mačka je takoñer sisavac. Iz toga možemo zaključiti da mačke i psi imaju neke zajedničke osobine koje pripadaju općim osobinama sisavaca, ali se mačke i razlikuju od pasa u onim osobinama koje su specijalizirane za mačke. Odnosi specijalizacije i generalizacije su proporcionalni i hijerarhijski. Proporcionalni su jer je specijalizacija suprotno od generalizacije. Stoga su pas i mačka specijalizacije sisavca, a sisavac je generalizacija psa i mačke.

Ti su odnosi i hijerarhijski jer čine stablo odnosa u kojem se specijalizirani topovi f granaju iz više generaliziranih tipova. Što se po hijerarhiji više pomičete prema gore, | to j e generalizacija veća. Kako biste generalizirali osobinu da i mačke i psi rañaju žive j mlade, pomičete se do sisavaca. Pomicanje kroz hijerarhiju prema dolje predstavlja j specijalizaciju. Stoga je mačka specijalizacija sisavca koja ima pandže (osobina) i prede (ponašanje).

|

Slično tome, ako kažete da ListBox i Button jesu Controls, naznačujete kako Controls i imaju osobine i ponašanja koja se mogu pronaći u oba tipa. Drugim riječima, Control "■ generalizira zajedničke osobine ListBox i Button, dok ListBox i Button specijaliziraju posebne osobine i ponašanja.

0 UML-u Unified Modeling Language (UML) je standardizirani „jezik za opis sustava ili načina poslovanja. Dio UML-a koji je koristan za svrhe ovog poglavlja je skup dijagrama koji se koriste za dokumentiranje odnosa izmeñu 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 način prikazan na slici 5-1. Strelica kreće iz klase koja je specijalizirana i pokazuje generaliziranu klasu.

Slika 5-1. Odnos ,,toje“ Dvije klase često imaju zajedničku funkcionalnost, a ta se zajednička svojstva zatim faktoriraju u zajedničku osnovnu klasu. Kod sastavljen na taj način jednostavniji je za održa'vanje i može se lakše koristiti u drugim projektima. Pretpostavimo kako ste počeli stvarati objekte na način prikazan na slici 5-2.

100

|

Programiranje C#

Slika 5-2. Izvoñenje iz Control

Nakon rada s gumbima RadioButtons, CheckBoxes i Commandshvatite kako postoje odreñene zajedničke osobine i ponašanja koja su više specijalizirana od Control, ali su općenitija od ostale tri. Te zajedničke osobine i ponašanja možete faktorirati u zajedničku osnovnu klasu Button i promijeniti hijerarhiju nasljeñivanja, kao što je prikazano na slici 5-3. Ovo je primjer korištenja generalizacije u objektno orijentiranom razvoju.

Ovaj UML dijagram prikazuje odnos izmeñu 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. Možete, dakle, reći 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 meñusobnih odnosa tipova (klasa). kakvih instanci te klase. J Stoga, ako DrawWindow() označite s abstract u klasi Control, možete izvoziti iz Control, ali ne možete stvoriti objekte Control. Svaka izvedena klasa morala bi implementirati v DrawWindow(). Ako izvedena klasa ne uspije implementirati apstraktnu metodu, klasa Jjjće isto bila apstraktna te instanciranje ponovno ne bi bilo moguće. -^Metoda se s abstract označava tako da se ključna riječ abstract napiše na početku spdefinicije metode: r‘

abstract public void DrawWindow();

(Kako metoda ne može imati implementaciju, na stavljaju se vitičaste zagrade već ■iSamo točka zarez.) Ako postoji jedna ili više apstraktnih metoda, definicija klase se takoñer mora označiti - sabstract, kao u sljedećem primjeru: abstract public class Control

U primjeru 5-2 prikazano je stvaranje apstraktne klase Control i apstraktne metode DrawWindow().

Poglavlje 5: Nasljeđivanje 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;

} // Premošćena inačica 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 može instan' ; cirati. Ako zamijenite prvi član polja: winArray[oJ = new ListBox(l,2 ,"First List Box");

sa sljedećim kodom: winArray[o] = new Control(l,2 );

'program će generirati sljedeću pogrešku: mak 1 0 /8 . JgBnačno, 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 stvarajujednostavni 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 očekivani rezultat - pozivanje konstruktora i vrijednost ,’fispisana u WriteLine(): In Fraction Constructor(int, int) t^

!?

fl: 3/4 in Fraction Constructor(int, int) f2: 2/4

^Sljedeći red u Main() poziva statički operator+. Svrha ovog operatora je zbrajanje dva ikr^zlomka i vraćanje zbroja u obliku novog razlomka: Fraction f3 = fl + f2; Console.UriteLine("f 1 + f2 = f3: { 0 } " , f3.ToStringO);

pogled na rezultat otkriva način na koji operator+ funkcionira: In operator+ In Fraction Constructor(int, int) <

fl + f2 = f3: 5/4

^Pozivaše operatori, zatim konstruktor za f 3 koji uzima dvije int vrijednosti koje predstavljaju brojnik i nazivnik rezultirajućeg razlomka. ''Nova provjera u Main() zbraja int i Fraction f 3 i rezultirajuću vrijednost dodaje novom razlomku f 4 :

1 Ponovimo još jednom: 1/2—4/8, 3/4-6/8, 4/8+6/8=10/8. U primjeru razlomak nije skraćen kako bi kod > ostao što jednostavniji.

■m-

Pog/avlje 6: Preopteređvanje 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 operatoraIn Fraction Constructor(int, int) f3 + 5 =

| I

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 proslijeñen do konstruktora zaf 4 .| U konačnoj provjeri stvara se novi razlomak (f 5). Provjerite je li on jednak razlomku'! | f 2 . Ako jest, ispišite 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 način stvaranja f 5 , a zatim pozivanje preopterećenog 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 korisnički definiran tip, lakša alternativa klasi. Strukture su slične klasama u toliko što mogu sadržati konstruktore, svojstva, metode, .f^polja, operatore, ugniježñene tipove i indekse (pogledajte poglavlje 9). , Postoje i značajne razlike izmeñu klasa i struktura. Na primjer, strukture ne podržavaju ifnasljeñivanje niti destruktore. Što je još važnije, iako je klasa referentni tip, struktura je ^'Vrijednosni tip (više informacija o klasama i tipovima potražite u poglavlju 3). Strukture Jflti stoga korisne za predstavljanje objekata koji ne zahtijevaju semantiku referenci. f ‘Općeprihvaćeno stajalište je da se strukture trebaju koristiti samo za tipove koji su jmali, jednostavni i po ponašanju i karakteristikama slični ugrañenim tipovima. Napomena za C++ programere: značenje konstrukcije strukture u C # uvelike je drugačije od onog u C++. Struktura je u C++ potpuno jed' ni*.1 naka klasi, osim što je podrazumijevana postavka vidljivosti (javna nasuprot privatnoj) drugačija. U C # strukture su vrijednosni tipovi, dok su klase referentni tipovi, a strukture u C# imaju i druga ograničenja koja su opisana u ovom poglavlju.

*« J

frakture su nešto učinkovitije po upotrebi memorije u poljima (za više informacija gledajte poglavlje 9). One, meñutim, mogu biti manje učinkovite kada se koriste nekim kolekcijama. Kolekcije koje uzimaju objekte očekuju reference, a strukture oraju biti zapakirane. Pakiranje i raspakiravanje usporava izvedbu pa klase mogu ti učinkovitije u većim kolekcijama. |§H ovom ćete poglavlju naučiti 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 identična onoj za definiranje klase: [atributi] [modifikator pristupa] struct identifikator [:popis sučelja] { č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 ) );

1

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 podržavaju nasljeñivanje. One su implicitno izvedene iz object (isto kao i svi tipovi u C# , uključujući ugrañene tipove), ali ne mogu ?'-•.nasljeñivati od drugih klasa niti struktura. Strukture su takoñer implicitno zapečaćene v • Ai*(tojest, nijedna klasa niti struktura se ne može izvesti iz strukture). Strukture, meñu»j; tim, poput klasa mogu implementirati više sučelja. Ostale razlike uključuju: ~v Nema destruktora niti prilagoñenog podrazumijevanog konstruktora Strukture ne mogu sadržati destruktore niti prilagoñene konstruktore bez parametara (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 neće pokrenuti te morate eksplicitno inicijalizirati sva polja. 'L Nema inicijalizacije U strukturi ne možete inicijalizirati polje instance. Stoga, nije dopušteno 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 korištenje. Dok privatni » d a ć i člana promoviraju sakrivanje i učahurivanje podataka, neki programeri drže ako je to prevelik napor za strukture. Oni podatke člana označavaju kao javne i tako

Poglavlje 7: Strukture

|

131

pojednostavnjuju implementaciju strukture. Drugi programeri misle kako svojstva omogućavaju jasno i jednostavno sučelje te da dobra programerska praksa nalaže sakrivanje podataka čak i u jednostavnim objektima. Zahvaljujući novoj mogućnosti refaktoriranja u Visual Studiju javne varijable možete jednostavno pretvoriti u privatne sa pridruženim javnim svojstvima. Varijablu jednostavno pritisnete desnom tipkom miša i odaberete Refactor -* Encapsulate Field. Visual Studio će javnu varijablu pretvoriti u privatnu i stvoriti svojstvo s pristupnicima get i set.

] j i j 1

Stvaranje struktura Instancu strukture možete stvoriti koristeći ključnu riječ new u iskazu dodjeljivanja, isto kao da se radi o klasi. U primjeru 7-1 klasa Tester stvara instancu Location na sljedeći način: Location locl = new Location(200,300);

Ovdje je nova instanca nazvana lo cl i prosljeñuju joj se dvije vrijednosti,

200

i 300.

Strukture kao vrijednosni tipovi Definicija klase Tester u primjeru 7-1 sadrži strukturu objekta’ Location (locl) koja je stvorena s vrijednostima 2 0 0 i 3 0 0 . Sljedeći red koda poziva konstruktora Location: Location locl = new Location(200,300);

Zatim se poziva W riteLine(): Console.WriteLine("Locl location: {0}", locl);

WriteLine() očekuje objekt, ali, naravno, Location je struktura (vrijednosni tip). Prevoditelj automatski pakira strukturu (kao sto bi to učinio s bilo kojim drugim vrijednosnim tipom) i taj se zapakirani objekt prosljeñuje do W riteLine(). Za zapakirani objekt poziva se ToStringO a kako struktura (implicitno) nasljeñuje od object, ona može odgovoriti polimorfno, premošćujući metodu na isti način na koji bi to učinio bilo koji drugi objekt: Locl location: 200, 300

P akiran je m ožete izb jeći 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 arijablu v rijed nosno g tipa gdje v rijed nosn i tip pru ža prem ošćiv anje m etode ToString.

U ovoj knjizi termin objekt koristim i za referentne tipove i za vrijednosne tipove. U svijetu objektno orijentiranog programiranja postoji neslaganje oko takve prakse, ali ja se tješim činjenicom da je Microso vrijednosne tipove implementirao kao da su naslijedili od korijenske klase Object (i stoga za bdo koji vrijednosni tip, uključujući ugrañene cipove poput int, možete pozivati sve metode Object).

132

|

Programiranje C#

.

Vl;i__

SU’ meñutim, vrijednosni objekti i kad se prosljeñuju do metode oni se propo vrijednosti - kao što možete vidjeti u sljedećem redu koda u kojem se objekt tici prosljeñuje metodi myFunc(): jeđuju

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 pozivajuću metodu (Main()) i ponovno pozovete WriteLine() vrijednosti ostaju nepromijenjene: Locl location: 200, 30 0

'ć0-

Itruktura je proslijeñena kao vrijednosni objekt te je u myFunc() stvorena kopija. Pokucajte deklaraciju promijeniti u class: public class Location

’iponovno pokrenite provjeru. Generira se sljedeći 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 ključne riječi new Budući 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 rezultirajući objekt Location je stvoren na stogu. Operator newpoziva konstruktor Location. Meñutim, za razliku od klase, struktura se može stvoriti bez korištenja operatora new. To je u sklad s načinom definiranja varijabli ugrañenih tipova (poput int), kao što je prikazano u primjeru 7 -2 .

Upozorenje: u ovom primjeruje prikazan način stvaranja strukture bez korištenja operatora newjer se C# i C++ po tome razlikuju. Stvaranje struktura bez ključne riječi newdonosi malo prednosti i na taj se način mogu stvoriti programi koji su manje razumljivi, više podložni pogreškama i teži za održavanje. Nastavite na vlastitu odgovornost.

Drugi način za rješavanje ovogproblema je upotreba ključne riječi ref (kao stoje objašnjeno upoglavlju v koja dopušta 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#

ritnjer

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 )

{ Location locl;

/ / Nema poziva konstruktora

locl.xVal = 75; U Inicijalizira članove locl.yVal 225; Console.WriteLine( locl );

m

} '■ } , /} H U primjeru 7-2 lokalne varijable se inicijaliziraju izravno prije pozivanja metode lo cl j prije prosljeñivanja objekta do WriteLine(): fa.-

7$R,

‘4 r -

locl.xVal = 75;

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);

} došlo bi do pogreške prevoditelja: Use of unassigned local variable 'locl'

" Kad dodijelite sve vrijednosti, možete im pristupiti preko svojstava x i y: static void Main() II;

{ Location locl;

vr >.y

I#"

.

locl.xVal = 7 5 ;

// Dodjeljuje varijablu članicu

locl.yVal = 225; locl.x = 300; locl.y = 4 0 0 ; Console.WriteLine(locl);

// Dodjeljuje varijablu članicu // Koristi svojstvo // Koristi svojstvo

}

Budite oprezni prilikom korištenja svojstava. Iako ona daju podršku za učahurivanje tako što stvarne vrijednosti čine privatnima, sama su svojstva zapravo metode članice, a metodu članicu ne možete pozvati dok ne inicijalizirate sve varijable članice.

Poglavlje 7: Strukture

|

135

POGLAVLJE 8

Sučelja

Sučelje (engl. interface) je ugovor koji klijentu jam či kako će se klasa ili struktura ponašati. Kad klasa (ili struktura) implementira sučelje ona svakom potencijalnom klijentu kaže „ja jam čim da ću podržati metode, svojstva, dogañaje i indekse ovog sučelja" (informacije o metodama i svojstvima potražite u poglavlju 4, informacije o dogañajima u poglavlju 12, a više o indeksima u poglavlju 9). Sučelje nudi alternativu apstraktnoj klasi za stvaranje ugovora izmeñu klasa i njihovih I klijenata. Ti se ugovori očituju korištenjem ključne riječi interface koja deklarira refe- 1 rentni tip koji učahuruje ugovor. j Prilikom definiranja sučelja možete definirati metode, svojstva, indeksere i/ili dogañaje koje će implementirati klasa koja implementira sučelje. Sučelja se često usporeñuju s apstraktnim klasama. Apstraktna klasa služi kao osnovna klasa za obitelj izvedenih klasa, dok bi se sučelja trebala miješati s ostalim stablima nasljeñivanja. Ono što je u ovom poglavlju navedeno za klasu vrijedi i za strukturu, osim ako nije drugačije navedeno.

Kad klasa implementira sučelje, ona mora implementirati sve dijelove tog sučelja (metode, svojstva itd.). Klasa, u stvari, kaže „ pristajem na ispunjavanje ugovora koji je definiran ovim sučeljem". * . 4*,

Napomena za Java programere: C # ne podržava upotrebu polja konstanti (konstanti članica) u sučelju. Najbliža alternativa je korištenje f jf; nabrojanih konstanti (enumeracija).

U poglavlju 5 saznali smo kako nasljeñivanje iz apstraktne klase implementira odnos to-je. Implementacija sučelja, s druge strane, definira odnos drugačiji od ovog, a koji se naziva (nimalo iznenañujuće) odnos implementacije. Postoji fina razlika izmeñu ova dva odnosa. Auto je vozilo, ali bi mogao implementirati sposobnost MožeSeKupitiAkoU zmešVelikiKredit (kao što to, na primjer, može i kuća). 136

Mješavine USomerv*Neu

Pretvaranje polja Polja se mogu meñusobno pretvarati ako su njihove dimenzije jednake i ako je moguće pretvaranje izmeñu tipova elemenata. Implicitna se pretvorba može provesti ako se elementi mogu implicitno pretvarati; u suprotnom pretvaranje mora biti eksplicitno. Polje izvedenih objekata se, naravno, može pretvoriti u polje osnovnih objekata. U primjeru 9-7 prikazana je pretvorba polja korisnički definiranih tipova Employee u polje objekata.

180

|

Programiranje C#

I

primjer 9-7. Pretvaranje polja Ijegion Using directives using System; using System.Collections.Ceneric; using System.Text; (fendregion namespace ConvertingArrays // Stvara objekt koji // možemo č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 počinje stvaranjem jednostavne klase Employee, kao i ranije u poglavlju. Klasa Tester sada sadrži novu statičku metodu PrintArray() koja kao parametar prihvaća 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 premošćuje 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. Budući 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 možda nije potrebno, ali ne može biti štetno i omogućava vam polimorfno tretiranje tih objekata.

Sortiranje polja U Array postoje dvije korisne metode Sort() i Reversef). One su potpuno podržane za

polja ugrañenih C # tipova poput String. Primjena tih metoda na klase koje ste sami stvorili nešto je kompliciranija jer morate implementirati sučelje IComparable (pogledajte odjeljak „Implementacija sučelja 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. Korištenje 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 počinje stvaranjem polja myArray koje sadrži nizove s riječima: "Uho", "is", "John", "Galt”

Polje se ispisuje, a zatim se prosljeñuje 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 sličan način primjer stvara drugo polje myOtherArray koje sadrži sljedeće riječi: "We", "Hold", "These", "Truths", "To", "Be", "Self", "Evident",

Ti se elementi prosljeñuju 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 padajućeg popisa myListBox koja sadrži popis nizova koji su spremljeni u jednodimenzionalnom polju, privatnoj varijabli članici myStrings. Kontrola padajućeg popisa sadrži svojstva i metode članove te svoje polje nizova. Meñutim, prikladno bi bilo polju padajućeg popisa pristupiti s pomoću indeksa, kao da se radi o polju.' Na primjer, takvo bi svojstvo dopuštalo sljedeće iskaze: string theFirstString = my!istBox[o]; string theLastString = myListBox[!ength-l];

Indekser (engl. indexer) je konstrukcija jezika C # koja dopušta da kolekcijama unutar klase pristupite koristeći poznatu sintaksu [ ] za polja. Indekser je posebna vrsta svojstva i sadrži pristupnike get i set koji odreñuju njegovo ponašanje. Svojstvo indeksera se unutar klase odreñuje sljedećom sintaksom: tip

this

[argument tipa]{get; set;}

Povratni tip odreñuje tip objekta koji će indekser vratiti, dok argument tipa odreñuje kakav će se argument koristiti za indeksiranje kolekcije koja sadrži ciljne objekte. Iako se za vrijednosti indeksa obično koriste cjelobrojne vrijednosti, kolekciju možete indeksirati i s drugim tipovima, uključujući nizove. Možete čak i pužiti indekser s više parametafa da biste stvorili višedimenzionalno polje! Ključna riječ th is služi kao referenca objekta u kojem se indekser pojavljuje. Kao i za ostala svojstva, morate definirati pristupnike get i set koji odreñuju 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 padajućeg popisa (ListBoxTest) koja sadrži jednostavno polje (myStrings) i jednostavni indekser za pristupanje sadržaju. Napomena za C++ programere: indekser ima otprilike istu svrhu kao i . Pre°Pterec,vanje indeksnog operatora u C++ ([]). Indeksni operator se u C # ne može preopteretiti, zbog čega je uveden indekser

Primjer 9-9. Korištenje 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 prosljeđene konstruktoru foreach ( string s in initialStrings ) strings[ctr++] = s;

}

}

// Dodaje niz na kraj popisa public void Add( string theString ) if ( ctr >= strings.Length ) // Obrađuje 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. Korištenje jednostavnog indeksera (nastavak)

{ get if ( index < 0 || index >= strings.Length )

{

U Obrađuje loš indeks

return strings[index];

} set // Dodavanje samo kroz metodu za dodavanje i-f ( index >= ctr )

{

// Obrađuje pogreške

} 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 padajućeg popisa sveli smo na nekoliko značajki koje su nam bitne. Popis zanemaruje sve što ima veze s korisničkom kontrolom i fokusira se samo na popis nizova koje padajući popis održava i na metode za rad s njima. U stvarnoj aplikaciji ovo je, naravno, samo mali dio metoda padajućeg popisa čiji je glavni zadatak prikaz nizova i omogućavanje izbora. prvo treba obratiti pažnju na dva privatna člana: private string[] strings; private int ctr = 0;

Padajući popis u ovom programu održava jednostavno polje nizova: strings. Ponovno, u pravom biste padajućem popisu vjerojatno koristili složeniji i dinamičniji 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 jednostavnosti, polju dodaju redoslijedom primanja. |

u '.' J

_ _ _ 3&

Kako ne možete znati koliko će se nizova dodati, koristite ključnu riječ Params’ kao što je opisano ranije u ovom poglavlju.

Metoda Add() iz ListBoxTest samo pridružuje novi niz unutarnjem polju. Ključna metoda iz ListBoxTest je indekser. Indekser nema naziva, stoga koristite ključnu riječ this: public string this[int index]

Sintaksa indeksera vrlo je slična 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 zatraženi indeks prihvatljiv, vraća zatraženu vrijednost: get

{ if (index < 0 [| index >= strings.Length)

{ // Obrađuje loš Indeks

} return strings[index];

} Metoda s e t() provjerava da li indeks koji postavljate već ima vrijednost u padajućem popisu. Ako nema, ona postavku tretira kao pogrešku (novi se elementi u ovom pristupu mogu dodati samo s pomoću Add). Pristupnik set iskorištava prednost implicitnog parametra value koji predstavlja sve ono što je dodijeljeno s pomoću indeksnog operatora:

Poglavlje 9: Polja, indekseri i kolekcije

|

187

set

{ if (index >= ctr )

{ // Obrađuje pogreške

} else strings[index] = value;

} Stoga, ako napišete: lbt[5] = "Hello World"

prevoditelj će za objekt pozvati metodu indeksera s e t() i niz Hellokiorld proslijediti kao implicitni parametar value.

In d ekseri i d o d je ljiv a n je U primjeru 9-9 ne možete dodijeliti indeksu koji nema vrijednost. Stoga, ako napišete: lbtjlO] = "wow!” ;

pokrenut ćete obradu pogrešaka u metodi se t() koja će primijetiti d a je proslijeñeni indeks (10) veći od brojača (6). Naravno, za dodjeljivanje možete koristiti metodu s e t (); samo morate upravljati indeksima koje primite. Kako biste to učinili, metodu set () možete promijeniti tako da umjesto trenutne vrijednosti brojača (counter) provjerava vrijednost Length bafera. Ako je unesena vrijednost za indeks koji još uvijek nema vrijednost, trebate ažurirati c tr: { // Dodavanje samo kroz metodu za dodavanje if (index >= strings.Length )

{ // Obrađuje pogreške

} 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 proslijeñenim vrijednostima (npr. provjera je li proslijeñen negativan indeks i premašuje li indeks veličinu temeljnog polja strings[ ]).

To vam omogućava stvaranje „rijetkog" polja u kojem možete dodijeliti na poziciju 10 bez dodjeljivanja na poziciju 9. Ako napišete:

188

|

Programiranje C#

T? ■ '4

:

l :

■4 v

jzlaz će biti: lbt[0] lbt[1] lbt[2] lbt[3] lbt[4] lbt[5] lbt[6]

Hello Universe Who Is lohn Galt

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 onačno se svaka v rije d n o st p rik a z u je u petlji: for (in t i = 0;i= strings.Length )

{ // Obrađuje loš indeks

} •

return strings[index];

} set

{ strings[index] = value;

}

190

j

}

Programiranje C#

primjer 9-10. Preopterećimnje 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 )

{ // Obrađuje 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. Preopterećivanje 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 II II

Kraj for Kraj main Kraj tester

} Primjer 9-10 je gotovo identičan primjeru 9-9, dodano je jedino preopterećeno indeksiranje koje može odgovarati nizu i metoda fmdString koja podržava taj indeks. Metoda fmdString jednostavno iterira kroz nizove unutar myStrings dok ne pronañe niz koji počinje ciljnim nizom koji se koristi u indeksu. Kada pronañe takav niz, vraća indeks tog niza; u suprotnom vraća vrijednost - l . U metodi Main() možemo vidjeti kako korisnik u indeks prosljeñuje segment niza, kao da se radi o cjelobrojnoj vrijednosti: lbt["Hel"] = “GoodBye";

Time se poziva preopterećeni indeks koji provodi osnovnu provjeru pogrešaka (u ovom slučaju provjerava sadrži li proslijeñeni niz barem jedno slovo) i zatim vrijednost (Hel) prosljeñuje do fmdString. Zatim vraća indeks koji se koristi za indeksiranje polja myStrings: return this[findString(index)];

Vrijednost set funkcionira na isti način: myStrings[findString(index)] = value;

a\

možete vidjeti uklanjanjem komentara iz sljedećeg reda u metodi flain(): lbt["xyz"] = "oops";

Ispravno rješavanje problema kad dobar niz nije pronañen ostavljeno je , kako se kaže, kao vježba za čitatelja. Poruku o pogrešci možete prikazati ili korisniku možete omogućiti oporavak od pogreške.

192

|

Programiranje C#

Sučelja kolekcija NET kostur pruža dva skupa standardnih sučelja za enumeriranje i usporeñivanje kolekcija: tradicionalne (nesigurne za tipove) i nove, sigurne za tipove, generičke kolekcije. U ovoj knjizi usredotočit ćemo se na nova sučelja kolekcija sigurna za tipove jer su takva sučelja mnogo bolja. Sučelje ICollection bilo kojeg specifičnog tipa možete deklarirati tako da opći tip u deklaraciji sučelja () 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 upotrebi slični predlošcima u jeziku C++. Meñutim, budući da se generički tipovi proširuju u specifičan tip tijekom izvoñenja, J IT prevoditelj može dijeliti kod izmeñu više instanci, te je kod znatno kraći od onog koji se generira upotrebom C++ predložaka.

Ključna generička sučelja 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

Sučelje

Svrha

ICollection

Osnovno sučelje za generičke kolekdje.

IEnumerator IEnumerable

Enumerira kolekdju s pomoću iskaza foreach.

ICollection

Implementiraju ga sve kolekdje jer pruža metodu CopyTo( ), kao i svojstva Count, IsSynchronized i SyncRoot.

IComparer

Usporeñuje dva objekta unutar kolekdje kako bi se kolekcija mogla sortirati.

lComparable lList

Koriste ga kolekdje koje se mogu indeksirati po poljima.

IDictionary

Koristi se za kolekdje temeljene na parovima ključ/vrijednost kakva je Dict ionary.

Sučelje IEnumerable Iskaz foreach u ListBoxTest možete podržati implementacijom sučelja IEnumerable (pogledajte primjer 9-11). IEnumerable sadrži samo jednu metodu, GetEnumerator() koja vraća implementaciju sučelja IEnumerator. Jezik C# pruža posebnu pomoć pri stvaranju enumeratora, korištenjem nove ključne riječi 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 inačicama u C # su dostupna inegenerička sučelja (npr. ICollection, IEnumerator), ali ona nisu obrađena u ovoj knjizi jer su zastarjela.

Poglavlje 9: Polja, indekseri i kolekdje

|

193

Primjer 9-11.

List B ox

kao klasa koja se može enumerirati (nastavak)

using System.Text;

#endregion namespace Enumerable

{ public class ListBoxTest : IEnumerable

{ private string[] strings; private int ctr = 0; // Klase koje se mogu nenumerirati mogu vratiti enumerator public IEnumerator 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 proslijeđene 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 ) • { // Obrađuje loš indeks

} return strings[index];

} set

{

194

|

Programiranje C#

frimj e r ^

~ ' - ' ■ s* Box k

ao klasa koja se može 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 počinje u metodi Main() stvaranjem novog objekta ListBoxTest i prosljeñivanjem dva niza do konstruktora. Nakon stvaranja objekta stvara se polje Strings u kojem ima prostora za osam nizova. Četiri dodatna niza se dodaju s pomoću metode Add i drugi se niz ažurira, baš kao u prethodnom primjeru. Velika promjena u ovoj inačici programa je pozivanje petlje foreach koja uzima svaki niz iz padajućeg popisa. Petlja foreach automatski koristi sučelje IEnumerable pozivajući metodu GetEnumerator(). Metoda GetEnumerator je deklarirana tako da vraća lEnumerator niza: public IEnumerator GetEnumerator()

Implementacija iterira kroz polje nizova, pružajući svaki niz:

Poglavlje 9: Polja, indekseri i kolekcije

|

195

-foreach ( string s in strings )

{ yield return s;

} Sve sto je potrebno za praćenje redoslijeda elemenata, ponovno postavljanje iteratora i tako dalje, pruža kostur.

Ograničenja Ponekad morate osigurati da su elementi koje dodajete generičkom popisu u skladu s odreñenim ograničenjima (npr. da su izvedeni iz odreñene osnovne klase ili da implementiraju odreñeno sučelje). U sljedećem primjeru implementirat ćemo pojednostavljen jednostruko povezan popis koji se može sortirati. Popis se sastoji od čvorova (Nodes) i svaki Node mora zadovoljavati uvjet da tipovi koji mu se dodaju implementiraju sučelje IComparer. To možete učiniti sljedećim iskazom: public class Node : IComparable where T : IComparable

Ovaj iskaz definira generički Node koji sadrži tip T. Node T implementira sučelje IComparable, što znači da se dva čvora T mogu usporediti. Klasa Node je ograničena (where T : IComparable) na samo one tipove koji implementiraju sučelje IComparable. Tip T,

stoga, možete zamijeniti bilo kojim tipom koji implementira sučelje IComparable. U primjeru 9-12 prikazana je cjelokupna implementacija koja je analizirana u sljedećim odlomcima.

Primjer 9-12. Upotreba ograničenja using System; using System.Collections.Ceneric; namespace UsingConstraints

{ public class Employee : IComparable

{ private string name; public Employee(string name)

{ this.name = name;

} public override string To String O

{ • return this.name;

} U Implementira sučelje public int CompareTo(Employee rhs)

{ return this.name.CompareTo(rhs.name);

}

196

|

Programiranje C#

primjer 9-12. Upotreba ograničenja (nastavak) public bool Equals(Employee rhs)

{ return this.name ~

rhs.name;

} I I Čvor mora implementirati IComparable od Node od T. I I Ograničava Nodes da uzimaju samo stavke koje implementiraju IComparable I I upotrebom ključne riječi where. public class Node : IComparable where T : IComparable

{ // Članovi polja private T data; private Node next = nuli; private Node prev = nuli; // Konstruktor public Node(T data)

{ this.data = data;

} I I Svojstva public T Data { get { return this.data; } } public Node Next

{ get { return this.next; }

} public int CompareTo(Node rhs)

{ // Funkcionira zbog ograničenja return data.CompareTo(rhs.data);

} public bool Equals(Node rhs)

{ return this.data.Equals(rhs.data);

} I I Metode public Node Add(Node 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 sljedeči i-f (this.prev != nuli)

{

Poglavlje 9; Polja, indekseri i kolekcije

|

Primjer 9-12. Korištenje ograničenja (nastavak) this.prev.next = newNode; newNode.prev = this.prev;

} // Postavlja prev u tekućem čvoru da pokazuje na novi čvor this.prev = newNode; // Vraća newNode u slučaju da je to novo zaglavlje return newNode;

}

else

// Ide nakon mene

.

{

.

.

.

// ako imam sljedeći, prosljeđuje novi čvor // na uspoređivanje if (this.next '.= nuli)

{

this.next.Add(newNode);

} // Nemam sljedeći pa postavi novi čvor // da bude moj sljedeći 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 where T : IComparable

{ // Polja članovi private Node headNode = nuli; // Svojstva // Indekser public T this[int index]

198

|

Programiranje C#

Primjer 9-12. Korištenje ograničenja (nastavak)

{ get

{ int ctr = o; Node node = headNode; while (node != nuli && ctr Objekt Employee mora implementirati prilagoñenu inačicu metode CompareTo() koja preuzima usporedbu i usporeñuje 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 unaprijeñen 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

208

1 Programiranje C#

ifritnjer 9-15- Sortiranje polja prema ID-ovima zaposlenika godinama staža (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;

}

}

/'/ Statička 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 prilagođeni uspoređivao 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 staža (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;

} // Ugniježđena klasa koja implementira IComparer public class EmployeeComparer : IComparer

{ // Privatna varijabla stanja private Employee.EmployeeComparer.ComparisonType whichComparison; // Enumeracija tipova za uspoređivanje 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 staža (nastavak) static void Main()

{ List empArray = new List(); // Generira slučajne 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 sadržaj 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 sadržaj 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 možete vidjeti Employee objekte redoslijedom kojim su dodani u H L ist. Vrijednosti identifikatora zaposlenika i godine staža poredane su nasumično U II drugom se bloku vide rezultati sortiranja prefha identifikatoru zaposlenika, a u trećem '% se vide rezultati sortiranja prema godinama staža. l| Ako stvarate kolekcije kao u primjeru 9-11 i želite implementirati sučelje IComparer, vjerojatno ćete korištenjem ranije opisanih ograničenja morati osigurati da svi tipovi smješteni u polje implementiraju sučelje IComparer (kako bi se mogli sortirati).

Redovi Red (engl. queue ) predstavlja kolekciju koja funkcionira na načelu prvi-unutra, prvivan (engl. first in, first out, FIFO). Obično se usporeñuje 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 ograničenim resursom. Na primjer, možda ćete trebati poslati poruke resursu koji istovremeno može obraditi samo jednu poruku. U tom biste slučaju stvorili red poruka kako biste svojim klijentima mogli reći: „Vaše su nam poruke bitne i zato se obrañuju 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()

Određuje pripada li red Oueue.

CopyTo()

Kopira elemente Oueue u postojeće jednodimenzionalno polje.

Dequeu e()

Briše i vraća objekt na početku Oueue.

Enqueue()

Dodaje objekt na kraj Oueue.

GetEnumerato r O

Vraća enumerator Oueue.

Peek()

Vraća objekt na početku Oueue, ali ga ne briše.

ToArray()

Kopira element u novo polje.

Elemente možete dodati u red naredbom Enqueue, a iz reda ih možete ukloniti naredbom Dequeue ili s pomoću 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 intOueue = new Oueue(); U Popunjava polje for ( int i = O; i < 5 ; i++ )

intOueue.Enqueue( i * 5 );

} // Prikazuje red Console.Write( "intOueue values:\t" ); PrintValues( intOueue ); // Briše element iz reda. Console.WriteLine( "\n(Dequeue)\t{0 }", intOueuee.Dequeue() ); // Prikazuje red. Console.Write( "intOueue values:\t" ); PrintValues( intOueue ); // Briše 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 myCollection) {

: IEnumerator 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 uštedjeli na prostoru, no naredbu Enqueue možete koristiti i za korisnički definirane objekte. U izlazu možete 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 omogućava da prvi element vidite bez uklanjanja. Kako klasa Oueue podržava enumeriranje, možete ju proslijediti do metode PrintValues koja je pružena kao sučelje IEnumerable. Ova pretvorba je implicitna. U metodi PrintValues pozivate GetEnumerator, jedina metoda svih IEnumerable klasa. To vraća IEnumerator koje zatim možete koristiti za enumeraciju svih objekata u kolekciji.

Stogovi Stog (engl. stack ) je kolekcija koja funkcionira na načelu posljednji-unutra, prvi-van (engl. last in, first out, LIFO), poput hrpe tanjura na švedskom stolu ili hrpe novčića na vašem 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(). Najvažnije 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 ( )

Utvrñuje 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 postojeće jednodimenzionalno polje.

CetEnumerator()

Vraća enumerator S tack.

PeekO

Vraća objekt svrha S ta c k , ali ga ne uklanja.

PopO

Vraća i briše objekt svrha S tack .

Push()

Dodaje objektna vrh S tack.

ToArray()

Kopira elemente u novo polje.

Tipovi L ist, Oueue i Stack sadrže preopterećene metode CopyTo() i ToArray() za kopira-

nje elemenata u polje. U slučaju Stack metoda CopyTo() će kopirati elemente u postojeće jednodimenzionalno polje, prepisujući sadržaj polja počevši od indeksa koji zadate. Metoda ToArray() vraća novo polje u kojem se nalazi sadržaj 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 intStack = new Stack(); // Popunjava polje for ( int i = 0; i < 8; i++ )

{ intStack.Push( i * 5 );

} // Prikazuje stog. Console.Write( "intStack values:\t" ); PrintValues( intStack ); // Briše 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 ); // Briše 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 briše. Console.WriteLine( "\n(Peek)

Vt{0} ,

intStack.Peek() ); // Prikazuje stog. Console.Write( "intStack values'.Vt" );

1

PrintValuesj intStack ); // Deklarira objekt polja koji će sadržavati // 12 cjelobrojnih vrijednosti int[] targetArray = new int[i2]; for (int i = 0; i < targetArray.Lengthj i++) targetArray[i] = i * 100 + loo; // Prikazuje vrijednosti odredišne instance Array. Console.WriteLine( "\nTarget array:

);

PrintValues( targetArray ); // Kopira cjelokupni izvorni Stack u odredišnu // instancu Array s početkom na indeksu 6. intStack.CopyTo( targetArray, 6 ); // Prikazuje vrijednosti odredišne instance Array. Console.WriteLine( "\nTarget array after copy: PrintValues( targetArray );

} public static void PrintValues( IEnumerable myCollection ) ^

IEnumerator 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() može se vidjeti pregledom ciljnog polja prije i nakon pozivanja metode |jjyTo()- Elementi polja se prepisuju počevši od zadanog indeksa (6).

.ječnici ? R j e č n i k (engl. dictionary) je kolekcija koja ključ povezuje s vrijednosti. U rječniku odreiSltJenogjezika se riječ (ključ) povezuje s odgovarajućom definicijom (vrijednost).

Vrijednost rječnika možete vidjeti ako zamislite kako vam je potreban popis glavnih f t gradova američkih saveznih država. To možete postići tako da ih stavite u polje: stringf] stateCapitals = new string[50];

i P o lje stateCapitals će sadržati pedeset glavnih gradova. Svakom se glavnom gradu

•pristupa kao pomaku u polju. Na primjer, ako želite pristupiti glavnom gradu države Arkansas, potrebno je znati kako je Arkansas četvrta savezna država prema abecednom redu: string capitalOfArkansas = 5tateCapitals[3];

Korištenje polja za pristup glavnim gradovima prilično je nepraktično. Naposljetku, ako mi je potreban glavni grad države Massachusetts, ne mogu jednostavno odrediti kako je Massachusetts 21. država po abecedi. Mnogo bi praktičnije bilo glavni gradi povezati s nazivom države. Rječnik vam omogućava da vrijednost (u ovom slučaju, glavni grad) spremite pod ključem (u ovom slučaju, naziv države). Rječnik koji nudi .N ET kostur može bilo kakav ključ (niz, cjelobrojnu vrijednost, objekt itd.) povezati s bilo kojom drugom vrijednosti (nizom, cjelobrojnom vrijednosti, objektom itd.). Obično je ključ, razumljivo, prilično kratak, dok je vrijednost prilično složena. Dobar se rječnik mora odlikovati jednostavnim dodavanjem i brzim vraćanjem vrijednosti (pogledajte tablicu 9-6). Tablica 9 - 6 . M etode i svojstva rje čn ika ( Metoda ili svojstvo Count

Svrha Javno svojstvo koje uzima broj elemenata u Dictionaxy.

It:em()

lndekserzaDictionary.

Keys

Javno svojstvo koje uzima kolekciju s ključevima za Dictionary (pogledajte i svojstvo Values).

Values

Javno svojstvo koje uzima kolekciju koja sadrži vrijednosti u Dict ionary (pogledajte i svojstvo

Add ( )

Dodaje unos s odreñenim ključem (Key) i vrijednosti (Value).

Clear( )

Uklanja sve objekte izDictionary.

Keys).

Poglavlje 9: Polja, indekseri i kolekcije

|

217

Tablica 9-6. Metode i svojstva rječnika (nastavak)



Metoda ili svojstvo

Svrha

ContainsKey()

Utvrñuje pripada li odreñeni ključ D ic t io n a r y .

ContainsValue()

Utvrñuje pripada li odreñena v r ije d n o s tO ic tio n a ry .

GetEnumerator()

Vrača enumerator za D ic t io n a r y .

GetObjectData()

Implementira sučelje I S e r i a l i z a b l e i vrača podatke potrebne za serijalizaciju D ic t io n a r y .

Remove()

Uklanja unos s odreñenim ključem.

Ključ u klasi Dictionary može biti primitivni tip ili instanca korisnički definiranog tipa (objekt). Objekti koji se koriste kao ključevi u Dictionary moraju implementirati metode GetHashCode() i Equals. U većini slučajeva možete jednostavno koristiti naslijeñenu implementaciju izO bject.

IDictionary Rječnici implementiraju sučelje IDictionary (K označava tip ključa, a V označava tip vrijednosti). IDictionary pruža javno svojstvo Item. Ono vraća vrijednost sa zadanim ključem. Deklaracija svojstva Item u C# glasi: V[K key] {get; set;}

U C# svojstvo Item je implementirano s pomoću indeksnog operatora ([]). Stoga elementima svih Dictionary objekata možete pristupiti koristeći sintaksu pomaka, kao da se radi o polju. U primjeru 9-18 prikazano je dodavanje elemenata u Dictionary koji se zatim vraćaju s pomoću 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 rječnik. Dictionary Dictionary = new Dictionaiy(); 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 počinje instanciranjem novog Dictionary. Tip ključa i vrijednosti deklagjranisu kao string. jJDictionary se dodaju četiri para ključ-vrijednost. U ovom se primjeru broj zdravstvenog osiguranika dodaje imenu osobe (namjerno su korišteni lažni brojevi osiguranika). iNakon dodavanja elemenata odreñenom se unosu u rječniku pristupa korištenjem broja osiguranika kao ključa. Ako kao ključ koristite referentni tip i taj tip može mutirati (nizovi ne mogu mutirati), ne smijete mijenjati vrijednost objekta ključa nakon što ga počnete koristiti u rječniku. Ako, na primjer, kao ključ koristite objekt Employee i promijenite identifikator zaposlenika, doći će do problema ako metode Equals i GetElashCode, koje rječnik konzultira, koriste to svojstvo.

Poglavlje 9: Polja, indekseri i kolekcije

|

219

POGLAVLJE 10

Nizovi i regularni izrazi

Nekad su ljudi kupovali računala isključivo zbog obrade brojčanih vrijednosti. Prva računala su se najprije koristila za izračun putanje projektila (iako neki nedavno objavljeni dokumenti otkrivaju kako su se koristila i za dešifriranje). U svakom slučaju, nekoć se programiranje učilo na katedrama za matematiku velikih sveučilišta, a informatika se smatralo matematičkom disciplinom. Danas se većina programa bavi nizovima slova, a ne nizovima brojeva. Ti se nizovi obično koriste za obradu riječi, rad s dokumentima i stvaranje Web-stranica. C # pruža ugrañenu podršku za potpuno funkcionalan tipa string. Što je još važnije, C # nizove tretira kao objekte koji učahuruju sve metode za manipulaciju, sortiranje i pretraživanje koje se obično primjenjuju na niz slova. Napomena za C++ programere: u C # niz je tip prve klase, a ne polje znakova.

_LflS* Složene operacije s nizovima i usporeñivanje uzoraka potpomognuti su regularnim izrazima (engl. regular expressions ). C # kombinira snagu i složenost sintakse regularnih 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 naučiti 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 ulančavaju te kako se novi nizovi mogu izraditi s pomoću klase StringBuilder. Uz to ćete naučiti kako se klasa RegEx može koristiti za usporeñivanje nizova na temelju složenih regularnih izraza

Nizovi c # nizove tretira kao tipove prve klase koji su fleksibilni, moćni i jednostavni za korištenje. U programiranju u jeziku C # za tip kostura (npr. int ili Int32) obično se koristi C # alias, ali možete slobodno koristiti i temeljni tip. C # programeri stoga strin g (malo početno slovo) i temeljni tip kostura String (veliko početno slovo) koriste naizmjenično.

Deklaracija klase String glasi: public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable

Ova deklaracija otkriva kako se radi o zapečaćenoj klasi, što znači da izvoñenje iz klase String nije moguće. Klasa takoñer implementira četiri sistemska sučelja - Icomparable, ICloneable, IConvertible i IEnumerable - koja odreñuju funkcionalnosti koje klasa String dijeli s ostalim klasama u .NET kosturu. Svaki objekt strin g je nepromjenjivi slijed Unicode znakova. Činjenica da je String nepromjenjiva klasa znači da metode koje naizgled mijenjaju niz zapravo vraćaju promijenjenu kopiju. Izvorni niz ostaje netaknut u memoriji dok se ne sakupi kao otpad. To može 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, sučelje IComparable implementiraju tipovi čije se vrijednosti mogu poredati. Nizovi se, na primjer, mogu poredati po abecedi. Svaki se niz može usporediti s nekim drugim nizom kako bi se odredio njihov redoslijed u poredanom popisu. Klase IComparable implementiraju metodu CompareTo. Sučelje IEnumerable takoñer je opisano u poglavlju 9, a omogućava vam korištenje konstrukta foreach za enumerañju niza string kao kolekcije chars. Objekti ICloneable mogu stvoriti nove instance s istom vrijednosti koju imaju izvorne instance. U ovom je slučaju moguće klonirati niz kako bi se proizveo novi niz koji sadrži iste vrijednosti kao izvorni niz. Klase ICloneable implementiraju metodu Clone().

Redanje niza je jedna od brojnih leksičkih operacija koje djeluju na vrijednost niza i u obzir uzimaju kulturološke informacije koje se temelje na eksplicitno deklariranoj kulturi ili na implicitnoj tekućoj 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 usporeñuje redoslijed i stoga je, bez obzira na kulturu, „a“ veće 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 vraća 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 slučaju sx je stvoren k a o klon o d si. Prvi iskaz FJriteLine ispisat ć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 vraća sadržaj 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 pružaju metode koje olakšavaju 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 najčešće 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 sadržati i kontrolne znakove (engl. escape characters), kao što su \n i \t, koji počinju obrnutom kosom crtom (\). Dva ranije navedena znaka služe za označavanje 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 ođenje putanje), tu ob rn ut u k os u crtu m or at e označiti 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 pomoću doslovnih literala nizova (engl. verbatim string literals), koji počinju simbolom @ . To konstruktoru String govori kako se niz može koristiti doslovno, čak i ako zauzima više redova ili sadržava kontrolne znakove. U doslovnom literalu niza obrnute kose crte i znakovi iza njih smatraju se samo dodatnim znakovima u nizu. Stoga su sljedeće 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 više redova:

222

|

Programiranje C#

string literalTwo = "Line OneNnLine Two"; string verbatimLiteralTwo = @"Line One Line Two";

(TJ, I uV

Ako unutar doslovnog niza imate dvostrukenavodnike, morateihizdvokako prevoditelj znao na kojem mjestu se doslovni niz završava

Ove su deklaracije ponovno istoznačne. Možete koristiti onu koja vam se čini prikladnija ili jednostavnija.

Metoda ToStringO Još jedan način stvaranja nizova jest pozivanje metode ToStringO na objekt i dodjeljivanje rezultata varijabli niza. Svi ugrañeni tipovi premoščuju ovu metodu kako bi se pojednostavila pretvorba vrijednosti (često se radi o brojčanoj vrijednosti) u njenu nizovnu reprezentaciju. U sljedećem 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 vraća objekt String koji se zatim dodjeljuje integerString. Klasa String pruža brojne preopterećene konstruktore koji podržavaju različite tehnike za dodjelu vrijednosti niza tipovima string. Neki od tih konstruktora omogućavaju vam stvaranje niza prosljeñivanjem polja znakova ili pokazivača na znak. Prosljeñivanje polja znakova kao parametra konstruktoru String stvara novu instancu niza kompatibilnu s CLR-om. Za prosljeñivanje pokazivača na znak potreban je marker unsafe koji je objašnjen u poglavlju 22.

Rad s nizovima Klasa string pruža razne metode za usporedbu, pretraživanje i rad s nizovima, a najvažnije 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 statičko polje koje predstavlja prazan niz.

Compare()

Preopterećena javna statička metoda koja usporeñuje dva niza.

CompareOrdinal()

Preopterećena javna statička metoda koja usporeñuje dva niza bez obzira na lokacijske ili kulturološke postavke.

Concat()

Preopterećena javna statička metoda koja novi niz stvara iz jednog ili više nizova.

Copy()

Javna statička metoda koja novi niz stvara kopiranjem drugog niza.

Equals()

Preopterečena javna statička metoda i metoda instance koja utvrñuje 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()

Preopterećena javna statička metoda kojaformatira nizspomoću specifikacijeformata.

Join()

Preopterećena javna statička metoda koja ulančava zadani nizizmeđu svakog elementa polja nizova.

Chars

Indekser niza.

Length

Brojznakova u instanci.

Com pare To O

Niz uspoređuje sdrugim nizom.

CopyTo()

Kopira zadani broj znakova u polje Unicode znakova.

EndsWith()

Označava odgovara lizadani nizzavršetku ovog niza.

Equals()

Određuje imaju lidva niza istuvrijednost.

Insert()

Vraća novi nizsumetnutim zadanim nizom.

Lastlndex0f()

Daje indeks posljednje pojave određenog 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()

Briše zadani brojznakova.

Split()

Vraća podnizove odvojene određenim znakovima u polju niza.

StartsWith()

Označava počinje iinizzadanim znakom.

Subst ri ng O

Dohvaća podniz.

ToCharArray()

Kopira znakove izniza u poljeznakova.

ToLower()

Vraća kopiju niza napisanu malim slovima.

ToUpper()

Vraća kopiju niza napisanu velikim slovima.

Trim()

Uklanja sve pojave skupa zadanih znakova spočetka izavršetka niza.

TrimEnd()

Ponaša se kao Tr im (), alina završetku niza.

TrimStart()

Ponaša se kao Trim(),ali na početku niza.

U primjeru 10-1 prikazana je upotreba nekih od navedenih metoda, uključujući Compare(), Concat() (i preopterećeni 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 uspoređivanja

// Uspoređuje 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 ); // Preopterećeno uspoređivanje, uzima Boolean //parametar (true = ignorira razliku između 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 preopterećeni 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 preopterećeni operator string s9 = s8; Console.WriteLine( "s9 = s8: {0}", s9 ); // Tri naćina za uspoređivanje. 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 dužina 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 završava 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" ) ); // Vraća indeks podniza Console.WriteLine( "\nThe first occurrence of Training " ); Console.WriteLine( "in s3 is {0}\n", S3.1nd ex0f( "Training" ) ); // Umeće rijeć excellent prije training string slO = s3.Insert( 101, "excellent " ); Console.WriteLine( "slO: {0}\n", slO ); // Možete kombinirati dva niza na sljedeći naćin: string sli = s3 .Insert( s3.Index0f( "Training" ), "excellent " ); Console.WriteLine( "sli: {0 }\n", sli );

}

}

}

Primjer 10-1 počinje 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 treći je doslovni literal niza. Prvo se s i usporeñuje sa s2. Metoda Compare() je javna statička metoda klase string i preopterećena je. Prva preopterećena inačica uzima dva niza i usporeñuje ih: // Uspoređuje 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 može vratiti različite 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 veći od drugog niza U ovom slučaju 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 preopterećenu inačicu metode Compare() koja uzima treći, Boolean, parametar čija vrijednost odreñuje treba li se u usporedbi zanemariti razlika izmeñu velikih i malih slova. Ako je vrijednost ovog parametra „zanemari razliku" true, usporedba se izvodi bez obzira na razliku izmeñu velikih i malih slova, kao u sljedećem 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 izmeñu velikih i malih slova zanemaruje i rezultat je nula, što označava kako su dva niza identična (bez obzira na razliku izmeñu velikih i malih slova): compare insensitive s4: abcd, s2: ABCD, result: 0

U primjeru 10-1 zatim se nastavljaju nizovi. Postoji nekoliko načina da se to postigne. Možete koristiti metodu ConcatO koja je statička javna metoda klase string: string s6 = string.Concat(sl,s2);

ili jednostavno možete koristiti preopterećeni operator ulančavanja (+): string s7 = si + s2;

U oba se slučaja u izlazu vidi kako je ulančavanje bilo uspješno: s6 concatenated from si and s2: abcdABCD s7 concatenated from si + s2: abcdABCD

Slično tome se i kopiranje niza može izvesti na dva načina. Prvi je s pomoću statičke metode Copy(): string s8 = string.Copy(s7);

Poglavlje 10: Nizovi i regularni izrazi

|

227

Time se zapravo stvaraju dva zasebna niza s istim vrijednostima. Budući da se nizovi ne mogu mijenjati, ovaj je način beskoristan. Bolje je koristiti preopterećeni 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 pruža tri načina za provjeru jednakosti dva niza. Prvo, možete koristiti preopterećenu metodu Equals() i izravno upitati s9 ima li s8 istu vrijednost: Console.WriteLine("\nDoes s9.Equals(s8)?: {o}", s9.Equals(s8));

Drugi je način prosljeñivanje oba niza do statičke metode Equals() klase String: Console.WriteLine(''Does Equals(s9,s8)?: {0}", string.Equals(s9,s8));

Konačno, možete koristiti i operator jednakosti (==) klase String: Console.WriteLine("Does s9==s8?: {0}", s9 == s8);

Svi će načini vratiti Boolean vrijednost, kao što je prikazano u izlazu: Does 59.Equals(s8)?: True Does Equals(s9,s8)?: True Does s9==s8?: True

Sljedećih par redova u primjeru 10-1 koristi indeksni operator ([]) za traženje zadanog znaka unutar niza, a svojstvo Length koriste za vraćanje 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 sljedeći: String s9 is 8 characters long. The 5 th character is A

Metoda EndsWith() pita niz da li se na završetku niza nalazi podniz. Stoga, s3 možete prvo pitati završava li s Training (što nije slučaj), a zatim završava li s Consulting (što je istinito): I I Testira da li niz završava

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 može 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, a metoda In sert() umeće novi podniz u kopiju izv orn og niza. Sljedeći 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 možete koristiti za umetanje riječi excellent i razmaka u tai niz Riječ se, zapravo, umeće u kopiju niza koju vraća metoda In serti) i zatim se dodie ljuje nizu slO: aoajestring slO = s3.Insert(ioi,"excellent"); Console.WriteLine("siO: {o}\n“,slo);

Izlaz je sljedeći: slO: Liberty Associates, Ine. provides custom .NET development, on-site excellent Training and Consulting

Naposljetku, te operacije možete i kombinirati: string sli = s3.Insert(s3.IndexOf("Training"),"excellent ")• Console.WriteLine("su: {0}\n",sll);

da biste dobili identičan izlaz: sli: Liberty Associates, Ine. provides custom .NET development, on-site excellent Training and Consulting

Traženje podnizova Tip String pruža preopterećenu metodu Substring() kojom se iznizova izdvajaju podmzovi. Obje inacice uzimaju indeks koji označava početak izdvajanja, a jedna od dvije mac.ce uzima i drugi indeks koji označava gdje završiti operaciju. Metoda Substring ) ilustrirana je u primjeru 1 0 -2 . angu

Primjer 10-2. Korištenje 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 počinje na o // i završava na ix (početak sljedeće rijeci // tako da si ima one two three

// Pronalazi zadnji razmak u si ix = sl.Last!ndexOf( " " )! // Postavlja s3 na podniz započinjući na // ix, razmak nakon "two" plus jos jedan // thus s3 = "three" string s3 = sl.Substring( ix + 1 ); // Vraća si na podniz koji započinje na 0 // i završava na ix, tako da je niz "one two" si = sl.Substring( 0, ix ); // Vraća ix na razmak između // "one" i "two" ix = sl.Last!ndexOf( " " )> // Postavlja s4 na podniz kojim započinje jedan // razmak nakon ix, tako da je podniz wo .

-

. .U/- 4- r i n n {

1y

+

1

Ii

// vraća si na podniz koji z a p o č i n j e m o // i završava 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 počinje na U nuli , string s5 = s l. Su bs tn ng ( ix + l J,

230

|

Programiranje C#

:t,;mjer 10-2. Korištenje 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 rješenje problema izdvajanja riječi iz &za, a\i je dobra prva aproksimacija. Primjer počinje stvaranjem niza si: string si = “0ne Two Three Four";

tim se ix pridružuje vrijednost zadnjeg razmaka u nizu: ix=sl.LastIndexOf(" ");

e se ix+l izdvaja na završetak reda, a nizu s2 se dodjeljuje vrijednost Four: deći je korak uklanjanje riječi Four iz niza si. To se može učiniti tako da se nizu si dodijeli podniz koji počinje s 0, a završava s ix. si = si.Substring(o,ix);

^fijednost ix se ponovno dodjeljuje posljednjem (preostalom) razmaku koji pokazuje početak riječi 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 rješenje nije elegantno, ali funkcionira i ilustrira upotrebu Substring. Ona nalikuje 'otrebi aritmetike pokazivača u C++, ali nema pokazivača niti nesigurnog koda.

Seljenje nizova inkovitije rješenje problema prikazanog u primjeru 10-2 jest korištenje metode i t ( ) klase String koja služi za razlaganje nizova na podnizove. Za korištenje |ode S p lit() treba proslijediti polje graničnika (znakovi koji označavaju mjesto :le riječi), a metoda vraća 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. Korištenje 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 graničnika za dijeljenje rečenice char[] delimiters = new char[]

{ Space, Comma

1; string output = int etr = l; // Dijeli niz i zatim prolazi kroz rezultirajuće // Polje nizova foreach ( string subString in sl.Split( delimiters ) )

{ output += ctr++; output += output += subString; output += "\n";

} Console.WriteLine( output );

}

}

} Primjer počinje stvaranjem niza za analizu; string si = "One,Two,Three Liberty Associates, Ine.” ;

Graničnici se postavljaju na znakove razmaka i zareza. Zatim se za ovaj niz poziva metoda S p lit( ), a rezultati se prosljeñuju do petlje foreach: foreach (string subString in sl.Split(delimiters)) M e t o d a Split koristi ključnu riječ params pa se k o d m o ž e skratiti na: foreach (string subString in sl . SplitC

',

U potpunosti je izostavljena deklaracija polja.

Počnite s inicijalizacijom izlaza na prazan niz, a zatim izlazni niz sagradite u četin koraka. Ulančajte vrijednost c tr. Zatim dodajte dvotočku, zatim podniz koji je vratilo

232

|

Programiranje C#

i

dijeljenje, a zatim novi red. Prilikom svakog se ulančavanja stvara nova kopija niza, , i sva se četiri koraka ponavljaju za svaki podniz koji metoda StringO pronañe. Ovo ; ponavljanje kopiranja niza je vrlo neučinkovito. problem je u tome što tip niza nije predviñen za ovakvu operaciju. Vi zapravo želite

$ stvoriti novi niz dodavanjem formatiranog niza kroz petlju. Potrebna vam je klasa % StringBuilder.

' Rad s dinamičkim 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

Objašnjenje

Chars

(ndekser.

Length

Uzima ili postavlja duljinu S t r in g B u ild e r .

A ppendj) AppendFormat ( )

5, -

Preopterećena javna m etoda koja niz znakova dodaje na završetak trenutnog S t r in g B u ild e r . Preopterećena javna m etod a koja specifikatore form ata zam jenjuj form atiranom vrijednosti objekta.

In s e r t ( )

Preopterećena javna metoda koja na zadani položaj umeće niz znakova.

Remove()

Uklanja zadane znakove.

R e p la c e O

Preopterećena javna m etoda koja sve instance zadanih znakova zam jenjuje novim znakovima.

■Za razliku od String, StringBuilder se može promijeniti. Prilikom modifikacije StringBuilder 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. Korištenje 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 .

Korištenje klase StringBuilder (nastavak)

// Ko ns tante za razmak i zarez const char Space = ' '; const char Cotnma = ','; // Polje grani čnika s ko jima će se rečenica 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.

Ograničenja graničnika Budući da ste proslijedili graničnike i za razm ake i za zareze, razm ak izm eñu „Assocu ateS“ i Ine “ se vraća 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 izmeñu One, Two i Three), razm ak (kao onaj izm eñu Liberty i Associates) zarez iza kojeg slijedi razm ak tretira na isti način. O va posljednja tnačtca je najproblem atičnija 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 regularnom 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 sljedeći niz: One,Two,Three Liberty Associates, Ine.

? vraća se bilo koji ili svi pripadajući podnizovi (npr. Liberty ili One) ili modificirane inačiče pripadajućih podnizova (npr. LIBerTV ili OnE). Učinak regularnog izraza odreñen je sintaksom samog regularnog izraza. Regularni se izraz sastoji od dva tipa znakova: literata i metaznakova. Literal je znak * koji želite pronaći u ciljnom nizu. Metaznak je poseban simbol koji služi kao naredba za analizator (engl. parser) regularnih izraza. Analizator je stroj odgovoran za razumijevanje regularnog izraza. Ako, na primjer, napišete regularni izraz: ,'(From|To| Subject |Date):

.

■ njemu će odgovarati svi podnizovi koji sadrže riječi ,,Froin“, ,,To“, „Subject" ili „Date" i ako počinju novim redom (A) i završavaju dvotočkom (:). Znak Au ovom slučaju analizatoru regularnih izraza govori kako traženi niz mora počinjati novim redom. Riječi „Froin" i ,,To“ su literali, a metaznakovi - lijeve i desne zagrade (()) te okomite crte (|) - se koriste za grupiranje literala u skupove i označavanje kako se treba poklopiti bilo koja od opcija (A je takoñer metaznak koji se koristi za označavanje početka reda).

r Stoga bi se sljedeći red: v 5

A(From|To|Subject|Date):

mogao pročitati kao: „Pronañi sve nizove koji počinju novim redom iza kojeg slijedi bilo koji od četiri literala, From, To, Subject ili Date iza kojih slijedi dvotočka. Potpuno objašnjenje regularnih izraza suviše je složeno za ovu knjigu, » no sv* regularr|i izrazi koji se koriste u primjerima su objašnjeni. Ako «TV želite u potpunosti razumjeti regularne izraze, preporučam vam knjigu Mastering Regular Expressions u izdanju 0 ’Reilly Media, Ine.

41

Upotreba regularnih izraza: Regex •NET kostur pruža objektno orijentiran pristup usklañivanju i zamjeni regularnih izraza.

Poglavlje 10: Nizovi i regularni izrazi

|

235

49 \

Regularni izrazi se u C # temelje n a r e g e x p iz jezika Perl 5, uključujući

~ *& T

4»M?' J __J

kvalifikatore (??, *?, + ?, {n,m}), pozitivno inegativno traženje te uvjetnu £ ovom poglavlju.

Korištenje R egex g ru p a ' Podnizove koji su pronañeni često je korisno grupirati radi analize odgovarajućeg niza. Na primjer, možda ćete htjeti pronaći i grupirati sve IP adrese pronañene bilo

IP adrese se koriste za obilježavanje računala u mreži i obično imaju oblik x .x .x.x., pri čemu x može biti bilo koja znamenka od 0 do 255 (npr. 192.168.0.1.).

, Klasa Group omogućava stvaranje grupa rezultata na temelju sintakse regularnog izraza ' i predstavlja rezultate iz jednog izraza za grupiranje. Izraz za grupiranje označava grupu i pruža regularni izraz: u grupu će se dodati bilo £ koji poñniz koji odgovara regularnom izrazu. Na primjer, za stvaranje grupe ip možete napisati: -3?

@"(?(\d|\.)+)\s"

ip Klasa Match izvodi iz Group i sadrži kolekciju Groups u kojoj se nalaze sve grupe prona-

. ñene s pomoću Match. Stvaranje i upotreba kolekcije Groups i klasa Group prikazana je u primjeru 10-8. . Primjer 10-8. Korištenje 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. Korištenje 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 više znamenki ili dvotočki iza kojih slijedi bjelina Regex theReg = new Regex( @"(?(\d|\:)+)\s" + // ip address = jedna ili više znamenki iza kojih slijedi točka @"(?(\d|\.)+)\s" + // site = jedan ili više znakova (8"(?\S+)" ); // Uzima kolekciju pronađenih 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 počinje stvaranjem niza za traženje: string st ringl = "

04:03:27 127.0.0 .0

Liber tyAsso ciates . com";

Ovaj niz može biti jedan od mnogih koji su zabilježeni u dnevniku Web poslužitelja ili koji su dobiveni kao rezultat pretraživanja 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 rješavate stvarni problem, možda ćete morati provesti složenije upite i koristiti druge graničnike i složenija pretraživanja. U primjeru 10-8 željeli smo stvoriti jedan Regex objekt za pretraživanje nizova ovog tipa koji bi se onda podijelili u tri grupe: time, ip address i s ite . Niz regularnog izraza je prilično jednostavan te je primjer lako razumjeti. Meñutim, upamtite kako biste u

240

|

Programiranje C#

Istvarnom pretraživanju vjerojatno koristiti samo dio izvornog niza, a ne cijeli izvorni

Spiz kako je ovdje prikazano.

// grupa time = jedna ili više znamenki ili dvotočki // iza kojih slijedi bjelina Regex theReg = new Regex((8"(?(\d|\:)+)\s" + // ip address = jedna ili više znamenki ili točka // iza kojih slijedi razmak @"(?(\d|\.)+)\s" + // site = jedan ili više znakova @"(?\S+)");

^ Usredotočimo se na znakove koji stvaraju grupu: ,’sŽŽi •'£ § > ,

(?(\d]\:) J

1

v

g Zagrade služe za stvaranje grupe. Sve izmeñu otvorene zagrade (neposredno ispred upitnika) t zatvorene zagrade (u ovom slučaju nakon znaka +) je jedna neimenovana gruPaNiz ? toj grupi daje naziv time i grupa se povezuješ odgovarajućim tekstom to , jest regularnim izrazom ( \d|\: )+) \x'''. O vaj se regularni izraz može interpretirati kao T/ »Je“ na **1vise znamenki, odnosno dvotočki iza kojih slijedi razmak“. Na sličan način niz ? imenuje grupu site . Kao i primjer 10-7, i primjer 10-8 traži '•pkolekciju svih rezultata: MatchCollection theMatches = theReg.Matches(stringi);

^ Primjer 10-8 prolazi kroz kolekciju Matches i pronalazi sve Match objekte. 1 Ak°ie duljina (Length) objekta Match veća od nula, Match je pronañen; 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. Moguće je, meñutim, pronaći 1 više od jednog izraza unutar niza. To možete učiniti tako da strin g l u primjeru 10-8 j modificirate na sljedeći način kako bi umjesto jednog pružio više 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 127.O.O.O bar. co m

"

" +

"04:03:29

" ;

Time se u MatchCollection stvaraju tri rezultata pod nazivom theMatches. Generira se sljedeći izlaz: theMatch: 04 :03: 27

127.0.0.0

Liber tyAssoci ates. co m

time: 04 :03: 27 ip: .O.O.O

127

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 sadrži tri Match objekta. Svaki put se kroz vanjsku petlju foreach pronalazi sljedeći Match u kolekciji i prikazuje se njegov sadržaj, foreach (Match theMatch in theMatches)

Za svaki pronañeni Match objekt možete ispisati cijeli rezultat, razne grupe ili oboje.

Korištenje kolekcije CaptureColIection Svaki put kad objekt Regex pronañe 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. Ključno svojstvo objekta Capture jest length koje označava duljinu pronañenog podniza. Kada Match upitate za duljinu, uzet ćete Capture.Length jer Match izvodi iz Group koja zauzvrat izvodi iz Capture. ' *»,

She ma nasljeñivanja regularnih izraza u .NET-u dopušta Match da u svoje sučelje uključi metode i svojstva ovih roditeljskih klasa. Group je, u odreñenom smislu, dohvaćanje: dohvaćanje koje učahuruje zami-f '' sao grupiranja podizraza. Match je, zauzvrat, Group: Match je učahurenje svih grupa podizraza koji čine cijeli rezultat regularnog izraza (više o odnosu to je i drugim odnosima potražite u poglavlju 5).

U CaptureColIection se obično nalazi samo jedan Capture, ali to ne mora obavezno biti tako. Može se na primjer dogoditi da analizirate niz u kojem se naziv tvrtke može

242

|

Programiranje C#

Snalaziti na jednom od dva položaja. Kako biste te položaje grupirali ujedan rezultat,

na dva mjesta u uzorku regularnog izraza stvorite grupu ?: Regex theReg = new Regex(@"(?(\d|\:)+)\s" +

< M-

@"(?\S+)\s" + @"(?(\dl\.)+)\s" + @ “(?\S+)\s");

| Ova grupa regularnog izraza uzima sve odgovarajuće nizove znakova koji se nalaze iza time, ali i sve odgovarajuće nizove koji se nalaze iza ip. Pomoću ovog regularnog i izraza možete analizirati sljedeći niz: string stringl = "04:03:27 lesse 0.0.0.127 Liberty

■U nizu se na oba zadana položaja nalaze nazivi. Rezultat je sljedeći: theMatch: 04:03:27 Jesse 0.0.0.127 Liberty time: 04:03:27 ip: 0.0.0.127 Company: Liberty

Što se dogodilo? Zašto grupa Company prikazuje Liberty? Gdje je prvi termin koji je takoñer pronañen? To se dogodilo jer je drugi termin prepisao prvi. Grupa je, meñutim, uhvatila oba termina. To se može 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*.

t H

public static void Main()

{

// Niz za analiziranje H obratite pozornost da se ime H pojavljuje na dva mjesta string stringl = “04:03:27 3esse 0,0.0.127 Liberty U

H #

Regularni izraz koji grupira tvrtku dvaput

Regex theReg . new Regex( @"(?«:ompany>\S+)\s" +

H

#"(?(\d|\.)+)\s" +

^

@''(?\S+)\s" );

(?(\d| V.)+)\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 počinje s traženjem kolekcije krozkoju će iterirati. theMatch je objekt koji sadrži kolekciju Groups. Kolekcija Groups ima indekser koji uzima niz i vraća jedan objekt Group. Stoga sljedeći red vraća jedan objekt Group: theMatch.Groups["company"]

Objekt Group ima kolekciju Captures. Stoga sljedeći red vraća 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 lokalnoj varijabli cap koja je tipa Capture. U izlazu možete vidjeti kako postoje dva elementa za uzimanje: Desse i Liberty. Drugi element prepisuje prvi u grupi, te se prikazuje samo vrijednost Liberty. Ako, meñutim, pogledate kolekciju Captures, možete vidjeti kako su uzete obje vrijednosti.

244

|

Programiranje C#

Obrada i;

C# , kao i mnogi drugi objektno orijentirani jezici, pogreškama i neuobičajenim uvjetima upravlja s pomoću iznimki. Iznimka (engl. exception) je objekt u kojem su učahu-

'0i

i; •

i

m

$i

rene informacije o neobičnoj pojavi u programu. Važno je napraviti razliku izmeñu programerskih pogrešaka, korisničkih pogrešaka i iznimki. Programersku pogrešku (engl. bug) čini programer i trebalo bi ju ispraviti prije isporuke koda. Iznimke ne predstavljaju zaštitu od programerskih pogrešaka. Iako programerska pogreška može prouzročiti izbacivanje iznimke, u njihovoj obradi ne smijete se osloniti na iznimke. Programerske pogreške trebate ispraviti. Pogrešku može izazvati i akcija korisnika. On, na primjer, može unijeti broj na mjestu na kojem se očekuje slovo. Korisnička pogreška takoñer može izbaciti iznimku, ali to možete spriječiti hvatanjem pogrešaka s pomoču koda za provjeru valjanosti. Kori-

■m sničke pogreške bi se trebale predvidjeti i spriječiti kad god je to moguće. Š8s’

Čak i ako uklonite sve programerske pogreške i predvidite sve korisničke pogreške može doći do predvidljivih problema koji se ne mogu spriječiti. Može, na primjer, ponestati memorije ili možete pokušati otvoriti datoteku koja više ne postoji. Iznimke se ne mogu spriječiti, ali se mogu obraditi kako ne bi blokirale program. Kad program naiñe na neočekivanu situaciju, na primjer kad ponestane memorije, on izbacuje (ili „pokreće ) iznimku. Kad se iznimka izbaci, zaustavlja se izvedba trenutne metode i stog se odmotava dok se ne pronañe odgovarajuću metodu za obradu iznimki. To znači da će se metoda koja se trenutno izvodi prekinuti ako ne može obraditi iznimku, a pozivajuća metoda će dobiti priliku za obradu iznimke. Ako nijedna od pozivajućih metoda ne može obraditi iznimku, to će učiniti CLR, a program će se odmah ugasiti.

Metoda za obradu iznimki (engl. exception handler) je blok koda projektiran za obradu izbačene iznimke. Metode za obradu iznimki implementiraju se kao iskazi catch. U idealnim uvjetima, ako je iznimka uhvaćena i obrañena, program može ispraviti problem i nastaviti s radom. Čak i ako program ne može nastaviti s radom, hvatanjem iznimke omogućava se ispis smislene poruke o pogrešci i prikladno zatvaranje programa.

Ako u metodi postoji kod koji se mora izvoditi bez obzira na izbacivanje iznimke (npr radi oslobañanja dodijeljenih resursa), taj kod možete staviti u blok finally unutar kojeg će on sigurno biti pokrenut, čak i ako doñe 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 sadrži razne tipove iznimki koje program može koristiti. U te se tipove ubrajaju ArgumentNullException, InvalidCastException i OverflowException te mnogi drugi. Napomena za C++ programere: u C # se ne može izbaciti bilo koji objekt - on mora biti izveden iz System. Exception.

Iskaz throw Kako bi se u C # klasi objavila neuobičajena situacija, izbacuje se iznimka. Za to se koristi ključna riječ throw. Sljedeći red koda stvara novu instancu System.Exception i zatim je izbacuje: throw new System.Exception();

Izbacivanje iznimke odmah zaustavlja izvedbu, a CLR traži metodu za obradu iznimki. Ako se metoda za obradu iznimki ne može pronaći u tekućoj metodi, izvedbeni okoliš odmotava stog i provjerava pozivajuće metode sve dok ne pronañe 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 režimu za ispravljanje pogrešaka, pojavit će se okvir s porukom „Exception was unhandled" („Iznimka nije obrañena") kao što je prikazano na slici 11-1. r

Exception of type 'Svstem.&fception' was throrn Troubteshooting T#ps Get general help fbr this exceptiorij _ Search far more Help Online...

iii cij

Actlons View Detail... Slika 11-1. Iznimka koja nije obrañena

^

-I

^k° pridsnete View Detail prikazat će se pojedinosti iznimke koja nije obrañena (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’ v

j System.Exception.

Izvoñenje se smjesta zaustavlja, a CLR u Func2 traži 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 s.?0 Data

{"Exception of type 'System.Exception' was thrown."} nuli nuli “Exception of type 'System.Exception' vvas thrown.”

HelpLink » S lnnerException Message Non-Public members '

Source

\ StackTrace ajffl Static members

~~

i{System.Collections.UstDictionaryInternal}

i “ThrowingAnException“ ," at ThrowingAnExceptlon.Test.Func20 in c:\ldocuments and setthgs

| (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 pomoću ključne riječi catch. :| U primjeru 11-2 iskaz throw se izvodi unutar bloka try , a blok catch obavještava kako J je iznimka obrañena. | 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

{

4(

Console.WriteLine( "Exception caught and handled." );

}

vi' ;

i

Console.WriteLine( "Exit Func2 ..." );

} I' primjer 11-2 gotovo je identičan primjeru 11-1, razlikuje se samo po tome što sadrži i •blok try/catch. Blok try se obično stavlja oko potencijalno „opasnog" iskaza, na prifjnjer onog za pristup datoteci, dodjelu velikog prostora u memoriji i slično. f za iskaza try nalazi se generički iskaz catch. Iskaz catch iz primjera 11-2 je generički ‘er nije odreñeno kakve tipove iznimki treba hvatati. U ovom će slučaju iskaz hvatati ;vc izbačene iznimke. Korištenje iskaza catch za hvatanje odreñenih tipova iznimki objašnjeno je kasnije u ovom poglavlju.

poduzimanje korektivnih akcija U primjeru 11-2 iskaz catch samo izvještava 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 pokušava otvoriti datoteku V koja je samo za čitanje, može se pozvati metoda koja korisniku omogućava promjenu atributa te datoteke. Ako je programu ponestalo memorije, korisniku se može pružiti mogućnost zatvaranja drugih aplikacija. Ako nema drugog izlaza, blok catch može [Ispisati poruku o pogrešci kako bi korisnik znao što nije u redu.

Poglavlje 1 1 : O brada izn im ki

|

249

Odmotavanje stoga poziva Pažljivo pogledajte izlaz primjera 11-2. Primijetit ćete kako je kod ušao u Main()i Funcl(), Func2() i blok try. Nema naznake d aje kod izašao iz bloka try , iako se vidi da je izašao iz Funcl(), Func2() i Main(). Što se dogodilo? Izvoñenje se zaustavlja odmah nakon izbacivanja iznimke i predaje se bloku catch. Ono se uopće neće vratiti izvornoj stazi koda. Nikad neće doći do reda koji ispisuje iskaz ex it za blok try . Blok catch upravlja pogreškom, a izvoñenje se zatim vraća kroz kod koji slijedi iza catch. Kada ne bi bilo bloka catch, stog bi se zbog iznimke odmotao, ali to se ovdje ne dogaña jer postoji blok catch. Iznimka se sada obrañuje. Problemi više ne postoje i program nastavlja s radom. Ovo će biti nešto 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 obrañuje u metodi Func2(), već u Funcl(). Kad je pozvana metoda Func2() ispisuje se iskaz Enter i zatim se izbacuje iznimka. Izvoñenje se zaustavlja, a izvedbeni okoliš traži metodu za obradu iznimki koja, meñutim, ne postoji. Stog se odmotava i izvedbeni okoliš metodu za obradu pronalazi u Funcl(). Poziva se iskaz catch, a izvoñenje se nastavlja odmah nakon iskaza catch, ispisujući iskaz Exit za Funcl() i zatim za Main(). Bitno je da razumijete zašto se iskaz Exiting Try Block i Exit Func2 ne ispisuju. Ovo je klasičan ptimjer u kojem smještanje koda u program za ispravljanje pogrešaka i prolazak kroz njega mogu pojasniti stvari.

Stvaranje namjenskih iskaza catch Dosad smo koristili samo generičke iskaze catch. Mogu se napisati i namjenski iskazi catch koji obrañuju samo neke iznimke, na temelju njihovog tipa. Primjer 11-4 prikazuje kako se označava 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 Pokušaj dijeljenja dva broja I l i

obrade moguće iznimke

public void TestFunc()

{ try

{ double a = 5; double b = 0;

Console.WriteLine( "{0} / {1} - \2f

>

a, b, DoDivide( a, b ) );

} // Prvo ide tip najčešće izvođene iznimke catch ( System.DivideByZeroException ) Console.Writetine(

"DivideByZeroException caught!

);

catch ( 5ystem.ArithmeticException ) Console.Writetine( "ArithmeticException caught!' );

} // Generički tip iznimke je zadnji catch Console.WriteLine( “Unknown exception caught" );

} } // Dijeli ako je dozvoljeno public double DoDivide( double a, double b )

{

if ( b == 0 ) throw new System.DivideByZeroException(),

I

i

■3

I

lf throw new System.ArithmeticException(); return a / b;

U ovom primjeru metoda DoOivide() ne dopušta da se nula podijeli nekirndjugim

"I ne postoji odgovarajuća iznimka; dijeljenje nule drugim brojem je dopuštena m a S k a o p e r L ja i ne treba se izbaciti iznimka. Pretpostavtmo meñunm « 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 odgovarajuću. Kad program pokrenete s a=5 i b=7, generirat Je se sljedeći izlaz: 5 / 7 = 0.7142857142857143

®j.ema očekivanju, iznimka nije izbačena. Meñutim, ako vrijednost a postavite na fiulu, generira se ovaj izlaz: ArithmeticException caught! Izbačena je iznimka i izvedbeni okoliš provjerava prvu iznimku DivideByZeroExcepliort. Budući da to nije odgovarajuća iznimka on prelazi na sljedeću 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 pažljivi jer je DivideByZeroException izveden iz ArithmeticException. Ako i*,' iskaze catch navedete obrnutim redoslijedom, DivideByZeroException uskladit će se poklopiti s metodom za ArithmeticException te iznimka neće doći do metode za obradu DivideByZeroException. Zapravo, u slučaju obrnutog redoslijeda nijedna iznimka neće doprijeti do metode DivideByZeroException. Prevoditelj će prepoznati kako metodi DivideByZeroException nije moguće pristupiti i generirat će pogrešku u prevoñenju!

“Iskazi try/catch se mogu rasporediti tako da se neke odreñenije iznimke hvataju u jednoj metodi, a da se druge iznimke, koje su više generičke, hvataju u metodama koje $6 nalaze više u pozivnom stogu. Točan raspored bit će odreñen vašim specifičnim 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 nešto više. Pretpostavite li da bi metoda E mogla ‘baciti iznimku, blok try/catch trebate smjestiti duboko u kodu kako bi se ta iznimka Jtvatila što bliže mjestu na kojem je problem nastao. Korisno je i stvoriti općenitije “etode za obradu iznimki na višim mjestima u kodu kako vam ne bi promakle nepredviñene iznimke.

skazfinally ■nekim instancama izbacivanje iznimke i odmotavanje stoga može uzrokovati proñem. Ako ste, na primjer, otvorili datoteku ili na drugi način zauzimate resurs, može £m zatrebati mogućnost zatvaranja datoteke ili pražnjenja privremene memorije. *ko postoji akcija koju morate izvesti bez obzira na izbacivanje iznimke (na primjer, ako trebate zatvoriti datoteku), možete izabrati izmeñu dvije strategije. Možete

Poglavlje 11: Obrada iznimki

|

253

zatvoriti opasnu akciju u blok try i zatim zatvoriti datoteku i u bloku catch i u bloku try. Meñutim, time se kod duplicira i povećava se opasnost od pogrešaka. U C# dostupna je i prikladnija alternativa u obliku bloka finally. Izvoñenje koda u bloku finally je zajamčeno bez obzira na izbacivanje iznimke/ Metoda TestFunc() u primjeru 11-5 prvo simulira otvaranje datoteke. Metoda izvodi odreñene matematičke operacije i datoteka se zatvara. Izmeñu otvaranja i zatvaranja datoteke može se izbaciti iznimka. Ako se to dogodi, datoteka može ostati otvorena. Razvojni inženjer zna kako će se na kraju metode, bez obzira na sve, datoteka zatvoriti pa se pozivanje metode za zatvaranje datoteke premješta u blok finally gdje se ono može izvesti bez obzira na izbacivanje iznimke.

Primjer 11-5. Korištenje 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 ađuje moguće 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 najčešće izvođene iznimke

• Ako iznimku izbacite iz bloka fmally, nije sigurno da će blok fmally dovršiti izvoñenje.

254

|

Programiranje C#

I I

••

primjer 11

5.

Korištenje 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 uštedjelo 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 izvođenje bloka finally potreban je blok try. Pogrešno je izaći 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 označavalo ! h k PT c 3 1 3° S nlSm° pnstuPlh lznlmci niti pregledali sam objekt Exception g b je k t System. Exception pruža razne korisne metode i svojstva. Svojstvo Message daje

°,

iJ E aC1,e 1ZJ nimC1’ "aPnmjerraz,°g nJen°g izbacivanja. Svojstvo Message može se l t " kod koB Je Izbacl° iznimku može svojstvo Message postaviti kao argument ■sl l f 0nstrUlktora iznimke. E r ° HelpLink pruža vezu do datoteke sustava pomoći povezane s iznimkom. U mvvo se svojstvo može i zapisivati.

Poglavlje 11: Obrada iznimki

|

255

r**—

Tkv

Napomena za VB6 programere: u C # trebate biti pažljivi prilikom deklariranja i distanciranja varijabli objekata u istom redu koda. Postoji li mogućnost izbacivanja pogreške u metodi konstruktora, možda deklaraciju i instancijaciju varijable trebate staviti unutar bloka try. To, meñutim, može uzrokovati ograničenje dosega varijable na blok try te se ona neće m oći referencirati unutar blokova catch i finally. Najbolje je varijablu objekta deklarirati ispred bloka try i instancirati je unutar bloka try.

Svojstvo StackTrace se može samo čitati, a postavlja ga izvedbeni okoliš. U primjeru 11-6 se svojstvo Exception.HelpLink postavlja i dohvaća kako bi korisnik dobio informacije o DivideByZeroException. Svojstvo iznimke StackTrace može dati trag u stogu za iskaz pogreške. Trag u stogu prikazuje stog poziva: niz poziva metoda koje prethode metodi u kojoj je izbačena 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();

} // Pokušava podijeliti dva broja // i obrađuje moguće 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" );

} // Najčešće izvođene 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 trače: {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 redo1 ; e 3 P °zlvanja tj. u tragu se vidi kako se pogreška pojavila u metodi DoDivide() koju ^ je pozvala metoda TestFunc(). Kad su metode duboko ugniježñene, trag u stogu vam ||moze pomoći 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 prosljeñuje se prilagoñena poruka pa se ispisuje zadana poruka: DivideByZeroException! Msg: Attempted to divide by zero.

Poglavlje 11: Obrada iznimki

|

257

Ovaj red koda se može modificirati kako bi se proslijedila podrazumijevana porukanew DivideByZeroException( "You tried to divide by zero which is not meaning-ful");

U ovom slučaju, izlazna poruka oslikavat će prilagoñenu 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 omogućava da korisniku pružite korisne informacije. Uz to, ispisuje se i StackTrace uzimanjem svojstva StackTrace objekta iznimke: Console.WriteLine("\nHere's a stack trače: {o}\n", e.StackTrace);

U izlazu ovog poziva vidi se potpuni StackTrace do trenutka izbacivanja iznimke: Here's a stack trače: 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 skraćeni pa vaš izlaz može izgledati drugačije.

Prilagoñene iznimke Ugrañeni tipovi iznimki koje CLR pruža, zajedno s prilagoñenim porukama prikazanim u prethodnom primjeru, bit će vam dovoljni za pružanje dovoljno detaljnih informacija catch bloku. Ponekad će vam, meñutim, biti potrebne zasebne metode za obradu iznimki ovisno o uzroku iznimke. Za to ćete morati stvoriti vlastite prilagoñene tipove iznimki (stoga možete stvoriti i zasebne metode za obradu iznimki). Prilagoñeni tipovi iznimki mogu dodati viš? informacija ili sposobnosti, no glavni je razlog postojanje drugačijeg tipa kojeg blok catch može razlikovati. Microsoft preporuča da se u potpunosti izbjegava izbacivanje osnovnog objekta Exception, pa čak i objekta ApplicationException. Te je objekte najbolje tretirati kao apstraktne tipove.

258

|

Programiranje C#

jednostavno je potrebno stvoriti vlastitu klasu prilagoñene iznimke. Jedino je oeraničenj da ona mora izvoditi (izravno ili neizravno) iz System.ApplicationException. U primjeru 11-7 prikazano je stvaranje prilagoñene 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();

} // Pokušava podijeliti dva broja // i obrađuje moguću iznimku public void TestFunc() try

{ Console.WriteLine( "Open file here” ); double a = 0 ; double b = 5 ; Console.WriteLine( "{oj / {i} = { 2 }", a, b, DoDiviđe( a, b ) ); Console.WriteLine( "This line may or may not print" );

// Najčešće izvođen tip iznimke ide prvi catch ( System.DivideByZeroException e ) Console.WriteLine( "\nDivideByZeroException! Msg: {o}”,

Poglavlje 11: Obrada iznimki

[

259

Primjer 11-7. Stvaranje prilagoñene iznimke (nastavak) e.Message ); Console.WriteLine( "NnHelpLink: {o}\n“, e.HelpLink );

}

catch ( MyCustomException e )

{

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 prihvaća niz message i prosljeñuje ga do osnovne klase, kao što je opisano u poglavlju 4. U ovom slučaju, prednost izrade klase prilagoñene iznimke je u tome što ona bolje odražava odreñeni dizajn klase Test u kojoj djelitelj nula nije

260

|

Programiranje C#

a a

jopušten. Umjesto prilagoñene iznimke može se koristiti i ArithmeticException, no |o bi moglo zbuniti druge programere jer se djelitelj nula obično ne smatra aritmetičJkom pogreškom.

Ponovno izba civa nje izn im k i •Možda ćete htjeti da blok catch izvede neku početnu korektivnu akciju i zatim ponovno izbaci iznimku do vanjskog bloka try (u pozivajućoj metodi). On može izbaciti istu Sili može izbaciti drugačiju iznimku. Ako izbaci drugačiju iznimku, možda će izvornu ‘ iznimku uključiti unutar nove iznimke kako bi pozivna metoda mogla razumjeti pretili hodne iznimke. Svojstvo InnerException nove iznimke uzima izvornu iznimku. Neke iznimke imaju smisla samo u kontekstu u kojem su izbačene. To se osobito odnosi na, primjerice, NullReferenceException koja može biti rezultat pogrešnog unosa. Kada to ne možete predvidjeti prethodnom provjerom unosa, iznimku treba uhvatiti i zatim izbaciti ArgumentException koja će pozivatelju dati točniju informaciju o uzroku problema. Ponekad je na granicu sloja ili dizajna dobro postaviti metodu za hvatanje kako bi se uhvatile neočekivane iznimke. U tom slučaju možete izbaciti prilagoñenu iznimku InternalErrorException koja klijentskom kodu signalizira kako je s komponentom nešto pošlo u krivom smjeru.

Kakoje InnerException takoñer iznimka, i ona može imati unutarnju iznimku. Stoga se §| cijeli lanac iznimki može ugnijezditi unutar drugog lance, kao što se figurice babuške % 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

Stvaranje prilagoñene iznimke (nastavak)

1 1 -7 .

public class Test

{ public static void Main()

{ Test t = new Test(); t.TestFunc();

public void TestFunc()

{ try

{ DangerousFunci();

} // Ako uhvati prilagođenu 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 prilagođenu iznimku catch ( System.Exception e )

•{ MyCustomException ex = new HyCustomException( "E3 - Custom Exception Situation!", throw ex;

}

}

public void DangerousFunc2()

262

|

Programiranje C#

11-7. Stvaranje prilagoñene iznimke (nastavak) try

{ DangerousFunc3();

}

.i s is t t

// Ako uhvati DivideByZeroException poduzima // korektivne akcije i izbacuje opće iznimke catch ( System.DivideByZeroException e ) 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 može biti pomalo nejasan. Najbolji način za razumijevanje ovog koda je propustiti ga kroz alat za ispravljanje pogrešaka. Počnite 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 inače ima vlastitu poruku o pogrešci ali • slobodno možete proslijediti i prilagoñenu poruku. Ovdje, kako bi se slijed dogañaja što lakše identificirao, proslijeñena je prilagoñena poruka El - DivideByZeroException. Iznimka izbačena u metodi DangerousFunc4() uhvaćena je u bloku catch unutar metode ! DangerousFunc3(). Logika metode DangerousFunc3() je da u slučaju uhvaćene 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 riječi throw.

Iznimka se stoga ponovno izbacuje do metode DangerousFunc2() koja ju hvata, poduzima akciju ispravljanja i izbacuje novu iznimku tipa Exception. U konstruktor te nove iznimke, metoda DangerousFunc2() prosljeñuje prilagoñenu poruku (E2 - Func2 caught divide by zero) i izvornu iznimku. Stoga izvorna iznimka (El) postaje InnerException za novu iznimku (E2). Metoda DangerousFunc2() zatim izbacuje tu novu iznimku E2 do metode DangerousFuncl(). Metoda DangerousFuncl() hvata iznimku, izvodi odreñenu akciju te stvara novu iznimku tipa MyCustomException. Ona novi niz (E3 - Custom Exception Situation!)prosljeñuje do konstruktora, isto kao i upravo uhvaćenu iznimku (E2). Upamtite, upravo uhvaćena 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, ispisujući njihove poruke: while (inner != nuli)

{ Console.WriteLine("{0}",inner.Message); inner = inner.InnerException;

} U izlazu se vidi lanac izbačenih i uhvaćenih iznimki: Retrieving exception history... E2 - Func2 caught divide by zero El - DivideByZero Exception

264

1 Programiranje C#

Mjesto toga možete 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 trače --at RethrowingExceptions.Test.DangerousFunc2 () in c:\rethrowingexceptions\ rethrowingexceptions.cs:line 89

t*v

at RethrowingExceptions.Test.DangerousFuncl() in c:\rethrowingexceptions\ rethrowingexceptions.cs:line 6l --- End of inner exception stack trače --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 dogañaji

Kada umre neki državnik, predsjednik Sjedinjenih Američkih Država obično 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 mogućnosti otići na pogreb, predsjednik mora poslati nekog drugog, na primjer ministra vanjskih poslova ili čak prvu damu. On ovlaštenje delegata neće čvrsto povezati sa samo jednom osobom kako bi ga mogao dodijeliti bilo kojoj osobi koja je sposobna dobro oba- ’f viti meñunarodni protokol. Predsjednik unaprijed definira koje će se ovlaštenje dodijeliti (odlazak na pogreb), koji n će se parametri prenijeti (sućut, ljubazne riječi) te kojoj se povratnoj vrijednosti nada | (dobra volja). On zatim ovlaštenje dodjeljuje odreñenoj osobi u prikladnom trenutku j dok traje njegov mandat. 1 U programiranju se često suočavamo sa situacijama u kojima je potrebno izvesti odre- > ñenu akciju ali se ne zna unaprijed koja će se metoda ili objekt pozvati na izvoñenje. / Na primjer, gumb može znati kako treba obavijestiti objekt da je pritisnut, ali možda : i ne zna koji objekt ili objekte treba obavijestiti. Umjesto da gumb povežete s odreñenim objektom, povezat ćete ga s delegatom i zatim tog delegata pri izvoñenju programa j dodijeliti odreñenoj metodi. i. U ranim, mračnim i primitivnim danima programiranja, programi bi počeli izvoñenje i zatim prolazili kroz sve korake do kraja. Ako je korisnik bio uključen u proces, interakcija je bila strogo kontrolirana i ograničena na popunjavanje polja. Za današnji model programiranja s grafičkim korisničkim sučeljem potreban je drugačiji pristup, poznatiji kao programiranje temeljeno na dogañajima (engl. event driven programjning). Moderni program prikazuje korisničko sučelje i čeka na akciju korisnika. Korisnik može izvesti više različitih akcija, poput odabira opcije iz izbornika, pritiska na gumb, osvježavanja tekstualnih polja, pritiska na ikonu i tako dalje. Svaka akcija izaziva dogañaj. Drugi se dogañaji mogu izazvati bez izravne akcije korisnika. Takvi su, na primjer, dogañaji koji odgovaraju na otkucaj unutarnjeg sata, prijem poruke elektroničke pošte, dovršetak operacije kopiranja datoteke itd.

266

Jp| j dogañaj je učahurena zamisao da se „dogodilo nešto“ na što program mora odgov o r i t i . Dogañaji i delegati su usko povezani koncepti jer je za fleksibilno upravljanje »dogañajima potrebno da se odgovor na dogañaj otpremi do odgovarajuće metode za Slpbradu dogañaja. Metoda za obradu dogañaja se u C# najčešće implementira preko feelegata. Bpelegati se koriste i kao povratni pozivi kako bi jedna klasa drugoj mogla reći „učini 'dvo i obavijesti me kad si gotova".

^ Delegati Delegati (engl. delegates) su u C# objekti prve klase i za njih postoji potpuna podrška u jeziku. Delegat je tehnički referentni tip koji se koristi za učahurivanje metode s ' odreñenim potpisom i povratnim tipom.- U delegata se može učahuriti bilo koja odgo£u varajuća metoda To se C ++ i mnogim drugim jezicima može postići preko pokazivača metode i pokazivača metoda članica.

Delegat se stvara s pomoću ključne riječi delegate iza koje se navodi povratni tip i potpis metoda koje mu se mogu delegirati, kao u sljedećem primjeru: public delegate int WhichIsFirst(object objl, object obj2);

Ovom se deklaracijom definira delegat WhichIsFirst koji će učahuriti bilo koju metodu koja kao parametre uzima dva objekta i vraća tip int. ;Nakon definiranja delegata u njega možete učahuriti metodu članicu tako što ćete ga ; instancirati prosljeñivanjem metode koja odgovara navedenom povratnom tipu i potpisu. Možete koristiti i anonimne metode, kao što je opisano kasnije. U oba se slučaja delegat može koristiti za pozivanje učahurene metode.

Korištenje delegata za zadavanje metoda tijekom izvedbe Delegati razdvajaju klasu koja deklarira delegata od klase koja delegata koristi. Pretpostavimo, na primjer, kako želite stvoriti jednostavno generičku kontejnersku klasu Pair koja može sadržavati i sortirati bilo koja dva objekta koji joj se proslijede. Ne možete unaprijed znati kakve će objekte klasa Pair sadržavati, ali stvaranjem metoda unutar objekata kojima se može prepustiti sortiranje možete odreñivanje redoslijeda objekata delegirati samim tim objektima. £ Različiti će se objekti različito sortirati (na primjer, par objekata Counter može se sori£ ttrati brojčanim redoslijedom, dok se par objekata Button može sortirati abecednim

V

Ako je metoda metoda instance, delegat učahuruje i ciljni objekt.

Poglavlje 12: Delegati i događaji

|

267

redoslijedom prema nazivima). Kao autor klase Pair želite da objekti od kojih se pa r; 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 •<

i.

objekti sortiraju. Ovaj zahtjev možete definirati i s pomoću sučelja. Delegati su manji od sučelja i finije granulacije. Klasa Pair ne treba implementirati cijelo sučelje već samo treba definirati potpis i povratni tip metode koju želi pozvati. Za to služe delegati - oni definiraju povratni tip i potpis metoda koje se mogu pozvati kroz sučelje. U ovom slučaju klasa Pair će deklarirati delegata WhichIsFirst. Kad je klasi Pair potrebna informacija o redoslijedu sortiranja objekata, ona poziva delegata i svoja dva objekta prosljeñuje kao parametre. Ovlaštenje za odreñivanje redoslijeda objekata delegira se metodi koju je delegat učahurio: public delegate Comparison WhichIsFirst( T objl, T obj2 )

U ovoj je definiciji delegat WhichIsFirst definiran tako da učahuruje metodu koja kao parametre prima dva objekta i vraća 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 zajedničkih karakteristika, osim što obje implementiraju metode koje može učahuriti 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 učahurili odgovarajuće metode koje će odrediti redoslijed objekata Student i Dog. Promotrimo ovaj postupak korak po korak. Počinjete sa stvaranjem konstruktora Pair koji uzima dva objekta i sprema ih u privatno polje: public Pair( T firstObject, T secondObject )

{ thePair[o] = firstObject; thePairfl] = secondObject;

} Zatim preopterećujete 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 možete ispisati. Spremni % za sortiranje i ispis rezultata sortiranja. Ne možete unaprijed znati o kakvim se .|bjektima radi pa ovlaštenje za odreñivanje redoslijeda objekata delegirate samim Objektima. Jpase Dog i Student implementiraju metode koje delegat MhichlsFirst može učahu| lti. Tijekom izvoñenja ovaj delegat može učahuriti bilo koju metoda koja uzima dva .objekta i vraća tip Comparison. '‘a klasu Pair sada možete definirati metodu S ort(): public void Sort(WhichIsFirst theDelegatedFunc)

{ jMš:

if (theDelegatedFunc(thePair[o],thePair[lj) == Comparison.theSecondComesFirst)

3P

T temp = thePairfo];

*=;■

thePair[i] = temp;

thePair[o] = thePairfl];

} ^Ova metoda prihvaća parametar: delegat tipa WhichIsFirst pod imenom theDelegajedFunc. Metoda Sort() ovlaštenje za odreñivanje redoslijeda objekta u Pair delegira

I

Jiietodi učahurenoj 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. C°je analogno funkcioniranju ostalih parametara. Ako imate metodu koja kao parajjhetar uzima int: int SomeHethod (int myParam){//...}

-|fiaziv parametra je myParam, no proslijeditise može bilo koja int vrijednost ili varijabla. |Shčno tome, parametar u primjeru delegata ima ime theDelegatedFunc, no proslijediti |se može 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, napišite metodu koja vraća vrijednost theFirstComes . First, a ako želite da na prvom mjestu bude ime drugog studenta, napišite metodu koja jVraća vrijednost theSecondComesFirst. Ako proslijedite ,,Amy, Beth“, metoda vraća -|theFirstComesFirst, a ako proslijedite „Beth, Amy“, ona vraća theSecondComesFirst.

Poglavlje 12: Delegati i događaji

|

269

Ako se vrati vrijednost theSecondComesFirst, metoda Sort() će elemente u svom navesti obrnutim redoslijedom, postavljajući Amy na prvo mjesto, a Beth na drug0 Zatim dodajte još jednu metodu, ReverseSort(), koja obrće 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 kaže da prvi element dolazi na prvo mjesto. Budući 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 vraća theFirstComesFirst (tj. Amy se treba nalaziti na prvom i mjestu), o budući da se radi o obrnutom redoslijedu, dolazi do zamjene vrijednosni i na prvom se mjestu nalazi Beth. To omogućava da koristite istu delegiranu metodu ■ kao kod metode S o rt(),b ez potrebe da objekt podržava metodu koja vraća 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 premošćuje metodu ToStringO i drugu koja će se učahuriti kao delegirana metoda. Student mora premostiti metodu ToStringO tako da u Pair ta metoda, koja poziva ToStringO za sadržane objekte, ispravno funkcionira. Implementacijom se samo vraća ime studenta (koje već jest objekt niza): public override string To Strin g O { return name;

} ' Student mora implementirati i metodu kojoj Pair. Sort() može delegirati ovlaštenje za odreñivanje 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 usporeñuje |gjva niza i vraća vrijednost manje od nule ako je prvi manji, veće od nule ako je drugi Jljiiz manji i nulu ako su nizovi jednaki. Ta je metoda detaljnije obrañena u poglavlju

JpO. Obratite pozornost da primijenjena logika vraća theFirstComesFirst samo ako je

||prvi niz manji. Ako su nizovi jednaki ili je drugi niz veći, metoda vraća theSecondKtornesFirst.

Jtjñetoda WhichStudentComesFirst() kao parametre uzima dva objekta i vraća tip ComHjparison. 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 težini tako da se lakši psi g f nalaze ispred težih. Potpuna deklaracija klase Dog glasi: public class Dog { public Dog(int weight)

{ this.weight=weight;

} U Psi se sortiraju po težini public static Comparison WhichDogComesFirst( Object ol, Object 0 2 )

m

{ Dog dl = (Dog) ol; Dog d2 = (Dog) 0 2 ; return dl.weight > d2.weight ? Comparison.theSecondComesFirst : Comparison.theFirstComesFirst;

m 1

public override string ToSt ring O return weight.ToStringO;

. 1 private int weight;

■Klasa dok takoñer premošćuje metodu ToStringO i implementira statičku 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 dinamički dodijeliti deleJ|; gatu u vrijeme izvoñenja. Delegirane m e t o d e mo že te nazvati k a k o g o d želite, n o korištenje para-

ko d a T o kod m ože 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 ključna riječ ^legate, iza koje se navode parametri koji će biti proslijeñeni metodi te zatim tijelo jnetode unutar vitičastih zagrada i na kraju točka-zarez. Ova „metoda" nema naziva pa kažemo da je riječ o anonimnoj metodi. Ona se može

pozvati samo kroz delegata, no to je upravo ono što vam je potrebno.

Dohvat vrijednosti iz višeodredišnih delegata U većini situacija metode koje ćete učahuriti s višeodredišnim delegatom vratit će void. Višeodredišni se delegati, u stvari, najčešće koriste s dogañajima, a sjetit ćete ,e da prema pravilu sve dogañaje implementiraju delegati koji učahuruju metode koje vraćaju void (i prihvaćaju dva parametra: pošiljatelja i objekt EventArgs). Mogu se, meñutim, stvoriti višeodredišni delegati za metode koje ne vraćaju vrijednost void. U sljedećem ćete primjeru stvoriti vrlo jednostavnu testnu klasu s delegatom koji učahuruje bilo koju metodu koja ne uzima parametre ali vraća cjelobrojnu

# ' vrijednost: public class

ClassHithDelegate

{ public delegate int DelegateThatReturnsInt(); public DelegateThatRetuinsInt theDelegate;

Kako biste to testirali, implementirajte dvije klase koje se pretplaćuju na delegat. Prva učahuruje metodu koja povećava vrijednost brojača i tu vrijednost vraća kao cjelobrojnu 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 takoñer služi za održavanje brojača, no njena delegirana metoda udvostručuje vrijednost brojača i vraća tu udvostručenu vrijednost:

Poglavlje 12: Delegati i događaji

|

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, naizmjenično se pozivaju obje učahurene metode i svaka vraća vrijednost: int result = theDelegate(); Console.WriteLine( "Delegates fired! Returned result: {o}” , result);

Problem je u tome što svaka metoda vraća 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 vrijednosti 1, 2, 3, 4, 5, no te su vrijednosti prepisane vrijednostima koje je vratila druga metoda. Vaš je cilj naizmjenični prikaz rezultata svake metode. Za njegovo postizanje morate preuzeti na sebe pozivanje metoda koje su učahurene u višeodredišnom delegatu. To možete učiniti uzimanjem popisa pozivanja od delegata i eksplicitnim pozivanjem svake učahurene 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 prikazuje prije pozivanja sljedeće metode. Promjena se vidi u izlazu:

296

|

Programiranje C#

'Hi

Delegates

fired! Retur ned result: i

Delegates

fired! R etu rned result:

Delegates

fired! Re turn ed result:

Delegates

fired! Returned result:

Delegates

fired! Retur ned result:

Delegates

fired! Retur ned result:

Delegates

fired! Returned result:

Delegates

fired! Retur ned result:

2 2 4 3 6 4 8

Delegates fired! Returned result: 5 Delegates fired! Returned result: 10

_-l?

•J|,Prva de,eglrana metoda odbr° java ( .

1 2 , 3 , 4, 5), dok druga udvostručuje vrijednost (2 , >W 4 , 6 , 8 , 1 0 ). Cijeli je izvorni kod prikazan u primjeru 12-5.

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

f

Ifregion Using directives using System; it using System.Collections.Generic; using System.Text; {jk using System.Threading; ‘

#endregion Vnamespace InvokingDelegatedMethodsManually f j

public class ClassIVithDelegate // Višeodredišni delegat koji učahuruje metodu // koja vraća int public delegate int DelegateThatReturnsInt(); public DelegateThatReturnsInt theDelegate; public void Run()

{

for ( ; ; ) { // Spava pola sekunde Thread.Sleep( 500 );



if ( theDelegate != nuli )

{ // Eksplicitno poziva svaku delegiranu metodu foreach ( DelegateThatReturnsInt del in theDelegate.GetInvocationList() )

I

int result = del(); Console.WriteLine(

Poglavlje 12: Delegati i događaji

|

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 dogañaja Može se ispostaviti kako metodama za obradu dogañaja treba previše vremena da ■‘odgovore na dogañaj. U tom slučaju, obavještavanje kasnijih metoda može trajati vrlo jugo, dok čekate i rezultate ranijih metoda. Pretpostavimo, na primjer, kako metoda ■DisplayC°unte r() u FirstSubscriber mora obaviti mnogo posla oko izračuna povratnog rezultata. To bi izazvalo kašnjenje u obavješćivanju SecondSubsriber o dogañaju. To možete 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 obavještavanja metode IfirstSubscriber dolazi do kašnjenja od četiri sekunde. Umjesto pozivanja svih metoda j kroz delegate (kao što je prikazano ranije) možete za svaki delegat pozvati metodu ||)BeginInvoke(). To će uzrokovati asinkrono pozivanje metoda i moći ćete nastaviti s f radom, bez čekanja na povratni rezultat pozvane metode.

|tza

razliku od metode Invoke(), metoda BeginInvoke() odmah vraća. Ona stvara gposebnu dretvu u kojoj izvodi svoje akcije' (više informacija o dretvama potražite u /»poglavlju 20).

1

•»To, meñutim, predstavlja problem, jer ipak želite dobiti rezultate od pozvanih metoda. IpSla raspolaganju su vam dvije mogućnosti. Prva je stalno provjeravanje svake delegira n e metode kako biste vidjeli je li rezultat dostupan. To je kao da od svog pomoćnika Ižatražite da obavi neki zadatak, a onda ga svakih pet sekundi zovete i pitate „Jesi li ||otov?“ (čime gubite i svoje i njegovo vrijeme). Svom pomoćniku zapravo trebate reći OJapravi ovo i nazovi me kad dobiješ rezultat".

^Metode povratnog poziva ■pelegiranje zadatka i primanja povratnog poziva kad je zadatak obavljen možete * postići korištenjem povratnih poziva koji se implementiraju (kakvo iznenañenje!) s pomoću delegata. .N ET kostur pruža mehanizam za povratne pozive definiranjem ■delegata AsyncCallBack'.

t N E T pruža pon ud u dretvi, a nova dretva će najvjerojatnije biti preuzeta iz te ponude.

Poglavlje 12: Delegati i događaji

|

299

[Serializable] public delegate void AsyncCallback( IAsyncResult ar

); Atribut S e rializable je opisan u poglavlju 18. Ovdje, meñutim, možete vidjeti kako je AsyncCallBack delegat za metodu koja vraća void i uzima jedan argument, objekt-* tipa IAsyncResult. To sučelje je definirano .N ET kosturom, a CLR će metodu pozvati s objektom koji implementira sučelje pa ne morate poznavati pojedinosti sučelja. Možete ' jednostavno koristiti objekt koji vam je ponuñen. Evo kako to radi. Od delegata zatražite 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 učahurena u del (to jest, DisplayCounter) i kad metoda završi, obavijest ćete primiti preko metode ResultsReturned. Metoda kojoj treba uzvratiti poziv (ResultsReturned) mora odgovarati povratnom tipu I i potpisu delegata AsyncCallBack te mora vraćati vrijednost void i uzimati objekt tipa i IAsycResult: % private void ResultsReturned(IAsyncResult iar)

ñ

{

I

Kad je metodi uzvraćen poziv, .N ET kostur prosljeñuje objekt IAsyncResult. Drugi | parametar za Beginlnvoke je vaš delegat i on se sprema u svojstvo AsyncState sučelja | IAsyncResult kao Object. Unutar metode povratnog poziva ResultsReturned taj Object | možete izdvojiti i pretvoriti ga u njegov izvorni tip: DelegateThatReturnsInt del = (DelegateThatReturnsInt)iar.AsyncState;

Sada taj delegat možete upotrijebiti za pozivanje metode EndInvoke(), prosljeñujući primljeni objekt IAsyncResult kao parametar: int result = del.Endlnvoke(iar);

|

\

| |

|

Metoda EndInvoke() vraća vrijednost pozvane (i sada dovršene) metode koju dodjelju- J jete lokalnoj varijabli result i koju sada slobodno možete prikazati korisniku. | I Ukupni efekt je da u metodi Run() naizmjenično dobivate registrirane metode (prvo ! FirstSubscriber.DisplayCounter, a zatim SecondSubscriber.Doubler) i svaku pozivate asinkrono. Izmeñu pozivanja prve i druge metode nema čekanja jer ne čekate da DisplayCounter vrati vrijednost. Kad DisplayCounter (ili Doubler) ima rezultate, poziva se metoda povratnog poziva (ResultsReturned) i koristite objekt IAsyncResult proslijeñen kao parametar za uzimanje 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 // Viseodredišni delegat koji ućahurava metodu // koja vraća 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 // prosljeđuje delegat kao objekt stanja d e f )n InV°ke( 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 );

3Poglavlje 12; Delegati i događaji

|

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 korištene konzolne aplikacije, Premda se konzolne aplikacije mogu jednostavno implementirati, vrijeme je da ;se pozabavite pravim razlogom zašto uopće učite C# jezik, a to je izgradnja Windows !|.Web aplikacija. % ranim danima Windowsa, aplikacije su se izvodile na stolnim računalima, u savršenoj izoliranosti. S vremenom, projektanti su našli korisnim da rasporede aplikacije iiljem mreže, sa korisničkim sučeljem na jednom računalu, a bazom podataka na ■tlrugom. Ova podjela odgovornosti, odnosno distribuiranje aplikacije, naziva se dvoplojna arhitektura, ili pristup klijent-poslužitelj. Kasnije, kada su projektanti počeli Ijcoristiti Web poslužitelje da udome poslovne objekte koji mogu upravljati pristupom Ibazi podataka, razvili su se troslojni, odnosno višeslojni pristupi. Kad se Web tek pojavio, postojala je jasna razlika izmeñu Windows i Web aplikacija. |Vindows aplikacije su se izvodile na stolnim računalima ili u lokalnoj mreži, a Web racije su se izvodile na udaljenom poslužitelju i pristupano im je preko pregledlika. Ova razlika je postala manja kako su Windows aplikacije počele koristiti usluge :ba. Mnoge nove usluge uključuju izvoñenje poslovne logike na klijentu, pohranjivanje podataka na poslužitelju baze podataka i udaljena neovisna računala na Webu. Klasične 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 izmeñu Windows i Web aplikacija je u tome tko posjeduje ^korisničko sučelje. Da li će aplikacija svoje korisničko sučelje prikazati u pregledniku ||li će korisničko sučelje biti u sklopu programa koji se izvodi na stolnom računalu? #4

T iTi L.-v, L

Čak i razlika u tome tko posjeduje korisničko sučelje je donekle proizvoljna, pošto sučelja prikazana u pregledniku mogu imati komponente koje se izvode lokalno, a samostalne aplikacije mogu imati ugrañene Web preglednike.

Ogromne su prednosti Web aplikacija. Može im se pristupiti s pomoću bilo kojeg pre glednika koji se može spojiti na poslužitelj. Takoñer, poslužitelj se može ažurirati bezi potrebe za dijeljenjem novih DLL-ova korisnicima. I S druge strane, ako aplikacija nema koristi od pristupa Webu, moguće je postići veću kontrolu nad izgledom i uvidom u aplikaciju, odnosno moguće je postići bolju izvedbu izradom samostalnih aplikacija. | .N ET nudi blisko povezane, ali različite skupove alata za programiranje Windows ili 1 Web aplikacija. Oba pristupa su temeljena na pretpostavci da mnoge aplikacije imaju 1 korisnička sučelja koja s korisnikom komuniciraju preko obrazaca ili kontrola kao što 1 su gumbi, padajući 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 sljedećim stranicama ćete naučiti kako izraditi Windows aplikaciju koristeći Visual i Studio. Ova aplikacija će obuhvatiti veći broj C# metoda koje su razmatrane u pret- | hodnim poglavljima. i|

Izrada jednostavnog Windows Forms obrasca

1

|

Windows Forms je alat za izradu Windows aplikacija. .N ET kostur nudi obimnu podršku za razvoj Windows aplikacija, čiji je središnji dio kostur za Windows obrasce. Nije iznenañujuće da Windows obrasci koriste metaforu obrasca. Ideja je posuñena od vrlo uspješne VB okoline i podržava brzi razvoj aplikacija. Premda je to diskutabilno, C# je prva okolina za razvoj koja je spojila alate za brzi razvoj iz VB okoline sa kvalitetnim objektno orijentiranim karakteristikama obitelji C jezika.

| ^ 1 | | ;j|

Korištenje alata Visual Studio Designer

i |

Iako je moguće napraviti Windows aplikaciju koristeći bilo koji editor (čak i Notepad!) J i prevoñenje iz naredbene linije, nerazumno je tako raditi kad Visual Studio.NET f toliko olakšava posao. is | Da biste počeli 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, pokušajte 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

y3w Cin tesdLobw r«sA v«*acion

: Office wm dow s

f

' a SiTidrj oevte oatabase ttt Other Languages ffl O ther Projec t Types

®)

trc* l b e r y W ndow s Con e b C ontrct U bra y

f E

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

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

SobtJbnName:

fftogSharpWrclow sft

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 ------------------------- - __ I

E lO a a t e D N c tia r i t o S d u t J m

U

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 ” ” l’k» ™ id »

i.'s s s s s B s s s s s s f u” liM

* - —

P '» -

. **» ■ «

Poglavlje 13: Programiranje Windows aplikacija

|

307

Kad je Toolbox prikazan, možete povući 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

>t Pohter r B ActiveDoasnentHost . a>( Buttan

p. C h K & n 0 Cabrfbfeg 133 Corrtoep* 9 ConttxtHenuSb^

6trfd

Oetug

Data

Toolj

Vfttdan

Help

^forort-e*IDesijo]*\

&

;^ W » n E » iib f« f rSabtk inVregC... r j) x

•(Sr-g

_ w Sduben ‘PnJ5CSharpW«Sow5Fotm' ( i pro Q a PiooCSharpW MowsfMm

I

®; Sg|Poml.t» LaRefntnces

vUer .

j g FfcmLavnrtPAnd ’ 0 FoidBfrtMS«rfXal»g..

>j]i^^^i^3SSS3ga Sofu&en EKpiorcr j3JOw*VSev* I

b b c ll Syst«n.W indow j/orm iJ .abtt

*

ip B fc B Lila,..b d^Masasifi^an^-. ’: |(N8ffle) l

45 05

f f l FcnOtatog

fACC S lble t Oi«:

( 3 Im sgdist

■; AcceisIbteNam« AccessIbleRole .Oefadt (A llo ftO rop Ppbe •‘.Anchor Top.Loft

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 možete 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. Možete koristiti prozor Properties da podesite svojstva raznih kontrola. Na primjer, da biste dodali tekst natpisu la b e ll, možete upisati riječi „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. (Takoñer možete 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 lakši nego mijenjanje ovih svojstava izravno u kodu (iako je i to naravno moguće). Kad je obrazac podešen, 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 sadrži kod za obrazac, gdje možete upisati logiku za upravljanje dogañajima 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 1F6» & Srt V« froject bM Mu) CKATooHWNwt«t> 1ifl*ijj*idf.kj 9 . ■ .A’-J & *>•i'*•>'->• I.Cttu) *AnftfV «a Ctvoftirost



"

i

,-:v b š

__

jfci slika 13-4. Promjena pisma

ProgCSharpV/indovnForm - Microsoft Dovetopmeni Environment

PV bE 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 sadržajem lokalnog tvrdog diska.

I I Najprije briše sve čvorove. tvw.Nodes.Clear();

326

|

Programiranje C#

primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak) II // //

Uzima logičke diskove i stavlja ih u korijenske čvorove. Popunjava polje s logičkim 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, neče biti dodan na popis.

foreach ( string rootDirectoryName in strDrives )

try

{ // //

Popunjava polje sa mapama prve razine. Ako disk nije spreman

II bit će izbačena 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 pogreške 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 I I I Uzima sve podmape ispod I I I proslijeđenog čvora mape. I I I Dodaje ih u stablo mapa. I I I Proslijeđeni 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 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 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 inače III znak za prekid I I I 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 )

{

328

|

// Uzima datoteke za ovaj čvor. Filelnfof] files = dir.GetFilesO;

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 I II Pravi sortirani popis svih I I I datoteka odabranih za kopiranje III u odredišnu mapu I I I private void btnCopy_Click( object sender, System.EventArgs e )

{ // Uzima popis List fileList = CetFileList();

l

// Kopira datoteke foreach ( Filelnfo file in fileList )

I

try

{ // Osvježava natpis da prikazuje napredak lblStatus.Text = "Copying " + txtTargetDir.Text + file.Name + "... Application.DoEvents(); // Kopira datoteku u odredišnu mapu file.CopyTo( txtTargetOir.Text + "\\" + file.Name, chkOverwrite.Checked );

} catch ( Exception ex )

{ I I Možda želite nešto više od I I prikazivanja poruke MessageBox.Show( ex.Message );

} lblStatus.Text = "Done."; Application.DoEvents();

} I I I III Govori korijenu svakog stabla da poništi III odabir svih čvorova ispod I I I

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 najvišem položaju za svaki disk // i govori mu da se rekurzivno očisti foreach ( TreeNode node in tvwSource.Nodes ) SetCheck( node, false );

} } I I I III Ako korisnik pritisne Cancel zatvara se I II _

.. „ . private void btnCancel_Click(object sender, EventArgs e) Application.Exit();

} I I I III S popisom i polje na raspolaganju III popunjava popis imenima III svih odabranih datoteka I I I // Popunjava ArrayList sa punim putanjama // odabranih datoteka private void GetCheckedFiles( TreeNode node, List -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 III Uzima čvor i vraća III punu putanju ///

330

|

Programiranje C#

Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak) private string GetParentString( TxeeNode node )

{ // Ako je ovo korijenski čvor (c:\) vraća tekst iF ( node.Parent == nuli )

{ return node.Text;

} else

{ // Vraća 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 III III

Dijele ju operacije brisanja i kopiranja Stvara sortirani popis

III odabranih datoteka I I I private List GetFileList()

{ // Stvara nesortirano polje punih putanja datoteka List FileNames = new List(); 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 FileList = new List(); // 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 lakše kad bi imali praznu vrijednost? fileList.Add( file );

}

}

// Stvara instrancu sučelja IComparer IComparer comparer = ( IComparer ) new FileComparer(); // Prosljeđuje komparator metodi za sortiranje tako da je popis // sortiran s metodom za sortiranje. fileList.Sort( comparer ); return fileList;

I I I III Provjerava de li korisnik zaista želi brisanje. III Pravi popis i briše svaku datoteku. I I I 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", MessageBoxButtons.OKCancel,

// Natpis // Gumbi

HessageBoxIcon.Exclamat ion, MessageBoxDefaultButton.Button2 );

// Ikone // Podrazumijevani gumb

// Ako je korisnik siguran... if ( result == System.Windows.Forms.DialogResult.OK )

{ // prolazi kroz popis i briše datoteke. // Uzima popis odabranih datoteka. List fileNames = GetFileList(); foreach ( Filelnfo file in fileNames )

{ try

{ // Ažurira 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 Možda želite nešto više od // prikazivanja poruke MessageBox.Show( ex.Message );

}

}

lblStatus.Text = "Done.’1; Application.DoEvents();

}

}

I I I III

Uzima punu putanju odabrane mape

I II i kopira ju u txtTargetDir I I I 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 završavat će s obrnutom kosom crtom // Briše kosu crtu if ( theFullPath.EndsWith( "\\" ) )

{ theFullPath = theFullPath.Substring( 0 , theFullPath.Length - 1 );

} // Dodaje putanju u polje za tekst txtTargetDir.Text = theFullPath;

I I I III Obilježava svaki čvor ispod tekućeg 'III s trenutnom vrijednosti obilježenog čvora I I I 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 /// rekurzivno postavlja ili briše oznake za potvrdu I I I

Poglavlje 13: Programiranje Windows aplikacija

|

333

Primjer 13-1. Izvorni kod FileCopier aplikacije (nastavak) private void SetCheck( TreeNode node, bool check )

{ // Traži sve čvorove potomke ovog čvora foreach ( TreeNode n in node.Nodes )

{ n.Checked = check;

// Potvrđuje č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 podržava novi stil komentara za dokumentaciju sa tri kose crte (///). Možete vidjeti ove komentare razbacane svuda u primjeru 13-1. Visual Studio editor prepoznaje ove komentare i pomaže da ih se ispravno oblikuje. C # prevoditelj sakuplja ove komentare u X M L datoteku. Možete ju napraviti zadavanje /doc prekidača prilikom prevoñenja u naredbenom retku. Na primjer, možete prevesti program iz primjera 13-1 sa ovom naredbom: esc Formi.cs /doc:XMLDoc.XML

Isti ishod možete postići u Visual Studiju pritiskom na ikonu projekta FileCopier u prozoru Solution Explorer, odabirom opcije View Propery Pages u Visual Studio izborniku i zatim odabirom opcije Build property page. Potvrdite polje XMLDocumentation File i upišite ime X M L datoteke koju želite napraviti, na primjer FileCopier.XML. Dio datoteke koja je napravljena za aplikaciju FileCopier iz prijašnjih odlomaka je prikazan u primjeru 13-2.

334

|

Programiranje C#

lpm)er

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

fo» FileCopier cmember name="T:FileCopier.frmFileCopier"> Obrazac za demonstraciju Windows Forms implementacije cmember name="F:FileCopier.frmFileCopier.components">

1

Required designer variable.

j: -

cmember name="M:FileCopier.frmFileCopier.Dispose(5ystem.Boolean)”>

i i ■

Clean up any resources being used. cmember name="M:FileCopier.frmFileCopier.InitializeComponent"> Required method for Designer support - do not modify the contents of this method with the code editor. cmember name="M:FileCopier.frmFileCopier.GetSubDirectoryNodes (System.Windows.Forms.TreeNode,System.String,System.Boolean,System.Int32)”> Uzima sve podmape ispod proslijeđenog čvora mape. Dodaje ih u stablo mapa. Proslijeđeni parametri su roditeljski čvor za tu podmapu, puna putanja podmape, i Boolean zastavicu koja govori da li treba uzeti datoteke iz podmape. cmember name="M:FileCopier.frmFileCopier.btnCopy_Click (5ystem.Object,5ystem.EventArgs)">

Pravi sortirani popis svih datoteka odabranih za kopiranje u odredišnu mapu

Dokument je velik, i iako je ljudima čitljiv, nije previše koristan u tom obliku. Možete, meñutim, napisati XSLT datoteku da prevede XML u HTM L, ili možete učitati XML dokument u bazu podataka za dokumentaciju. Takoñer možete povući datoteku iz

Poglavlje 13: Programiranje Windows aplikacija

I

335

prozora File Explorer u Windows Explorer koji osigurava zgodno sučelje 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

©B** ' t© - [ij) [g|

Help

^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

jp»HQ8........;j U t o

Ostornti **

1

F(leCoplorc/nsrne> ••

s$1$

bly> - cmembers? one - on ember n am e=' e='T :F ll eCo eCopl er.frm Fl leCo eCopl er-> 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> on cenc en c al , exltc/summ3rv> ■ f t e 3d c n l y , 'j V i s b f e k .W ld ih

F ñ ls e T ru e 100

t jA u lo S ltt O lt e r ia ,; ^ C o n ( e x t M e n u S t d p

N one in o n e j C o m p sn v N am e T ru e Automate

v iR e s e ta tte - 'S o r t M o d e • .• ■ 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 rtiTag ŠOtSPlñf 1 % H e s tfs r T e jr t ' ; x B M lsr C o tm iT y p e

-C .

C o n ip a n y N sr n e C o tn p a n y N a m 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.



c

Sliku 14-7. Ureñivanje 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

lA Jfr e d t F t A le tk is le

: A d d re tt

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

tA n a T tie fc > E m p o c e d e d o sy h e le d o * jA r V o n s o M o r e n o T a g u e r la jA io u n d lh e H a n

{A n e T i u il o • A n lo n io M a e n o

|0w n a J 0w n e

(d e t te U b er tp

jS e te t R e p te te n ta ir / e

B e ig k jtd s s n a b b k o o B le u e i S e e O e H te te ts e n

.O r it ih e B a a k n d jH e n n e M o o s

|0i d e i A d r a r h t i M a | S eie t R e p ie s e n te tiv e

B lo o Je s d d s l p fere e t Ih

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 . j M e l e d e i o s 2312 jM h e c o O .F . 120H e n o v e t S o jA c to n B e i g u r tv ijg e n 8 jU t e i F a t t e t s t i . 57 iM a o th e im

BćbdoCcrnidasp t e & j r s d e r

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 « M e d in S o cn m ei (O w rw

[S tr« b o u g

] C / A i a q u L 67

6k »

jM a d b d

i 28023

iB o t to m -D o lla f M a k e i s jB tB e v e ie g e s

L e u ren ce L eb h en E fa eb eth Ih c o h i/ i d a id A th v r e iih

f c c c a r t h g M a v jg e , jS e le t R e p ie te n le tiv e

*| 12. n j e d e t B o u ñ w $ 123T « a v r a $ s e n B l v d . |F a u n t t e « y C i r c u s

iM a se fc T *« w etse n London

113008 1T 2F 8M 4 j E C 25N T

' ^ r

i

....... ii

j 0w n «

24. p l a c e K ^ b e t

E iB e ii n

P

v

n a

I i 22f » ] o 5021 06023 01720 $•95822 88306

; C a J u » C o m r d e t M i tevai [C e r tto c a n e ic i a l M o c te z u m a

.P e ln c to S iT ip to n [F r e n c isc o C h e n g

j$abiAgent [M a rk etin g M e n e g a

:Ce«io333 |:S i e n a s d e G i e n e d a 9333

0u e n o 3A i r e i M 64^ 0. F .

iU H O | 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. Mreža podataka

^ogledajte područje ispod mreže podataka i vidjet ćete tri objekta: northwindDataSet f stomerDataConvertor i customersTableAdapter kao što je prikazano na slici 14-9.

Poglavlje 14: Pristup pođadma kroz AD0.NET

|

351

DccluroliW cDstof)i.‘play-M lcroto«»cve'apmcPtl.nviipnm tM U* X. /S«l«^«OB^»lOeiW»V(.

I;,‘ l i i a S ^ ’D*d *« Bv9M X W < l pr«(5j

' " 6 tC 3oc*d M snsp

U HTML pogledu datoteke UelloWeb.aspx možete vidjeti da je u tijelu stranice obraf t zac zadan korištenjem standardne HTML oznake za obrazac:

1



|: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 poslužiteljske obrade. Bilo koja oznaka s ovim atributom se uzima kao (poslužiteljska kontrola koju izvodi ASP.NET kostur na poslužitelju. Unutar obrasca Visual Studio je postavio div oznake kako bi omogućio umetanje kontrola i teksta. Kad ste napravili prazan Web Forms obrazac prva stvar koju biste mogli poželjeti učiniti je dodati tekst na stranicu. Prebacivanjem u HTML pogled možete dodati skriptu i HTML kod direktno u datoteku baš kao što to možete kod klasičnog ASP-a. Dodavanje sljedećeg reda koda u tijelo HTML stranice će prikazati pozdravnu poruku i trenutno lokalno vrijeme na stranici: :

Hello World! It is now

Oznake funkcioniraju jednako kao i u klasičnom ASP-u, pokazujući da kod dolazi izmeñu njih (u ovom slučaju C# ). Znak =odmah na početku oznake izaziva da ASP.NET prikaže vrijednost, jednako kao poziv metode Response.Write(). Jednako tako biste mogli napisati red koda: Hello World! It is now

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

) Pac>. - 0

■@

Tools

[1

H e lp

^

...............L

P 5 « rc h ^ F a v crto #

Address l^rhttp:;/localho5t:27396/Programmin9CSharpWeb/HelloWeb.aspx v j 0

j 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

Omogućavanje otkrivanja pogrešaka Kad pritisnete F5 pokrećete 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 pogrešaka) i pojavit će se dijaloški okvir Debugging N ot Enabled, kao na slici 15-5. U ovom dijaloškom 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 otkrivanje pogrešaka 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 ) |

_______ ___________ _______ 7 u k T l5^ D y a l o s k i 7k v i r k o j i ć ^ H d d a toteke 364

1

Programiranje C#

^

^

OK

|[

Cancel

Help

p o g reša k a b e z V e b .c o n f

odavanje kontrola pslužiteljske kontrole možete dodati na Web Forms obrazac na tri načina: upisivanjem HTML koda u HTM L stranicu, povlačenjem kontrola s palete s alatima na |jesign stranicu ili programski, dodavajući ih prilikom izvoñenja. Na primjer, pretpostavimo da želite koristiti gumbe kako biste korisniku omogućili da odabere jednog fjd tri isporučitelja iz Northwind baze podataka. Možete napisati sljedeći HTML kod '.'unutar elementa u HTML prozoru: 'http://w*w.w3.org/l999/xhtm1" > )/otd1yt< > formmeihod»'post”actlorc»'Dafau1t.aspx" 1d»”forml'> >uctontype-”rad1o“name»”ftad^oBirtionL^stl”valufi-"!" /x1abe! < / t r > « r ^ ^ i d ''R » d ' * o B u t t o xype-"rad1o" na/ne-'-RadloBirttonUstl" valueO" /xlabol for«"fiad10ButconLlstl_lSonltedPackage

'fSg;] for«"Rad< o< n/ (.!iastlbe_12x'/ttd yp oe'‘u tt^ orn^i.d 1sJi< l_lf2)_1»JtFeud»e"rA aladsihol6piprlxntg >e-"radlo“name-'Rad1oBirtxont.1stl"value-"3" /xlabel

■ ■ '*

< A r>

' < /4foU ?>

Slika 15-14. HTML kod koji ASP.NETšalje pregledniku

Dodavanje kontrola i dogañaja Dodavanjem još nekih kontrola možete napraviti kompletan obrazac koji korisnici mogu upotrijebiti. To ćete učiniti dodavanjem prikladnije pozdravne poruke (,,Welcome 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 neće dobiti nikakvu nagradu za dizajn, ali njegova upotreba će razjasniti velik broj ključnih pitanja o Web Forms obrascima.

Local in tra n e t

Slika 15-15. Kompletan obrazac za isporučitelje 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"> Choose Shipper Vhead>

■j:body>

Kad korisnik pritisne Order gumb provjerit ćete je li korisnik unio svoje ime i pružiti povratnu informaciju o odabranom isporučitelju. Zapamtite, prilikom projektiranja ne možete znati ime isporučitelja (ono se uzima iz baze podataka) pa ćete u padajućem popisu morati provjeriti odabrano ime (i identifikator). Da biste sve to postigli prebacite se u Design način rada i dvaput pritisnite gumb Order. Visual Studio će prikazati stranicu s kodom i generirati metodu za obradu dogañaja za Click dogañaj gumba.

A^

^

I

i koristi standardne komunikacijske protokole za Internet.

1 Od 1999. godine pojam SOAP je prestao nešto 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 podržavaju SOAP poželjna su, ali ne i ‘ obavezna, još dva protokola: opis metoda koje pruža pojedina usluga a koje klijent može razumijeti i koristiti te opis svih takvih usluga dostupnih na pojedinoj Web lokaciji ili ' URL-u. .NET kostur pruža prvi od ta dva opisa s pomoću jezika Web Service Description Language (WSDL) koji je tvrtka Microsoft razvila zajednički s IBM-om i drugima. WSDL je XML shema koja se koristi za opis dostupnih metoda Web usluga tj. njenog sučelja.

Podrška na poslužiteljskoj strani Sva podrška potrebna za izradu Web usluga se nalazi unutar .NET kostura i pružaju je klase unutar System.Web.Services imenskog prostora. Izrada Web usluge ne zahtijeva od vas posebno programiranje. Vi samo trebate napisati kod za implementaciju, dodati [WebMethod] atribut i pustiti poslužitelju da obavi ostalo. O atributima se detaljnije govori u poglavlju 18.

Podrška na strani klijenta Web uslugu koristite pisanjem klijentskog koda koji se ponaša kao da komunicira izravno s lokalnim objektom, ali koji zapravo komunicira s poslužiteljem i to preko posrednika. Posao posrednika je da predstavlja poslužitelja na klijentskom računalu, da zapakira klijentove zahtjeve u SOAP poruke koje se šalju poslužitelju i da pribavi odgovore koji sadrže rezultat.

Izrada Web usluge Da bismo ilustrirali metode za implementaciju Web usluga u C # jeziku korištenjem klasa usluga .NET kostura izradit ćemo jednostavan kalkulator i onda pristupiti njegovim metodama preko Weba. Počnite sa zadavanjem Web usluge. Da biste to učinili, definirajte klasu koja nasljeñuje od klase System.Web.Services. WebService. Najlakši način 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

Lenguage:

jv is u a lC ir

J ^ | e n ts | i2 0 0 5 Books\C 5harp 1e\Source\Chapter 15\CakulatorW S

.:

r

ok

S rovfse...

j

Cancel

'| I

. Slika 15-18. Izrada Web usluge

Visual Studio .N ET stvara kostur Web usluge i pruža primjer metode usluge koju ..možete 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 traženu operaciju i vraća vrijednost istog tipa. Na primjer, evo koda za izračunavanje 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 izložili svaku od tih metoda kao W eb uslugu samo dodajte [WebMethod] atribut prije deklaracije svake metode: [UebMethod]

Ne trebate izložiti sve metode klase kao Web usluge. Možete odabrati metode dodavanjem [WebMethod] atributa samo onim metodama koje želite izložiti. To je sve što trebate učiniti - .NET se brine za ostalo.

WSDL i im enski prostori Vaša Web usluga koristit će WSDL XML dokument da opiše krajnje točke koje su dostupne preko Weba. Unutar bilo kojeg WSDL dokumenta moraju biti korišteni XML imenski prostori kako bi krajnje točke imale jedinstvena imena. Podrazumijevani XML imenski prostor je http://tempuri.org, ali to ćete trebati promijeniti prije nego što Web uslugu učinite javno dostupnom. Možete promijeniti XML imenski prostor korištenjem Web Service atributa. [WebService(Namespace= "http://www.LibertyAssociates.com/WebServices/")]

Ne očekuje se da se na ovoj URL adresi nalazi dokument. URL adrese se koriste jer su praktičan 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 izvoñenjem programa u Visual Studiju ,NET vidjet ćete autoklimatski generiranu poslužiteljsku Web stranicu koja opisuje Web uslugu, kao što je

I

■ 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 omogućava 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

Q

©Back -

0

Tools

[ž| i i

Help

^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 _______________________________

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

;^1 Q Go

, . ; [ 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 upišete vrijednost tri u prvo polje, četiri u drugo polje i pritisnete Invoke, zatražit ć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

© B ack

V ie w

F a v o rlte s

■ © ' [ * }

T o o ts H )

H e lp j P

S e»ch

^ F a v o r lte s

Address g ) http://localhost:19975/CalculatDrWSyService.asmxAžow £ 3 RoboForm

Q \

0

-

%

m^

B Go

-li Unks

® Passcsrds **■ {EUIdtentittes ^ j|Q Safenotes

8 K / d o u b le >

JDone

i

j^

localintranet

Slika 15-21. Pozivanje Pow() metode

Pregledavanje WSDL sažetka Puno posla automatski obavlja umjesto vas. H TM L stranice koje opisuju Web uslugu i njene metode se stvaraju i uključuju 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 možete : 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 @Back ' ; Adđress j

li) lsi ii ^Psearcb ^FavoriSM

0 -

http://localhost:19975/Calci.iiatDfWS^erf«:e.asm>;?wsdl

^ ' J

gRobcform - @Passcards *• jb] IdenOties - (gjsafenotes - [SjFlllForrrts * I*}Sav©

w0 .

m

;#

[*10 M I i %

r.-iT (L^Hide

; trk* ^eioglheT"

10

- 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 / '

3r,

.,Tnlns:?o'tpc-nc=''litt|)://S(:h(*nk)i.Kinl«»oo|).Qrg/soop/fiii(:ortiiiq/" v.m!ns:n.ime-="hU|)://schi?nui»s.xinlsoap.org/vfsc1l/inirMO/" tn;="hllp://lempuri.org/" xrnlri2: littp :// vfv - < 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“ />

400

|

Programiranje f#

primjer 16-3. Prikazivanje rezultata (nastavak) Position





Z a p o t p u n o objašnjenje polja predložaka idrugih elemenata korištenih na ovoj stranici pogledajte knjigu P r o g ra m ir a n je A S P . N E T .



Drugi stupac je takoñer predložak polja, ovaj put sa zaglavljem T itle stupca. Sam naslov jc prikazan zadavanjem vrijednosti naslovnog stupca u tekućem redu skupa



^podataka s kojim je GridView objekt povezan te okružujući taj naslov s hipervezom prema odgovarajućoj stranici na Amazon.com. To naslov pretvara u hipervezu koju korisnik može pritisnuti.



Programming Visual Basic .NET 2 nd Edition

Prva dva stupca su složenija. Prvi zato stoje potrebno malo truda da se napravi rowNumber (pogledajte sljedeći kod), a drugi zato što trebamo umotati povezanu vrijednost (ISBN i naslov) u hipervezu. Sljedeća četiri stupca su jednostavnija zato što su samo povezani s podacima. Prvi povezani stupac u zaglavlju ima tekst Author, označen je kao read0nly i povezan je sa stupcem za autore u redu DataSet tablice s kojom je ovaj GridView povezan:

.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 «,

: '

I

. . . .

>f

S lika 1 7 -2 . M a n i fe s t p r o z o r

Sljedeći red sklopa je referenca na sklop iz primjera 12-3. Možete takoñer vidjeti da se ovaj sklop sastoji od samo jednog modula. Zasad možete zanemariti ostatak metapodataka.

Sklopovi s više modula Sklopovi mogu sadržavati više od jednog modula, ali Visual Studio 2005 to ne podržava. Sklop s jednim modulom ima samo jednu datoteku koja može biti EXE ili DLL datoteka. Taj modul sadrži sve tipove i implementacije za aplikaciju. Manifest sklopa •jeugrañen unutar ovog modula. Svaki modul ima svoj manifest koji je odvojen od manifesta sklopa. Manifest modula 'sadržava 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 takoñer može sadržavati resurse koji su mu potrebni. Sklop s više modula sastoji se od više datoteka (nijedna ili više EXE datoteka i nijedna ili više DLL datoteka, iako morate imati barem jednu EXE ili DLL datoteku). Manifest sklopa se u ovom slučaju može nalaziti u zasebnoj datoteci ili može biti ugrañen u jednom od modula. Kad je sklop referenciran, prilikom izvoñenja se učitava datoteka (koja sadrži manifest i zahtijevani moduli po potrebi.

Poglavlje 17: Sklopovi i rad s inačicama

|

417

Izrada sklopa sviše modula Da bismo prikazali upotrebu sklopova s više modula, u sljedećem primjeru je napravljen par vrlo jednostavnih modula koje možete smjestiti u jedan sklop. Prvi modul je Fraction klasa. Ona će vam omogućiti 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. Takoñer postoji Add() metoda koja uzima drugi razlomak i vraća zbroj, pretpostavljajući da razlomci imaju zajednički nazivnik. Ova klasa je namjerno napravljena jednostavno, 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 takoñer nalazi u ProgCS imenskom prostoru. To je dovoljno za izradu sklopa. Upotrijebite Assemblylnfo.cs datoteku za dodavanje metapodataka u sklop. Upotreba metapodataka je obrañena 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 učini za vas.

Visual Studio izrañuje samo sklopove s jednim modulom. Možete napraviti resurs s više modula korištenjem /addModules opcije u naredbenom redu. Najlakši način da prevedete i izradite sklop s više modula je upotrebom makefile datoteke koju možete napraviti u Notepadu ili bilo kojem drugom programu za ureñivanje teksta.

Poglavlje 17: Sklopovi i rad s inačicama

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

' * £ ; ograničenja 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 više 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 objašnjena). 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 program bez ikakvog modifikatora. \J podmapi\bin ćete pronaći 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:TRAČE

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 počinje definiranjem sklopa koji želite napraviti: ASSEMBLY= MySharedAssembly.dll

420

|

Programiranje C#

Zatim se zadaju mape koje ćete koristiti - izlaz se smješta 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 odredišnoj mapu (bin). Ovo upućuje l'nmake program (program koji izvodi makefile datoteku) da izgradnja $(DEST)\$(ASSli.VlBLV; ovisi o tri druga navedena odredišta i pruža 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, smještajući 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 uputite nmake program kako da napravi Calc.dll. Za to trebate izvorni kod Calc.cs. Zadajte programu nmake sljedeću naredbu da napravi DLL: $(DEST)\Calc.dll: Calc.cs $(DEST) $(CSC) $(MODULETARGET) /r :$(REFERENCES: =;) /out:$@ %s

Zatim učinite isto za Fraction.dll: $(DEST)\Fraction.dll: Fraction.es $(DEST) $(CSC) S(MODULETARGET) /r:$(REFERENCES: =;) /out:$@

%s

Rezultat izvoñenja 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 sadrži samo manifest, kao na slici 17-3.

Poglavlje 17: Sklopovi i rad s inačicama

|

421

Ako proučite manifest, vidjet ćete metapodatke za biblioteke koje ste napravili, kao na slici 17-4.

.pub pubUcfcegtaite« • (87 .urr 7: 0: 3600:8

ii)

jss*wi>ly Hji Hjisnarcafls flsit«01 «01ii

■sssi i£££ Si!

- ! jj j; I

:ćS™iSSE Si! •“SS ‘.SSS Si!

S| • ć A AB Ft C1 10 27 9C 80 59 65 67

// *••

“".„“•Ti« «•« » f « *•” ■»« “ « FI ’* “

7(1 k£ 91 »F > .c la ss M l * r n p ubl ublic pipi-ogCS.Fractlon

ern public progCS .ngiCde

;rS!!:=T!SS5!SiSli-««-«—

.ltugebase 8x80600900 Fil« »Ugn»eiit 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 manifestom i Calc.dll i Fraction.dll s potrebnim tipovima i implementacijama.

Ispitivanje sklopa Da biste koristili ove module napravit ćete upravljački 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 neće učitati 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 učitava 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 važno je da u Main() ne stavite kod koji ovisi o modulima. Ne želite da se moduli učitaju zajedno s Main() metodom jer tako Fraction i Calc objekti neće biti smješteni u Main(). Kad pozovete UseFraction i UseCalc moći ćete vidjeti da su moduli individualno učitavani.

Učitavanje sklopa Sklop se učitava u aplikaciju korištenjem AssemblyResolver u procesu koji se zove ispitivanje (engl. probing). Unutar .N ET kostura Assembiy Resolver se automatski poziva i ne trebate ga eksplicitno pozvati. Njegov posao je da učita 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 podmapa koji je sastavio korisnik i koje se nalaze ispod korijenske lokacije koja je navedena u konfiguracijskoj datoteci aplikacije). Postavite točku prekida u drugom redu Main() metode, kao što je prikazano na slici 17-5. Prekinite izvoñenje kod točke prekida i otvorite prozor Modules. Samo dva modula su učitana, kao što je prikazano na slici 17-6.

Poglavlje 17: Sklopovi i rad s inačicama

I

423

£ Programming_C5harp.Test

l^MainO

namespace Progv9Htttiing_CShai:p t using Sy3tem; i i

U

public cla ss Test < // main Mili not load the shared asserttoly s t a t ic void Hain() <

li

t . UseCS (); t.T Jse F cactionl);

Slika 17-5. Točka prekida u Main() metodi

; 0 v s t o s t u tid 'l a Sy»t y»tam.Wravr avrtio.\GfeC _M... C:^wi>cOWS\sii€nt*r\GaC_M... C.\W lM 30W \*!«n*#»\GAC J^... C ;\W W X>*S\*!errtt*ViAi:,J4... r A / t a ii r m u and S«W *js\Xft... C:\WffC Ow S\«ierrt*r\GAC „3...

v«' y« V« Yes r« y« v«

C;VW M DOW S\«K«rttAGAC>t- Y« C:\0txi*iw iU andSette>0rtJM... No C:\DocumenU and SetthQS\M ... No C:\Occtm eritt and Settr^sUes... No

Hi Cocto No No No No N> No No

Sym M Statut N oirtrD cfebj... No vytrfccfe ba. . N a ir o to b lN .. Novytrfcctsbe... NonrrC ohbe... H0 5yoW5fca... Ud t r o t * * toa..

i-e

Nos/otJdstea.. ea... ... S y otrtbaded. 5y. ........ z.Y.;.. 4.

ImiSlika 17-8. Manifest datoteke MySharedAssembly.dll »Dodavanjem jakog imena potpisali ste ovaj sklop (točne vrijednosti će biti drugačije 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 učinili, pozicionirajte se u fpmapu u kojoj se nalazi DLL i unesite sljedeće u odzivniku: siv sn -T MySharedAssembly.dll

Pomoćni program sn razlikuje velika i mala slova. Ne pišite 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 skraćena vrijednost javnog ključa za taj sklop koja se naziva oznaka §javnog ključa (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 | Sljedeći korak je da smjestite biblioteku u GAC. Da bi to učinili, otvorite prozor Explo| rer i pozicionirajte se u %SystemRoot% mapu. Kad dvaput pritisnete Assembly podi j mapu Explorer se pretvara u GAC preglednik.

H■

možete povući i i ispustiti u GAC preglednik ili možete pozvati ovaj pomoJ|ćni program za odzivnik: Gacutil /i mySharedAssembly.dll

Poglavlje 17: Sklopovi i rad s inačicama

|

429

kazana u GAC pregledniku vrijednosti koju je vratio sn pomoćni program: Public key token is 01fad8eCrf0941a4d

Ovo je prikazano na slici 17-9. Assembly Name

‘______________________

d|Ž| Microsoft,StdForm at iSijMIcrosoft.VisualBasic 5Ž|Microsoft.VisualBasic.Com patibility ^ M i c r o s o f t .VisualBasic,Compatibility .Data

Version

Culture 1 Public Key Tokari

7 .0 . 8 .0 . 8 .0 . 1200.0 8 . 0 , 1200.0 8 . 0 . 1200.0

3300.0 1200.0

b03f5f7flld50a3a b03f5f7fl Id50a3a b03f5f7f Ud50a3a b03f5f7fl Id50a3a

iiSiJMicrosoft .VisualC afijM icro so ft. VisualC. ApplicationVerifier

8 .0 . 1200.0

1.0.0.0

b03F5f7flld50a3a b03f5f7f 1Id50a3a b03f5f7fl Id50a3a

2lŽ| M icrosoft, VisualC. VSCo deParser

8 .0 . 1200.0 8 . 0 , 1200.0

b03f5f7flld50a3a b03f5f7flld50a3a

jS b Microsoft. VisualBasic.Vsa

il^ jM icrosoft. VisualC. VSCodeProvider ^ M i c r o s o f t .VisualStudio

2 .0.

i{Žj Microsoft. VisualStudio.CornmandBars

8.0.

SpjiJMicrosoft .VisualStudio.Configuration

2.0.3600.0

3600.0

0.0

b03f5f7flld50a3a b03f5f7flld50a3a b03f5f7flld50a3a

Slika 17-9. GAC Kad je to obavljeno, imate dijeljeni sklop kojem može 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 DLLovi za ovu biblioteku u njegovoj izravnoj putanji. Upravo ste napravili i upotrijebili dijeljeni sklop.

Ostali potrebni sklopovi Manifest.sklopa sadrži i reference prema drugim sklopovima. Svaka takva referenca uključuje ime drugog sklopa, broj inačice, potrebnu kulturu i neobavezne oznake javnih ključeva drugih sklopova (digitalne potpise). Kultura je niz znakova koji predstavlja jezične i nacionalne karakteristike prikaza za osobu koja koristi vaš program. Kultura odreñuje, 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, naglašavao sam da .NET aplikacija sadrži kod, podatke i metapo|datke. Metapodaci predstavljaju informacije o podacima, to jest, informacije o tipovima, kodu, sklopovima i tako dalje, koje su pohranjene zajedno s programom. Ovo rnpoglavlje proučava 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 vašim podacima, metodama, klasama j samom programu. Atributi se umeću u metapodatke i vidljivi su s pomoću alata S|;BLDasm i drugih alata za čitanje metapodataka. | Refleksija (engl. reflecetion) je proces kojim program može čitati svoje metapodatke ili metapodatke drugog programa. Kaže se da program odražava sebe ili neki drugi proigram izvlačeći metapodatke iz odraženog sklopa i koristeći ih da informira korisnika |ili promijeni ponašanje programa.

r

Atributi

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 sučeljem da ukaže kako bi ciljna klasa trebala izvoditi iz |IUnknown a ne iz IDispatch prilikom izvoženja u COM. Programiranje COM sučelja je E,detaljno obrañeno u poglavlju 22. |U poglavlju 17 vidjeli ste ovaj atribut: [assembly: AssemblyKeyFile("c:\\myStrongName.key")]

431

Time se umeću metapodaci u sklop da zadaju jako ime programa.-

Atributi Neki atributi su pruženi kao dio CLR-a ili kostura. Pored njih možete napraviti i atribute koji su prilagoñeni vašim potrebama. Većina programera će koristiti atribute koje pruža kostur, iako izrada vlastitih atributa može biti moćan 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 sklop, drugi na klasu ili sučelje, a neki, kao [VJebMethod], na članove klase. Ovi objekti predstavljaju ciljeve atributa. Mogući atributi su deklarirani u AttributeTargets enumeraciji i detaljno su opisani u tablici 18-1.

Tablica 18-1. Mogući ciljevi atributa Im e člana

U p o tre b a

A li

Prim jenjuje se na jed a n od sljedećih elem en ata: sklop, klasa, konstruktor, delegat, enumeracija, dogañaj, polje, sučelje, 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 dogañaj

F ie ld

P rim jenjuje se na polje

In t e r f a c e

Prim jenjuje se na sučelje

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 više od samog umetanja metapodataka. C# prevoditelj pazi na ovaj atribut (kao i na neke druge) koji izaziva poseban način rada. U ovom slučaju, čita ključ datoteke i koristi taj ključ za digitalni potpis sklopa. Obično, meñutim, atributi su samo statički metapodaci koje se umeće u sklop.

432

|

Programiranje C#

I ? 1 i

primjena atributa Šprimjenite atribute na njihove ciljeve stavljanjem u uglate zagrade odmah ispred ciljanog elementa (osim u slučaju sklopova, kad ih stavljate na početak datoteke). i jdožete kombinirati atribute slaganjem jednog iznad drugog: [assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile(".\\keyFile.snk")]

1Xo može biti izvedeno i odvajanjem atributa zarezima: [assembly: AssemblyDelaySign(false), assembly: AssemblyKeyFile("A\keyFile.snk")]

|||p:

A tribute sklopa m orate sm jestiti nakon svili using deklaracija te prije koda.

'S'iV'Č Mnogi atributi se koriste za meñudjelovanje s COM-om, o čemu ćemo detaljnije govoriti 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 veći broj atributa, uključujući atribute za sklopove (kao sto je AssemblyKeyFileAttribute), konfiguriranje te atribute za inačice. Jedan od atributa s kojim ćete se najčešće 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 može 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 slučaju to je deklaracija klase. Ključna činjenica o atributima je da znate kad su vam potrebni. Zadatak koji trebate W iobaviti diktirat će njihovu upotrebu. #

I

Prilagoñeni atributi Možete napraviti atribute koji su prilagoñeni vašim potrebama te ih koristiti prilikom izvoñenja kako vam odgovara. Pretpostavite, na primjer, da vaš odjel želi voditi evidenciju o ispravljenim pogreškama. Već imate bazu podataka o svim pogreškama, ali biste htjeli povezati svaki izvještaj o pogrešci s odreñenim ispravkom u kodu. Mogli biste dodati komentare u kod: // Pogreška 3 2 3 . Ispravio lesse Liberty 1/1/2005.

To biste lako uočili u izvornom kodu, ali ne postoji veza prema pogrešci 323 u bazi podataka. Prilagoñeni atribut bi mogao biti upravo ono što tražite. Zamijenili biste komentar s nečim 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, nañe ova zabilješkeo ispra vljenoj pogrešci te da ažurira bazu podataka. Atribut bi imao ulogu komentara ali bi takoñer omogućavao da programski pribavite podatke s pomoću alata koje biste sami napisali. ■».

a

Ovo je. m ožda neprirodan prim jer jerb i atributi bili prevedeni u finalni kod za isporu ku .

.A*

Deklariranje atributa Atributi, kao i većina drugih stvari u C# jeziku, su uključeni u klase. Da biste napravili prilagoñeni atribut izvedite novu prilagoñenu klasu atributa iz System.Attributepublic class BugFixAttribute : System.Attribute

Trebate uputiti prevoditelja s kojim elementima ovaj atribut može 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 pruža, ako vam je tako drago, meta-metapodatke, to jest, podatke o metapodacima. Konstruktoru atributa AttributeUsage prosljeñujete dva argumenta. Prvi argument je skup zastavica koje ukazuju na cilj, u ovom slučaju klasu i njen konstruktor, polja, metode i svojstva. Drugi argument je zastavica koja ukazuje da li navedeni element može primiti više od jednog takvog atributa. U ovom primjeru, AllowMultiple je postavljen na true, pokazujući da se članovima klase može dodijeliti više atributa BugFixAttribute.

Imenovanje atributa Novi prilagoñeni atribut u ovom primjeru je nazvan BugFixAttribute. Uobičajeno je dodati riječ attribute imenu atributa. Prevoditelj to podržava tako što dozvoljava da nazovete atribut skraćenim imenom. Prema tome, možete napisati: [BugFix(l23, "lesse Liberty", "01/01/05", Comment="3edna manje")]

Prevoditelj će prvo potražiti atribut BugFix te, ako ga ne pronañe, potražit će BugFixAttribute.

Konstruiranje atributa Atributi uzimaju dva tipa parametara: pozicijske i imenske. U BugFix primjeru, programerovo ime, identifikator pogreške i datum su pozicijski parametri, a komentar 434

|

Programiranje C#

|fe imenski parametar. Pozicijski parametri se prosljeñuju kroz konstruktor po redosliIjedu 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;

} } RUobičajeno je za pozicijske parametre napraviti svojstva samo za čitanje: public int BuglD

{ get

{ return buglD;

}

}

Upotreba atributa gpt’Kad ste definirali atribut možete ga početi koristiti tako što ćete ga staviti odmah |ispred cilja. Kako biste testirali BugFixAttribute iz prethodnog primjera, sljedeći proigram stvara jednostavnu klasu MyMath i dodjeljuje joj dvije metode. Dodijelite BugFixAt. tributes atribut klasi za bilježenje povijesti održavanja 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 prilagođeni 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 prilagoñenim atributima (nastavak) II Pristupnik public string Date

{ get

|:S§

{ return date;

} II

1'

} Pristupnik

public string Programmer { get

I*

{

Sli

return programmer;

} }

1 i ,

I

I

}

// ********* Dodjeljivanje atributa klasi ******** [BugFixAttribute( 121, "lesse Liberty", "01/03/05" )] [BugFixAttribute( 107, "lesse Liberty", "01/04/05", Comment = "Fixed off by one errors" )] public class MyMath { 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 možete vidjeti, atributi nemaju nikakvog utjecaja na izlaz programa. Zapravo, 1 u ovom trenutku imate samo moju riječ da atributi uopće postoje. Brzi pogled na metapodatke korištenjem ILDasm alata otkriva da su atributi ipak na svom mjestu Poglavlje 18: Atributi i refleksija

|

437

(slika 18-1). U sljedećem 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 ft £

C u s to m A ttrib u te s ,P ro p e rtie s 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 ( ) 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

ft £

A P ro g ra m m e r : in s ta n c e s trin g O 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 način da im pristupite, a idealno bi bilo da im pristupate prilikom izvoñenja. Klase u imenskom prostoru Reflection zajedno sa System.Type klasom osiguravaju podršku za ispitivanje metapodataka i rad s njima. Refleksija se općenito koristi za sljedeća četiri zadatka.

438

|

Programiranje C#

Pr■egledavanje m e ta p od a tak a

Mogu ga koristiti alati i pomoćni programi koji žele prikazati metapodatke.

i Otkrivanje tipova

Omogućava da ispitate tipove u sklopu te da s njima radite ili da ih instancirate. To može biti korisno prilikom izrade prilagoñenih skripti. Na primjer, mogli biste omogućiti korisnicima da meñudjeluju s programom korištenjem 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

Omogućava progameru da pozove svojstva i metode na objektima koji su dinamički distancirani uz upotrebu otkrivanja tipova. Ovo se naziva i dinamičko pozivanje (engl. d y nam ical invocation). Stvaranje tipova p rilikom izv oñenja

Najekstremnija upotreba refleksije je izrada novih tipova tijekom izvoñenja i njihova upotreba za izvoñenje zadataka. To biste mogli koristiti kada se prilagoñena klasa napravljena za vrijeme izvoñenja izvodi brže nego općenitiji kod napravljen prilikom prevoñenja.

Pregled metapodataka (J ovom dijelu upotrijebit ćete C# podršku za refleksiju da biste učitali metapodatke iz MyMath klase. Započnite 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 učahuruje reprezentaciju tipa objekta. Ta klasa je primarni način za pristupanje metapodacima. Type izvodi iz Memberlnfo te učahuruje informacije o članovima klase (metodama, svojstvima, poljima, dogañajima i tako dalje).

*

Sljedeći korak je da pozovete GetCustomAttributes na objekt Memberlnfo prosljeñujući tip atributa kojeg želite naći. Natrag dobijate polje objekata tipa BugFixAttribute: object[] attributes; attributes = inf.GetCustomAttributes(typeof(BugFixAttribute),false);

Sad možete 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 prilagođenih 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 možete koristiti za istraživanje sadržaja sklopa. Možete pronaći tipove pridružene modulu; metode, polja, svojstva, dogañaje pridružene tipu kao i potpise svih metoda tipa, sučelja koja tip podržava i osnovnu klasu tipa. Da biste počeli, dinamički učitajte sklop korištenjem Assembly. Load() statičke metode. Klasa Assembly za potrebe refleksije učahuruje sam sklop. Jed an od potpisa Load metode je: public static Assembly.Load(AssemblyName)

U sljedećem primjeru proslijedite jezgrenu biblioteku metodi Load(). Datoteka Mscor-

lib.dll sadrži jezgrene klase .N ET kostura: Assembly a = Assembly.Load("Mscorlib");

Kad je sklop učitan možete pozvati GetTypes() da vratite polje Type objekata. Objekt Type je ključni dio refleksije. Type predstavlja deklaracije tipa (klase, sučelja, polja,

vrijednosti i enumeracije): Type[] types = a.GetTypes();

440

|

Programiranje C#

Sklop vraća polje tipova koje možete 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 neću cijelog navoditi. Evo kratkog 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 računalu sadržavalo 2373 unosa. U inačici 1.1 sam u svom računalu našao 1426 unosa. Dečki iz M icro_

_

A*

«>,'

softa su bili vrijedni!

Poglavlje 18: Atributi i refleksija

|

441

Refleksija tipa Možete reflektirati samo jedan tip izMscorlib sklopa. Da biste to učinili izdvojite tipi^ sklopa s pomoću 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 Možete od tipa Assembly zatražiti sve njegove članove korištenjem metode GetMembers() 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 prilično 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

Pronalaženje metoda tipa

Možete se usredsrediti samo na metode, isključujući polja, svojstva i ostale članove. Da biste to učinili, uklonite poziv metode GetMembers(): Memberlnfo!] mbrInfoArray = theType.GetMembers();

; i dodajte poziv metode GetMethods(): mbrInfoArray = theType.GetMethods();

/Izlaz sada sadrži 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

Pronalaženje samo odreñenih članova tipa Konačno, da biste još više suzili pretragu možete upotrijebiti FindMembers metodu da nañete samo odreñene članove tipa. Na primjer, možete suziti pretragu na metode čije ime počinje 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 tražite. Tu spadaju Ali, Constructor, Custom, Event, Field, Method, Nestedype, Property i Typeinfo. MemberTypes.Method ćete koristiti da nañete metodu. BindingFlags Enumeracija koja nadzire na koji način refleksija izvodi pretragu. Postoji mnogo BindingFlags vrijednosti, uključujući 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 slučaju prosljeñujete Get* da biste našli samo one metode koje počinju sa Get. Kompletan popis dobiven filtriranjem ovih metoda je prikazan u primjeru 18-6. Primjer 18-6. Traženje odreñenih članova ttregion Using directives using using using using

System; System.Collections.Generic; System.Reflection; 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. Traženje odreñenih članova (nastavak) Console.WriteLine( " { 0 } is a { i } 1', mbrlnfo, mbrInfo.MemberType );

} }

i

t Kasno povezivanje H' Kad pronañete metodu moguće ju je pozvati korištenjem refleksije. Na primjer, možda v budete htjeli pozvati Cos() metodu iz klase System.Math koja vraća kosinus kuta. Možete, naravno, pozvatiCos() i u normalnom toku koda, ali refleksija vam omogućava povezivanje s tom metodom tijekom izvoñenja. To se zove kasno povezivanje (engl. late binding) i nudi fleksibilnost odabira objekta za povezivanje tijekom izvoñenja i njegovog programskog pozivanja. To može biti korisno kod izrade prilagoñene skripte koju bi mogao pokrenuti korisnik ili kad radite s objektima koji možda nisu dostupni prilikom prevoñenja. Na primjer, korištenjem kasnog povezivanja program može meñudjelovati 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 možete dinamički učitati instancu klase koristeći statičku metodu klase Activator. Pošto je Cos() statička metoda ne trebate praviti instancu System.Math klase (a i ne možete jer System.Math nema javni konstruktor). Activator klasa sadrži četiri metode (sve su statičke) koje možete koristiti da napravite objekte (lokalno ili udaljeno) ili da uzmete reference postojećih objekata. Te četiri Vmetode su: i CreateComlnstanceFrom : Stvara instace COM objekta. CreatelnstanceFrom Stvara referencu na objekt iz odreñenog sklopa i tipa imena. ; GetObject Koristi se kod rasporeñivanja objekata. Rasporeñivanje (engl. marshaling) je detaljno obrañeno 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 možete pozvati metodu nad objektom morate uzeti metodu koju trebate od objekta theMathType. Da biste to učinili pozvat ćete GetMethod() proslijedivši ' • potpis Cos metode. Potpis metode, sjetit ćete se, je ime metode (Cos) i njeni parametri tipa. Cos()imasamo jedan parametar: realan broj (double). Meñutim, Type.GetMethod uzima dva pararne tra. Prvi je ime metode koju tražite, a drugi su parametri. Ime se prosljeñuje 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 pozivanjem statičke metode Type.GetType() i prosljeñivanjem niza System.Double. Sad imate Methodlnfo objekt na kojem možete pozvati metodu. Da biste to učinili morate proslijediti objekt nad kojim se metoda poziva i stvarnu vrijednost parametara opet u polju. Postoje metoda statička, 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, sadrži parametre tipa. Drugi, parameters, sadrži stvarnu vrijednost. Ako bi metoda uzela dva argumenta, trebali biste deklarirati ova polja da sadrže dvije vrijednosti. Ako metoda nije uzela nijednu vrijednost, opet možete napraviti polje, ali mu veličinu postavite na nula! Type[] paramTypes = new Type[0]; Ispravno je iako izgleda čudno.

Primjer 18-7 prikazuje dinamičko pozivanje Cos() metode. Primjer 18-7. Dinamičko pozivanje metode #region Using directives using using using using

446

System; System.Collections.Generic; System.Reflectlon; System.Text;

I

Programiranje C#

( Primjer 18-7. Dinamičko pozivanje metode (nastavak) Kendregion | namespace DynamicallyInvokingAMethod

U \

I

public class Tester

{

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 možete ^upotrijebiti refleksiju kako biste našli sklop na korisnikovom računalu, postavili upit ||o dostupnim metodama i dinamički pozvali neku od tih metoda.

Poglavlje 18: Atributi i refleksija

I

447

POGLAVLJE 19

Rasporeñivanje i rad na daljinu

Dani integriranih programa koji rade u sklopu jednog procesa na jednom računalu su, ako ne mrtvi, onda barem ozbiljno ranjeni. Današnji programi se sastoje od složenih komponenata koje se izvode u više procesa, često preko mreže. Web je omogućio distribuirane aplikacije na način koji je bio nezamisliv čak i prije nekoliko godina a trend je prema distribuciji odgovornosti. Drugi trend je prema centralizaciji poslovne logike na velikim poslužiteljima. Iako se ovi trendovi čine kontradiktorni, zapravo su sinergistički: poslovni objekti se centraliziraju dok korisničko sučelje i dio srednjeg sloja postaju distribuirani. Krajni učinak je taj da objekti trebaju moći komunicirati meñusobno na daljinu. Objekti koji se izvode na poslužitelju i koji upravljaju Web korisničkim sučeljem trebaju moći meñudjelovati s poslovnim objektima koji su smješteni na centraliziranim poslužiteljima u korporacijskim sjedištima. Proces pomicanja objekta preko granice zove se rasporeñivanje po vrijednosti (engl. marshal by value). Granice postoje na različitim razinama apstrakcije u programu. Najočitija granica je izmeñu objekata koji se izvode na raličitim računalima . Proces pripremanja objekta za daljinski pristup se naziva rasporeñivanje (engl. marshaling). Na jednom računalu, objekti će se možda trebati rasporeñivati preko konteksta, aplikacijskih domena ili granica procesa.

Proces je u biti aplikacija koja se izvodi. Ako objekt programa za obradu teksta želi meñudjelovati s objektom u proračunskoj tablici, oni moraju komunicirati preko granica procesa. Procesi su podijeljeni u aplikacijske domene. Aplikacijske domene (engl. application domains) su dalje podijeljene u razne kontekste (engl. contexts ). Aplikacijske domene djeluju kao jednostavni procesi, a konteksti stvaraju granice unutar kojih se nalaze objekti sa sličnim pravilima. Objekti će povremeno biti rasporeñivani preko granica konteksta i aplikacijskih domena kao i preko granica procesa i računala. Kad se objekt rasporeñuje po vrijednosti to izgleda kao da ga se šalje kroz žicu od jednog računala prema drugom, kao što se kapetan Kirk teleportira na površinu planeta nekoliko stotina kilometara ispod orbitirajućeg USS Enterprisea.

448

U Z vjezdanim s ta z am a , Kirk je zaista premješten na planet, ali u .NET-u to je iluzija. Ako stojite na površini planeta, mogli biste misliti da vidite pravog Kirka i razgovarate s njim, ali uopće ne razgovarate s Kirkom već s posrednikom, možda hologramom, čiji je posao da prenese vašu poruku do Enterprisea, pravom Kirku. Izmeñu vas i Kirka postoji veći broj „odvoda". Odvod (engl. sink) je objekt čiji je posao da provodi pravila. Na primjer, ako vam Kirk pokuša reći nešto što bi moglo utjecati na razvoj vaše civilizacije, odvod primarne direktive bi mogao zabraniti prijenos. '

Kad pravi Kirk odgovori, on šalje odgovor kroz razne odvode dok poruka ne doñe 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. Nažalost, ispada da je gospodin Sulu cijelo vrijeme kontrolirao hologram. Više sreće u sljedećoj epizodi.

Stvarni prijenos vaše poruke obavlja kanal (engl. channel). Posao kanala je da zna kako prenijeti poruku od Enterprisea do planeta. Kanal radi s formaterom (engl. formatter). Formater osigurava da je poruka u odgovarajućem formatu. Možda govorite samo vulkanski, a jadni kapetan ne. Formater može 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 omogućava 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 računala na drugom kraju ureda ili svijeta. Pored toga, ovo poglavlje opisuje 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 vlastitog, procesa. Ako imate otvorene programe Word, Excel i Visual Studio, imate tri procesa koji se izvode. Ako otvorite Outlook, pokreće se novi proces. Svaki proces Wi je podijeljen na jednu ili više aplikacijskih domena. Aplikacijska domena djeluje kao proces, ali koristi manje resursa.

W

Aplikacijske domene mogu se neovisno pokretati i zaustavljati. One su sigurne, jednostavne i svestrane. Aplikacijska domena može pružiti otpornost na pogreške. Ako pokrenete objekt u drugoj aplikacijskoj domeni i onda se izvoñenje prekine, to će srušiti aplikacijsku domenu, ali ne i cijeli program. Možete zamisliti Web poslužitelj koji koristi aplikacijsku domenu za izvoñenje korisnikovog koda. Ako u kodu ima neki problem, Web poslužitelj može nastaviti raditi. Aplikacijsku domenu učahurava instanca klase AppDomain, koja pruža veći broj metoda i svojstava. U tablici 19-1 je skraćeni popis.

Poglavlje 19: Raspoređivanje i rad na daljinu

|

449

Tablica 19-1 Metode i svojstva AppDomain klase Metoda ili svojstvo

Detalji

CurrentDomain

Javno statičko svojstvo koje vrača aplikacijsku dom enu za trenutnu dretvu

CreateDomain()

Preopterečena javna statička m etoda koja stvara novu aplikacijsku dom enu

GetCurrentThreadID()

Javna statička m etoda koja vrača id en tifikato r tren u tn e dretve

Unload()

Javna statička m etoda koja briše zadanu aplikacijsku dom enu

FriendlyName

Javna svojstvo koje vrača lako prijateljsko im e za aplikacijsku dom enu

DefineDynamicAssembly()

Preopterečena javna statička m etod a koja definira dinam ički sklop u trenutnoj

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 učitava 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

aplikacijskoj dom eni

Aplikacijske domene takoñer podržavaju mnoštvo dogañaja (uključujući AssemblyLoad, AssemblyResolve, ProcessExit i ResourceResolve) koji se javljaju kad su sklopovi pronañeni, učitani, izvedeni i ispražnjeni. Svaki proces ima incijalnu aplikacijsku domenu a može imati i dodatne aplikacijske domene ako ih napravite. Svaka aplikacijska domena postoji u točno jednom procesu. Svi programi u ovoj knjizi su imali jednu aplikacijsku domenu - podrazumijevanu. Svaki proces ima svoju podrazumijevanu aplikacijsku domenu. U mnogim, možda u većini programa koje pišete, podrazumijevana aplikacijska domena će biti sve što vam treba. Meñutim, postoje slučajevi kad jedna domena nije dovoljna. Mogli biste napraviti drugu aplikacijsku domenu ako trebate pokrenuti biblioteku koju je napisao drugi programer. Možda ne vjerujete biblioteci i želite ju izolirati u njenoj domeni tako da, ako metoda u toj biblioteci prouzroči blokiranje programa, onda će samo izolirana domena biti zahvaćena. Da ste autor softvera Internet Information Server (IIS), Microsoftovog poslužitelja za Web stranice, mogli biste pokrenuti novu aplikacijsku domenu za svaki dodatak ili svaku virtualnu mapu koju udomljavate. To bi osiguralo otpornost na pogreške tako da, ako bi se jedna Web aplikacija blokirala, to ne bi utjecalo na cijeli Web poslužitelj. Takoñer je moguće da druga biblioteka zahtijeva drukčiji sigurnosni okoliš. Stvaranje druge aplikacijske domene omogućava da istovremeno postoje dva sigurnosna okoliša. Svaka aplikacijska domena ima svoj sigurnosni sustav, pa aplikacijska domena može služiti 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 može 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 može biti više dretvi koje se izvode (pogledajte poglavlje 22). Da biste vidjeli kako rade aplikacijske domene, postavimo sljedeći primjer. Pretpostavimo da želite da program instancira klasu Shape, ali u drugoj aplikacijskoj domeni.

a* Nema pravog razloga za smještanje klase Shape u drugu aplikacijsku domenu, osim da se prikaže kako ovaj koncept funkcionira. Moguće je, meñutim, da složenijim objektima zatreba druga aplikacijska domena kako bi se osigurao drugi sigurnosni okoliš. Nadalje, ako stvarate klase koje bi mogle sudjelovati u riskantnom ponašanju, možda biste se htjeli zaštititi tako da ih pokrenete u drugoj aplikacijskoj domeni.

Normalno, učitali 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 okolišu mogli biste pokrenuti metode Shape klase u odvojenoj dretvi, ali radi jednostavnosti ćete zasad zanemariti dretve (dretve su detaljno obrañene u poglavlju 20). Izbjegavanjem ovih pitanja, možete primjer sačuvati jednostavnim i koncentrirati se na detalje stvaranja i korištenja aplikacijskih domena i premještanja objekata preko granica aplikacijskih domena.

Stvaranje i upotreba aplikacijskih domena Napravite novu aplikacijsku domenu pozivanjem statičke metode CreateDomain() na klasi AppDomain: AppDomain ad2 = AppDomain.CreateDomain("Shape Domain");

Time se stvara nova aplikacijska domena s prijateljskim imenom Shape Domain. Prijateljsko ime (engl. friendly name) je pogodnost za programera. To je način za programski rad s domenom, bez znanja o internom prikazu domene. Možete provjeriti prijateljsko ime domene u kojoj radite tako da provjerite svojstvo System.AppDomain.CurrentDomain.FriendlyName.

Kad ste instancirali AppDomain objekt, možete napraviti instance klasa, sučelja itd. korištenjem 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 načina upotrebe:

Poglavlje 19: Raspoređivanje i rad na daljinu

|

4S1

ObjectHandle oh = ad2.CreateInstance( "ProgCSharp", "ProgCSharp.Shape", false, System.Reflection.BindingFlags.CreateInstance, nuli, new o bje d[] {3 , 5}, nuli, .nuli, nuli );

// // // // // // // // //

Ime sklopa Ime tipa s imenskim prostorom Ignorira mala i velika slova Zastavica Spona Argumenti Kultura Aktivacijski atributi 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 omogućava dinamičko povezivanje sklopa prilikom izvoñenja. Njen posao je da vam omogući prosljeñivanje informacija o objektu koji želite napraviti, da napravi taj objekt za vas i da poveže referencu na taj objekt. U velikoj većini slučajeva, uključujući ovaj primjer, koristit ćete podrazumijevanu sponu što se postiže prosljeñivanjem nuli. Možete napisati vlastitu sponu koja bi mogla, na primjer, usporediti vaš identifikator s bazom podataka i preusmjeriti povezivanje na drugi objekt, ovisno o vašem identitetu i dopuštenjima. Povezivanje (engl. binding ) se najčešće odnosi na dodavanje imena objektu. Dinamičko povezivanje se odnosi na sposobnost dodavanja imena kad se program izvodi, za razliku od slučaja kad se to radi tijekom prevoñenja. U ovom prim jeru, Shape objekt je povezan s varijablom instance u vrijeme izvoñenja preko metode Createlnstance() aplikacijske domene.

Zastavice za povezivanje pomažu sponi da fino prilagodi svoj način rada prilikom povezivanja. U ovom primjeru, upotrijebite vrijednost Createlnstance enumeracije BindingFlags. Podrazumijevana spona obično povezuje samo javne klase, ali možete dodati zastavice tako da spona povezuje i javne klase ako imate odgovarajuća dopuštenja. Kad povezujete sklop prilikom izvoñenja nemojte ga postaviti tako da se učita u vrijeme prevoñenja. Radije programski zadajte koji sklop želite i s njim povežite varijablu prilikom izvoñenja programa. Konstruktor kojeg pozivate uzima dvije cjelobrojne vrijednosti koje moraju biti stavljene u polje objekata (new ob jec tf] {3 ,5 })- Možete poslati nuli vrijednost za kulturu jer ćete koristiti podrazumijevanu (en) kulturu i nećete specificirati aktivacijske ili sigurnosne atribute. Dobivate natrag identifikator objekta (engl. ob jed handle) - tip koji se koristi za prosljeñivanje objekta (u omotanom stanju) izmeñu više aplikacijskih domena bez učitavanja metapodataka omotanog objekta u svakom objektu kroz kojeg identifikator ObjectHandle prolazi. Stvarni objekt možete dobiti pozivanjem Unwrap() metode na identifikatoru objekta i pretvaranjem rezultirajućeg objekta u stvarni tip, u ovom slučaju Shape.

452

|

Programiranje C#

Createlnstance() metoda pruža mogućnost da napravite objekt u novoj aplikacijskoj

domeni. Ako biste napravili objekt korištenjem new operatora, bio bi napravljen u trenutnoj aplikacijskoj domeni.

1 Rasporeñivanje preko granica aplikacijskih domena

Napravili ste Shape objekt u Shape domeni ali mu pristupate preko Shape objekta u originalnoj domeni. D a biste pristupili Shape objektu u drugoj domeni, morate rasporediti objekt preko granice domene.

Rasporeñivanje je proces pripremanja objekta za prelazak preko granice. Još jednom, poput kapetana Kirka koji se teleportira na površinu planeta. Rasporeñivanje se postiže na dva načina: po vrijednosti ili po referenci. Kad se objekt rasporeñuje po vrijednosti (engl. marshal by value), radi se kopija objekta. To je kao da vas nazovem preko telefona i zamolim da mi pošaljete kalkulator, a vi nazovete trgovinu i kažete im da mi dostave kalkulator koji je identičan vašem. Mogu koristiti kopiju jednako kao i original, ali unošenje brojeva na mojoj kopiji kalkulatora nema učinka na original.

T-

Rasporeñivanje po referenci je gotovo isto kao da mi pošaljete svoj kalkulator. Evo kako to funkcionira. Vi mi zapravo ne dajete original već ga, umjesto toga, držite u svojoj kući i šaljete mi posrednika koji je jako pametan: kad ja pritisnem gumb na posredniku, on šalje signal originalu u vašoj kući i broj se pojavljuje tamo. Pritiskanje gumba na posredniku za mene je jednako pritiskanju gumba na originalnom kalkulatoru.

'i

Razumijevanje rasporeñivanja s posrednicima

^ 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 meñuspremnik 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 meñuspremniku u memoriji da isprazni svoj sadržaj.

^

''

Trebate sami zatvoriti sve tokove iako će ih program za završavanje ( en g l. f i n a l i z e r ) zatvoriti ako ih pustite da izañu iz dosega. Urobustnom iT>.' programu uvijek biste trebali eksplicitno zatvoriti meñuspremnik.

Prim jer21-5 sadrži 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 meñuspremnika (nastavak) public static void Main() // Stvara i pokreće instancu Tester t = new Tester(); t.Run(); } // Pokreće 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 međuspremnicima na // početak 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 većim datotekama ovaj primjer bi se trebao izvoditi brže nego primjer 21-4.

Rad s tekstualnim datotekama Ako znate da datoteka koju učitavate (ili zapisujete) sadrži samo tekst možete koristiti StreamReader i StrearaWriter klase. One su napravljene da olakašaju rad s tekstom Na primjer, one podržavaju ReadLine() i WriteLine() metode koje čitaju i pišu red teksta odjednom. Već ste koristili WriteLine() s objektima Console. Da biste napravili StreamReader instancu prvo napravite Filelnfo objekt i onda pozovite 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 () vraća StreamReader za datoteku. Kad imate StreamReader možete čitati dato-

teku red po red: do { text = stream.ReadLine(); } while (text != nuli);

ReadLine() učitava po jedan red sve dok ne doñe do kraja datoteke. StreamReader će

vratiti nuli kad doñe do kraja datoteke. Da biste napravili StreamWriter klasu pozovite StreamWriter konstruktor prosljeñujući mu puno ime datoteke u koju želite upisivati: Streamklriter writer = new StreamWriter((s>"C:\ test\source\folder3.bak", false);

Drugi parametar je logički argument append. Ako datoteka već postoji, true će izazvati da se novi podaci dodaju na kraju datoteke, a false će pisati preko postojeće datoteku. U ovom slučaju proslijedite false tako da piše preko datoteke ako već postoji. Sad možete napraviti petlju za ispisivanje sadržaja 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 sadrži kompletan izvorni kod.

Primjer 21-6. Učitavanje 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 pokreće ju

508

|

P r o g r a m i r a n je C#

P r im je r

21-6. Učitavanje iz tekstualne datoteke i ispisivanje u tekstualnu datoteku (nastavak) Tester t = new Tester(); t .Run();

// Pokreće 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 sadržaj originalne datoteke se ispisuje na zaslon i upisuje u novu datoteku. Pogledajte sintaksu za ispisivanje na konzolu: Console.WriteLine(text)j

Ona je približno identična sintaksi koja je korištena za upisivanje u datoteku: writer.WriteLine(text);

Ključna razlika je u tome d aje WriteLine() metoda od Console statička, dok je WriteLine() metoda od StreamWriter, koja je nasljeñena 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 znači da su sve druge aktivnosti zaustavljene dok program učitava ili ispisuje podatke. Pohranjivanje podataka u meñuspremnik ili učitavanje iz njega može (relativno govoreći) dugo potrajati, posebno ako je pom oćno spremište spor disk ili (još gore) izvor na Internetu. Ako radite s velikim datotekama, ili kad učitavate ili ispisujete preko mreže, 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 omogućava da započnete učitavanje i okrenete se drugim stvarima dok C LR ispunjava vaš zahtjev. .N ET kostur pruža asinkroni U/l s pomoću BeginRead() i BeginWrite() metoda Stream klase. Redoslijed je da pozovete BeginRead() na datoteci i onda prijeñete na drugi posao, možda u drugoj dretvi. Kad se učitavanje dovrši, metoda s povratnim pozivom će vas 0 tome obavijestiti. Tada možete obraditi podatke koji su učitani, pokrenuti drugo učitavanje i nastaviti s drugim poslom. Pored tri parametra koje ste koristili kod binarnog učitavanja (meñuspremnik, pomak 1broj bajtova koji se učitavaju), BeginRead() traži još d e l e g a t a i o b j e k t s t a n ja . a «

o) { String s = Encoding.ASCII.CetString (buffer, 0, bytesRead); Console.WriteLine(s); inputStream.BeginRead( buffer, 0, buffer.Length, myCallBack, nuli); }

Sada možete raditi nešto drugo dok se podaci učitavaju, ali možete upravljati učitanim podacima (u ovom slučaju, ispisivanjem na konzolu) svaki put kad je meñuspremnik pun. Primjer 21-7 sadrži 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; // Međuspremnik za pohranu učitanih 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 Veličina međuspremnika const int BufferSize = 2 5 6 ; // Konstruktor AsynchIOTester() { // Otvara ulazni tok inputStream = File.OpenRead( @"C:\test\source\AskTim.txt'' ); // Dodjeljuje međuspremnik 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 // Pomak 0, buffer.Length, // (BufferSize) myCallBack, // Delegat povratnog poziva nuli ); // Lokalni objekt stanja // Radi neki posao dok se podaci učitavaju 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 ) ; // // // if

Ako imamo b ajto v e, stvara od n jih niz i prikazu je ga. Zatim započinje ponovno. U suprotnom smo gotov i, ( 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. Učitavanja se obavljaju u pozadini dok druga dretva broji i ispisuje broj nakon 1 0 0 0 odbrojanih. Kad se učitavanja završe ispisuju se na konzolu i program nastavlja s brojanjem (skratio sam ispis da razjasnim izlaz). U stvarnim aplikacijama mogli biste obraditi korisničke zahtjeve ili proračunavati vrijednosti dok je asinkroni U /I zauzet uzimanjem ili spremanjem u datoteku ili bazu podataka.

Ulaz i izlaz podataka preko mreže Zapisivanje u udaljeni objekt na Internetu ne razlikuje se puno od pisanja u datoteku na lokalnom računalu. Možda to budete htjeli učiniti ako vaš program treba spremiti podatke na računalu u mreži ili ako stvarate program koji prikazuje podatke na monitoru spojenom na drugo računalo u mreži. Mrežni U/I se temelji na upotrebi tokova

514

I

P r o g r a m i r a n je C#

podataka stvorenih korištenjem pristupnih točaka. Pristupne točke su vrlo korisne za aplikacije koje se temelje na pristupu klijent-poslužitelj, za aplikacije za izravnu razmjenu datoteka (engl. peer to peer) i prilikom pozivanja udaljenih procedura. Pristupna točka je objekt koji predstavlja krajnju točku za komunikaciju izmeñu procesa koji komuniciraju preko mreže. One mogu raditi s raznim protokolima, uključujući UDP i TCP. U ovom dijelu stvaramo TCP/IP vezu izmeñu poslužitelja i klijenta. TCP/ IP je konekcijski protokol sličan toku podataka koji se koristi za mrežnu komunikaciju. Konekcijski znači da korištenjem 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 mreže, možete simuliran mrežnu komunikaciju izvoñenjem dva procesa na istom računalu.

Moguće je da više aplikacija na jednom računalu komunicira s raznim klijentima istovremeno (na primjer, mogli biste pokrenuti Web poslužitelj, FTP poslužitelj i program koji pruža podršku za proračune). Prema tome, svaka aplikacija mora imati jedinstveni identififkator tako da klijent može reći koju aplikaciju traži. Taj identifikator je poznat kao ulaz (engl. pori). Zamislite IP adresu kao telefonski broj, a ulaz kao kućni broj. Poslužitelj instancira TcpListener i obavještava slušatelja da osluškuje veze na odreñenom ulazu. Konstruktor za TcpListener ima dva parametra, IP adresu i in t koja predstavlja ulaz na kojem bi slušatelj trebao osluškivati. Klijentske aplikacije se spajaju na zadanu IP adresu. Na primjer, adresa Yahoo stranica je 6 6 .9 4 .2 3 4 .1 3 . Takoñer, klijenti se moraju spojiti na odreñeni ulaz. Podrazumijeva se da se svi Web preglednici spajaju na ulaz 80. Brojevi ulaza su u rasponu od 0 do 65535 (na primjer, 216). Meñutim, neki brojevi su rezervirani.' Ulazi su podijeljeni u sljedeća područja:

I

• 0-1023: dobro poznati ulazi • 1024-49151: registrirani ulazi • 49152-655 35: dinamički i/ili privatni ulazi Za popis svih dobro poznatih i registriranih ulaza, pogledajte http://

www.iana.org/assignments/port-numbers.

Kad je slušatelj napravljen pozovite Start() metodu na njemu, javljajući slušatelju da * počne prihvaćati mrežne veze. Kad je poslužitelj spreman da počne odgovarati na pozive | : od klijenta, pozovite AcceptSocket (). Dretva u kojoj ste pozvali Accept Socket () uzalud radi % (čekajući tužno pokraj telefona, stiskajući svoje virtualne ruke, nadajući se pozivu). i:

Ako program koristite u mreži koja ima vatrozid, pitajte administratora mreže 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

Možete zamisliti da napravite najjednostavnijeg slušatelja na svijetu, koji strpljivo čeka klijentov poziv. Kad primi poziv, meñudjeluje s tim klijentom isključujući sve druge klijente. Sljedećih nekoliko klijenata koji pozovu će se spojiti, ali će automatski biti stavljeni na čekanje. Dok ovi slušaju ugodnu glazbu i dok im se govori da je njihov poziv važan i da će biti obrañen kad doñu na red, oni će uzalud gubiti vrijeme u svojim dretvama. Kad se red za čekanje popuni, sljedeći pozivatelji će dobiti isti signal ili signal da je zauzeto. Moraju onda prekinuti i čekati da naša jednostavna pristupna točka završi s trenutnim klijentom. Ovaj model dobro funkcionira za poslužitelje koji primaju jedan ili dva zahtjeva tjedno, ali se ne prilagoñava dobro stvarnim situacijama. Većina poslužitelja treba obrañivati tisuće, čak i desetke tisuća zahtjeva u minuti! Da bi obrañivale velik broj veza, aplikacije koriste asinkroni ulaz i izlaz da prihvate poziv i stvore pristupnu točku s vezom prema klijentu. Originalni slušatelj se onda vraća osluškivanju, čekajući sljedećeg klijenta. Aplikacija tako može obrañivati mnogo poziva. Svaki put kad je poziv prihvaćen, nova pristupna točka je stvorena. Klijent nije svjestan ovog trika kojim je nova pristupna točka stvorena. Što se tiče klijenta, spojio se na IP adresu i port koji je tražio. Primjetite da nova pristupna točka uspostavlja vezu s klijentom. To je potpuno drukčije nego kod UDP protokola koji koristi protokol bez veze. Kod TCP/IP protokola, kad je veza uspostavljena, klijent i poslužitelj znaju kako komunicirati meñusobno, a da ne moraju ponovno adresirati svaki paket.

Stvaranje poslužitelja za mrežni tok podataka Da bi napravili mrežni poslužitelja za TCP/IP tok podataka prvo napravite TcpListener objekt da osluškuje na TCP/IP ulazu koji ste odabrali. Ja sam proizvoljno odabrao ulaz 6 5 0 0 0 meñu raspoloživim 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, možete mu narediti da počne osluškivati; t c p L is t e n e r .S t a r t ( ) ;

Sad čekajte da klijent zatraži vezu: Socket so cketForC lien t = tc p L isten er.A ccep tS o ck et();

AcceptSocket metoda od TcpListener objekta vraća Socket objekt koji predstavlja suče-

lje Berkeley pristupne točke (engl. Berkeley socket interface) i koji je povezan na odreñenu krajnju točku. AcceptSocket() je sinkrona metoda koja neće vratiti dok ne primi zahtjev za vezu. 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() {

AšynchNetworkServer app = new AsynchNetworkServer(); app.Run(); }

p riv ate void Run() {

// Stvara novi TcpListener i pokreće ga U slu š a ju ć i na ulazu 65000 IPAññress 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, prihvaća vezu II i vraća novu pristupnu točku 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 sluša Socket socketForClient = tcpListener.AcceptSocket(); Console.WriteLine( "Client connected" ); ClientHandler handler = new ClientHandler( socketForClient ); handler.StartRead(); } } } }

Poslužitelj se pokreće i počinje osluškivati na ulazu 6 5 0 0 0 . Ako se klijent spoji, poslužitelj će instancirati ClientHandler koji će izvoditi ulaz/izlaz s klijentom dok poslužitelj čeka sljedećeg klijenta. U ovom primjeru ispisujete niz znakova primljen od klijenta na konzolu u OnReadComplete() i OnWriteComplete() metodama. Ispisivanje na i!«' konzolu može zaustaviti dretvu dok se ne završi. U finalnom programu ne želite blokiranje u ovim metodama je r koristite dretvu iz ponude dretvi. Ako doñe do zaustavljanja u OnReadComplete() ili u OnWriteComp lete()on da to može izazvati dodavanje dretvi u ponudu dretvi što je neučinkovito i narušit će performanse i skalabilnost.

Kod klijenta je vrlo jednostavan. On stvara tcpSocket za ulaz na kojem će poslužitelj osluškivati ( 6 5 0 0 0 ) i NetworkStream objekt za tu pristupnu točku. Onda ispisuje poruku u tok podataka i prazni meñuspremnik. Klijent stvara StreamReader za učitavanje 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 using using using

System; System.Collections.Generic; System.Net.Sockets; 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 mreže (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 mrežni poslužitelj se ne blokira dok obrañuje veze s klijentima ali p epusta upravljanje nm vezama instancama ClientHandler. Klijenti ne bi smjeli osjetiti kašnjenje dok poslužitelj obrañuje 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 mreže Sad možete kombinirati znanja koje ste stekli o asinkronom čitanju datoteka i asinkronim mrežnim tokovima da biste napisali program koji isporučuje datoteku klijentu na zahtjev. Poslužitelj će početi s asinkronim učitavanjem na pristupnoj točki čekajući da uzme ime datoteke od klijenta. Kad imate ime datoteke, možete početi s asinkronim učitavanjem datoteke na poslužitelju. Kako svaki puni meñuspremnik datoteke postaje dostupan, možete početi s asinkronim ispisivanjem natrag klijentu. Kad asinkrono ispisivanje klijentu završi, možeti započeti još jedno učitavanje datoteke. Na ovaj način „dodajete" se naprijed-natrag puneći meñuspremnik iz datoteke i ispisujući sadržaj meñuspremnika klijentu. Klijent ne treba ništa raditi osim učitavati tok podataka koji mu šalje poslužitelj. U sljedećem primjeru klijent će ispisivati sadržaje datoteke na konzolu, ali biste lako mogli i započeti asinkrono zapisivanje u novu datoteku na klijentu i tako napraviti program za kopiranje datoteka preko mreže. Struktura poslužitelja ne razlikuje se od strukture prikazane u primjeru 21-10. Još jednom ćete napraviti ClientHandler klasu, ali ovaj put dodajte AsyncCallBack s imenom myFileCallBack kojeg ćete inicijalizirati u konstruktoru zajedno s povratnim pozivima za učitavanje i ispisivanje preko mreže: 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 beskonačnu petlju u kojoj ćete pozvati AcceptSocket(). Ako imate pristupnu točku instancirajte ClientHandler i pozovite StartRead(). Kao u prethodnom primjeru, StartRead() pokreće BeginRead() prosljeñujući meñuspremnik i delegata u OnReadComplete. Kad učitavanje iz mrežnog toka završi poziva se metoda OnReadComplete () i uzima ime datoteke iz meñuspremnika. Ako je vraćen tekst, OnReadComplete() uzima niz znakova iz meñuspremnika upotrebom statičke 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 možete otvoriti tok podataka u datoteku i koristiti isto asinkrono učitavanje koje je korišteno 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, // 0, // buffer.Length, // myFileCallBack, // nuli); //

Čuva rezultate Pomak Veličina meduspremnika Delegat povratnog poziva lokalni objekt stanja

Ovo učitavanje datoteke ima vlastiti povratni poziv koji će biti upućen kad ulazni tok učita puni meñuspremnik iz datoteke s poslužiteljevog diska. Kao sto je maloprije spomenuto, ne biste trebali poduzimati ništa u ulazno/,2|azn°J metodi što bi moglo jako dugo blokirati dretvu. Pozivza otvav ranj e datoteke i početak učitavanja se obično stavlja u pomoćnu dretvu umjesto da se izvodi u metodi OnReadComplete(). Ovo je pojednostavnjeno za ovaj primjer da bi se izbjeglo skretanje pažnje s važnih pitanja.

Kad je meñuspremnik pun poziva se metoda OnFileCompletedRead() koja provjerava da lije ista ucitano iz datoteke. Ako jest, počinje asinkrono upisivanje preko mreže: if (bytesRead > o) // Ispisuje na klijenta networkStream.BeginWrite( buffer, o, bytesRead, callbackWrite, nuli);

Ako je OnFileCompletedRead pozvana i ništa nije ucitano onda to znači da je cijela datoteka poslana. Poslužitelj reagira zatvaranjem NetworkStream objekta i pristupne točke dajući na taj način do znanja klijentu da je transakcija gotova: networkStream.Close(); Socket.Close(); networkStream = nuli;

Socket = n u li;

Kad se upisivanje preko mreže završi poziva se OnWriteComplete() metoda što pokreće drugo učitavanje 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 počinje s drugim učitavanjem datoteke i nastavlja se sve dok datoteka ne bude potpuno učitana i poslana klijentu. Klijentov kod jednostavno ispisuje ime datoteke u mrežni tok da pokrene učitavanje 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 započinje petlju čitajući iz mrežnog toka sve dok poslužitelj više ne pošalje ni jedan bajt. Kad je poslužitelj gotov, mrežni tok podataka se zatvara. Počnite inicijaliziranjem Boolean vrijednosti na fa lse i stvaranjem meñuspremnika za prihvaćanje bajtova koje pošalje poslužitelj: 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: meñuspremnik, pomak od kojeg počinje čitanje i veličinu meñuspremnika: 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 možete postaviti Boolean vrijednost fOuit na true i tako prekinuti izvoñenje petlje: i f (bytesRead == 0) fOu it = tru e ;

Ako ste primili bajtove, možete ih ispisati na konzolu ili u datoteku ili možete učiniti što god želite s vrijednostima koje je poslužitelj 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 izañete iz petlje zatvorite NetworkStream: stream To Serv er.C lose();

Kompletan kod poslužitelja 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 mrežnog poslužitelja datoteka # region Using d irectiv e s using using using using using using

System; System. C o llectio n s.C e n e ric; System .I0; System.Net; System .N et.Sockets; System .Text;

# endregion namespace AsynchNetworkFileServer {

pu blic c la s s AsynchNetworkFileServer {

c la s s ClientHandler {

p riv ate p riv ate p riv ate p riv ate p riv ate p riv a te p riv ate p riv ate

const in t Bu fferSize = 2 5 6 ; b yte[] b u ffer; Socket Socket; NetworkStream networkStream; Stream inputStream; AsyncCallback callbackRead; AsyncCallback callback W rite; 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 meñuspremnika // za čuvanje datoteka bu-ffer = new b y te[2 5 6 ]; // Stvaran je mrežnog 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 mrežnog 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

mrežnog poslužitelja datoteka (nastavak)

asinkronog

I I u mrežni tok callbackNrite = new AsyncCallback( this.OnWriteComplete );

// Započinje čitanje niza od klijenta public void StartRead() { // Čita s mreže // i uzima ime datoteke networkStream.BeginRead( buffer, o, buffer.Length, callbackRead, nuli ); } // Kad je pozvan natrag od strane čitača prikazuje niz // i vrača 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 ); // Osvježava konzolu Console.Write( "Opening file {o}", fileName ); // Otvara ulazni tok datoteke inputStream = File.OpenRead( fileName ); // Započinje čitanje datoteke inputStream.BeginRead( buffer,

// Čuva rezultat

o, buffer.Length, myFileCallBack, nuli );

// Pomak // Veličina međuspremnika // Delegat povratnog poziva // 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 mrežnog poslužitelja datoteka (nastavak) } } // Kad imate pun međuspremnik datoteke void OnFileCompletedRead( IAsyncResult asyncResult ) { int bytesRead = inputStream.EndRead( asyncResult ); // Ako je učitan 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” ); // Započinje učitavanji : 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 započinje U slušanje 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 mrežnog poslužitelja datoteka (nastavak) IPAddress localAddr = IPAddress.Parse( "127.0.0.1" ); TcpListener tcpListener = new TcpListener( localAddr, 65000 ); tcpListener.Start(); // Nastavlja slušanje dok šaljete datoteku for ( ; ; ) {

// Ako se klijent poveže, prihvaća vezu // i vraća novu pristupnu točku socketForClient II dok tcpListener nastavlja slušati Socket socketForClient = tcpListener.AcceptSocket(); if ( socketForClient.Connected ) Console.WriteLine( "Client connected" ); ClientHandler handler = new ClientHandler( socketForClient ); handler.StartRead(); } } } } }

Primjer 21-13. Implementiranje klijenta asinkronog mrežnog poslužitelja datoteka using using using using

System; System.Net.Socket's; System.Threading; 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 mrežnog poslužitelja 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 poslužitelju System.IO.StreamWriter writer = new System.I O .StreamWriter(streamToServer); writer.Write(message); writer.Flush(); bool fOuit = false; / / Dok podaci stižu sa // poslužitelja, nastavalja čitati while (!fOuit) { // Međuspremnik za čuvanje odgovora char[] buffer = new char[BufferSize]; // Odgovor čitanja System.IO.StreamReader reader = new System.IO.StreamReader(streamToServer); // Provjerava koliko j e bajtova // smješteno u međuspremnik 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 mrežnim čitanjem, napravili ste prilagodljivu aplikaciju koja može obrañivati zahtjeve od većeg broja klijenata.

Web tokovi Umjesto čitanja iz toka podataka koji pruža poslužitelj, možete 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 pomoću URI adrese, što je slično U RL adresi Web stranice. Možete koristiti WebRequest objekt da napravite WebResponse objekt koji će učahuriti objekt na koji pokazuje URI. To jest, možete pozvati GetResponse() na WebRequest objektu da bi dobili pristup objektu na koji pokazuje URI. Ono što se vraća je učahureno u WebResponse objekt. Onda od WebResponse objekta možete tražiti Stream objekt pozivanjem metode GetResponseStream(). GetResponseStream() vraća tok podataka koji učahuruje sadržaj Web objekta (na primjer, toka podataka s Web stranicom). Sljedeći primjer vraća sadržaj Web stranice kao tok podataka. Da bi pribavili Web stranicu, trebat ćete koristiti HttpWebRequest. HttpWebRequest izvodi iz WebRequest i pruža dodatnu podršku za rad s H T T P protokolom. Da biste napravili HttpWebRequest, pretvorite tip WebRequest objekta koji ste dobili od statičke C reate() metode iz WebRequestFactory: HttpWebRequest WebRequest = (HttpWebRequest) WebRequest.Create ("http://www.libertyassociates.com/book_edit.htm'1);

C reate() je statička metoda objekta WebRequest. Kad joj proslijedite U RI, stvara se instanca HttpWebRequest. Metoda je preopterećena na tip parametra. Vraća razne izvedene tipove ovisno o tome što je proslijeñeno u nju. Na primjer, ako proslijedite URI stvara se objekt tipa HttpWebRequest. Povratni tip je WebRequest pa zato morate pretvoriti vraćenu vrijednost u HttpWebRequest.

Stvaranje HttpWebRequest objekta uspostavlja vezu prema Web stranici. Ono što dobijete natrag od poslužitelja je učahureno u HttpWebResponse objekt koji je H TTP potklasa općenitije WebResponse klase: HttpWebResponse WebResponse = (HttpNebResponse) WebRequest.GetResponse();

Sad možete otvoriti StreamReader na toj stranici pozivanjem GetResponseStream() metode WebResponse objekta: StreamReader StreamReader = new StreamReader( WebResponse,GetResponseStream(), Ertcoding.ASCII);

Možete čitati iz tog toka podataka baš kao što ste čitali iz mrežnog toka podataka. Primjer 21 -14 sadrži kompletan ispis. P rim je r 21-14. Č itanje Web stranice kao to ka H T M L podataka ttregion Using directives using using using using

534

|

System; System.Collections.Generic; System.I0; System.Net;

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 učahurava // 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): Liberty Associates 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 Posrednička .N ET biblioteka klasa

AxCalcControl.pdb Datoteka za otkrivanje pogrešaka Kad je to učinjeno možete se vratiti u Choose Toolbox Items prozor, ali ovaj put odaberite .N ET Framework Components. Sad možete potražiti 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 | • AssemblvName

" 1 Namespace^-:

ACC8,f^a^l>*'Airn

,

lD lre c to ^ |

.1:

Operator koji se koristi za pristupanje članovima tipa.

Upotreba pokazivača gotovo nikad se ne zahtijeva i ne preporučuje. Ako ipak koristite pokazivače, kod morate označiti s oznakom unsafe. Kod je tako označen jer s pomoću pokazivača možete izravno manipulirati memorijskim lokacijama. To je mogućnost koje inače nije pružena unutar C # programa. U nepouzdanom kodu možete izravno pristupiti memoriji, pretvarati izmeñu pokazivača i integralnih tipova, uzimati adrese varijabli i tako dalje. Zauzvrat, odričete se skupljanja otpada i zaštite protiv neinicijaliziranih varijabli, visećih pokazivača i pristupa memoriji izvan granica polja. Drugim

570

|

P ro g ra m ira n je C#

riječima, nepouzdani kod stvara otok C++ koda unutar inače sigurne C # aplikacije,

pa kod neće raditi u situacijama djelomičnog povjerenja. Da biste vidjeli primjer kada bi ovo moglo biti korisno, čitajte datoteku na konzolu upućivanjem dva W in32 API poziva: CreateFile i ReadFile. ReadFile uzima kao svoj drugi parametar pokazivač na meñuspremnik. Deklaracije dviju uvezenih metoda nije drukčija 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 prosljeñuje 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, UseDefault, UseDefault);

I I creationDisposition // flagsAndAttributes // templateFile

} APIFileReader klasa implementira samo jednu metodu, Read(), koja poziva ReadFile(). Ona prosljeñuje identifikator datoteke napravljen u konstruktoru klase, zajedno s pokazivačem na meñuspremnik, brojem bajtova za uzimanje i referencom na varijablu koja će sadržavati broj učitanih bajtova. Nama je ovdje zanimljiv pokazivač na meñuspremnik. Da biste uputili API pozive morate koristiti pokazivače. Pošto ćete mu pristupiti preko pokazivača, meñuspremnik treba biti pričvršćen (pinned) u memoriji. Ne smije se dozvoliti .N ET kosturu da pomiče meñuspremnik tijekom sakupljanja otpada. Da biste to postigli upotrijebite fixed ključnu riječ C # jezika.

P oglavlje 22 : .NET i COM p ro g ra m iran je

|

571

Ona omogućava da uzmete pokazivač na dio memorije koji koristi meñuspremnik i označite tu instancu tako da ju sakupljač otpada ne pomiče. Blok iskaza nakon ključne riječi fixed stvara doseg unutar kojeg će memorija biti pričvršćena. Na kraju tog bloka, oznaka instance će biti uklonjena tako da se može pomicati. Ovo je poznato pod imenom deklarativno pričvršćivanje (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 označena s ključnom riječi unsafe. Ona stvara nepouzdani kontekst i omogućava da napravite pokazivače. Da biste ovakav kod preveli, morate koristiti / unsafe opciju prevoditelja. Najlakši način da to učinite 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:

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 Build

Eftors and V V arrtngs................................... W anm g Level:

Debug Resouces

[Č£BUG;TRACE

|3

3 §)

Sunvess warrings: | ,-Treat W artngs

S etthgs

as

1

& rors •••

...................................- - - - - -

© N one O s ie d f lc Warrincis | ........................................................................................................... i O

ab

-O u tput O u tout Path: i

jbm\Debug\

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#

. J _!

Probni program instancira APIFileReader i ASCIIEncoding objekte. Prosljeñuje ime datoteke konstruktoru APIFileReader i zatim stvara petlju da ponavljanjem napuni meñuspremnik pozivanjem Read() metode, koja upućuje AP1 poziv ReadFile. Vraćeno je polje bajtova i zatim pretvoreno u niz korištenjem GetString() metode ASCIIEncoding objekta. Taj niz je prosljeñen Console.Write() metodi za ispisivanje na konzoli. Kompletan izvorni kod je prikazan u primjeru 22 -12 . P r im je r 2 2 -1 2 . K o rišten 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 eću 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. Korištenje pokazivača u UseDefault, OpenExisting, UseDefault, UseDefault );

C#

programu (nastavak)

Atributi // creationDisposition // flagsAndAttnbutes // templateFile

U

} public unsafe int Read( byte[] butfer, int index:

{

int count )

int bytesRead = 0; fixed ( byte* bytePointer = buffer )

{

ReadFile( fileHandle, bytePointer + index, count, &bytesRead, o

)i

// hfile // lpBuffer // nBytesToRead // nBytesRead // overlapped

}

return bytesRead;

} } class Test public static void Main() // Stvara instancu APIFileReader, // i prosljeđuje ime postojeće 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 Učitava datoteku u meduspremnik i prikazuje konzolu while ( fileReader.Read( buffer, 0, BuffSize ) .= 0 )

^

}

}

Console.Write( "{0}", asciiEncoder.GetString( buffer ) );

}

Ključni dio koda u kojem stvarate pokazivač na meñuspremnt nik u memoriji korištenjem ključne riječi fixed je podebljan pokazivače jer to zahtijeva AP1 poziv.

574

j

P ro g ra m ira n je C#

k i fiksirate meñusprem. Ovdje trebate koristiti

DODATAK

C# ključne riječi

abstract Moñifikator klase koji označava da se iz klase mora izvoditi da bi bila instancirana. as Pretvara lijevi operand u tip odreñen desnim operanñom i vraća nuli umjesto da izbaci iznimku ako ne uspije. base Varijabla koja ima isto značenje kao i th is osim što pristupa implementaciji osnovne klase člana. bool Logički tip podataka koji može 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 odreñeni 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 nameće provjeravanje aritmetičkih granica izraza ili bloka iskaza. cla ss Proširiv 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 prevoñenja i može biti samo unaprijed definiranog tipa. continue Iskaz koji preskače preostale iskaze u bloku i nastavlja prema sljedećem ponavljanju petlje, decimal 16-bitni precizni decimalni tip podataka. default Oznaka u switch iskazu koja odreñuje koju akciju izvesti ako nijedan case iskaz ne odgovara switch iskazu. delegate Tip za definiranje potpisa metode tako da instance delegata mogu držati 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 evaluira fa lse.

enum Vrijednosni tip koji definira skupinu imenovanih brojčanih konstanti. event . . . Modifikator člana za polje delegata ili svojstvo koje zadaje da se može pristupiti samo metodama += i -= delegata. e x p licit Operator koji definira eksplicitno pretvaranje. extern . Modifikator metode koji označava da je metoda implementirana sa neupravljamm kodom. fa lse Logički 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 pričvršćivanje referentnog tipa tako da ga sakupljač otpada ne pomiče tijekom aritmetičkih operacija s pokazivačem. float

Tip podataka s pomičnim 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 vraća vrijednost svojstva. goto

Iskaz za preskakanje koji odlazi do oznake unutar iste metode i istog dosega u kojem se nalazi točka preskakanja. if

Uvjetni iskaz koji izvodi svoj blok iskaza ako njegov izraz evaluira true. im plicit Operator koji definira implicitno pretvaranje, in Operator izmeñu tipa i IEnumerable u foreach iskazu, int

Cjelobrojni tip podataka sa predznakom duljine četiri bajta. interface

Ugovor koji odreñuje članove koje klasa ili struktura mogu implementirati da primi generičke usluge za taj tip. Internal

Modifikator pristupa koji pokazuje da je tip ili član tipa pristupačan 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 omogučio suradnju više dretvi. l°ng Integralni tip podataka sa predznakom duljine osam bajtova.

Dodatak: C#ključne riječi

)

577

namespace Preslikava skup tipova u zajedničko ime. new

Operator kojt poziva konstruktor na tipu, alocirajući novi objekt na gomilu ako je taj tip referentni ili inicijalizirajući objekt ako je to vrijednosni tip vrijednosti. •■Ključna riječ je preopterećena kako bi sakrila naslijeñenog člana. nuli

Literal referentnog tipa koji pokazuje da nijedan objekt nije referenciran. object Tip iz kojeg izvode svi drugi tipovi, operator Modifikator metode koji preopterećava operatore. ° Ut Modifikator parametra koji pokazuje da se parametar prosljeñuje po referenci i mora ga dodijeliti metoda koju se poziva. override Modifikator metode koji pokazuje da metoda klase premošćuje virtualnu metodu klase ili sučelja. Modifikator parametra koji zadaje da zadnji parametar metode može prihvatiti više parametara istog tipa. private . . Modifikator pristupa koji pokazuje da samo sadržavajući tip može pristupiti članu. publi-Lc«.

,

Modifikator pristupa koji zadaje da je tip ili član tipa dostupan svim drugim tipovima readonly . . Modifikator polja koji zadaje da polje može biti dodijeljeno samo jednom u svojoj deklaraciji ili konstruktoru svog sadržavajućeg tipa. ref

Modifikator parametra koji zadaje da se parametar prosljeñuje po referenci i dodjeljuje prije prosljeñivanja metodi. return

Iskaz za preskakanje koji izlazi iz metode i zadaje povratnu vrijednost ako metoda vraća 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 može izvoditi, set Ime pristupnika koji postavlja vrijednost svojstva, short Integralni tip podataka s predznakom duljine dva bajta. sizeof Operator koji vraća veličinu strukture (u bajtovima). stackalloc Operator koji vraća 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 znakova. struct

Vrijednosni tip koji kombinira podatke i funkcionalnost u jednoj jedinici. switch Iskaz odabira koja omogućava odabir izmeñu više mogućnosti ovisno o vrijednosti unaprijed definiranog tipa. this

Varijabla koja referencira trenutnu instancu klase ili strukturu. throw Iskaz za preskakanje koja izbacuje iznimku kad nastupi nenormalno stanje. true

Logički literal. try Iskaz koji pruža mogućnost obrade iznimke ili prerani izlazak u bloku iskaza. typeof Operator koji vraća tip objekta kao System.Type objekt. uint

Integralni tip podataka s predznakom dužine četiri bajta. unchecked Iskaz ili operator koji sprječava provjeru aritmetičkih granica.

Dodatak: C#ključne riječi

|

579

unsafe

Modifikator metode ili iskaza koji dozvoljava izvoñenje pokazivačke aritmetike unutar odreñenog bloka. ushort

Integralni tip podataka bez predznaka dužine dva bajta. using Zadaje da se tipovi iz odreñenog 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 može biti premošćena izvedenom klasom. void

Ključna riječ koja se koristi umjesto tipa za metode koje nemaju povratnu vrijednost. volatile

Pokazuje da polje može biti promijenjeno od strane operativnog sustava ili druge dretve. while

Iskaz petlje za iteriranje kroz blok iskaza sve dok izraz na početku svake iteracije ne evaluira false.

580

|

P ro g ra m ira n je C#

Kazalo

Sim boli { } (vitičaste zagrade) bijeli prostor i, 50 definiciranje svojstava i ponašanja klasa u, 10 smještanje blokova iskaza u, 40 () (zagrade) grupiranje u regularnim izrazima, 241 metaznakovi regularnih izraza, 235 ugnježñivanje za pravilan redoslijed operacija, 59 « » (pomicanje), operator, 58 I (okomita crta) metaznak regularnog izraza, 235 II (uvjetni ILI), operator, 59 ~ (valovita crta), u deklaracijama konstruktora, 81 < (manje od), operator, 56 - (oduzimanje), operator, 52 . (točka), operator, 13 pozivanje metode, 12 pristup članovima i bijeli prostor, 13 > (veče od), operator, 56 - - (smanjenje), operator, 55 1(NE), operator, 57 != (nejednakost), operator, 56 # (ljestve), u direktivama predprocesora, 60 ' # define, iskaz, 60 # elif, iskaz, 62 # else, iskaz, 61-62 # endif, iskaz, 61-62 # endregion, iskaz, 62 # if, iskaz, 61 # region iskaz, 62 # undef, iskaz, 61 % (modulo), operator, 52 %= (modulo i pridruživanje), operator, 54 & (logičko I), operator kao adresa C++ operatora, 570

& & (I), operator, 57, 59 , (zarez), u deklaracijama polja ' (množenje), operator, 52 '= (množenje i pridruživanje), operator, 54 + (zbrajanje), operator, 52 preopterećivanje, 120 ++ (povećanje), operator, 54 += (zbrajanje i pridruživanje), operator, 55 /(dijeljenje), operator, 52 /= (dijeljenje i pridruživanje), operator, 55 :: (odvajanje dosega), operator, C++, 12 ; (točka zarez) definicije C# klasa i, 65 napomena za C++ programere, 10 završavanje C# iskaza s, 37 -= (oduzimanje i pridruživanje), operator, 54 = (pridruživanje), operator, 52 >= (veće ili jednako), operator, 56 (pristup članovima), operator, 570 [] (uglate zagrade) deklaracije polja, 168 pristup članovima polja, 179 [] (indeks), operator, 169 pronalaženje odreñenog znaka u nizu, 228

A Abort(), metoda (Tliread), 476 AcceptSocket(), metoda, 516, 521 (TcpListener), 516

Kazalo

|

581

AppendFormatO, metoda (StringBuilder), 234 Access, baza podataka, primjer, 346 Activator, klasa, 445 ActiveX, kontrole stvaranje, 549 uvoženje, 548 u .NET, 552 Add(), metoda, 386, 418 (Dictionary), 217 (List), 201 AddRangeO, metoda (List), 201 ADO.NET objektni model, 341-343 DataAdapter, 342 DataReader, 343 DataRelations, 342 DataTables i DataColumns, 342 DBCommand i DBConnection, 342 osnovne klase, 341 Rows kolekcija, Data! able, 342 OLE DB Managed Provider, 346-348 početak rada s, 343-346 povezivanje podataka, kontrole za, 349-353 podešavanje DataSet, 353-355 ^ programsko popunjavanje mreže podataka, 352-353 pregled, 337 adrcsa-od (Sr), operator, 570 Amazon Web usluge klijentska aplikacija (primjer), 383 skup alata za programere, 391 Amazon.com, 389 analiza Web stranica, 536 analiza Web stranica, aplikacija za, 390 analizator stranice (regularni izrazi), 233 anonimne metode, 6, 293 aplikacije klijent Web usluga izrada, 389 pretraživanje prema kategoriji, 408 prikaz rezultata, 399 konzolne, 12 sastavljanje u cjelinu, 389 uvoženje ActiveX kontrola i LOM, komponenata u, 548 Windows (pogledajte Windows Forms) aplikacijske domene, 448 dogañaji, 450 dretve u usporedbi s, 451 konteksti, 459 metode i svojstva, 450 primjer, 455-458 rasporeñivanje objekata preko granica, 453 stvaranje i upotreba, 451 AppDomain klasa, 450 CreateDomainO, metoda, 451 append, argument, 508 Append(), metoda (StringBuilder), 233

(File), 496 (Filelnfo), 497 Application, klasa, DoEvcntsO metoda iz, 324 apstraktne klase, 109, 575 ograničenja, 112 primjer, 109-110 sučelja u usporedbi s, 136, 152 apstraktne metode, 109 primjer, 109-110 ArgumentException, 261 argumenti, 68 dogañaja, 359 ArithmeticException, 253 aritmetički operatori, 52 as operator, 150-151, 575 is operator u usporedbi s, 151 asinkroni U/I, 491, 510, 516 ASP kontrole, 367 ASP.NET, 356-388 C# programiranje i, 356 DataGriñ u, implementiranje (primjer), 404 -406 datoteke s kodom, 362-364 kontrole, dodavanje na Web Forms obrasce, 365-367 metode za obradu dogañaja, 359 poslužiteljske kontrole, 365 poslužiteljske kontrole, 367 povezivanje podataka, kontrole za, 367-377 stanje poslužiteljskih kontrola, 359 stranice s pozadinskim kodom u inačici 2 .0 ,3 5 8 pregled, 357-361 stvaranje, 361-364 Web kontrole, 358 Web stranice, stvaranje (primjer), 399 životni ciklus, 360 ASP.NET 1.1, napomena za programere aspx.es datoteka, 377 pozadinski kod, model, 362 aspñabel, kontrola, 402 ,aspx, nastavak imena datoteke, 361-362 pohranjivanje stranica korisničkog sučelja u, 358 Assembly, cilj atributa, 432 Assenrbly.Load(), statička metoda, 440 AssemblyInfo.cs, datoteka, 418 AssemblyLoad, dogañaj, 450 AssemblyResolve, dogañaj, 450 AssemblyResolver, učitavanje sklopova s pomoću, 422 Atributes, svojstvo (Directorylnfo), 492 (Filelnfo), 496 atributi, 7, 431-447 ciljevi, 432 definirani, 431

prilagoñeni, 433 deklariranje, 434 imenovanje, 434 konstruiranje, 434 primjer, 43.5 upotreba, 435 primjena, 433 Autoeomplete, svojstvo tehnologije Intellisense, 15 AutoPostBack, svojstvo, 359 autorska Weh stranica, 389 AWSProñuctData, objekt, 397 Axlmp, naredbeni uslužni program, 548, 553 ažuriranje baze podataka (klijent Web usluge), 399

destruktori, 81 enum tipovi, 35 gcnerici (C# ) u usporedbi s C++ predlošcima, 193 imenski prostori, 12 indekseri, 185 iskaz case, 44 iznimke, izbacivanje, 246 ključna riječ implicit, 123 konstruktor za kopiranje, 75 logički vrijednosni tip, 25 metoda Main(), 10 nasljeñivanje, 113 nepridružene varijable, 30 nizovi, 220, 221 pretprocesori, 61

B

preopterećivanja operatora, logički parovi, 122 privatno ili zaštićeno nasljeñivanje, 102 referentni parametri, 90 referentni tipovi, 24 strukture, 129 točka zarez, 10, 65 uvjetni izrazi, 38 virtualne metode, premošćivanje, 106

base, ključna riječ, 575 baze podataka definirane, 337 relacijske, 337-341 13eginRead(), metoda, 5 1 1 (Stream), 503, 510 BeginWrite(), metoda (Stream), 503, 510 Berkeley sučelje pristupne točke, 516 beskonačne petlje, 521 bezuvjetno grananje, 37, 38 biblioteke tipova, 558 bijeli prostor, 36, 50 binarne datoteke, 503-505 binarni formater, 453 binarni operatori, 121 binarno čitanje datoteke, 503 BinaryFormatter, 537 BinaryReader, klasa, 503 BinarySeareh(), metoda (List), 201 (System.Array), 167 BinaryWriter, klasa, 503 BindingFlags, parametar, 444 bool, tipovi, 575 Boolean izrazi, 38 break, iskaz, 38, 50-52, 575 upotreba sa svvitch iskazima, 42 brisanje, operator za, napomena za C i C++ programere, 26 brisanje datoteka, 500 brzi razvoj aplikacija, 356 BufferedStream, klasa, 503, 505 byte, tip, 575

c C i C++ programeri, napomena za apstraktne klase, ograničenja u C# , 112 binarni operatori, 121 brisanje, operator za, 26

C# definicije klase, 7 jezik ASP.NET i, 356 osnove, 23-63 pregled, 6 ključne riječi, 575-580 lock, iskaz, 480 naredbeni prevoditelj, prevoñenje Hello World programa s pomoću, 19 vrlo tipizirane klase kolekcija, 24 Web Forms obrasci, upotreba, 357 Capacity, svojstvo (List), 201, 203 CaptureCollection, klasa, 242-244 case, iskaz, 575 napomena za C i C++ programere, 44 napomena za VB6 programere, 44 catch, iskaz, 248-253, 575 namjenski iskaz catch, stvaranje, 251-253 odmotavanje stoga poziva, 250 postupak korektivnih akcija, 249 ChannelServices, klasa, 463 char, tip, 27, 575 Chars, polje (String), 224 (StringBuilder), 233 CharSet, parametar, 568 checked, operator, 575 ciljevi atributa, 432 cjelobrojne vrijednosti dijeljenje, 52 pretvaranje razlomaka u/iz, 126 veličine (short, int ili long), 26

K azalo

]

583

Class, cilj atributa, 432 class, ključna riječ, 10 Clear, obrada pritisaka 11a gumb (primjer), 320 C lear(), metoda (Dictionary), 2)7 (List), 201 (Queue), 212 (Stack), 214 (System.Array), 167 Clone(), metoda, 221, 228 (Stack), 214 (String), 222 sučelje ICloneable, 73 Close(), metoda, 83 CLR (Common Language Runtime), 4, 5 asinkroni U/l, 510 dijeljeni sklopovi i, 425 glavna ili prva metoda klase, 10 podrška za dretve, 471 pokretanje statičkih konstruktora, 78 rasporeñivanje po referenci i, 454 serijaliziranje objekata, 536 CLS (Common Language Specification), 4 Collections, imenski prostor, 13 Columns, kolekcija (DataTable), 342 COM (Component Object Model) programiranje, 548-574 uvoženje COM komponenata, 556 biblioteka tipova u .NET, 558 COM DLL-a u .NET, 558 kasno povezivanje i refleksija, 562 testni program, stvaranje, 559 commandString, parametar, 343 Common Language Runtime (pogledajte CLR) Common Language Specification (CLS), 4 Common Type System (CTS), 4 Compare(), metoda (String), 223, 226, 227, 271 CompareOrdinal(), metoda (String), 223 (String), 224 CompareTo(), metoda, 200, 203, 207 prilagoñena inačica (primjer), 208 Component Object Model (pogledajte COM) Concat(), metoda (String), 223, 227 Configuration, imenski prostor, 13 connectionString, parametar, 343 Console, klasa W rite(), metoda, 49, 573 W riteLine(), metoda, 28, 509 Console, objekt ispis teksta na monitor, 12 operator točka i, 13 const, ključna riječ, 576 Constructor, cilj atributa, 432 (List), 201 (Queue), 212 (Stack), 214

584

|

Kazalo

ContainsKey(), metoda (Dictionary), 218 ContainsValue(), metoda (Dictionary), 218 ContextBoundObject, 461 continue, iskaz, 38, 50-52, 576 C.ontrol, klasa apstraktna, stvaranje, 112 Dra\vWindow(), metoda, označavanje polimorlizmom, 103 stvaranje polja Control objekata, 103 Copy, impiementiranje dogañaja gumba (primjer), 321-324 sortiranje popisa odabranih datoteka, 322-324 uzimanje odabranih datoteka, 321 Copy(), metoda (File), 496 (String), 223, 227 (Systein.Array), 167 CopyTo(), metoda (Filelnfo), 497, 500 (List), 202 (Queue), 212 (Stack), 215, 217 (String), 224 C o s(), metoda, 445 dinamičko povezivanje, 445 Count, svojstvo (Dictionary), 217 (List), 201 (Queue), 212 (Stack), 214 CreateO, metoda (Directorylnfo), 493 (File), 496 (Filelnfo), 497 (WebRequest), 534 CreateChildControls(), metoda, 360-361 CreateComlnstanceFrom(), metoda (Activator), 445 CreateDirectory(), metoda (Directory), 492 CreateDomain(), metoda (AppDomain), 450, 451 CreateFile(), metoda, 571 Createlnstance(), metoda, 179, 451, 453 (System.Array), 167 Createlnstance(), metoda (Activator), 445 CreatelnstanceFrom(), metoda (Activator), 445 CreateSubDirectory(), metoda (Directorylnfo), 4 9 3 ,4 9 9 CreateText(), metoda (File), 496 CreationTime, svojstvo (Directorylnfo), 492 (Filelnfo), 496 CTS (Common Type System), 4 CurrentDomain, svojstvo (AppDomain), 450

č čarobnjak Data Sovirce Configurator, 349 članovi instance, 77 članovi klase, 65 članovi instance ili statički članovi, 77

D Data, imenski prostor, 13 DataAdapter, objekt, 342 DataColumn, objekti, 342 DataColumnCollection, objekti, 342 DataGrid implementiranje (primjer aplikacije) 404-406 ltem_Bound, metoda, 407 programsko popunjavanje, 352-353 DataReader, objekti, 343 DataRelation, objekti, 341 DataRotv, objekti, 338 DataSet, klasa, 338, 341 podešavanje, 353-355 stvaranje DataSet, 343 svojstvo Relations, 342 DataTable, objekti, 338, 342 kolekcija Rows, 342 DataTableCollection, 341 datoteke, 492-502 binarne, čitanje, 503-505 mijenjanje, 499-502 rad s, 496-499 klasa Filelnfo, 496-497 metode klase File, 496 tekstualne, čitanje i pisanje, 507 DBCommand, objekt, 342 DBConnection, objekt, 342 decimalni tip podataka, 26, 576 napomena za Java programere, 25 Declarative Referential lntegrity (DRI), 339 Decrement(), metoda (lnterlocked), 483 default, ključna riječ, 576 DefineDynamicAssembly(), metoda (AppDomain), 450 deklarativni jezici, 339 deklarativno pričvršćivanje, 572 deklarativno Web programiranje, 357 Delegate, cilj atributa, 432 delegate, ključna riječ, 267, 576 (pogledajte takoñer dogañaji) delegati, 7, 267-281 anonimne metode, korištenje, 294 dogañaji i, 282-290 implementiranje dogañaja s delegatima 286-289 rješavanje problema s delegatima s pomoću dogañaja, 289 kao svojstva, 277

klase ThreañStart, 472 koje zahtijeva BeginReañj), 510 metode instance i, 276 pozivanje metoda delegata (primjer), 272-275 statički, 276 višeodredišni, 278-281 višeoñredišni, dohvat vrijednosti iz, 295-302 asinkrono pozivanje dogañaja, 299-302 metode pozivanja, 299-302 zadavanje tijekom izvedbe, 267-275 delegirane metode, ručno pozivanje (primjer), 297-299 Deletc, obrada dogañaja gumba (primjer), 324-334 DeleteO, metoda (Directorylnfo), 493 (File), 496 (Filelnfo), 497 Dequeue(), metoda (Queue), 212 Deserialize(), metoda (SoapFormatter), 470 deserijalizacija, 538, 542 primjer, 539 destruktori, 81-83 dispose u usporedbi s, 82 koje ne podržavaju strukture, 129 deva zapis (način imenovanja), 13, 35 digitalni potpisi, 426 dijeljeni resursi, simuliranje, 480 dijeljeni sklopovi, 424 DLL pakao i, 425 inačice, 425 izgradnja, 427 stvaranje jakog imena, 428 ostali potrebni sklopovi, 430 s više modula, 416 dijeljenje (/), operator, 52 Dim i New, ključne riječi (VB6), 70 dinamički nizovi, 233-234 ograničenja graničnika, 234 dinamičko povezivanje, 452 dirCounter, varijabla, 493 Directory, klasa, 492, 503 metode, 492 Directory, svojstvo (Filelnfo), 496 DirectoryInfo, klasa, 492, 501 GetFiles(), metoda, 496 metode, 492 stvaranje instance, 493-495, 499 Directorylnfo, objekti, 314 dirSub.Attributes, svojstvo, 315 Dispose(), metoda, 82, 361 pozvana metodom CIose(), 83 pozvana s pomoću iskaza, 38-84 Distributed interNet Applications (DNA), arhitektura, 4 Div(), metoda, 379 DivideByZeroException, 253

K azalo

|

5 85

spajanje, 475

dizajniranje aplikacija, 389 DLL datoteke COM komponente, uvaženje, 556 dijeljeni sklopovi i, 425 pozivanje funkcija s pomoću P/Invoke, 567 sklopovi i, 414 sklopovi s više modula i, 416 DNA (Distributed interNet Applications), arhitektura, 4 do, iskaz, 37, 576 do-whi!e, petlje, 47 dobro poznati posltižiteljski objekti, 461 registriranje, 463 automatsko, 54 indekseri i, 188 redoslijed operatora, 59 DoEvents(), metoda (Application), 324

(pogledajte takoñer delegati) dogañaji, 281-294 aplikacijske domene, 450 asinkrono pozivanje, 299 delegati i, 282-290 implementiranje dogañaja s pomoću delegata, 286-289 rješavanje problema delegata, 289 Delete, metoda za obradu dogañaja (primjer), 324-334 dodavanje Web Forms obrascima, 372-377 event, upotreba ključne riječi s, 290-294 implementiranje dogañaja gumba Copy (primjer), 321-324 klijenta Web usluge (primjer), 396 objavljivanje i pretplačivanje na, 281 obrada dogañaja kontrole TreeView (primjer), 317-320 OnRorvDataBound, 402 RorvDataBound, 407 Web Forms obrasca, 358 povratni u usporedbi s nepovratnim, 359 doseg, 13 sklopovi kao granica za sadržane tipove, 415 varijable u petlji, 49 double, tip, 26, 576 DrawWindow(), metoda, 22 klase Control, označivanje kao virtualne, 103 pozivanje polja objekata Control, 103 dretve, 472-479 aplikacijske domene u usporedbi s, 451 pokretanje, 472-475 prekidanje, 476 sinkronizacija, 480-489 stanje natjecanja, 489-490 upotreba lnterlocked, 482-484 upotreba lokota, 484-485 upotreba monitora, 485-489 zastoj, 490

stanje natjecanja, 489-490 zastoj, 489-490 DRI (Declarative Referential lntegrity), 339 duboka kopija, 75 dvodimenzionalna polja deklariranje, 174 inicijali ziranje, 175-176 pravokutno polje (primjer), 174 zupčasto polje cjelobrojnih vrijednosti, 177-179

E eksplicitna implementacija sučelja, 156-165 pristup zapečaćenim klasama i s vrijednosnim tipovima, 160-165 sakrivanje člana, 159 selektivno izlaganje metoda, 159 eksplicitno pretvaranje, 27, 123 konverzija izmeñu enum i cjelobrojnog tipa, 35 tipovi za raspakiravanje, 115 element predloška stupca, 402 elementi polja, 168 else, iskaz, 576 Emacs, ureñivanje programa s, 16 Empty, polje (String), 223 EndRead(), metoda, 512 EndsWith(), metoda (String), 224, 228 Enqueue(), metoda (Queue), 212 Enter(), metoda (Monitor), 485 EntryPoint, parametar, 568 Enum, ciljevi atributa, 432 enum, ključna riječ, 33 enumeracije, 31, 33-35 deklariranje, 33 enum, iskaz, 33 enum tipovi, 576 pretvaranje izmeñu enum i cjelobrojnog tipa, 35 enumeratori, popis, 33 Environment, klasa, 314 Equals(), metoda, 113, 200 ključni objekt rječnika, 218 premošćivanje virtualne, 122 (String), 223, 228 Event, cilj atributa, 432 event, ključna riječ, 576 EventArgs, klasa, 282-283 ExactSpelling, parametar, 568 EXE (izvedbena datoteka) J1T kompilacija i, 19 sklopovi i, 414 sklopovi s više modula i, 416 ExecuteAssembly(), metoda (AppDomain), 450

!

Exists(), metoda (File), 496 (List), 202 Exists, svojstvo (DirectoryInfo), 492 (Filelnfo), 496 E x it(), metoda (Monitor), 486, 488 Extension, svojstvo

F false, ključna riječ, 576 FCL (Framework Class Library), 4 imenski prostori i, 12 klase Web Forms obrazaca, 358 Field, cilj atributa, 432 File, klasa, 492, 503 metode, 496 OpenRead() i OpenWrite(), 503 FileAttributes, klasa, 315 FileCopier, aplikacija (primjer), 310-334 implementira nje dogañaja gumba Copv, 321- 324 sortiranje popisa odabranih datoteka 322- 324 uzimanje odabranih datoteka, 321 Delete, metoda za obradu dogañaja 324-334 osnovni obrazac korisničkog sučelja, stvaranje, 312 TreeVie\v, obrada dogañaja, 317-320 TreeVierv, popunjavanje kontrole, 313-317 Filelnfo, klasa, 503 CopyTo(), metoda, 500 metode i svojstva, 496-497 Filelnfo, objekti, 316 FileStream, klasa, 503 FileSystemlnfo, klasa, 492 F ill(), metoda (DataAdapter), 342 FillDirectoryTree(), metoda, 313, 315 FilterName, polje (Type), 443-444 Finalize(), metoda, 113 finally, iskaz, 253-255, 576 finalna klasa (Java), 112 Fin d(), metoda (List), 202 FindAU(), metoda (List), 202 FindMembers(), metoda (Type), 443 fixed, iskaz, 574 fixed, ključna riječ, 572 float, tip podataka, 26, 577 Flush(), metoda (Stream), 503 for, petlje, 37, 48-50, 577 foreach, iskaz, 37, 50-52, 171-184, 441, 577 upotreba s lEnumerable, 195-196, 221 Form at(), metoda (String), 224 formateri, 449, 453, 470 podrazumijevani, 463 upotreba za serijaliziranje podataka, 537

formatirani nizovi, dodavanje, 235 1raction, definiranje konverzija i operatora za klasu, 123-128 Framervork Class Libra ry (FCL), 4 imenski prostori i, 12 klase Web Forms obrazaca, 358 FriendlyName, svojstvo (AppDoinain), 450 FullName, svojstvo (Directorylnfo), 316, 493 (Filelnfo), 496 funkcije, 10 funkcije članice, 10

G GAC (Global Assembly Cache), 425, 427 generalizacija, 99-101 generici, 6 klasa List IComparable, iinplementiranje, 204-207 IComparer, implementiranje, 207-212 ograničenja upotrebe, 196-201 sučelje lEnumerable, 193-196 get, ključna riječ, 577 get, pristupnik, 95 get(), metoda i indekseri, 187-188 GetAttributes(), metoda (File), 496 GetCheckedFiles(), metoda, 321 GetCreationTimesO, metoda (Directory), 492 (File), 496 GetCurrentThreadlD(), metoda (AppDomain), 450 GetData(), metoda (AppDomain), 450 GetDirectories(), metoda (Directory), 492 (DirectoryInfo), 314, 493 GetEnumerator(), metoda (Dictionary), 218 (lEnumerable), 194 (List), 202 (Queue), 212 (Stack), 214 (System.Array), 167 GetFileList(), metoda, 321 GetFilesO, metoda (Directory), 492 (Directorylnfo), 316, 493, 496 GetFileSystemlnfos(), metoda (DirectoryInfo) 493 GetHashCode(), metoda, 113, 218 GetLastAccessTime(), metoda (File), 496 GetLastWriteTime(), metoda (File), 496 GetLength(), metoda (System.Array), 167 GetLocalDrives(), metoda (Directory), 492 (Environment), 314

K azala

|

587

GetLowerBound(), metoda (System.Array), 167 GetMembers(), metoda (Type), 442 GetMethod(), 446 GetMethods(), metoda (Typc), 443 GetO bjecr(), metoda (Activator), 445 GetO bjectData(), metoda (Dictionary), 218 GetParent(), metoda (Directory), 492 GetParentS'tring(), metoda, 32 0 -3 2 L GetRange(), metoda (List), 202 GetResponse(), metoda, 534 GetResponseStream(), metoda (NVebResponse), 534 GetString(), metoda, 573 GetSubDirectoryNodes(), metoda, 315-316 (Type), 445 GetType(), metoda, 113, 442 GetUpperBound(), metoda (System.Array), 167 .gif datoteke, 414 Global Assembly Cache (GAC), 425, 427 globalne metode, 77 gomila alokacija elemenata polja, 168 definirana, 26 goto, iskaz, 38, 45, 577 iskaz switch, upotreba s, 42, 44 grafičko korisničko sučelje (GUI) alati za izradu, 12 grananje, ključne riječi za bezuvjetno grananje, 38 uvjetno grananje, 38 GridLayout režim, dodavanje kontrola na Web Forms obrasce, 365 GridViews, stvaranje, 402 Group, klasa (Regex), 239-242 GUI (grafičko korisničko sučelje) alati za izradu, 12 gumbi povezivanje podataka i, 367

H Hejlsberg, Anders, 7 Hello World, program, 9-22 klase, objekti i tipovi, 9-15 prevoñenje i pokretanje, 18-19 razvoj, 16 ureñivanje, 16 uzorak koda, 9 HelpLink, svojstvo (Exception), 255 HTML dodavanje Web Forms obrascima, 363 kontrole, dodavanje Web Forms obrascima, 365 poslužiteljske kontrole, 367

588

|

Kazalo

stvaranje tri GridVietv kontrole, 402 tokovi podataka, čitanje Web stranice kao, 534 WSDL ugovor, pregled, 382 HTTP sesije, 359 HTTPChannel, tip, 463 HttpWebRequest, 534 HttpWebResponse, objekt, 534

I I, operator (&&r), 57, 59 IAsyncResult, sučelje, 511 ICalc, sučelje, 462 ICloneable, sučelje, 75 ICloneable objekti, nizovi i, 221 ICollection, sučelje, 193 [Comparable, sučelje, 193, 196-201 implementiranje, 204-207 nizovi i, 221 IComparer, sučelje, 193,322 implementiranje, 207-212 ograničenja, 212 lConvertible, klase, 222 IDataReader, sučelje, 338 IDE (Integrated Development Enviroment), 16 identifikatori, 35 definiranje, 60 poništavanje definicije, 61 IDeserializationCallback, sučelje, 542 lDictionary, sučelje, 193, 218 IDisposable, sučelje, 82 IDL (Interface Definition Language), 7 lEnumerable, sučelje, 193-196, 214 nizovi i, 221 IEnumerator, sučelje, 193, 214 if, iskaz, 577 ugniježñeni, 40-42 iskaz switch kao zamjena, 42 if...else, iskaz, 38 IFormatter, sučelje, 537 lLDasm, alat, 415 ILI, operator (|), 59 ILI, operator (j|), 57, 59 IList, sučelje, 193 imenovanje, načini, 15 ,,deva“ i Pascalov zapis, 35 mañarski zapis, 35 imenski prostori, 12-13 ispisivanje cijelih, 15 napomena za C++ programere, 12 napomena za Java programere, 12 stvaranje u Visual Studiju .NET, 17 System.Text.RegularExpressions, 236 u primjerima koda, 29 XM L, za WSDL dokumente, 380

IMessage, sučelje, 483 nnplicit, operator, 577 implicitno pretvaranje, 27 123 pakiranje, 115 ’ in, iskaz, 37 in, operator, 577 inacica, broj za dijeljene sklopove 425 (pogledajte takoñer sklopovi) ’ inačica, praćenje, 414 apstraktne klase u usporedbi sa sučeljima, s pomoću ključnih riječi new i override, 107 Increment(), metoda (Interlocked) 483 indekser, deklariranje svojstva, 184 indekseri, 184-192 definirani, 184 dodjeljivanje i, 188 get(), metoda i, 187-188 preopterećivanje indeksera, 189-192 ser(), metoda i, 187-188 sintaksa, 187 tliis, ključna riječ, 184 IndexOf(), metoda (List), 202 (String), 229 (System.Array), 167 inicijalizatori, 73-75 ImtializeO, metoda (System.Array), 167 lnnerException, svojstvo (Exception), 261 JnputMream, klasa, metoda Readf) 503 lnsert(), metoda (List), 202 (String), 224, 229 (StringBuilder), 233 lnsertRange(), metoda (List), 202 instance, 9 brojanje s pomoću statičkih polja, 80 razlika izmeñu klase i, 65 instanceof (Java), 147 int, tip, 26, 578 Intellisense, svojstvo Autocomplete 15 Interface, cilj atributa, 432 interface, ključna riječ, 137 Interface Definition Language (1DL), 7 Interlock, klasa, 480 Interlocked, klasa, 482-483 internal, ključna riječ, 107 interna!, modifikator pristupa, 68, 577 internal protected, ključna riječ, 107 InternalErrorException, 261 Internet, pozivanje Web usluga preko, 377 Internet Information Server(IlS) 450 lnterruptf), metoda (Thread) 476 IP adrese, 515 is, operator, 147-150, 577 u usporedbi s operatorom as, 151

IsBackground, svojstvo (Thread) 477 lsFixedSize, svojstvo (System.Array), 167 iskazi, 37,60-63 bezuvjetno grananje, 38 bijeli prostor u, 36 iteracija, 45-52 continueibreak, 50-52 do-while, petlja, 47 for, petlja, 48-50 foreach, 50 goto, 45 while, petlja, 46 izrazi, 35 uvjetno grananje, 38 if...else, 38 svvitch, 42-45 ugniježñeni if iskaz, 40-42 iskazi, blokovi, 40 IsReadOnly, svojstvo (System.Array), 167 s5ynchromzed, svojstvo (System.Array), 167 tem, element za klase kolekcije 202 Item, objekt, 397, 413 Item, svojstvo(IDictionary), 218 Item(), metoda (Dictionary), 217 (List), 201 ItemLookup, objekt, 397 ItemLookupRequest, objekt, 397 ItemSearch, objekt, 413 ItemSearchRequest, objekt, 413 ItemSearchResponse, objekt, 413 Item_Bound, metoda (DataGrid), 407 iteracija, iskazi za, 37 , 45-52 continue i break, 50-52 do-while, petlja, 47 for, petlja, 48-50 foreach, 50 goto, 45 while, petlja, 46 izdavači, 281 izlaz na zaslon, ispisivanje 28 iznimke, 245-265 definirane, 245 Exception, objekti, 255-258 izbacivanje i hvatanje, 246 -255 catch, iskaz, 248-253 finally, iskaz, 253-255 throw, iskaz, 246-248 metode za obradu iznimaka, definirane, 245 ponovno izbacivanje, 261-265 prilagoñene, 258-261 izolirano spremište, 5 44 čitanje iz, 545 pisanje u, 545 izravan pristup memoriji, 8 izravna razmjena datoteka, 515

K azalo

|

589

izrazi, 35, 48 Boolean, 38 regularni (pogledajte regularni izrazi) uvjetni, 59 izvedene klase, 102, 113 apstraktne klase kao osnovna klasa, 109 premošćivanje virtualne metode osnovne klase, 108 izvoženje .NET lcomponenara, 564

J jaka imena za sklopove, 426 java, napomena za programere decimalni tip, 25 imenski prostori, 12 konstante Članice, 136 M ain(), metoda, 10 pravokutna polja, 174 referentni parametri, 85 statičke metode, pozivanje , 78 statički konstruktori, 79 ugniježñene klase, 118 zapečaćena klasa, 112 javna svojstva AppDomain klase, 450 Filelnfo klasa i, 496-497 javne statičke metode, prekidanje dretvi, 476 javni ključevi, 426 oznake za, 428 javni pristup, modifikator za, 68 ,107 , 578 jedan modul, sklop s, 416 jedan poziv, objekti s, 461 jednakost, operator (==), 56 operator pridruživanja (=) u usporedbi s, 41 premošćivanje, 122 jednostavni tipovi popis, 25 JIT (Just In Time), prevoditelj, 6, 19 Jo in (), metoda (String), 224 (Thread), 490

K kanali, 449, 453 registriranje na klijentu, 465 stvaranje, 463 kasno povezivanje, 107, 445, 558 refleksija i, 562 KeepAlive, zastavica, 476 Keys, svojstvo (Dictionary), 217 KeywordRequest, objekt, 412 klasa, članovi, 65 članovi instance ili statički članovi, 77 klase apstraktne (pogledajte apstraktne klase) C# , podrška za definiranje i rad s, 7

590

\

Kazalo

definiranje, 65-70 argumenti metode, 68-70 modifikatori pristupa, 67 Time, klasa (primjer), 66 definiranje tipova, 10 implementiranje više sučelja, 140, 157 implemetiranje sučelja, 153 instanca klase u usporedbi s, 65 ja v n e ,107 klasa Object kao korijenska klasa, 113 kolekcije unutar, pristup {pogledajte indekseri) metode, 10 odnos izmeñu, UML dijagrami, 100 pregled, 64 razlika izmeñu struktura i, 129, 131 refleksija, 439 statičke, 79 stvaranje i imenovanje u Visual Studiju .NET, 17 ugnježñivanje, 117 zapečaćene,112 klijenti mrežnog toka podataka, stvaranje, 519 rad na daljinu, 469-470 izgradnja, 465 za asinkrone mrežne tokove, 521 klijentska podrška, .NET Web uslugama, 378 klijentski aktivirani poslužiteljski objekti, 461 ključ i vrijednost, veze izmeñu, 217-219 referentni tip kao ključ, 219 ključne riječi u C# , 575-580 kod odvajanje korisničkog sučelja od, 358 područje, sažimanje u Visual Studiju, 62 ponovna upotreba, 102 smjernice za stil (Microsoft), 15 kolekcije klasa List, 201-212 implementiranje iComparable, 204-207 implementiranje lComparer, 207-212 klase, 166 klase vrlo tipiziranih, 24 MatchCollection, 238-239 redovi, 212-214 rječnici, 217-219 stogovi, 214-217 sučelja, 193-196 IComparable, 196-201 lEnumerable, 193-196 ureñivanje Listltems, 365 komentari, 11 u stilu jezika C (/*... 7 ), 11 u više redova, 11 XM L, 334-336 za dokumentaciju, 334-336

n i

$ I

komponentno orijcntii ' i i no programiranje, 7 konstante, 31 enumeracije kao zamjena za, 3.3 enumerirane, 33 inieijaliziranje, 32 ponovno inieijaliziranje za vrijeme prevoñenja, 32 simbolične, 31 konsrrukrorza kopiranje, 75 konstruktori, 70-72 deklariranje, 71 osnovne klase, pozivanje, 106 podrazumijevani, 107 preopterećeni, definirani, 73 preopterećivanje, 90-93 statički, 78 strukture, 131 konteksti, 448, 459 kontekstno povezani i okretni objekti, 459 rasporeñivanje preko granica, 459 kontekstno vezani objekti, 459 kontrole ActiveX stvaranje, 549 u .NET-u, 552 uvoženje, 548 aspdabel, kontrola, 402 dodavanje na Web Forms obrasce. 365-367 poslužiteljske, 367 povezane s podacima, ADO.NET, 349-355 DataGriñ, programsko popunjavanje, 352-353 podešavanje DataSet, 353-355 povezivanje podataka s, 367-377 TreeVie\v obrada dogañaja, 317-320 popunjavanje, 313-317 Web, 357 poslužiteljske, 363 uobičajene, 27 konzola aplikacije, 12 ispisivanje na asinkroni poslužitelj za mrežne tokove podataka, 521 klijent mrežnog toka podataka, 519 korijenska klasa, 113 korisnička konfiguracija, informacije o, 544 korisnički definirani tipovi, 23 serijalizacija, 536 korisničko sučelje, 358 kraj reda (napomena za Visual Basic programere), 37 kultura, 430

L LastAccessTime, svojstvo (Direaorylnfo), 493 (Filelnfo), 496 LascindexOf(), metoda (List), 202 (String), 224 (System.Array), 167 LastlVritelime, svojstvo (Directorylnfo), 493 (Filelnfo), 497 Length, polje (String), 228 (StringButlder), 233 Length, svojstvo (Capture), 242 (Filelnfo), 497 (String), 228 (Systeni.Arrny), 167, 169 Listltem Collection Editor, 365 literati, 31 literali (regularni izraz), 235 literati niza, 222, 226 Directory info, objekt, stvaranje, 493 označeni simbolom 239 Load, dogañaj, 360 Load(), metoda (AppDomain), 450 (Assembly), 440 LoadPostData(), metoda, 360 LoadViewSrate(), metoda, 360 lock, iskaz, 577 lock, ključna riječ, 484 logičko ekskluzivno ILI, operator H , 59 logičko ILI (|), operator, 59 logički operatori, 57 logički vrijednosni tip, napomena za C i C++ programere, 25 lokalne varijable, prikaz vrijednosti u programu za uklanjanje pogrešaka, 21 lokoti, sinkroniziranje dretvi, 480, 484-485 long, tip, 26, 577

M mañarski zapis, 35 M ain(), metoda, 10, 78 asinkroni U/I i, 511 izgradnja poslužitelja koji koristi, 462 konzolne aplikacije i, 12 napomena za C++ programere, 10 napomena za Java programere, 11 SingleCall, upotreba, 467 static ključna riječ i, 16 završne točke i, 469-470

K azalo

|

591

makefile datoteka za sklop s više modula, 418 manifesti, 415 dijeljeni sklop (primjer), 428 sklopa sviše modula (primjer), 421 manje ili jednako (=), operator, 56 veće od(>), operator, 56 veličina polja, 201 veze (višestruke), obrada od strane mrežnog poslužitelja za tokove podataka, 521 vidljivost klase i njenih članova (pogledajte modifikatori pristupa) VievvState, svojstvo, 360 virtual, ključna riječ, 103, 580 virtualne metode, 103 klase Object, 113 premošćivanje, 103, 107 virtualni stroj, .NET CLR-a, 5 više modula, sklopovi s, 416 izgradnja, 416 ' ispitivanje, 421 makefile datoteka, upotreba, 418 učitavanje sklopa, 422 višedimenzionalna polja, 174-179 granice dimenzija, 179 višeodredisni delegati, dohvat vrijednosti iz, 295-302 Visual Studio .NET imenski prostor, stvaranje, 17 kontrole ActiveX, uvoženje, 548, 552 prednosti za razvoj softvera, 16 programi za uklanjanje pogrešaka, upotreba, 21-22 stvaranje konzolnih aplikacija, 17-18 uvoženje ActiveX kontrole u, 552 Web Forms obrasci, stvaranje, 358 Visual Studio .NET Designer alatni okvir, 307 izrada Windo-w Forms obrazaca, 306-310 prozor Properties, 308

void, ključna riječ, U , 580 volatile, ključna riječ, 580 vrijednosni parametri, 87 vrijednosni tipovi, 23 alokacija na stogu, 26 neiniñjalizirani, 72 objekti kao, 132 pakiranje i raspakiravanje, 115 polja, 169 pristup, 160-165 prosljeñivanje u metode, 88 strukture kao, 129, 132 ugrañeni, popis, 25 vrijednosti, vraćanje u parametrima, 85 vrlo tipizirani jezici, 23

w W ait(), metoda (Monitor), 485, 490 \Veb aplikacije dogañaji, 359 metode brzog razvoja primijenjene na, 356 prednosti, 306, 356 stanje, 359 Web tokovi podataka, 533 Web Forms obrasci, 6 dodavanje kontrola na, 365-367 poslužiteljskih, 367 povezivanje podataka s kontrolama, 367-377 pregled, 357-361 dogañaji, 358 podjela korisničkog sučelja, 358 životni ciklus, 360 stvaranje, 361-364 datoteke s kodom, 362-364 (pogledajte takoñer ASP kontrole) Web kontrole, 367 Web Service Description Language (pogledajte WSDL) Web usluge, 5 izgradnja .NET usluge kalkulator (primjer), 380 pregledavanje WSDL sažetka, 382 testiranje, 381 izrada .NET usluga, 378-383 klijentska aplikacija (primjer), 389 izrada klijenta, 389 pretraživanje prema kategoriji, 408 prikaz izlaza, 399 posrednik, stvaranje, 383-388 testiranje usluge, 386 Web.config, datoteka, 364 WebRequest, objekt, 534 WebRequestFactory, klasa, 534 WebResponse, objekt, 534 GetResponseStrearo(), metoda, 534

K a z a lo

|

601

ugovor, pregled, 382 X M L imenski prostor za W SD L dokum ente, 380

\vhile, iskaz, 37 while, petlja, 46 , 580 W iltam uth, Scott, 7 W in 32 API deklariranje metoda za uvoženje u C# program, 571 pozivanje metode s P/Invoke, 568 Windo\v klasa, metoda D raw W indow (), 22 Windo\vs Forms obrasci, 6 osnovni obrazac za korisničko sučelje, 312 aplikacije, stvaranje, 310-334 dogañaj gumba Delete, 324 -334 im plementiranje dogañaja gumba Copy, 321-324 kontrole TreeView, 313-317 metoda za obradu dogañaja TreeVievv, 319 obrada dogañaja TreeVievv, 317-320 rekurzija kroz podmape, 315 uzim anje datoteka iz mape, 316 dodavanje kontrola na, 554 izrada jednostavnih, 306-310 Visual Studio Designer, upotreba, 306 -310

.wsdl datoteka, 391 X X M L (Extensible Markup Language) imenski prostor za W SDL dokum ent, 38 0 klase u .N ET kosturu, 5 kom entari za dokum entaciju, 334 -336 SOAP, prednosti temeljenja na, 378 Y yield, ključna riječ, 194

z zapečaćene klase, 112 pristup, 160-165 zapisi u bazi podataka, 338 zaslon, ispisivanje izlaza na, 28 zastoj kod sinkronizacije dretvi, 490 završne točke, 4 6 3 , 4 67

Windo\vs.Forms imenski prostor, 310 W rite(), metoda (Console), 49 (Stream), 503 W rite L in e(), metoda, 12, 13 (Console), 509 pisanje izlaza na zaslon, 28 (StreamReader), 50 7 (StreamWriter), 507, 509 wsdl, alat, 382 W SDL (Web Service Description Language), 377 datoteke, stvaranje posrednika, 382

602

|

K a z a lo

razum ijevanje, 4 6 9 -4 7 0 ručno povezivanje usluge s, 4 67 toka, 491 ulazi kao, 515 zbrajanje, operator za (+), 52 p reo p terećiv ale, 120 znakovi, tipovi u regularnim izrazim a, 235

1 životni ciklus Web Form s obrasca, 360

0 autoru Jesse Liberty je autor desetaka knjiga, uključujući Programiranje ASP.NET. Direktor je tvrtke Liberty Associates, Ine. (www.libertyassociates.com) koja se bavi obukom o .NET tehnologiji, razvojem aplikacija po narudžbi i savjetovanjem. Nekad je radio kao šef elektroničkog odjela Citibanka te programski arhitekt i razvojni inženjer u tvrtkama AT&T, Ziff Daviš, Xerox i PBS.

0 knjizi Izgled naših knjiga nastao je na temelju komentara čitatelja, vlastitog iskustva i informacija iz distribucijskih kanala. Jedinstveni omoti dopunjuju se s našim jedinstvenim pristupom tehničkim temama, dajući osobnost i živost potencijalno monotonim temama. Životinja na omotu knjige Programiranje C# je afrički krunasti ždral. Ova visoka, mršava ptica seta se močvarama i ravnicama zapadne i istočne Afrike (zapadni i istočni afrički krunasti ždral, Belearica pavonia pavonia i Belearica regulorum biggericeps, redom). Odrasle ptice visoke su oko jedan metar i teške izmeñu tri i četiri kilograma. Oko 150 cm dugačka zvučna cijev unutar njihovog vrata (jedan njezin dio smotan je u prsnoj kosti) proizvodi jak glas koji se može čuti na udaljenosti od nekoliko kilometara. Žive oko 22 godine, a većinu života provode tražeći biljke, male životinje i kukce koje vole jesti (jedna od tehnika kojima krunasti ždral pronalazi kukce, usavršena tijekom milijuna godina postojanja ove vrste, je energično koračanje po močvarnom tlu kojim se ukusni kukci i crvi istiskuju na površinu. Oni su jedina vrsta ždralova koji se penju na stabla. To čine po noći, kad se spremaju na spavanje. Društveni i glasni, krunasti ždralovi okupljaju se u parove ili obitelji, a manje skupine se ponekad udružuju u jata u kojima može biti više od stotinu ptica. Na temelju njihovog plesa udvaranja nastali su plesovi lokalnih plemena. Omot knjige dizajnirao je Ellie Volckhausen na temelju predloška za cijelu seriju koji je stvorio Edie Freedman. Slika na omotu je originalna gravura iz 19. stoljeća. Omot je pripremljen u programu InDesign CS, a tekst je ispisan pismom Adobe ITC Garamond. Unutrašnjost knjige uredio je David Futato. Glavni tekst ispisan je pismom Linotype Birka, naslovi su ispisani pismom Adobe Myriad Condensed dok su odlomci koda ispisani pismom TheSans Mono Condensed. Ilustracije su napravili Robert Romano, Jessamyn Read i Lesley Borash u programima Macromedia Freehand MX i Adobe Photoshop CS. Sličice pored savjeta i upozorenja nacrtao je Christopher Bing.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF