You are on page 1of 259

Visual C#

Lucian Sasu May 30, 2005

Cuprins
1 Platforma Microsoft .NET 1.1 Prezentare general . . . . . . . . . . . . a 1.2 Arhitectura platformei Microsoft .NET . 1.3 Componente ale lui .NET Framework . . 1.3.1 Microsoft Intermediate Language 1.3.2 Common Language Specication 1.3.3 Common Language Runtime . . . 1.3.4 Common Type System . . . . . . 1.3.5 Metadata . . . . . . . . . . . . . 1.3.6 Assemblies . . . . . . . . . . . . . 1.3.7 Assembly cache . . . . . . . . . . 1.3.8 Garbage collection . . . . . . . . 1.4 Trsturi ale platformei .NET . . . . . . aa 2 Tipuri predenite, tablouri, string-uri 2.1 Vedere general asupra limbajului C# a 2.2 Tipuri de date . . . . . . . . . . . . . . 2.2.1 Tipuri predenite . . . . . . . . 2.2.2 Tipuri valoare . . . . . . . . . . 2.2.3 Tipul enumerare . . . . . . . . 2.3 Tablouri . . . . . . . . . . . . . . . . . 2.3.1 Tablouri unidimensionale . . . . 2.3.2 Tablouri multidimensionale . . 2.4 Siruri de caractere . . . . . . . . . . . 2.4.1 Expresii regulate . . . . . . . . 3 Clase, instructiuni, spatii de 3.1 Clase vedere general . . a 3.2 Transmiterea de parametri 3.3 Conversii . . . . . . . . . . 3.3.1 Conversii implicite nume . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 11 12 12 12 13 14 15 16 17 17 17 21 21 23 24 25 29 36 36 37 41 43 45 45 49 55 55

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

4 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.3.10 3.3.11 3.3.12 3.3.13 Spatii 3.4.1 3.4.2 Conversiile implicite ale expresiilor constante Conversii explicite . . . . . . . . . . . . . . Boxing i unboxing . . . . . . . . . . . . . . s Declaratii de variabile i constante . . . . . s Declaratii de etichete . . . . . . . . . . . . . Instructiuni de selectie . . . . . . . . . . . . Instructiuni de ciclare . . . . . . . . . . . . Instructiuni de salt . . . . . . . . . . . . . . Instructiunile try, throw, catch, nally . . . Instructiunile checked i unchecked . . . . . s Instructiunea lock . . . . . . . . . . . . . . . Instructiunea using . . . . . . . . . . . . . . de nume . . . . . . . . . . . . . . . . . . . . Declaratii de spatii de nume . . . . . . . . . Directiva using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 57 60 62 62 63 64 66 66 67 67 67 68 69 70 75 75 76 77 77 77 78 78 79 79 80 81 81 81 87 92 93 95 95 96 99 99 101

3.4

4 Clase 4.1 Declararea unei clase . . . . . . . 4.2 Membrii unei clase . . . . . . . . 4.3 Cmpuri . . . . . . . . . . . . . . a 4.3.1 Cmpuri instante . . . . . a 4.3.2 Cmpuri statice . . . . . . a 4.3.3 Cmpuri readonly . . . . . a 4.3.4 Cmpuri volatile . . . . . a 4.3.5 Initializarea cmpurilor . . a 4.4 Constante . . . . . . . . . . . . . 4.5 Metode . . . . . . . . . . . . . . . 4.5.1 Metode statice i nestatice s 4.5.2 Metode externe . . . . . . 4.6 Proprieti . . . . . . . . . . . . . at 4.7 Indexatori . . . . . . . . . . . . . 4.8 Operatori . . . . . . . . . . . . . 4.8.1 Operatori unari . . . . . . 4.8.2 Operatori binari . . . . . . 4.8.3 Operatori de conversie . . 4.8.4 Exemplu: clasa Fraction . 4.9 Constructori de instant . . . . . a 4.10 Constructori statici . . . . . . . . 4.11 Clase interioare . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

CUPRINS 5 Destructori. POO C# n 5.1 Destructori . . . . . . . . . . . . . . . . . . . . . . . 5.2 Specializarea i generalizarea . . . . . . . . . . . . . . s 5.2.1 Specicarea motenirii . . . . . . . . . . . . . s 5.2.2 Apelul constructorilor din clasa de baz . . . a 5.2.3 Operatorii is i as . . . . . . . . . . . . . . . s 5.3 Clase sealed . . . . . . . . . . . . . . . . . . . . . . . 5.4 Polimorsmul . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Polimorsmul parametric . . . . . . . . . . . . 5.4.2 Polimorsmul adhoc . . . . . . . . . . . . . . 5.4.3 Polimorsmul de motenire . . . . . . . . . . . s 5.4.4 Virtual i override . . . . . . . . . . . . . . . s 5.4.5 Modicatorul new pentru metode . . . . . . . 5.4.6 Metode sealed . . . . . . . . . . . . . . . . . . 5.4.7 Exemplu folosind virtual, new, override, sealed 5.5 Clase i metode abstracte . . . . . . . . . . . . . . . s 5.6 Interfete . . . . . . . . . . . . . . . . . . . . . . . . . 5.6.1 Clase abstracte sau interfete? . . . . . . . . .

5 105 . 105 . 107 . 107 . 108 . 110 . 110 . 111 . 111 . 111 . 111 . 113 . 114 . 116 . 117 . 119 . 120 . 126 127 . 127 . . . . . . . . . 128 131 132 135 135 135 142 142 146

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

6 Delegati. Evenimente. Structuri 6.1 Tipul delegat . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 Utilizarea delegatilor pentru a specica metode la run time . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.2 Delegati statici . . . . . . . . . . . . . . . . . . . . . 6.1.3 Multicasting . . . . . . . . . . . . . . . . . . . . . . . 6.2 Evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2.1 Publicarea i subscrierea . . . . . . . . . . . . . . . . s 6.2.2 Evenimente i delegati . . . . . . . . . . . . . . . . . s 6.2.3 Comentarii . . . . . . . . . . . . . . . . . . . . . . . 6.3 Structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Structuri sau clase? . . . . . . . . . . . . . . . . . . . 7 Tratarea exceptiilor. Atribute 7.1 Tratarea exceptiilor . . . . . . . . . . . . . . . 7.1.1 Tipul Exception . . . . . . . . . . . . . 7.1.2 Aruncarea i prinderea exceptiilor . . . s 7.1.3 Re ncercarea codului . . . . . . . . . . 7.1.4 Compararea tehnicilor de manipulare a 7.1.5 Sugestie pentru lucrul cu exceptiile . . 7.2 Atribute . . . . . . . . . . . . . . . . . . . . . 7.2.1 Generaliti . . . . . . . . . . . . . . . at . . . . . . . . . . . . . . . . . . . . erorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

147 . 147 . 147 . 148 . 159 . 161 . 162 . 163 . 163

6 7.2.2 7.2.3 7.2.4

CUPRINS Atribute predenite . . . . . . . . . . . . . . . . . . . . 164 Exemplicarea altor atribute predenite . . . . . . . . 167 Atribute denite de utilizator . . . . . . . . . . . . . . 170

8 ADO.NET 173 8.1 Ce reprezint ADO.NET? . . . . . . . . . . . . . . . . . . . . 173 a 8.2 Furnizori de date ADO.NET . . . . . . . . . . . . . . . . . 174 n 8.3 Componentele unui furnizor de date . . . . . . . . . . . . . . . 174 8.3.1 Clasele Connection . . . . . . . . . . . . . . . . . . . . 175 8.3.2 Clasele Command . . . . . . . . . . . . . . . . . . . . . 176 8.3.3 Clasele DataReader . . . . . . . . . . . . . . . . . . . . 176 8.3.4 Clasele DataAdapter . . . . . . . . . . . . . . . . . . . 176 8.3.5 Clasa DataSet . . . . . . . . . . . . . . . . . . . . . . . 176 8.4 Obiecte Connection . . . . . . . . . . . . . . . . . . . . . . . . 176 8.4.1 Proprieti . . . . . . . . . . . . . . . . . . . . . . . . . 177 at 8.4.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 179 8.4.3 Evenimente . . . . . . . . . . . . . . . . . . . . . . . . 179 8.4.4 Stocarea stringului de conexiune ier de congurare 179 n s 8.4.5 Gruparea conexiunilor . . . . . . . . . . . . . . . . . . 180 8.5 Obiecte Command . . . . . . . . . . . . . . . . . . . . . . . . 181 8.5.1 Proprieti . . . . . . . . . . . . . . . . . . . . . . . . . 181 at 8.5.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 182 8.5.3 Utilizarea unei comenzi cu o procedur stocat . . . . . 185 a a 8.5.4 Folosirea comenzilor parametrizate . . . . . . . . . . . 185 8.6 Obiecte DataReader . . . . . . . . . . . . . . . . . . . . . . . 187 8.6.1 Proprieti . . . . . . . . . . . . . . . . . . . . . . . . . 187 at 8.6.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 188 8.6.3 Crearea i utilizarea unui DataReader . . . . . . . . . . 188 s 8.6.4 Utilizarea de seturi de date multiple . . . . . . . . . . . 190 8.6.5 Accesarea datelor ntro manier sigur din punct de a a vedere a tipului . . . . . . . . . . . . . . . . . . . . . . 190 9 ADO.NET (2) 9.1 Obiecte DataAdapter . . . . . . 9.1.1 Metode . . . . . . . . . 9.1.2 Proprieti . . . . . . . . at 9.2 Clasa DataSet . . . . . . . . . . 9.2.1 Continut . . . . . . . . . 9.2.2 Clasa DataTable . . . . 9.2.3 Relatii ntre tabele . . . 9.2.4 Popularea unui DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 . 193 . 194 . 195 . 195 . 196 . 196 . 199 . 199

CUPRINS

9.3

9.2.5 Propagarea modicrilor ctre baza de date . . . . . . 200 a a Tranzactii ADO.NET . . . . . . . . . . . . . . . . . . . . . 202 n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 . 205 . 205 . 208 . 209 . 209 . 213 . 213 . 215 . 217 . 218 . 219 223 . 224 . 224 . 225 . 226 . 228 . 228 . 228 . 229 . 229 . 231 . 231 . 233 . 233 . 234 . 234 . 235 . 235 . 235 . 235 . 235

10 Fire de executie 10.1 Managementul threadurilor . . . . . . . . . . . 10.1.1 Pornirea threadurilor . . . . . . . . . . 10.1.2 Metoda Join() . . . . . . . . . . . . . . 10.1.3 Suspendarea relor de executie . . . . . 10.1.4 Omorrea threadurilor . . . . . . . . . a 10.1.5 Sugerarea prioritilor relor de executie at 10.1.6 Fire fundal i re prim-plan . . . . n s n 10.2 Sincronizarea . . . . . . . . . . . . . . . . . . . 10.2.1 Clasa Interlocked . . . . . . . . . . . . . 10.2.2 Instructiunea lock . . . . . . . . . . . . . 10.2.3 Clasa Monitor . . . . . . . . . . . . . . .

11 ASP.NET 11.1 Anatomia unei pagini ASP.NET . . . . . . . . . . . . . . . 11.1.1 Adugarea unui control Web . . . . . . . . . . . . . a 11.1.2 Adugarea scriptului inline . . . . . . . . . . . . . a 11.1.3 Code-behind . . . . . . . . . . . . . . . . . . . . . . 11.2 Clasa Page . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2.1 Evenimentul Init . . . . . . . . . . . . . . . . . . . 11.2.2 Evenimentul Load . . . . . . . . . . . . . . . . . . 11.2.3 Evenimentul Unload . . . . . . . . . . . . . . . . . 11.3 Crearea unei pagini ASP.NET folosind Visual Studio.NET 11.4 Controale server . . . . . . . . . . . . . . . . . . . . . . . . 11.4.1 Postback . . . . . . . . . . . . . . . . . . . . . . . . 11.4.2 Data Binding . . . . . . . . . . . . . . . . . . . . . 11.5 Controale Web . . . . . . . . . . . . . . . . . . . . . . . . 11.5.1 Label . . . . . . . . . . . . . . . . . . . . . . . . . . 11.5.2 Button . . . . . . . . . . . . . . . . . . . . . . . . . 11.5.3 LinkButton . . . . . . . . . . . . . . . . . . . . . . 11.5.4 TextBox . . . . . . . . . . . . . . . . . . . . . . . . 11.5.5 CheckBox . . . . . . . . . . . . . . . . . . . . . . . 11.5.6 RadioButton . . . . . . . . . . . . . . . . . . . . . 11.5.7 DropDownList . . . . . . . . . . . . . . . . . . . .

12 ASP.NET (2) 237 12.1 Controale de validare . . . . . . . . . . . . . . . . . . . . . . . 237 12.1.1 RequiredFieldValidator . . . . . . . . . . . . . . . . . . 238

8 12.1.2 RegularExpressionValidator . . . . . 12.1.3 RangeValidator . . . . . . . . . . . . 12.1.4 CompareValidator . . . . . . . . . . 12.1.5 CustomValidator . . . . . . . . . . . 12.1.6 ValidationSummary . . . . . . . . . . Comunicarea cu browserul . . . . . . . . . . 12.2.1 Generarea programatic continutului a 12.2.2 Redirectarea . . . . . . . . . . . . . . Cookies . . . . . . . . . . . . . . . . . . . . Fiierul de congurare al aplicatiei Web . . . s Fiierul global.asax . . . . . . . . . . . . . . s Managementul sesiunii . . . . . . . . . . . . ViewState . . . . . . . . . . . . . . . . . . . 12.7.1 Application . . . . . . . . . . . . . . Alte puncte de interes aplicatii ASP.NET n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 238 238 239 240 240 241 241 242 243 243 244 245 245 245

12.2

12.3 12.4 12.5 12.6 12.7 12.8

13 Servicii Web 13.1 Generaliti . . . . . . . . . . . . . . . . . . . . at 13.1.1 Componentele unui WS . . . . . . . . . 13.2 Discutii asupra blocurilor componente . . . . . . 13.2.1 Protocolul de transport . . . . . . . . . . 13.2.2 Schema de codicare . . . . . . . . . . . 13.2.3 Conventia de formatare . . . . . . . . . . 13.2.4 Mecanismul de descriere . . . . . . . . . 13.2.5 Mecanisme de descoperire . . . . . . . . 13.3 Crearea unui serviciu Web simplu . . . . . . . . 13.3.1 Adunarea a dou numere . . . . . . . . . a 13.3.2 Validarea unui numr de card de credit . a 13.3.3 SW pentru transmitere de continut binar 13.4 SOAP . . . . . . . . . . . . . . . . . . . . . . . 13.4.1 Header-e . . . . . . . . . . . . . . . . . . 13.4.2 Elementul body . . . . . . . . . . . . . .

247 . 247 . 247 . 248 . 248 . 248 . 249 . 250 . 250 . 251 . 251 . 252 . 253 . 254 . 255 . 256

Curs 1 Platforma Microsoft .NET


1.1 Prezentare general a

Platforma .NET este un nou cadru de dezvoltare a softului, sub care se vor realiza, distribui si rula aplicatiile de tip forme Windows, aplicatii WEB si servicii WEB. Ea const trei prti principale: Common Language a n a Runtime, clase pentru platform i ASP.NET. O infrastructur ajuttoare, as a a Microsoft .NET Compact Framework este un set de interfete de programare care permite dezvoltatorilor realizarea de aplicatii pentru dispozitive mobile precum telefoane inteligente i PDA-uri1 . s .NET Framework constituie un nivel de abstractizare ntre aplicatie i s kernel-ul sistemului de operare (sau alte programe), pentru a asigura portabilitatea codului; de asemenea integreaz tehnologii care au fost lansate de a catre Microsoft incepnd cu mijlocul anilor 90 (COM, DCOM, ActiveX, etc) a sau tehnologii actuale (servicii Web, XML). Platforma const cteva grupe de produse: a a 1. Unelte de dezvoltare - un set de limbaje (C#, Visual Basic .NET, J#, Managed C++, Objective-C, Python, Smalltalk, Eiel, Perl, Fortran, Cobol, Lisp, Haskell, Pascal, RPG, etc), un set de medii de dezvoltare (Visual Studio .NET, Visio), infrastructura .NET Framework, o bibliotec cuprinztoare de clase pentru crearea serviciilor Web (Web Sera a 2 vices) , aplicatiilor Web (Web Forms) i aplicatiilor Windows (Win s dows Forms). 2. Servere specializate - un set de servere Enterprise .NET, continuatoare ale lui SQL Server 2000, Exchange 2000, BizTalk 2000, etc, care pun la
1 2

Denitia ocial Microsoft, martie 2005, www.microsoft.com/net/basics/glossary.asp a Serviciilor Web - aplicatii care ofer servicii folosind Web-ul ca modalitate de acces. a

10

CURS 1. PLATFORMA MICROSOFT .NET dispozitie functionaliti diverse pentru stocarea bazelor de date, email, at 3 aplicatii B2B . 3. Servicii Web - cel mai notabil exemplu este .NET Passport - un mod prin care utilizatorii se pot autentica pe site-urile Web vizitate, folosind un singur nume i o parol pentru toate. Dei nu este omiprezent, multe s a s site-uri folosesc pentru a uura accesul utilizatorilor. l s 4. Dispozitive - noi dispozitive nonPC, programabile prin .NET Compact Framework, o versiune redus a lui .NET Framework: Pocket PC Phone a Edition, Smartphone, Tablet PC, Smart Display, XBox, set-top boxes, etc.

Motivul pentru care Microsoft a trecut la dezvoltarea acestei platforme este maturizarea industriei software, accentunduse urmtoarele directii: a a 1. Aplicatiile distribuite - sunt din ce ce mai numeroase aplicatiile de n tip client / server sau cele pe mai multe nivele (ntier). Tehnologiile distribuite actuale cer de multe ori o mare anitate fat de productor a a i prezint o carent acut a interoperrii cu Web-ul. Viziunea actual s a a a a a se deprteaz de cea de tip client/server ctre una care calculatoare, a a a n dispozitive inteligente i servicii conlucreaz pentru atingerea scopurilor s a propuse. Toate acestea se fac deja folosind standarde Internet neproprietare (HTTP, XML, SOAP). 2. Dezvoltarea orientat pe componente - este de mult timp cerut sima a plicarea integrrii componentelor software dezvoltate de diferiti proa ductori. COM (Component Object Model) a realizat acest deziderat, a dar dezvoltarea i distribuirea aplicatiilor COM este prea complex. s a Microsoft .NET pune la dispozitie un mod mai simplu de a dezvolta i s a distribui componente. 3. Modicri ale paradigmei Web - de-a lungul timpului sau adus a mbuntiri tehnologiilor Web pentru a simplica dezvoltarea aplicatiilor. a at ultimii ani, dezvoltarea aplicatiilor Web sa mutat de la prezentare In (HTML i adiacente) ctre capacitate sporit de programare (XML i s a a s SOAP). 4. Alti factori de maturizare a industriei software - reprezint contienti a s zarea cererilor de interoperabilitate, scalabilitate, disponibilitate; unul din dezideratele .NET este de a oferi toate acestea.
Bussiness to Bussiness - Comert electronic ntre parteneri de afaceri, diferit de a comertul electronic ntre client i afacere (Bussiness to Consumer B2C). s
3

1.2. ARHITECTURA PLATFORMEI MICROSOFT .NET

11

1.2

Arhitectura platformei Microsoft .NET

Figura 1.1: Arhitectura .NET Figura 1.1 schematizeaz arhitectura platformei Microsoft .NET. Orice a program scris ntr-unul din limbajele .NET este compilat Microsoft Intern mediate Language (MSIL), concordant cu Common Language Specican a tion (CLS). Aceste limbaje sunt sprijinite de o bogat colectie de biblioteci a de clase, ce pun la dispozitie faciliti pentru dezvoltarea de Web Forms, at Windows Forms i Web Services. Comunicarea dintre aplicatii i servicii se s s face pe baza unor clase de manipulare XML i a datelor, ceea ce sprijin s a dezvoltarea aplicatiilor cu arhitectur n-tier. Base Class Library exist pen a a tru a asigura functionalitate de nivel sczut, precum operatii de I/O, re de a executie, lucrul cu iruri de caractere, comunicatie prin retea, etc. Aceste s clase sunt reunite sub numele de .NET Framework Class Library, ce permite dezvoltarea rapid a aplicatiilor. La baza tuturor se a cea mai impora a tant component a lui .NET Framework - Common Language Runtime, a a care rspunde de executia ecrui program. Mediul de dezvoltare Visual a a Studio .NET nu este absolut necesar pentru a dezvolta aplicatii (exist i a s alternative open-source pentru el), dar datorit extensibilitii este cel mai a at

12

CURS 1. PLATFORMA MICROSOFT .NET

bine adaptat pentru crearea aplicatiilor. Evident, nivelul inferior este rezer vat sistemului de operare. Trebuie spus c platforma .NET nu este exclusiv a dezvoltat pentru sistemul de operare Microsoft Windows, ci i pentru arome a s 4 de Unix (FreeBSD sau Linux - a se vedea proiectul Mono ).

1.3
1.3.1

Componente ale lui .NET Framework


Microsoft Intermediate Language

Una din uneltele de care dispune ingineria software este abstractizarea. Deseori vrem s ferim utilizatorul de detalii, s punem la dispozitia altora a a o interfat simpl, care s permit atingerea scopului, fr a necesare a a a a aa cunoaterea tuturor detaliilor. Dac interfata rmne neschimbat, se pot s a a a a modica toate detaliile interne, fr a afecta actiunile celorlati beneciari ai aa codului. cazul limbajelor de programare, s-a ajuns treptat la crearea unor nivele In de abstractizare a codului rezultat la compilare, precum p-code (cel produs de compilatorul Pascal-P) i bytecode (binecunoscut celor care au lucrat Java). s n Bytecode-ul Java, generat prin compilarea unui ier surs, este cod scris s a ntrun limbaj intermediar care suport POO. Bytecod-ul este acelai timp o a n s abstractizare care permite executarea codului Java, indiferent de platforma int, atta timp ct aceast platform are implementat o main virtual t a a a a a a s a a Java, capabil s traduc mai departe ierul class cod nativ. a a a s n Microsoft a realizat i el propria sa abstractizare de limbaj, aceasta nus mindu-se Common Intermediate Language. Dei exist mai multe limbaje s a de programare de nivel nalt (C#, Managed C++, Visual Basic .NET, etc), la compilare toate vor produce cod acelai limbaj intermediar: Microsoft n s Intermediate Language (MSIL, sau IL pe scurt). Asemntor cu bytecod-ul, a a IL are trsturi OO, precum abstractizarea datelor, motenirea, polimorsaa s mul, sau concepte care s-au dovedit a extrem de necesare, precum exceptiile sau evenimentele. De remarcat c aceast abstractizare de limbaj permite a a rularea aplicatiilor independent de platform (cu aceeai conditie ca la Java: a s s existe o main virtual pentru acea platform). a s a a a

1.3.2

Common Language Specication

Unul din scopurile .NET este de a sprijini integrarea limbajelor astfel at nc programele, dei scrise diferite limbaje, pot interopera, folosind din plin s n motenirea, polimorsmul, s ncapsularea, exceptiile, etc. Dar limbajele nu
4

www.go-mono.com

1.3. COMPONENTE ALE LUI .NET FRAMEWORK

13

sunt identice: unele suport supra arcarea operatorilor (Managed C++, a nc C#), altele nu (Visual Basic .NET); unele sunt case sensitive, altele nu. Pentru a se asigua totui interoperabilitatea codului scris diferite limbaje, s n Microsoft a publicat Common Language Specication (CLS), un subset al lui CTS (Common Type System, vezi 1.3.4), continnd specicatii de reguli a necesare pentru integrarea limbajelor. CLS denete un set de reguli pentru compilatoarele .NET, asigurnd s a faptul c ecare compilator va genera cod care interfereaz cu platforma a a (mai exact, cu CLRul vezi mai jos) ntr-un mod independent de limbajul surs. Obiectele i tipurile create diferite limbaje pot interactiona fr a s n aa probleme suplimentare. Combinatia CTS/CLS realizeaz de fapt interoper a area limbajelor. Concret, se poate ca o clas scris C# s e motenit a a n a s a de o clas scris Visual Basic care arunc exceptii ce sunt prinse de cod a a n a scris C++ sau J#. n

1.3.3

Common Language Runtime

CLR este de departe cea mai important parte component a lui .NET a a Framework. Este responsabil cu managementul i executia codului scris s n limbaje .NET, aat format IL; este foarte similar cu Java Virtual Machine. n CLR instantiaz obiectele, face vericri de securitate, depune obiectele a a n memorie, disponibilizeaz memoria prin garbage collection. a urma compilrii unei aplicatii rezult un ier cu extensia exe, dar In a a s care nu este un executabil portabil Windows, ci un executabil portabil .NET (.NET PE). Acest cod nu este deci un executabil nativ, ci se va rula de ctre CLR, a ntocmai cum un ier class este rulat cadrul JVM. CLR s n folosete tehnologia compilrii JIT - o implementare de main virtual, s a s a a n care o metod sau o functie, momentul care este apelat pentru prima a n n a oar, este tradus cod main. Codul translatat este depus a a n s a ntr-un cache, evitnd-se astfel recompilarea ulterioar. Exist 3 tipuri de compilatoare a a a JIT: 1. Normal JIT - a se vedea descrierea de mai sus. 2. Pre-JIT - compileaz a ntregul cod cod nativ singur dat. mod n a a In normal este folosit la instalri. a 3. Econo-JIT - se foloete pe dispozitive cu resurse limitate. Compileaz s s a codul IL bit cu bit, elibernd resursele folosite de codul nativ ce este a stocat cache. n

14

CURS 1. PLATFORMA MICROSOFT .NET

esent, activitatea unui compilator JIT este destinat a In a a mbunti pera at formanta executiei, ca alternativ la compilarea repetat a aceleiai buci a a s at de cod cazul unor apelri multiple. Unul din avantajele mecanismului JIT n a apare clipa care codul, o dat ce a fost compilat, se execut pe diverse n n a a procesoare; dac maina virtual este bine adaptat la noua platform, atunci a s a a a acest cod va benecia de toate optimizrile posibile, fr a mai nevoie a aa recompilarea lui (precum C++, de exemplu). n

1.3.4

Common Type System

Pentru a asigura interoperabilitatea limbajelor din .NET Framework, o clas scris C# trebuie s e echivalent cu una scris VB.NET, o a a n a a a n interfat scris Managed C++ trebuie s e perfect utilizabil Managed a a n a a n Cobol. Toate limbajele care fac parte din pleiada .NET trebuie s aibe un set a comun de concepte pentru a putea integrate. Modul care acest deziderat n s-a transformat realitate se numete Common Type System (CTS); orice n s limbaj trebuie s recunoasc i s poat manipula nite tipuri comune. a as a a s O scurt descriere a unor faciliti comune (ce vor exhaustiv enumerate a at i tratate cadrul prezentrii limbajului C#): s n a 1. Tipuri valoare - general, CLR-ul (care se ocup de managementul i n a s executia codului IL, vezi mai jos) suport dou tipuri diferite: tipuri a a valoare i tipuri referint. Tipurile valoare reprezint tipuri alocate s a a pe stiv i nu pot avea valoare de null. Tipurile valoare includ tipuas rile primitive, structuri i enumerri. Datorit faptului c de regul s a a a a au dimensiuni mici i sunt alocate pe stiv, se manipuleaza ecient, s a reducnd overhead-ul cerut de mecanismul de garbage collection. a 2. Tipuri referint - se folosesc dac variabilele de un anumit tip cer a a resurse de memorie semnicative. Variabilele de tip referinta contin adrese de memorie heap i pot null. Transferul parametrilor se face s rapid, dar referintele induc un cost suplimentar datorit mecanismului a de garbage collection. 3. Boxing i unboxing - motivul pentru care exist tipuri primitive este s a a orice variabil .NET este acelai ca i Java: performanta. Ins s s n a n compatibil cu clasa Object, rdcina ierarhiei existente .NET. De a a a n exemplu, int este un alias pentru System.Int32, care se deriveaz din a System.ValueType. Tipurile valoare se stocheaz pe stiv, dar pot a a oricnd convertite a ntr-un tip referint memorat heap; acest mecana n ism se numete boxing. De exemplu: s

1.3. COMPONENTE ALE LUI .NET FRAMEWORK int i = 1; //i - un tip valoare object box = i; //box - un obiect referinta

15

Cnd se face boxing, se obtine un obiect care poate gestionat la fel a ca oricare altul, fcnduse abstractie de originea lui. a a Inversa boxing-ului este unboxing-ul, prin care se poate converti un obiect tipul valoare echivalent, ca mai jos: n int j = (int)box; unde operatorul de conversie este sucient pentru a converti de la un obiect la o variabil de tip valoare. a 4. Clase, proprieti, indexatori - platforma .NET suport pe deplin proat a gramarea orientat pe obiecte, concepte legate de obiecte ( a ncapsularea, motenirea, polimorsmul) sau trsturi legate de clase (metode, cms aa a puri, membri statici, vizibilitate, accesibilitate, tipuri interioare, etc). De asemenea se includ trsturi precum proprieti, indexatori, eveniaa at mente. 5. Interfete - reprezint acelai concept precum clasele abstracte din C++ a s (dar continnd doar functii virtuale pure), sau interfetele Java. O a clas care se deriveaz dintr-o interfat trebuie s implementeze toate a a a a metodele acelei interfete. Se permite implementarea simultan a mai a multor interfete ( rest motenirea claselor este simpl). n s a 6. Delegati - inspirati de pointerii la functii din C, ce permit programarea generic. Reprezint versiunea sigur a pointerilor ctre functii din a a a a C/C++ i sunt mecanismul prin care se trateaz evenimentele. s a Exist numeroase componente ale lui CLR care fac cea mai impora l tant parte a lui .NET Framework: metadata, assemblies, assembly cache, a reection, garbage collection.

1.3.5

Metadata

Metadata nseamn general date despre date. cazul .NET, ea a n In reprezint informatii destinate a citite de ctre main, nu de utilizatorul a a s a uman. Este stocat a mpreun cu codul pe care descrie. Pe baza metadatei, a l CLR tie cum s instantieze obiectele, cum s le apeleze metodele, cum s s a a a acceseze proprietile. Printr-un mecanism numit reection, o aplicatie (nu at

16

CURS 1. PLATFORMA MICROSOFT .NET

neaprat CLR) poate s interogheze aceast metadat i s ae ce expune a a a as a un obiect. Mai pe larg, metadata contine o declaratie a ecrui tip i cte o declaratie a s a pentru ecare metod, cmp, proprietate, eveniment al tipului respectiv. a a Pentru ecare metod implementat, metadata contine informatie care pera a mite arctorului clasei respective s localizeze corpul metodei. De asemena nc a a mai poate contine declaratii despre cultura aplicatiei respective, adic despre a localizarea ei (limba folosit partea de interfat utilizator). Mecanismul a n a prin care aceste informatii se folosesc pe parcursul rulrii de ctre aplicatii a a se numete reection. s

1.3.6

Assemblies

Un assembly reprezint un bloc functional al unei aplicatii .NET. El a formeaz unitatea fundamental de distribuire, versionare, reutilizare i pera a s misiuni de securitate. La runtime, un tip de date exist interiorul unui a n assembly (i nu poate exista exteriorul acestuia). Un assembly contine s n metadate care sunt folosite de ctre CLR. Scopul acestor assemblies este a s se asigure dezvoltarea softului mod plug-and-play. Dar metadatele a n nu sunt suciente pentru acest lucru; mai sunt necesare i manifestele. s Un manifest reprezint metadate despre assembly-ul care gzduiete tia a s purile de date. Contine numele assembly-ului, informatia despre versiune, referiri la alte assemblies, o list a tipurilor assembly, permisiuni de secua n ritate i altele. s Un assembly care este artit mp ntre mai multe aplicatii are de asemenea un shared name. Aceast informatie care este unic este optional, neaprnd a a a aa manifestul unui assembly daca acesta nu a fost gndit ca o aplicatie parn a tajat. a Deoarece un assembly contine date care descriu, instalarea lui poate l fcut copiind assemblyul directorul destinatie dorit. Cnd se dorete rua a n a s larea unei aplicatii continute assembly, maniferstul va instrui mediul .NET n despre modulele care sunt continute assembly. Sunt folosite de asemenea n i referintele ctre orice assembly extern de care are nevoie aplicatia. s a Versionarea este un aspect deosebit de important pentru a se evita aa s numitul DLL Hell. Scenariile precedente erau de tipul: se instaleaza o aplicatie care aduce nite iere .dll necesare pentru functionare. Ulterior, o s s alt aplicatie care se instaleaz suprascrie aceste iere (sau mcar unul din a a s a ele) cu o versiune mai nou, dar cu care vechea aplicatie nu mai functioneaz a a corespunztor. Reinstalarea vechii aplicatii nu rezolv problema, deoarece a a a doua aplicatie nu va functiona. Dei ierele dll contin informatie relativ s s la versiune interiorul lor, ea nu este folosit de ctre sistemul de operare, n a a

1.4. TRASATURI ALE PLATFORMEI .NET

17

ceea ce duce la probleme. O solutie la aceast dilem ar instalarea ierelor a a s dll directorul aplicatiei, dar acest mod ar disprea reutilizarea acestor n n a biblioteci.

1.3.7

Assembly cache

Assembly cache este un director aat mod normal directorul \Winnt n n \Assemblies. Atunci cnd un assembly este instalat pe o main, el va a s a adugat assembly cache. Dac un assembly este descrcat de pe Internet, a n a a el va stocat acest assembly cache, n ntr-o zon tranzient. Aplicatiile a a instalate vor avea assemblies ntr-un assembly cache global. acest assembly cache vor exista versiuni multiple ale aceluiai assemIn s bly. Dac programul de instalare este scris corect, va evita suprascrierea a assembly-urilor deja existente (i care functioneaz perfect cu acplicatiile s a instalate), adugnd doar noul assembly. Este un mod de rezolvare a proba a lemei DLL Hell, unde suprascrierea unei biblioteci dinamice cu o variant a mai nou putea duce la nefunctionarea corespunztoare a aplicatiilor antea a rior instalate. CLR este cel care decide, pe baza informatiilor din manifest, care este versiunea corect de assembly de care o aplicatie are nevoie. Acest a mecanism pune capt unei epoci de trist amintire pentru programatori i a a s utilizatori.

1.3.8

Garbage collection

Managementul memoriei este una din sarcinile cele mai consumatoare de resurse umane. Garbage collection este mecanismul care se declaneaz s a atunci cnd alocatorul de memorie rspunde negativ la o cerere de alocare de a a memorie. Implementarea este de tip mark and sweep: se presupune initial c toat memoria alocat se poate disponibiliza, dupa care se determin a a a a care din obiecte sunt referite de variabilele aplicatiei; cele care nu mai sunt referite sunt dealocate, celelalte zone de memorie sunt compactate. Obiectele a cror dimensiune de memorie este mai mare dect un anumit prag nu mai a a sunt mutate, pentru a nu crete semnicativ penalizarea de performant. s a general, CLR este cel care se ocup de apelarea mecanismului de In a garbage collection. Totui, la dorint, programatorul poate cere rularea lui. s a

1.4

Trsturi ale platformei .NET a a

Prin prisma celor prezentate pna acum putem rezuma urmtoarele tra a a sturi: a

18

CURS 1. PLATFORMA MICROSOFT .NET Dezvoltarea multilimbaj: Deoarece exist mai multe limbaje pentru a aceast platform, este mai uor de implementat prti specice lima a s a n bajele cele mai adecvate. Numarul limbajelor curent implementate este mai mare dect 10. Aceast dezvoltare are vedere i debugging-ul a a n s aplicatiilor dezvoltate mai multe limbaje. n Independenta de procesor i de platform: IL este independent s a de procesor. O dat scris i compilat, orice aplicatie .NET (al crei a as a a management este fcut de ctre CLR) poate rulat pe orice platform. a a a a Datorit CLR-ului, aplicatia este izolat de particularitile hardware a a at sau ale sistemului de operare. Managementul automat al memoriei: Problemele de tipul memory leakage nu mai trebuie s preocupe programatorii; overhead-ul ina dus de ctre mecanismul de garbage collection este suportabil, ind a implementat sisteme mult mai timpurii. n Suportul pentru versionare: Ca o lectie aat din perioada de nvt a DLL Hell, versionarea este acum un aspect de care se ine cont. t Dac o aplicatie a fost dezvoltat i testat folosind anumite compoa as a nente, instalarea unei componente de versiune mai nou nu va atenta la a buna functionare a aplicatiei discutie: cele dou versiuni vor coexista n a panic, alegerea lor ind fcut pe baza manifestelor. s a a Sprijinirea standardelor deschise: Nu toate dispozitivele ruleaz a sisteme de operare Microsoft sau folosesc procesoare Intel. Din aceast a cauz orice este strns legat de acestea este evitat. Se folosete XML i a a s s cel mai vizibil descendent al acestuia, SOAP. Deoarece SOAP este un protocol simplu, bazat pe text, foarte asemntor cu HTTP5 , el poate a a trece uor de rewall-uri, spre deosebire de DCOM sau CORBA. s Distribuirea uoar: Actualmente instalarea unei aplicatii sub Wins a dows nseamn copierea unor iere nite directoare anume, moa s n s dicarea unor valori regitri, instalare de componente COM, etc. n s Dezinstalarea complet a unei aplicatii este in majoritatea cazurilor a o utopie. Aplicatiile .NET, datorit metadatelor i reectrii trec de a s a aceste probleme. Se dorete ca instalarea unei aplicatii s nu s a nsemne mai mult dect copierea ierelor necesare a s ntr-un director, iar dezinstalarea aplicatiei s se fac prin tergerea acelui director. a a s Arhitectur distribuit: Noua losoe este de a asigura accesul la a a servicii Web distribuite; acestea conlucreaz la obtinerea informatiei a
5

Folosit la transmiterea paginilor Web pe Internet

1.4. TRASATURI ALE PLATFORMEI .NET

19

dorite. Platforma .NET asigur suport i unelte pentru realizarea acesa s tui tip de aplicatii. Interoperabilitate cu codul unmanaged: Codul unmanaged se refer la cod care nu se a totalitate sub controlul CLR. El a a n este rulat de CLR, dar nu beneciaz de CTS sau garbage collection. a Este vorba de apelurile functiilor din DLL-uri, folosirea componentelor COM, sau folosirea de ctre o component COM a unei componente a a .NET. Codul existent se poate folosi continuare. n Securitate: Aplicatiile bazate pe componente distribuite cer automat securitate. Modalitatea actual de securizare, bazat pe drepturile a a contului utilizatorului, sau cel din Java, care codul suspectat este n rulat ntr-un sandbox, fr acces la resursele critice este aa nlocuit n .NET de un control mai n, pe baza metadatelor din assembly (zona din care provine - ex. Internet, intranet, maina local, etc) precum i s a s a politicilor de securitate ce se pot seta.

20

CURS 1. PLATFORMA MICROSOFT .NET

Curs 2 Vedere general asupra a limbajului C#. Tipuri predenite. Tablouri. Siruri de caractere
2.1 Vedere general asupra limbajului C# a

C#1 este un limbaj de programare modern, simplu, obiectorientat. Este foarte asemntor cu Java i C++, motiv pentru care curba de aare este a a s nvt foarte lin. a Un prim exemplu de program, care contine o serie de elemente ce se vor alni continuare, este: nt n using System; class HelloWorld { public static void Main() { Console.WriteLine(Hello world!); } } Prima linie using System; este o directiv care specic faptul c se vor a a a 2 folosi clasele care sunt incluse spatiul de nume System; un spatiu de n nume este o colectie de tipuri sau o grupare de alte spatii de nume care pot
1 2

# se pronunt sharp a Eng: namespace

21

22

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

folosite ntr-un program (detalii vor date mai trziu). cazul de fat, a In a clasa care este folosit din acest spatiu de nume este Console. Mai departe, a orice program este continut ntro clas, cazul nostru HelloWorld. Punctul a n de intrare aplicatie este metoda Main, care nu preia acest exemplu nici n n un argument din linia de comand i nu returneaz explicit un indicator de as a stare a terminrii. a Pentru operatiile de intrareieire cu consola, se folosete clasa Console s s din spatiul de nume System; pentru aceast clas se apeleaz metoda static a a a a WriteLine, care aeaz pe ecran mesajul, dup care face trecerea la linie s a a nou. a O variant uor modicat a programului de mai sus, care se salut a s a n a persoanele ale cror nume este transmis prin linia de comand: a a using System; class HelloWorld { public static void Main( String[] args) { for( int i=0; i<args.Length; i++) { Console.WriteLine( Hello {0}, args[i]); } } } exemplul de mai sus metoda principal preia o list de parametri In a a transmii din linia de comand (un ir de obiecte de tip String) i va aa s a s s s pentru ecare nume Hello urmat de numele de indice i (numerotarea parametrilor ncepe de la 0). Constructia {0} va nlocuit cu primul a argument care urmeaz dup Hello {0}. La executarea programului de a a mai sus forma: HelloWorld Ana Dan, se va aa pe ecran: n s Hello Ana Hello Dan Metoda Main poate s returneze o valoare a ntreag, care s e folosit a a a de ctre sistemul de operare pentru a semnala dac procesul sa a a ncheiat cu succes sau nu3 . Mentionm faptul c limbajul este casesensitive4 . a a Ca metode de notare Microsoft recomand folosirea urmtoarelor dou a a a conventii:
3 4

De exemplu iere de comenzi prin testarea variabilei de mediu ERRORLEVEL n s Face distinctie ntre litere mari i mici s

2.2. TIPURI DE DATE

23

Conventie Pascal, care prima liter a ecrui cuvnt se scrie ca liter n a a a a mare conventia tip cmil este la fel ca precedenta, dar primul caracter al a a primului cuvnt nu este scris ca liter mare. a a general, conventia tip Pascal este folosit pentru tot ce este vizibil din exIn a teriorul unei clase, precum clase, metode, proprieti, etc. Doar parametrii at metodelor se scriu cu conventia cmil. Se recomand evitarea folosirii a a a notatiei ungare (numele unei entiti trebuie s se refere la semantica ei, at a nu la tipul de reprezentare). Cuvintele cheie (cuvinte rezervate care nu pot folosite ca i identicas tori) din C# sunt date tabelul 2.1: n Tabelul 2.1: Cuvinte cheie C# n abstract case const do explicit oat implicit is null params ref sizeof this uint using base catch continue double extern for in lock object private return static throw ulong virtual bool char decimal else false foreach int long operator protected sbyte string true unchecked void break checked default enum nally goto interface namespace out public sealed struct try unsafe while byte class delegate event xed if internal new override readonly short switch typeof ushort

2.2

Tipuri de date

C# prezint dou tipuri de date: tipuri valoare i tipuri referint. Tipua a s a 5 rile valoare includ tipurile simple (ex. char, int, oat) , tipurile enumerare
5

De fapt acestea sunt structuri, prezentate alt curs n

24

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

i structur i au ca principale caracteristici faptul c ele contin direct datele s as a referite i sunt alocate pe stiv sau inline s a ntrun struct. Tipurile referint a includ tipurile clas, interfat, delegat i tablou, toate avnd proprietatea a a s a c variabilele de acest tip stocheaz referinte ctre obiecte. Demn de rea a a marcat este c toate tipurile de date sunt derivate (direct sau nu) din tipul a System.Object, punnd astfel la dispozitie un mod unitar de tratare a lor. a

2.2.1

Tipuri predenite

C# contine un set de tipuri predenite, pentru care nu este necesar in a cluderea vreunui spatiu de nume via directiva using: string, object, tipurile ntregi cu semn i fr semn, tipuri numerice virgul mobil, tipurile bool s aa n a a i decimal. Tipul string este folosit pentru manipularea irurilor de caractere s s codicate Unicode; continutul obiectelor de tip string nu se poate modi 6 ca . Clasa object este rdcina ierarhiei de clase din .NET, la care orice tip a a (inclusiv un tip valoare) poate convertit. Tipul bool este folosit pentru a reprezenta valorile logice true i false. s Tipul char este folosit pentru a reprezenta caractere Unicode, reprezentate pe 16 biti. Tipul decimal este folosit pentru calcule care erorile determinate n de reprezentarea virgul mobil sunt inacceptabile, el punnd la dispozitie n a a a 28 de cifre zecimale semnicative. Tabelul de mai jos contine lista tipurilor predenite, artnd totodat aa a cum se scriu valorile corespunztoare: a Tabelul 2.2: Tipuri predenite.

Tip object string sbyte short int long byte ushort uint

Descriere rdacina oricrui tip a a o secvent de caractere Unicode a tip ntreg cu semn, pe 8 biti tip ntreg cu semn, pe 16 biti tip ntreg cu semn, pe 16 biti tip ntreg cu semn, pe 64 biti tip ntreg fr semn, pe 8 biti aa tip ntreg fr semn, pe 16 biti aa tip ntreg fr semn, pe 32 biti aa

Exemplu object a = null; string s = hello; sbyte val = 12; short val = 12; int val = 12; long val1 = 12; long val2=34L; byte val = 12; ushort val = 12; uint val = 12;

Spunem despre un string c este invariabil - engl. immutable a

2.2. TIPURI DE DATE Tabelul 2.2 (continuare) Tip Descriere Exemplu ulong tip ntreg fr semn, pe 64 de biti aa ulong val1=12; ulong val2=34U; ulong val3=56L; ulong va;4=76UL; oat tip cu virgul mobil, simpl precizie a a a oat val=1.23F; double tip virgul mobil, dubl precizie n a a a double val1=1.23; double val2=4.56D; bool tip boolean bool val1=false; bool val2=true; char tip caracter din setul Unicode char val=h; decimal tip zecimal cu 28 de cifre semnicative decimal val=1.23M;

25

Fiecare din tipurile predenite este un alias pentru un tip pus la dispozitie de sistem. De exemplu, string este alias pentru clasa System.String, int este alias pentru System.Int32.

2.2.2

Tipuri valoare

C# pune programatorului la dispozitie tipuri valoare, care sunt e struc turi, e enumerri. Exist un set predenit de structuri numite tipuri simple, a a identicate prin cuvinte rezervate. Un tip simplu este e de tip numeric7 , e boolean. Tipurile numerice sunt tipuri ntregi, virgul mobil sau decimal. n a a Tipurile intregi sunt sbyte, byte, short, ushort, int, uint, long, ulong, char; cele virgul mobil sunt oat i double. Tipurile enumerare se pot deni n a a s de ctre utilizator. a Toate tipurile valoare deriveaz din clasa System.ValueType, care la rndul a a ei este derivat din clasa object (alias pentru System.Object). Nu este a posibil ca dintrun tip valoare s se deriveze. O variabil de tip valoare a a ntotdeauna trebuie s contin o valoare (nu poate null, spre deosebire a a de tipurile referint). Atribuirea pentru un astfel de tip a nseamn copierea a valorii (clonarea) dintro parte alta. n Structuri Un tip structur este un tip valoare care poate s contin declaratii de a a a constante, cmpuri, metode, proprieti, indexatori, operatori, constructori a at sau tipuri imbricate. Vor descrise n ntro sectiune urmtoare. a
7

Engl: integral type

26

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Tipuri simple C# pune are predenit un set de tipuri structuri, numite tipuri simple. Tipurile simple sunt identicate prin cuvinte rezervate, dar acestea reprezint a doar aliasuri pentru tipurile struct corespunztoare din spatiul de nume a System; corespondenta este dat tabelul de mai jos: a n Tabelul 2.3: Tipuri simple i corespondentele lor cu tis purile din spatiul de nume System. Tabelul 2.3 Cuvnt rezervat Tipul alias a sbyte System.SByte byte System.Byte short System.Int16 ushort System.UInt16 int System.Int32 uint System.UInt32 long System.Int64 ulong System.UInt64 char System.Char oat System.Single double System.Double bool System.Boolean decimal System.Decimal Deoarece un tip simplu este un alias pentru un tip struct, orice tip simplu are membri. De exemplu, tipul int, ind un tip alias pentru System.Int32, urmtoarele declaratii sunt legale: a int i = int.MaxValue; //constanta System.Int32.MaxValue string s = i.ToString(); //metoda System.Int32.ToString() string t = 3.ToString(); //idem //double d = Double.ParseDouble("3.14"); Tipuri ntregi C# suport nou tipuri a a ntregi: sbyte, byte, short, ushort, int, uint, long, ulong i char. Acestea au urmtoarele dimensiuni i domeniu de valori: s a s sbyte reprezint tip cu semn pe 8 biti, cu valori de la -128 la 127; a

2.2. TIPURI DE DATE byte reprezint tip fr semn pe 8 biti, a aa ntre 0 i 255; s short reprezint tip cu semn pe 16 biti, a ntre -32768 i 32767; s ushort reprezint tip fr semn pe 16 biti, a aa ntre 0 i 65535; s int reprezint tip cu semn pe 32 de biti, a ntre 231 i 231 1; s uint reprezint tip fr semn pe 32 de biti, a aa ntre 0 i 232 1; s long reprezint tip cu semn pe 64 de biti, a ntre 263 i 263 1; s ulong reprezint tip fr semn pe 64 de biti, a aa ntre 0 i 264 1; s

27

char reprezint tip fr semn pe 16 biti, cu valori a aa ntre 0 i 65535. s Multimea valorilor posibile pentru char corespunde setului de caractere Unicode. Reprezentarea unei variable de tip ntreg se poate face sub form de ir a s de cifre zecimale sau hexazecimale, urmate eventual de un prex. Numerele exprimate hexazecimal sunt prexate cu 0x sau 0X. Regulile dup n a care se asigneaz un tip pentru o valoare sunt: a 1. dac irul de cifre nu are un sux, atunci el este considerat ca ind a s primul tip care poate s contin valoarea dat: int, uint, long, ulong; a a a 2. dac irul de cifre are suxul u sau U, el este considerat ca ind din as primul tip care poate s contin valoarea dat: uint, ulong; a a a 3. dac irul de cifre are suxul l sau L, el este considerat ca ind din a s primul tip care poate s contin valoarea dat: long, ulong; a a a 4. dac irul de cifre are suxul ul, uL, Ul, UL, lu, lU, Lu, LU, el este a s considerat ca ind din tipul ulong. Dac o valoare este afara domeniului lui ulong, apare o eroare la coma n pilare. Literalii de tip caracter au forma: car unde car poate exprimat printr un caracter, printro secvent escape simpl, secvent escape hexazecimal a a a a prima form poate folosit orice caracter sau secvent escape Unicode. In a a exceptnd apostrof, backslash i new line. Secvent escape simpl poate : a s a a \, \", \\, \0, \a, \b, \f, \n, \r, \t, \v, cu semnicatiile cunoscute din C++. O secvent escape hexazecimal a a ncepe cu \x urmat de 14 cifre hexa. Dei ca reprezentare, char este identic cu ushort, nu toate operatiile ce de s pot efectua cu ushort sunt valabile i pentru char. s

28

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

cazul care o operatie aritmetic produce un rezultat care nu poate In n a reprezentat tipul destinatie, comportamentul depinde de utilizarea operan torilor sau a declaratiilor checked i unchecked (care se pot utiliza surs sau s n a din linia de compilare): context checked, o eroare de depire duce la arunn as context unchecked, carea unei exceptii de tip System.OverowException. In eroarea de depire este ignorat, iar bitii semnicativi care nu mai as a ncap n reprezentare sunt eliminati. Exemplu: byte i=255; unchecked { i++; }//i va avea valoarea 0, nu se semnaleaza eroare checked { i=255; i++;//se va arunca exceptie System.OverflowException } Pentru expresiile aritmetice care contin operatorii ++, - -, +, - (unar i bi s nar), *, / i care nu sunt continute interiorul unui bloc de tip checked, s n comportamentul este specicat prin intermediul optiunii /checked[+|] dat din linia de comand pentru compilator. a Tipuri virgul mobil n a a Sunt prezente 2 tipuri numerice virgul mobil: oat i double. Tipun a a s rile sunt reprezentate folosind precizie de 32, respectivi 64 de biti, folosind formatul IEC 60559, care permit reprezentarea valorilor de 0 pozitiv i 0 s negativ (se comport la fel, dar anumite operatii duc la obtinerea acestor a dou valori), + i (obtinute prin artirea unui numr strict pozia s mp a tiv, respectiv strict negativ la 0), a valorii NotaNumber (NaN) (obtinut a prin operatii virgul mobil invalide, de exemplu 0/0 sau 1), precum n a a i un set nit de numere8 . Tipul oat poate reprezenta valori cuprinse s ntre 45 38 1.5 10 i 3.4 10 (i din domeniul negativ corespunztor), cu o pres s a cizie de 7 cifre. Double poate reprezenta valori cuprinse ntre 5.0 10324 i s 308 1.7 10 cu o precizie de 15-16 cifre. Operatiile cu oating point nu duc niciodat la aparitia de exceptii, dar a ele pot duce, caz de operatii invalide, la valori 0, innit sau NaN. n
8

Pentru detalii vezi [4], pag. 93.

2.2. TIPURI DE DATE

29

Literalii care specic un numr reprezentat virgul mobil au forma: a a n a a literalreal:: cifre-zecimale . cifre-zecimale exponentoptional sux-de-tip-realoptional . cifre-zecimale exponentoptional sux-de-tip-realoptional cifre-zecimale exponent sux-de-tip-realoptional cifre-zecimale sux-de-tip-real, unde exponent:: e semnoptional cifre-zecimale E semnoptional cifre-zecimale, semn este + sau -, sux-de-tip-real este F, f, D, d. Dac nici un sux de tip a real nu este specicat, atunci literalul dat este de tip double. Suxul f sau F specic tip oat, d sau D specic double. Dac literalul specicat nu a a a poate reprezentat tipul precizat, apare eroare de compilare. n Tipul decimal Este un tip de date reprezentat pe 128 de biti, gndit a folosit calcule a n nanciare sau care necesit precizie mai mare. Poate reprezenta valori aate a intervalul 1.0 1028 i 7.9 1028 , cu 28 de cifre semnicative. Acest tip n s nu poate reprezenta zero cu semn, innit sau NaN. Dac urma operatiilor, a n un numr este prea mic pentru a putea reprezentat ca decimal, atunci el a este fcut 0, iar dac este prea mare, rezult o exceptie. Diferenta principal a a a a fat de tipurile virgul mobil este c are o precizie mai mare, dar un a n a a a domeniu de reprezentare mai mic. Din cauza aceasta, nu se fac conversii implicite ntre nici un tip virgul mobil i decimal i nu este posibil n a a s s a mixarea variabilelor de acest tip ntr-o expresie, fr conversii explicite. aa Literalii de acest tip se exprim folosind ca sux-de-tip-real caracterele m a sau M. Dac valoarea specicat nu poate reprezentat prin tipul decimal, a a a apare o eroare la compilare. Tipul bool Este folosit pentru reprezentarea valorilor de adevr true i false. Literalii a s care se pot folosi sunt true i false. Nu exist conversii standard s a ntre bool i nici un alt tip. s

2.2.3

Tipul enumerare

Tipul enumerare este un tip valoare, construit pentru a permite declararea constantelor nrudite, ntro manier clar i sigur din punct de vedere al a as a

30

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

tipului. Un exemplu este: using System; public class Draw { public enum LineStyle { Solid Dotted, DotDash } public void DrawLine(int x1, int y1, int x2, int y2, LineStyle lineStyle) { if (lineStyle == LineStyle.Solid) { //cod desenare linie continua } else if (lineStyle == LineStyle.Dotted) { //cod desenare linie punctata } else if (lineStyle == LineStyle.DotDash) { //cod desenare segment linie-punct } else { throw new ArgumentException(Invalid line style); } } } class Test { public static void Main() { Draw draw = new Draw();

2.2. TIPURI DE DATE draw.DrawLine(0, 0, 10, 10, Draw.LineStyle.Solid); draw.DrawLine(0, 0, 10, 10, (Draw.LineStyle)100); } }

31

Al doilea apel este legal, deoarece valorile care se pot specica pentru un enum nu sunt limitate la valorile declarate enum. Ca atare, programatorul n trebuie s fac validri suplimentare pentru a determina consistenta valorilor. a a a cazul de fat, la apelul de metod se arunc o exceptie (exceptiile vor In a a a tratate pe larg ntr-un curs viitor). Ca i mod de scriere a enumerrilor, se sugereaz folosirea conventiei s a a Pascal att pentru numele tipului ct i pentru numele valorilor continute, a a s precum i evitarea folosirii prexelor sau suxelor pentru acestea. s Enumerrile nu pot declarate abstracte i nu pot derivate. Orice a s enum este derivat automat din System.Enum, care este la rndul lui derivat a din System.ValueType); astfel, metodele motenite de la tipurile printe sunt s a utilizabile de ctre orice variabil de tip enum. a a Fiecare tip enumerare care este folosit are un tip de reprezentare9 , pentru a se cunoate ct spatiu de memorie trebuie s e alocat unei variabile de s a a acest tip. Dac nu se specic nici un tip de reprezentare (ca mai sus), atunci a a se presupune implicit tipul int. Specicarea unui tip de reprezentare (care poate orice tip integral, exceptnd tipul char) se face prin enuntarea tipului a dup numele enumerrii: a a enum MyEnum : byte { small, large } Specicarea este folosit atunci cnd dimensiunea memorie este impora a n tant, sau cnd se dorete crearea unui tip de indicator (un tip ag) al crui a a s a numr de stri difer de numrul de biti alocati tipului int (modelare de a a a a ag-uri): enum ActionAttributes : ulong { Read = 1, Write = 2, Delete = 4, Query = 8,
9

Engl: underlying type

32

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Sync = 16 //etc } ... ActionAttributes aa=ActionAttributes.Read|ActionAttributes.Write | ActionAttributes.Query; ... mod implicit, valoarea primului membru al unei structuri este 0, i In s ecare variabl care urmeaz are valoarea mai mare cu o unitate dect precea a a denta. La dorint, valoarea ecrei variabile poate specicat explicit: a a a enum Values { a = 1, b = 2, c = a + b } Urmtoarele observatii se impun relativ la lucrul cu tipul enumerare: a 1. valorile specicate ca initializatori trebuie s e reprezentabile prin a tipul de reprezentare a enumerrii, altfel apare o eroare la compilare: a enum Out : byte { A = -1 }//eroare semnalata la compilare 2. mai multi membri pot avea aceeai valoare (manevr dictat de seman s a a tica tipului construit): enum ExamState { passed = 10, failed = 1, rejected = failed } 3. dac pentru un membru nu este dat o valoare, acesta va lua valoarea a a membrului precedent + 1 (cu exceptia primului membru vezi mai sus)

2.2. TIPURI DE DATE 4. nu se permit referinte circulare: enum CircularEnum { A = B, B }//A depinde explicit de B, B depinde implicit de A //eroare semnalata la compilare

33

5. este recomandat ca orice tip enum s contin un membru cu valoarea a a 0, pentru c anumite contexte valoarea implicit pentru o variabil a n a a enum este 0, ceea ce poate duce la inconsistente i bug-uri greu de s depanat enum Months { InvalidMonth,//are valoarea implicita 0, fiind primul element January, February, //etc } Tipurile enumerare pot convertite ctre tipul lor de baz i a a s napoi, folosind o conversie explicit (cast): a enum Values { a = 1, b = 5, c= 3 } class Test { public static void Main() { Values v = (Values)3; int ival = (int)v; } } Valoarea 0 poate convertit ctre un enum fr cast: a a aa

34

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI ... MyEnum me; ... if (me == 0) { //cod }

Urmtorul cod arat cteva din articiile care pot aplicate tipului a a a enumerare: obtinerea tipului unui element de tip enumerare precum i a s tipului clasei de baz, a tipului de reprezentare, a valorilor continute (ca a nume simbolice i ca valori), conversie de la un string la un tip enumers are pe baza numelui, etc. Exemplul este preluat din <FrameworkSDK> \Samples\Technologies\ValueAndEnumTypes. using System; namespace DemoEnum { class DemoEnum { enum Color { Red = 111, Green = 222, Blue = 333 } private static void DemoEnums() { Console.WriteLine("\n\nDemo start: Demo of enumerated types."); Color c = Color.Red; // What type is this enum & what is it derived from Console.WriteLine(" The " + c.GetType() + " type is derived from " + c.GetType().BaseType); // What is the underlying type used for the Enums value Console.WriteLine(" Underlying type: " + Enum.GetUnderlyingType( typeof(Color))); // Display the set of legal enum values

2.2. TIPURI DE DATE

35

Color[] o = (Color[]) Enum.GetValues(c.GetType()); Console.WriteLine("\n Number of valid enum values: " + o.Length); for (int x = 0; x < o.Length; x++) { Color cc = ((Color)(o[x])); Console.WriteLine(" {0}: Name={1,7}\t\tNumber={2}", x, cc.ToString("G"), cc.ToString("D")); } // Check if a value is legal for this enum Console.WriteLine("\n 111 is a valid enum value: " + Enum.IsDefined( c.GetType(), 111)); // True Console.WriteLine(" 112 is a valid enum value: " + Enum.IsDefined( c.GetType(), 112)); // False // Check if two enums are equal Console.WriteLine("\n Is c equal to Red: " + (Color.Red == c));//True Console.WriteLine(" Is c equal to Blue: " + (Color.Blue == c));//False // Display the enums value as a string using different format specifiers Console.WriteLine("\n cs value as a string: " + c.ToString("G"));//Red Console.WriteLine(" cs value as a number: " + c.ToString("D"));//111 // Convert a string to an enums value c = (Color) (Enum.Parse(typeof(Color), "Blue")); try { c = (Color) (Enum.Parse(typeof(Color), "NotAColor"));//Not valid, //raises exception } catch (ArgumentException) { Console.WriteLine(" NotAColor is not a valid value for this enum."); } // Display the enums value as a string Console.WriteLine("\n cs value as a string: " + c.ToString("G"));//Blue Console.WriteLine(" cs value as a number: " + c.ToString("D"));//333 Console.WriteLine("Demo stop: Demo of enumerated types."); }

36

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

static void Main() { DemoEnums(); } } }

2.3

Tablouri

cele mai multe cazuri nu se cunoate aprioric numrul de obiecte care In s a se vor folosi la momentul rulrii. O solutie pentru stocarea elementelor avnd a a acelai tip o reprezint tablourile. Sintaxa de declarare este asemntoare s a a a cu cea din Java sau C++, dar ecare tablou este un obiect, derivat din clasa abstract System.Array. Accesul la elemente se face prin intermediul indicilor a care ncep de la 0 i se termin la numrul de elemente-1; orice depire a s a a as indicilor duce la aparitia unei exceptii: System.IndexOutOfRangeException. O variabil de tip tablou poate avea valoare de null sau poate s indice ctre a a a o instant valid. a a

2.3.1

Tablouri unidimensionale

Declararea unui tablou unidimensional se face prin plasarea de paranteze drepte ntre numele tipului tabloului i numele su, ca mai jos10 : s a int[] sir; Declararea de mai sus nu duce la alocare de spatiu pentru memorarea irului; s instatierea se poate face ca mai jos: sir = new int[10]; Exemplu: using System; class Unidimensional { public static int Main() { int[] sir; int n;
Spre deosebire de Java, nu se poate modica locul parantezelor, adic nu se poate a scrie: int sir[].
10

2.3. TABLOURI Console.Write(Dimensiunea vectorului: ); n = Int32.Parse( Console.ReadLine() ); sir = new int[n]; for( int i=0; i<sir.Length; i++) { sir[i] = i * i; } for( int i=0; i<sir.Length; i++) { Console.WriteLine(sir[{0}]={1}, i, sir[i]); } return 0; } }

37

acest exemplu se folosete proprietatea11 Length, care returneaz numrul In s a a tuturor elementelor vectorului (lucru vizibil la tablourile multidimensionale rectangulare). De mentionat c acest context n i sir nu se pot declara a n s la un loc, adic declaratii de genul int[] sir, n; sau int n, []sir; sunt a incorecte (prima este corect din punct de vedere sintactic, dar ar rezulta c a a n este i el un tablou; al doilea caz, declaratia nu este corect sintactic). s n a Se pot face initializri ale valorilor continute a ntrun tablou: int[] a = new int[] {1,2,3}; sau forma mai scurt: n a int[] a = {1,2,3};

2.3.2

Tablouri multidimensionale

C# cunoate dou tipuri de tablouri multidimensionale: rectangulare i s a s 12 neregulate . Numele lor vine de la forma pe care o pot avea. Tablouri rectangulare Tablourile rectangulare au proprietatea c pe ecare dimensiune se a a a acelai numr de elemente. Se declar ca exemplul de mai jos: s a a n int[,] tab;
11 12

Pentru notiunea de proprietate, vezi la partea despre clase. Engl: jagged arrays.

38

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

unde tab este un tablou rectangular bidimensional. Instantierea se face: tab = new int[2,3]; rezultnd un tablou cu 2 linii i 3 coloane. Referirea la elementul aat pe a s linia i i coloana j se face cu tab[i, j]. s La declararea tabloului se poate face i initializare: s int[,] tab = new int[,] {{1,2},{3,4}}; sau, mai pe scurt: int[,] tab = {{1, 2}, {3, 4}}; Exemplu: using System; class Test { public static void Main() { int[,] tabInm = new int[10,10]; for( int i=0; i<tabInm.GetLength(0); i++ ) { for( int j=0; j<tabInm.GetLength(1); j++) { tabInm[i,j] = i * j; } } for( int i=0; i<tabInm.GetLength(0); i++) { for( int j=0; j<tabInm.GetLength(1); j++) { Console.WriteLine({0}*{1}={2}, i, j, tabInm[i,j]); } } Console.WriteLine(tabInm.Length={0}, tabInm.Length); } } Dup ce se aeaz tabla a s a nmultirii pn la 10, se va aa: tabInm.Length=100, a a s deoarece proprietatea Length d numrul total de elemente aat tablou (pe a a n toate dimensiunile). Am folosit a metoda GetLength(d) care returneaz ns a

2.3. TABLOURI

39

numrul de elemente aate pe dimensiunea numrul d (numrarea dimensia a a unilor ncepe cu 0). Determinarea numrului de dimensiuni pentru un tablou rectangular la a runtime se face folosind proprietatea Rank a clasei de baz System.Array. a Exemplu: using System; class Dimensiuni { public static void Main() { int[] t1 = new int[2]; int[,] t2 = new int[3,4]; int[,,] t3 = new int[5,6,7]; Console.WriteLine(t1.Rank={0}\nt2.Rank={1}\nt3.Rank={2}, t1.Rank, t2.Rank, t3.Rank); } } Pe ecran va aprea: a t1.Rank=1 t2.Rank=2 t3.Rank=3 Tablouri neregulate Un tablou neregulat13 reprezint un tablou de tabouri. Declararea unui a tablou neregulat cu dou dimensiuni se face ca mai jos: a int[][] tab; Referirea la elementul de indici i i j se face prin tab[i][j]. s Exemplu: using System; class JaggedArray { public static void Main() { int[][] a = new int[2][]; a[0] = new int[2];
13

Engl: jagged array

40

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI a[1] = new int[3]; for( int i=0; i<a[0].Length; i++) { a[0][i] = i; } for( int i=0; i<a[1].Length; i++) { a[1][i] = i * i; } for(int i=0; i<a.Length; i++) { for( int j=0; j<a[i].Length; j++ ) { Console.Write({0} , a[i][j]); } Console.WriteLine(); } Console.WriteLine(a.Rank={0}, a.Rank); }

} va scrie pe ecran: 0 1 0 1 4 a.Rank=1 Ultima linie aat se explic prin faptul c un tablou neregulat este un s a a a vector care contine referinte, deci este unidimensional. Initializarea valorilor unui tablou neregulat se poate face la declarare: int[][] myJaggedArray = new int [][] { new int[] {1,3,5,7,9}, new int[] {0,2,4,6}, new int[] {11,22} }; Forma de mai sus se poate prescurta la: int[][] myJaggedArray = { new int[] {1,3,5,7,9}, new int[] {0,2,4,6}, new int[] {11,22} };

2.4. SIRURI DE CARACTERE

41

2.4

Siruri de caractere

Tipul de date folosit pentru reprezentarea irurilor de caractere este clasa s System.String (pentru care se poate folosi aliasul string; reamintim c a este un tip predenit). Obiectele de acest tip sunt imutabile (caracterele continute nu se pot schimba, dar pe baza unui ir se poate obtine un alt ir). s s Sirurile pot contine secvente escape i pot de dou tipuri: regulate i de s a s tip verbatim14 . Sirurile regulate sunt demarcate prin ghilimele i necesit s a secvente escape pentru reprezentarea caracterelor escape. Exemplu: String a = "string literal"; String versuri = "vers1\nvers2"; String caleCompleta = "\\\\minimax\\protect\\csharp"; Pentru situatia care se utilizeaz masiv secvente escape, se pot folosi n a irurile verbatim. Un literal de acest tip are simbolul @ s naintea ghilimelelor de nceput. Pentru cazul care ghilimelele sunt alnite interiorul irului, n nt n s ele se vor dubla. Un ir de caractere poate reprezentat pe mai multe rnduri s a fr a folosi caracterul \n. Sirurile verbatim sunt folosite pentru a face referiri aa la iere sau chei regitri, sau pentru prelucrarea ierelor. s n s s Exemple: String caleCompleta=@"\\minimax\protect\csharp"; //ghilimelele se dubleaza intr-un verbatim string String s=@"notiunea ""aleator"" se refera..."; //string multilinie reprezentat ca verbatim String dialog=@"-Alo? Cu ce va ajutam? -As avea nevoie de o informatie."; Operatorii == i != pentru dou iruri de caractere se comport s a s a felul urmtor: dou iruri de caractere se consider egale dac sunt e n a a s a a amndou null, e au aceeai lungime i caracterele de pe aceleai pozitii a a s s s coincid; != d negarea relatiei de egalitate. Pentru a se compara dou a a iruri din punct de vedere al referintelor, trebuie ca mcar unul dintre obiecte s a s e convertit la object: a class Test { public static void Main() {
14

Engl: verbatim literals

42

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI string a = "Salut", b = a.Clone(); System.Console.WriteLine("a==b: {0}", a==b); System.Console.WriteLine("(object)a==b: {0}", (object)a==b); }

} La dispozitivul standard de ieire se va aa: s s a==b: true (object)a==b: false Alt operator care se poate utiliza este +, reprezentnd concatenarea: a String s = "Salut" + " " + "lume"; s += "!!!!"; Clasa String pune la dispozitie metode pentru: comparare (Compare, Com pareOrdinal, CompareTo), cutare (EndsWith, StartsWith, IndexOf, LastIna dexOf), modicare (a se elege obtinerea altor obiecte pe baza celui curent nt - Concat, CopyTo, Insert, Join, PadLeft, PadRight, Remove, Replace, Split, Substring, ToLower, ToUpper, Trim, TrimEnd, TrimStart)15 . Accesarea unui caracter aat pe o pozitie i a unui ir s se face prin folosirea parantezelor s drepte, cu aceleai restrictii asupra indicelui ca i pentru tablouri: s[i]. s s clasa object se a metoda ToString() care este suprascris ecare In a a n clas ale crei instante pot tiprite. Pentru obtinerea unei reprezentri a a a a diferite se folosete metoda String.Format(). s Un exemplu de folosire a functiei Split() pentru desprtirea unui ir a s n functie de separatori este: using System; class Class1 { static void Main(string[] args) { String s = "Oh, I hadnt thought of that!"; char[] x = { , , }; String[] tokens = s.Split( x ); for(int i=0; i<tokens.Length; i++) { Console.WriteLine("Token: {0}", tokens[i]);
15

A se vedea exemplele din MSDN.

2.4. SIRURI DE CARACTERE } } } va aa pe ecran: s Token: Token: Token: Token: Token: Token: Token: Oh I hadnt thought of that!

43

De remarcat c pentru caracterul apostrof nu este obligatorie secventa escape a cazul irurilor de caractere. Al doilea lucru care trebuie explicat este c al n s a doilea token este cuvntul vid, care apare a ntre cei doi separatori alturati: a virgula i spatiul. Metoda Split() nu face gruparea mai multor separatori, s lucru care ar de dorit prezenta a doi separatori alturati. Pentru aceasta n a se folosesc expresii regulate. Pentru a lucra cu iruri de caractere care permit modicarea lor se folosete s s clasa StringBuilder, din namespaceul System.Text.

2.4.1

Expresii regulate

cazul care functiile din clasa String nu sunt sucient de puternice, In n namespaceul System.Text.RegularExpresions pune la dispozitie o clas de a lucru cu expresii regulate numit Regex. Expresiile regulate reprezint o a a metod extrem de facil de a opera cutri/ a a a a nlocuiri pe text. Forma expresiilor regulate este cea din limbajul Perl. Aceast clas folosete o tehnic interesant pentru mrirea performana a s a a a elor: dac programatorul vrea, se scrie o secvent din mers pentru a t a a implementa potrivirea expresiei regulate, dup care codul este rulat16 . a Exemplul anterior poate rescris corect din puct de vedere al functionalitii at prin folosirea unei expresii regulate, pentru a prinde i cazul separatorilor s multipli adiacenti: class ExpresieRegulata { static void Main(string[] args) {
16

Codul este scris direct IL. n

44

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI String s = "Oh, I hadnt thought of that!"; //separator: virgula, spatiu sau punct si virgula //unul sau mai multe, orice combinatie Regex regex = new Regex("[, ;]+"); String[] strs = regex.Split(s); for( int i=0; i<strs.Length; i++) { Console.WriteLine("Word: {0}", strs[i]); }

} care va produce: Word: Word: Word: Word: Word: Word: Oh I hadnt thought of that!

Curs 3 Clase generaliti. at Instructiuni. Spatii de nume


3.1 Clase vedere general a

Clasele reprezint tipuri referint denite de utilizator. O clas poate s a a a a moteneasc o singur clas i poate implementa mai multe interfete. s a a as Clasele pot contine constante, cmpuri, metode, proprieti, evenimente, a at indexatori, operatori, constructori de instant, destructori, constructori de a clas, tipuri imbricate. Fiecare membru poate contine un nivel de accesare, a care controleaz gradul de acces la el. O descriere este dat tabelul 3.1: a a n Tabelul 3.1: Modicatori de acces ai membrilor unei clase Accesor public protected Semnicatie Acces nelimitat Acces limitat la clasa contintoare a sau la tipuri derivate din ea internal Acces limitat la acest assembly protected internal Acces limitat la acest assembly sau la tipuri derivate din clas a private Acces limitat la clas; a modicatorul implicit de acces

using System; 45

46

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

class MyClass { public MyClass() { Console.WriteLine("Constructor instanta"); } public MyClass( int value ) { MyField = value; Console.WriteLine("Constructor instanta"); } public const int MyConst = 12; public int MyField = 34; public void MyMethod() { Console.WriteLine("MyClass.MyMethod"); } public int MyProperty { get { return MyField; } set { MyField = value; } } public int this[int index] { get { return 0; } set { Console.WriteLine("this[{0}]={1}", index, value); } } public event EventHandler MyEvent; public static MyClass operator+(MyClass a, MyClass b)

3.1. CLASE VEDERE GENERALA { return new MyClass(a.MyField + b.MyField); } } class Test { static void Main() { MyClass a = new MyClass(); MyClass b = new MyClass(1); Console.WriteLine("MyConst={0}", MyClass.MyConst); a.MyField++; Console.WriteLine("a.MyField={0}", a.MyField); a.MyMethod(); a.MyProperty++; Console.WriteLine("a.MyProperty={0}", a.MyProperty); a[3] = a[1] = a[2]; Console.WriteLine("a[3]={0}", a[3]); a.MyEvent += new EventHandler(MyHandler); MyClass c = a + b; } static void MyHandler(object Sender, EventArgs e) { Console.WriteLine("Test.MyHandler"); } internal class MyNestedType {} }

47

Constanta este un membru al unei clase care reprezint o valoare constant, a a care poate evaluat la compilare. Constantele pot depinde de alte a constante, atta timp ct nu se creeaz dependente circulare. Ele sunt a a a considerate automat membri statici (dar este interzis s se foloseasc a a specicatorul static fata lor). Ele pot accesate exclusiv prin inn termediul numelui de clas (MyClass.MyConst), i nu prin intermediul a s vreunei instante (a.MyConst). Cmpul este un membru reprezentnd o variabil asociat unui obiect. a a a a

48

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Metoda este un membru care implementeaz un calcul sau o actiune care a poate efectuat asupra unui obiect sau asupra unei clase. Metodele a statice (care au antet cuvntul cheie static) sunt accesate prin intern a mediul numelui de clas, pe cnd cele nestatice (metode instant) sunt a a a apelate prin intermediul unui obiect. Proprietatea este un membru care d acces la o caracteristic a unui obiect a a sau unei clase. Exemplele folosite pn acum includeau lungimea unui a a vector, numrul de caractere ale unui ir de caractere, etc. Sintaxa a s pentru accesara cmpurilor i a proprietilor este aceeai. Reprezint a s at s a o alt modalitate de implementare a accesorilor pentru obiecte. a Evenimentul este un membru care permite unei clase sau unui obiect s a pun la dispozitia altora noticri asupra evenimentelor. Tipul acestei a a declaratii trebuie s e un tip delegat. O instant a unui tip delegat a a ncapsuleaz una sau mai multe entiti apelabile. Exemplu: a at public delegate void EventHandler(object sender, System.EventArgs e); public class Button { public event EventHandler Click; public void Reset() { Click = null; } } using System; public class Form1 { Button Button1 = new Button1(); public Form1() { Button1.Click += new EventHandler(Button1_Click); } void Button1_Click(object sender, EventArgs e ) { Console.WriteLine("Button1 was clicked!");

3.2. TRANSMITEREA DE PARAMETRI } public void Disconnect() { Button1.Click -= new EventHandler(Button1_Click); } }

49

Mai sus clasa Form1 adaug Button1 Click ca tratare de eveniment1 a pentru evenimentul Click al lui Button1. metoda Disconnect(), acest In event handler este aturat. nl Operatorul este un membru care denete semnicatia (supra arcarea) s nc unui operator care se aplic instantelor unei clase. Se pot supra arca a nc operatorii binari, unari i de conversie. s Indexatorul este un membru care permite unui obiect s e indexat a n acelai mod ca un tablou (pentru programatorii C++: supra arcarea s nc operatorului []). Constructorii instant sunt membri care implementeaz actiuni cerute a a pentru initializarea ecrui obiect. a Destructorul este un membru special care implementeaz actiunile cerute a pentru a distruge o instant a unei clase. Destructorul nu are parametri, a nu poate avea modicatori de acces, nu poate apelati explicit i poate s apelat automat de ctre garbage collector. a Constructorul static este un membru care implementeaz actiuni necea sare pentru a initializa o clas, mai exact membrii statici ai clasei. Nu a pot avea parametri, nu pot avea modicatori de acces, nu sunt apelati explicit, ci automat de ctre sistem. a Motenirea este de tip simplu, iar rdcina ierarhiei este clasa object (alias s a a System.Object).

3.2

Transmiterea de parametri

general, transmiterea parametrilor se face prin valoare. Acest lucru In nseamn c la apelul unei metode stiva gestionat de compilator se copiaz a a n a a
1

Engl: event handler

50

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

valoarea parametrului actual transmis, iar la revenire din metod aceast a a valoare va tears. Exemplicm mai jos acest lucru pentru tipurile valoare s a a i referint. s a using System; class DemoTipValoare { static void f(int x) { Console.WriteLine("la intrare in f: {0}", x ); ++x; Console.WriteLine("la iesire din f: {0}", x ); } static void Main() { int a = 100; Console.WriteLine("inainte de intrare in f: {0}", a); f( a ); Console.WriteLine("dupa executarea lui f: {0}", a); } } Executarea acestui program va avea ca rezultat: inainte de intrare in f: 100 la intrare in f: 100 la iesire din f: 101 dupa executarea lui f: 100 Pentru variable de tip referint, pe stiv se depune tot o copie a valorii a a a pentru un asemenea tip de variabil acest lucru obiectului. Ins a nseamn c a a pe stiv se va depune ca valoare adresa de memorie la care eset stocat obiectul a respectiv. Ca atare, metoda apelat poate s modice starea obiectului care a a se transmite: class Employee { public String name; public decimal salary; } class Test

3.2. TRANSMITEREA DE PARAMETRI { static void Main() { Employee e = new Employee(); e.name = "Ionescu"; e.salary = 300M; System.Console.WriteLine("pre: name={0}, salary={1}", e.name, e.salary ); Method( e ); System.Console.WriteLine("post: name={0}, salary={1}", e.name, e.salary ); } static void Method( Employee e ) { e.salary += 100; } } va avea ca rezultat: pre: name=Ionescu, salary=300 post: name=Ionescu, salary=400

51

Totui, chiar i cazul tipului referint s s n a ncercarea de a recrea interin orul unei metode un obiect transmis ca parametru nu are nici un efect dup a terminarea ei: class MyClass { public int x; } class Test { static void f(MyClass myClass) { Console.WriteLine("intrare in f: {0}", myClass.x); myClass = new MyClass(); myClass.x = -100; Console.WriteLine("iesire din f: {0}", myClass.x); }

52

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME static void Main() { MyClass myClass = new MyClass(); myClass.x = 100; Console.WriteLine("inainte de apel: {0}", myClass.x); f( myClass ); Console.WriteLine("dupa apel: {0}", myClass.x);

} } Ieirea acestui program va : s inainte de intrare in iesire din dupa apel: apel: 100 f: 100 f: -100 100

Exist situatii care acest comportament nu este cel dorit: am vrea ca a n efectul asupra unui parametru s se mentin i dup ce metoda apelat sa a as a a terminat. Un parametru referint este folosit tocmai pentru a rezolva problema a transmiterii prin valoare, folosind referint (un alias) pentru entitatea dat a a de ctre metoda apelant. Pentru a transmite un parametru prin referint, a a a se prexeaz cu cuvntul cheie ref la apel sau la declarare de metod: a a a using System; class Test { static void Swap( ref int a, ref int b) { int t = a; a = b; b = t; } static void Main() { int x=1, y=2; Console.WriteLine("inainte de apel: x={0}, y={1}", x, y); Swap( ref a, ref b ) Console.WriteLine("dupa apel: x={0}, y={1}", x, y); } }

3.2. TRANSMITEREA DE PARAMETRI

53

va realiza interschimbarea valorilor a i b. s Una din trsturile specice parametrilor referint este c valorile pentru aa a a care se face apelul trebuie s e initializate. Neasignarea de valori pentru x a i y exemplul de mai sus duce o eroare de compilare. Mai clar, exemplul s n de mai jos genereaza eroare la compilare, mesajul ind: Use of unassigned local variable x. class TestRef { static void f(ref int x) { x = 100; } static void Main() { int x; f(ref x); } } Exist cazuri care dorim s obtinem acelai efect ca la parametrii a n a s referint, dar fr a trebui s initializm argumentele date de ctre metoda a aa a a a apelant (de exemplu cnd valoarea acestui parametru se calculeaz ina a a n teriorul metodei apelate). Pentru aceasta exist parametrii de ieire2 , simia s lar cu parametrii referint, cu deosebirea c nu trebuie asignat o valoare a a a parametrului de apel: using System; class Test { static void Main() { int l = 10; double area; ComputeSquareArea( l, out area); Console.WriteLine("Area is: {0}", area); } static void ComputeSquareArea( double l, out double area ) {
2

Engl: output parameters

54

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME area = l * l; }

} Pentru toate tipurile de parametri de mai sus exist o mapare 1 la 1 a ntre 3 parametrii actuali i cei formali. Un parametru vector permite o relatie s de tipul unul-la-multi: mai multi parametri actuali pot referite prin in termediul unui singur parametru formal. Un astfel de parametru se declar a folosind modicatorul params. Pentru o implementare de metod, putem a avea cel mult un parametru de tip vector i acesta trebuie s e ultimul s a n lista de parametri. Acest parametru formal este tratat ca un tablou unidimensional: using System; class Test { static void F(params int[] args) { Console.WriteLine("# of parameters: {0}", args.Length); for( int i=0; i<args.Length; i++) { Console.WriteLine("args[{0}]={1}", i, args[i]); } } static void Main() { F(); F(1); F(1,2); F(new int[] {1,2,3}); } } Acest tip de transmitere se folosete i de ctre metoda WriteLine (sau Write) s s a a clasei Console, i.e. exist aceast clas o metod de tipul: a n a a a public static void WriteLine(string format, params object[] args){...}
3

Engl: parameter array

3.3. CONVERSII

55

3.3

Conversii

O conversie permite ca o expresie de un anumit tip s e tratat ca ind a a de alt tip. Conversiile pot implicite sau explicite, aceasta specicnd de a fapt dac un operator de cast (conversie) este sau nu necesar. a

3.3.1

Conversii implicite

Sunt clasicate ca i conversii implicite urmtoarele: s a conversiile identitate conversiile numerice implicite conversiile implicite de tip enumerare conversiile implicite de referinte boxing conversiile implicite ale expresiilor constante conversii implicite denite de utilizator Conversiile implicite pot aprea a ntro varietate de situatii, de exemplu apeluri de functii sau atribuiri. Conversiile implicite predenite nu determin a niciodat aparitia de exceptii. a Conversiile indentitate O conversie identitate convertete de la un tip oarecare ctre acelai tip. s a s Conversiile numerice implicite Conversiile numerice implicite sunt: de la sbyte la short, int, long, oat, double, decimal; de la byte la short, ushort, int, uint, long, ulong, oat, double, decimal; de la short la int, long, double, decimal; de la ushort la int, uint, long, ulong, oat, double, decimal; de la int la long, oat, double, decimal;

56

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME de la uint la long, ulong, oat, double, decimal; de la long la oat, double, decimal; de la ulong la oat, double, decimal; de la char la ushort, int, uint, long, ulong, oat, double, decimal; de la oat la double.

Conversiile de la int, uint, long, ulong la oat, precum i cele de la long s sau ulong la double pot duce la o pierdere a preciziei, dar niciodat la o a reducere a ordinului de mrime. Alte conversii numerice implicite niciodat a a nu duc la pierdere de informatie. Conversiile de tip enumerare implicite O astfel de conversie permite ca literalul 0 s e convertit la orice tip a enumerare (chiar dac acesta nu contine valoarea 0) - a se vedea 2.2.3, pag. a 29. Conversii implicite de referinte Conversiile implicite de referinte implicite sunt: de la orice tip referint la object; a de la orice tip clas B la orice tip clas A, dac B este derivat din A; a a a de la orice tip clas A la orice interfat B, dac A implementeaz B; a a a a de al orice interfat A la orice interfat B, dac A este derivat din B; a a a a de la orice tip tablou A cu tipul AE la un tip tablou B avnd tipul BE , a cu urmtoarele conditii: a 1. A i B au acelai numr de dimensiuni; s s a 2. att AE ct i BE sunt tipuri referint; a a s a 3. exist o conversie implicit de tip referint de la AE la BE a a a de la un tablou la System.Array; de la tip delegat la System.Delegate; de la orice tip tablou sau tip delegat la System.ICloneable; de la tipul null la orice variabil de tip referint; a a

3.3. CONVERSII Conversie de tip boxing

57

Permite unui tip valoare s e implicit convertit ctre tipul object sau a a System.ValueType sau ctre orice tip interfat pe care tipul valoare implea a l menteaz. O descriere mai amnuntit este dat sectiunea 3.3.4. a a a a n

3.3.2

Conversiile implicite ale expresiilor constante

Permit urmtoarele tipuri de conversii: a o expresie constant de tip int poate convertit ctre tipurile sbyte, a a a byte, short, ushort, uint, ulong, cu conditia ca valoarea expresiei con stante s se ae domeniul tipului destinatie; a n o expresie constant de tip long poate convertit la tipul ulong, dac a a a valoarea ce se convertete nu este negativ. s a Conversii implicite denite de utilizator Constau ntro conversie implicit standard optional, urmat de executia a a a unui operator de conversie implicit utilizator urmat de alt conversie ima a a plicit standard optional. Regulile exacte sunt descrise [4]. a a n

3.3.3

Conversii explicite

Urmtoarele conversii sunt clasicate ca explicite: a toate conversiile implicite conversiile numerice explicite conversiile explicite de enumerri a conversiile explicite de referinte unboxing conversii explicite denite de utilizator Din cauz c orice conversie implicit este de asemenea i una explicit, a a a s a aplicarea operatorului de cast este redundant: a int x = 0; long y = (long)x;//(long) este redundant

58

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Conversii numerice explicite Sunt conversii de la orice tip numeric la un alt tip numeric pentru care nu exist conversie numeric implicit: a a a de la sbyte la byte, ushort, uint, ulong, char; de la byte la sbyte, char; de la short la sbyte, byte, ushort, uint, ulong, char; de la ushort la sbyte, byte, short, char; de la int la sbyte, byte, short, ushort, int, char; de la uint la sbyte, byte, short, ushort, int, uint, long, ulong, char; de la long la sbyte, byte, short, ushort, int, uint, ulong, char; de la ulong la sbyte, byte, short, ushort, int, uint, long, char; de la char la sbyte, byte, short; de la oat la sbyte, byte, short, ushort, int, uint, long, ulong, decimal; de la double la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, decimal; de la decimal la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double; Pentru c astfel de conversii pot aprea pierderi de informatie, exist a n a a dou contexte care se fac aceste conversii: checked i unchecked. a n s context checked, conversia se face cu succes dac valoarea care se conIn a vertete este reprezentabil de ctre tipul ctre care se face conversia. s a a a In cazul care conversia nu se poate face cu succes, se va arunca exceptia Sysn tem.OverowException. context unchecked, conversia se face In ntotdeauna, dar se poate ajunge la pierdere de informatie sau la valori ce nu sunt bine nedenite (vezi [4], pag. 115116). Conversii explicite de enumerri a Conversiile explicite de enumerri sunt: a de la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double, decimal la orice tip enumerare;

3.3. CONVERSII

59

de la orice tip enumerare la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double, decimal; de la orice tip enumerare la orice tip enumerare. Conversiile de tip enumerare se fac prin tratarea ecrui tip enumerare a ca ind tipul ntreg de reprezentare, dup care se efectueaz o conversie a a implict sau explicit a a ntre tipuri (ex: dac se dorete conversia de la un tip a s enumerare E care are tipul de reprezentare int la un tip byte, se va face o conversie explicit de la int la byte; invers, se va face o conversie implict de a a la byte la int). Conversii explicite de referinte Conversiile explicite de referinte sunt: de la object la orice tip referint; a de la orice tip clas A la orice tip clas B, cu conditia ca A s e clas a a a a de baz pentru B; a de la orice tip clas A la orice tip interfat B, dac A nu este nederiva a a abil i A nu implementeaz pe B; as a de la orice tip interfat A la orice tip clas B, dac B nu este nederiva a a abil sau cu conditia ca B s implementeze A; a a de la orice tip interfat A la orice tip interfat B, dac A nu este derivat a a a din B; de la un tip tablou A cu elemente de tip AE la un tip tablou B cu elemente BE , cu conditiile: 1. A i B au acelai numr de dimensiuni; s s a 2. AE i BE sunt tipuri referint; s a 3. exist o conversie de referint explicit de la AE al BE a a a de la System.Array i interfetele pe care le implementeaz la orice tip s a tablou; de la System.Delegate i interfetele pe care le implementeaz la orice s a tip delegat. Acest tip de conversii cer vericare la runtime. Dac o astfel de conversie a eueaz, se va arunca o exceptie de tipul System.InvalidCastException. s a

60 Unboxing

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Unboxing-ul permite o conversie explicit de la object sau System.ValueType a la orice tip valoare, sau de la orice tip interfat la orice tip valoare care ima plementeaz tipul interfat. Mai multe detalii se vor da sectiunea 3.3.4. a a n Conversii explicite denite de utilizator Constau ntro conversie standard explicit optional, urmat de executia a a a unei conversii explicite, urmat de o alt conversie standard explicit optional. a a a a

3.3.4

Boxing i unboxing s

Boxingul i unboxingul reprezint modalitatea prin care C# permite s a utilizarea simpl a sistemului unicat de tipuri. Spre deosebire de Java, unde a exist tipuri primitive (care nu pot contine metode) i tipuri referint, C# a s a n toate tipurile sunt derivate din clasa object (alias System.Object). De exemplu, tipul int (alias System.Int32) este derivat din clasa System.ValueType care la rndul ei este derivat din clasa object (alias System.Object). Ca a a atare, orice int este compatibil cu object. Boxing Conversia de tip boxing permite oricrui tip valoare s e implicit convera a tit ctre tipul object sau ctre un tip interfat implementat de tipul valoare. a a a Boxingul unei valori const alocarea unei variabile de tip obiect i copierea a n s valorii initiale acea instant. n a Procesul de boxing al unei valori sau variabile de tip valoare se poate elege ca o simulare de creare de clas pentru acel tip: nt a sealed class T_Box { T value; public T_Box(T t) { value = t; } } Astfel, declaratiile: int i = 123; object box = i;

3.3. CONVERSII corespund conceptual la: int i = 123; object box = new int_Box(i); Pentru secventa: int i = 10;//linia 1 object o = i;//linia 2 int j = (int)o;//linia 3

61

procesul se desfoar ca gura 3.1: la linia 1, se declar i se initializeaz as a n as a o variabil de tip valoare, care va contine valoarea 10; valoarea va stocat a a pe stiv. La linia 2 se va crea o referint o ctre un obiect alocat heap, care a a a n va contine att valoarea 10, ct i o informatie despre tipul de dat continut a a s a ( cazul nostru, System.Int32). Unboxingul se face printrun cast explicit, n ca linia 3. n
i 10

o 10 j 10

System.Int32

Figura 3.1: Boxing i unboxing s Determinarea tipului pentru care sa fcut a mpachetarea se face prin intermediul operatorului is: int i = 123; object o = i; if (o is int) { Console.Write("Este un int inauntru!!!!"); } Boxingul duce la o clonare a valorii care va continut. a secventa: int i = 10; object o = i; i++; Console.WriteLine("in o: {0}", o); va aa valoarea nealterat 10. s a Altfel spus,

62

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.3.5

Declaratii de variabile i constante s

Variabilele i constantele trebuie declarate C#. Optional, pentru varis n abile se poate specica valoarea initial, iar pentru constante acest lucru este a obligatoriu. O variabil trebuie s aib valoarea asignat denit a a a a a nainte ca valoarea ei s e utilizat, cazul care este declarat interiorul unei a a n n a n metode. Este o eroare ca ntrun sub-bloc s se declare o variabil cu acelai a a s nume ca blocul contintor: n a void F() { int x = 3, y;//ok const double d = 1.1;//ok { string x = "Mesaj: ";//eroare, x mai este declarat //in blocul continator int z = x + y;//eroare, y nu are o valoare definita asignata } } Constantele au valori initiale care trebuie s se poat evalua la compilare. a a

3.3.6

Declaratii de etichete

O etichet poate prexa o instructiune. Ea este vizibil a a n ntregul bloc i toate sub-blocurile continute. O etichet poate referit de ctre s a a a o instructiune goto: class DemoLabel { int F(int x) { if (x >= 0) goto myLabel; x = -x; myLabel: return x; } static void Main() { DemoLabel dl = new DemoLabel(); dl.f(); } }

3.3. CONVERSII

63

3.3.7

Instructiuni de selectie

Instructiunea if Instructiunea if execut o instructiune functie de valoarea de adevr a a n a unei expresii logice. Are formele: if (expresie logica) instructiune; if (expresie logica) instructiune; else instructiune; Instructiunea switch Permite executarea unei instructiuni functie de valoarea unei expresii, n care se poate regsi sau nu a ntro list de valori candidat: a switch (expresie) { case eticheta: instructiune; case eticheta: instructiune; ... default: instructiune; } O etichet reprezint o expresie constant. O instructiune poate s i lipseasc a a a as a i acest caz se va executa instructiunea de la caseul urmtor, sau de la des n a fault. Sectiunea default poate s lipseasc. Dac o instructiune este nevid, a a a a atunci obligatoriu va avea la sfrit o instructiune break sau goto case expreas sieConstanta sau goto default. Expresia dup care se face selectia poate de tip sbyte, byte, short, ushort, a int, uint, long, ulong, char, string, enumerare. Dac valoarea expresiei se a regsete printre valorile specicate la clauzele case, atunci instructiunea a s corespunztoare va executat; dac nu, atunci instructiunea de la clauza a a a default va executat (dac ea exist). Spre deosebire de C i C++, e interzis a a a s s se foloseasc fenomenul de cdere de la o etichet la alta; continuarea a a a a se face folosind explicit goto. Subliniem c se permite folosirea unui selector a de tip string. switch (i) { case 0: Console.WriteLine("0"); break; case 1:

64

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME Console.Write("Valoarea "); goto case 2; case 2: case 3: Console.WriteLine(i); break; case 4: goto default; default: Console.WriteLine("Numar in afara domeniului admis"); break;//neaparat, altfel eroare de compilare

} Remarcm exemplul de mai sus c chiar i cazul lui default e necesar a n a s n s se foloseasc instructiune de salt ( cazul nostru break ); o motivatie ar a a n c aceast clauz default nu e necesar s e trecut ultima switch, ci chiar a a a a a n i pe prima pozitie desigur caz mai rar alnit. s nt Exist un caz care break, goto case valoare sau goto default pot s a n a lipseasc: cnd este evident c o asemenea instructiune break/goto nu ar a a a putea atins (i.e. sunt prezente instructiunile return, throw sau o ciclare a despre care se poate arma la compilare c este innit). a a

3.3.8

Instructiuni de ciclare

Exist 4 instructiuni de ciclare: while, do, for, foreach. a Instructiunea while Permite executarea unei instructiuni atta timp ct valoarea unei expresii a a logice este adevrat (ciclu cu test anterior). a a Sintaxa: while (expresie logica) instructiune; interiorul unei astfel de instructiuni se poate folosi o instructiune de salt In de tip break sau continue. while { r = a = b = } (r != 0) a%b; b; a;

3.3. CONVERSII Instructiunea do

65

Execut o instructiune o dat sau de mai multe ori, ct timp o conditie a a a logic este adevrat (ciclu cu test posterior). a a a Exemplu: do { S += i++; }while(i<=n) Poate contine instructiuni break sau continue. Instructiunea for Execut o secvent de initializare, dup care va executa o instructiune a a a atta timp ct o conditie este adevrat (ciclu cu test anterior); poate s a a a a a contin un pas de reinitializare (trecerea la pasul urmtor). Se permite a a folosirea instructiunilor break i continue. s Exemplu: for (int i=0; i<n; i++) { Console.WriteLine("i={0}", i); } Instructiunea foreach Enumer elementele dintro coletie, executnd o instructiune pentru ecare a a element. Colectia poate s e orice instant a unei clase care implementeaz a a a interfata System.Collections.IEnumerable. Exemplu: int[] t = {1, 2, 3}; foreach( int x in t) { Console.WriteLine(x); } Elementul care se extrage este de tip readonly (deci nu poate transmis ca parametru ref sau out i nu se poate aplica un operator care s schimbe s a i valoarea).

66

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.3.9

Instructiuni de salt

Permit schimbarea ordinii de executie a instructiunilor. Ele sunt: break, continue, goto, return, throw. Instructiunea break Produce ieirea fortat dintrun ciclu de tip while, do, for, foreach. s a Instructiunea continue Pornete o nou iteratie interiorul celui mai apropiat ciclu contintor s a n a de tip while, do, for, foreach. Instructiunea goto Goto permite saltul al o anumit instructiune. Are 3 forme: a goto eticheta; goto case expresieconstanta; goto default; Cerinta este ca eticheta la care se face saltul s e denit cadrul functiei a a n curente i saltul s nu se fac interiorul unor blocuri de instructiuni, s a a n deoarece nu se poate reface ntotdeauna contextul acelui bloc. Se recomand evitarea utilizrii intense a acestui cuvnt cheie, caz cona a a n trar se poate ajunge la fenomenul de spagetti code. Pentru o argumentare consistent a acestei indicatii, a se vedea articolul clasic al lui Edsger W. Dijka stra, Go To Statement Considered Harmful: http://www.acm.org/classics/oct95/ Instructiunea return Determin cedarea controlului funtiei apelante de ctre functia apelat. a a a Dac functia apelat are tip de retur, atunci instructiunea return trebuie a a s e urmat de o expresie care suport o conversie implicit ctre tipul de a a a a a retur.

3.3.10

Instructiunile try, throw, catch, nally

Permit tratarea exceptiilor. Vor studiate detaliu la capitolul de n exceptii.

3.3. CONVERSII

67

3.3.11

Instructiunile checked i unchecked s

Controleaz contextul de vericare de depire a domeniului pentru arita as metica pe ntregi i conversii. Au forma: s checked { //instructiuni } unchecked { //instructiuni } Vericare se va face la runtime.

3.3.12

Instructiunea lock

Obtine excluderea mutual asupra unui obiect pentru executarea unui a bloc de instructiuni. Are forma: lock (x) instructiuni X trebuie s e de tip referint (dac este de tip valoare, nu se face boxing). a a a

3.3.13

Instructiunea using

Determin obtinerea a unei sau mai multor resurse, execut o instructiune a a i apoi disponibilizeaz resursa: s a using ( achizitie de resurse ) instructiune O resurs este o clas sau o structur care implementeaz interfata System.Ia a a a Disposable, care include o sigur metod fr parametri Dispose(). Achizitia a a aa de resurse se poate face sub form de variabile locale sau a unor expresii; toate a acestea trebuie s e implicit convertibile la IDisposable. Variabilele locale a alocate ca resurse sunt readonly. Resursele sunt automat dealocate (prin apelul de Dispose) la sfritul instructiunii (care poate bloc de instructiuni). as Motivul pentru care exist aceast instructiune este unul simplu: uneori a a se dorete ca pentru anumite obiecte care detin resurse importante s se s a apeleze automat metod Dispose() de dezalocare a lor, ct mai repede cu a a putint. a Exemplu:

68

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

using System; using System.IO; class Test { static void Main() { using( TextWriter w = File.CreateText("log.txt") ) { w.WriteLine("This is line 1"); w.EriteLine("This is line 2"); } } }

3.4

Spatii de nume

cazul crerii de tipuri este posibil s se foloseasc un acelai nume In a a a s pentru tipurile noi create de ctre dezvoltatorii de soft. Pentru a putea a folosi astfel de clase care au numele comun, dar responsabiliti diferite, treat buie prevzut o modalitate de a le adresa mod unic. Solutia la aceast a a n a 4 problem este crearea spatiilor de nume care rezolv, printro adresare coma a plet astfel de ambiguiti. Astfel, putem folosi de exemplu clasa Buer din a at spatiul System (calicare complet: System.Buer), alturi de clasa Buer a a din spatiul de nume Curs3: Curs3.Buer. Crearea unui spatiu de nume se face prin folosirea cuvntului namespace: a using System; namespace Curs3 { public class Buffer { public Buffer() { Console.WriteLine("Bufferul meu!"); } } } Se pot de asemenea crea spatii de nume imbricate. Altfel spus, un spatiu de nume este o colectie de tipuri sau de alte spatii de nume.
4

engl: namespaces

3.4. SPATII DE NUME

69

3.4.1

Declaratii de spatii de nume

O declaratie de spatiu de nume const cuvntul cheie namespace, urmat a n a de identicatorul spatiului de nume i de blocul spatiului de nume, delimitat s de acolade. Spatiile de nume sunt implicit publice i acest tip de acces nu se s poate modica. interiorul unui spatiu de nume se pot utiliza alte spatii In de nume, pentru a se evita calicarea complet a claselor. a Identicatorul unui spatiu de nume poate simplu sau o secventa de identicatori separati prin .. Cea de a doua form permite denirea de a spatii de nume imbricate, fr a se imbrica efectiv: aa namespace N1.N2 { class A{} class B{} } este echivalent cu: a namespace N1 { namespace N2 { class A{} class B{} } } Dou declaratii de spatii de nume cu aceeai denumire contribuie la a s declararea unui acelai spatiu de nume: s namespace N1.N2 { class A{} } namespace N1.N2 { class B{} } este echivalent cu cele dou declaratii anterioare. a a

70

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.4.2

Directiva using

Directiva using faciliteaz primul rnd utilizarea spatiilor de nume i a a n a s tipurilor denite acestea; ele nu creeaz membri noi cadrul unitii de n a n at program care sunt folosite, ci au rol de a uura referirea tipurilor. Nu se n s pot utiliza interiorul claselor, structurilor, enumerririlor. n a Exemplu: e mai uor de s nteles un cod de forma: using System; class A { static void Main() { Console.WriteLine("Mesaj"); } } dect: a class A { static void Main() { System.Console.WriteLine("Mesaj"); } } Directiva using poate de fapt folosit att pentru importuri simbolice, a a ct i pentru crearea de aliasuri. a s Directiva using pentru import simbolic O directiv using permite importarea simbolic a tuturor tipurilor continute a a direct ntrun spatiu de nume, i.e. folosirea lor fr a necesar o calicare aa a complet. Acest import nu se refer i la spatiile de nume continute: a as namespace N1.N2 { class A{} } namespace N3.N4 { class B{};

3.4. SPATII DE NUME } namespace N5 { using N1.N2; using N3; class C { A a = null;//ok N4.B = null;//Eroare, N4 nu a fost importat } } Importarea de spatii de nume nu trebuie s duc la ambiguiti: a a at namespace N1 { class A{} } namespace N2 { class A{} } namespace N3 { using N1; using N2; class B { A a = null;//ambiguitate: N1.A sau N2.A? } }

71

situatia de mai sus, conictul (care poate foarte uor cazul care se In s n n folosesc tipuri produse de dezvoltatori diferiti) poate rezolvat de o calicare complet: a namespace N3 { using N1; using N2; class B {

72

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME N1.A a1 = null; N2.A a2 = null;//ok, nu mai este ambiguitate }

} Tipurile declarate interiorul unui spatiu de nume pot avea modicatori de n acces public sau internal (modicatorul implicit). Un tip internal nu poate folosit prin import afara assembly-ului, pe cnd unul public, da. n a Directiva using ca alias Introduce un identicator care servete drept alias pentru un spatiu de s nume sau pentru un tip. Exemplu: namespace N1.N2 { class A{} } namespace N3 { using A = N1.N2.A; class B { A a = null; } } Acelai efect se obtine cre un alias la spatiul de nume N1.N2: s nd namespace N3 { using N = N1.N2; class B { N.A a = null; } } Identicatorul dat unui alias trebuie s e unic, adic interiorul unui a a n namespace nu trebuie s existe tipuri i aliasuri cu acelai nume: a s s

3.4. SPATII DE NUME

73

namespace N3 { class A{} } namespace N3 { using A = N1.N2.A;//eroare, deoarece simbolul A mai este definit } O directiv alias afecteaz doar blocul care este denit: a a n a namespace { using R } namespace { class B { R.A a } } N3 = N1.N2; N3

= null;//eroare, R nu este definit aici

adic directiva de alias nu este tranzitiv. Situatia de mai sus se poate rezolva a a prin declarearea aliasului afara spatiului de nume: n using R = namespace { class B { R.A a } } namespace { class C { R.A b } } N1.N2; N3

= null;

N3

= null;

Numele create prin directive de alias sunt ascunse de ctre alte declaratii a care folosesc acelai identicator interiorul unui bloc: s n

74

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

using R = N1.N2; namespace N3 { class R{} class B { R.A a;//eroare, clasa R nu are membrul A } } Directivele de alias nu se inuenteaz reciproc: a namespace N1.N2{} namespace N3 { using R1 = N1; using R2 = N1.N2; using R3 = R1.N2;//eroare, R1 necunoscut }

Curs 4 Clase
4.1 Declararea unei clase

Declararea unei clase se face felul urmtor: n a atributeopt modicatori-de-clasaopt class identicator clasa-de-bazaopt corpclasa ;opt Modicatorii de clas sunt: a public - clasele publice sunt accesibile de oriunde; poate folosit att pentru a clase interioare, ct i pentru clase care sunt continute spatii de a s n nume; internal - se poate folosi att pentru clase interioare, ct i pentru clase care a a s sunt continute spatii de nume (este modicatorul implicit pentru n clase care sunt continute spatii de nume). Semnic acces permis n a doar clasa sau spatiul de nume care o cuprinde; n protected - se poate specica doar pentru clase interioare; tipurile astfel calicate sunt accesibile clasa curent sau cele derivate (chiar n a n dac clasa derivat face parte din alt spatiu de nume); a a private - doar pentru clase interioare; semnic acces limitat la clasa cona intoare; este modicatorul implicit; t a protected internal - folosibil doar pentru clase interioare; tipul denit este accesibil spatiul de nume curent, clasa contintoare sau tipurile n n a n derivate din clasa contintoare; a new - permis pentru clasele interioare; clasa astfel calicat ascunde un a membru cu acelai nume care este motenit; s s 75

76

CURS 4. CLASE

sealed - o clas sealed nu poate motenit; poate clas interioar sau a s a a a nu; abstract - clasa care este incomplet denit i care nu poate instantiat; as a folosibil pentru clase interioare sau continute spatii de nume; a n

4.2

Membrii unei clase

Corpul unei clase se specic felul urmtor: a n a { declaratii-de-membri };opt Membrii unei clase sunt artiti urmtoarele categorii: mp n a constante cmpuri a metode proprieti at evenimente indexatori operatori constructori (de instant) a destructor constructor static tipuri Acestor membri le pot ataati modicatorii de acces: s public - membrul este accesibil de oriunde; protected - membrul este accesabil de ctre orice membru al clasei contintoare a a i de ctre clasele derivate; s a internal - membrul este accesabil doar assembly-ul curent; n protected internal - reuniunea precedentelor dou; a private accesabil doar clasa contintoare; este specicatorul implicit. n a

4.3. CAMPURI

77

4.3

Cmpuri a

Un cmp reprezint un membru asociat cu un obiect sau cu o clas. a a a Modicatorii de cmp care se pot specica optional a naintea unui cmp sunt a cei de mai sus, la care se adaug modicatorii new, readonly, volatile, static, a ce vor prezentati mai jos. Pentru orice cmp este necesar precizarea unui a a tip de date, ce trebuie s aibe gradul de accesibilitate cel putin cu al cmpului a a ce se declar. Optional, cmpurile pot initializate cu valori compatibile. a a Un astfel de cmp se poate folosi e prin specicarea numelui su, e printr-o a a calicare bazat pe numele clasei sau al unui obiect. a Exemplu: class A { int a;//acces implicit de tip privat static void Main() { A objA = new A(); objA.a = 1;//se poate accesa in interiorul clasei } }

4.3.1

Cmpuri instante a

Dac o declaratie de cmp nu include modicatorul static, atunci acel a a cmp se va regsi orice obiect de tipul clasei curente care va instantiat. a a n Modicri ale acestor cmpuri se vor face independent pentru ecare obiect. a a Deoarece un astfel de cmp are o valoare specic ecrui obiect, accesarea a a a lui se va face prin calicarea cu numele obiectului: objA.a = 1; (dac modicatorii de acces permit aa ceva). a s Un cmp special este this (cuvnt cheie) care reprezint o referint la a a a a obiectul curent.

4.3.2

Cmpuri statice a

Cnd o declaratie de cmp include un specicator static, cmpul respectiv a a a nu apatine ecrei instante particular, ci clasei ai. Accesarea unui a n nss cmp static din exteriorul clasei se face exclusiv prin intermediul numelui de a clas: a

78 class B { public static int V = 3; static void Main() { B.V++; } }

CURS 4. CLASE

Dac se face calicarea unui astfel de cmp folosind un nume de obiect se a a semnaleaz o eroare de compilare. a

4.3.3

Cmpuri readonly a

Declararea unui cmp de tip readonly (static sau nu) se face prin specia carea cuvntului readonly declaratia sa: a n class A { public readonly string salut = Salut; public readonly string nume; public class A(string nume) { this.nume = nume; } } Atribuirea asupra unui cmp de tip readonly se poate face doar la declararea a sa (ca exemplu de mai sus) sau prin intermediul unui constructor. Valoarea n unor astfel de cmpuri nu se presupune a cunoscut la compilare. a a

4.3.4

Cmpuri volatile a

Modicatorul volatile se poate specica doar pentru tipurile: byte, sbyte, short, ushort, int, uint, char, oat, bool; un tip enumerare avnd tipul de reprezentare byte, sbyte, short, ushort, a int, uint; un tip referint a

4.4. CONSTANTE

79

Pentru cmpuri nevolatile, tehnicile de optimizare care reordoneaz instructiunile a a pot duce la rezultate neateptate sau nepredictibile programe multithreads n ing care acceseaz cmpurile fr sincronizare (efectuabil cu instructiunea a a aa a lock). Aceste optimizri pot fcute de ctre compilator, de ctre sistemul a a a a 1 de rulare sau de ctre hardware. Urmtoarele tipuri de optimizri sunt a a a afectate prezenta unui modicator volatile: n citirea unui cmp volatile este garantat c se va ampla a a a nt nainte de orice referire la cmp care apare dup citire; a a orice scriere a unui cmp volatile este garantat c se va petrece dup a a a a orice instructiune anterioar care se refer la cmpul respectiv. a a a

4.3.5

Initializarea cmpurilor a

Pentru ecare cmp declarat se va asigna o valoare implicit astfel: a a numeric: 0 bool: false char: \0 enum: 0 referint: null a

4.4

Constante

O constant este un cmp a crui valoare poate calculat la compia a a a lare. O constant poate prexat de urmtorii modicatori: new, public, a a a protected, internal, protected internal,private. Cuvantul new poate s se coma bine cu unul din ceilalti 4 modicatori de acces. Pentru un cmp constant e a obligatoriu s se asigneze o valoare calculabil la compilare: a a class A { public const int n=2; }
1

Engl: runtime system

80

CURS 4. CLASE

Tipul unei constante poate sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double decimal, bool, string, enum, referint. Valoarea care se a asignez unei constante trebuie s admit o conversie implicit ctre tipul a a a a a constantei. Tipul unei constante trebuie s e cel putin la fel de accesibil ca a i constanta ai. s nss Orice cmp constant este automat un cmp static. Un cmp constant a a a difer de un cmp static readonly: const-ul are o valoare cunoscut la coma a a pilare, pe cnd valoarea unui readonly poate initializat la runtime a a n interiorul constructorului (cel mai trziu, de altfel). a

4.5

Metode

O metod este un membru care implementeaz un calcul sau o actiune a a care poate efectuat de ctre un obiect sau o clas. Antetul unei metode a a a se declar felul urmtor: a n a atributeopt modicator-de-metodaopt tip-de-retur nume (lista-parametrilorformaliopt ) corp-metoda unde modicator-de-metoda poate : orice modicator de acces new static virtual sealed override abstract extern Tipul de retur poate orice tip de dat care este cel putin la fel de accesibil a ca i metoda ai sau void (absenta informatiei returnate); nume poate un s nss identicator de metod din clasa curent sau un identicator calicat cu nua a mele unei interfete pe care o implementeaz (NumeInterfata.numeMetoda); a parametrii pot de tip ref, out, params, sau fr nici un calicator; corpaa metoda este un bloc cuprins ntre acolade sau doar caracterul ; (dac este a vorba de o metod ce nu se implementeaz tipul curent). a a n Despre calicatorii virtual, override, sealed, new, abstract se va discuta mai pe larg ntro sectiune viitoare.

4.6. PROPRIETATI

81

4.5.1

Metode statice i nestatice s

O metod se declar a static dac numele ei este prexat cu moda a a a icatorul de metod static. O astfel de metod nu opereaz asupra unei a a a instante anume, ci doar asupra clasei. Este o eroare ca o metod static a a s fac referire la un membru nestatic al unei clase. Apelul unei astfel de a a metode se face prin NumeClasa.NumeMetoda sau direct NumeMetoda dac a este apelat din context static al aceleiai clase. a s O metod nestatic nu are cuvntul static specicat; ea este apelabil a a a a conjunctie cu un obiect anume. n

4.5.2

Metode externe

Metodele externe se declar folosind modicatorul extern; acest tip de a metode sunt implementate extern, de obicei alt limbaj dect C#. Deoarece n a o astfel de metod nu contine o implementare, corpul acestei metode este ;. a Exemplu: se utilizeaz metoda MessageBox importat din biblioteca dll a a User32.dll: using System; using System.Runtime.InteropServices; class Class1 { [DllImport("User32.dll")] public static extern int MessageBox(int h, string m, string c, int type); static void Main(string[] args) { int retVal = MessageBox(0, "Hello", "Caption", 0); } }

4.6

Proprieti at

O proprietate este un membru care permite acces la un atribut al unui obiect sau al unei clase. Exemple de proprieti sunt: lungimea unui ir, nuat s mele unui client, textul continut ntrun control de tip TextBox. Proprietile at sunt extensii naturale ale cmpurilor, cu deosebirea c ele nu presupun aloa a carea de memorie. Ele sunt de fapt nite metode (accesori) care permit citirea s sau setarea unor atribute ale unui obiect sau clase; reprezint modalitatea a de scriere a unor metode de tip get/set pentru clase sau obiecte.

82

CURS 4. CLASE

Declararea unei proprieti se face astfel: at atributeopt modicatori-de-proprietateopt tip nume {declaratii-de-accesor} Modicatorii de proprietate sunt: new, public, protected, internal, private, static, virtual, sealed, override, abstract, extern. Tipul unei proprieti specic tipul de proprietate introdus de declaratie, at a i.e. ce valori vor putea atribuite proprietii respective (dac accesorul de at a tip set a fost denit), respectiv care este tipul valorii returnate de aceast a proprietate (corespunztor accesorului de tip get). a Exemplu: using System; class Circle { private double radius; public double Radius { get { return radius; } set { radius = value; } } public double Area { get { return Math.PI * radius * radius; } set { radius = Math.Sqrt(value/Math.PI); } } } class Test { static void Main()

4.6. PROPRIETATI { Circle c = new Circle(); c.Radius = 10; Console.WriteLine(Area: {0}, c.Area); c.Area = 15; Console.WriteLine(Radius: {0}. c.Radius) } }

83

Un accesor de tip get corespunde unei metode fr parametri, care returneaz aa a o valoare de tipul proprietii. Cnd o proprietate este folosit at a a ntro expresie, accesorul get este o apelat pentru a returna valoarea cerut. a Un accesor de tip set corespunde unei metode cu un singur parametru de tipul proprietii i tip de retur void. Acest parametru implicit al lui set at s este numit ntotdeauna value. Cnd o proprietate este folosit ca destinatar a a ntro atribuire, sau cnd se folosesc operatorii ++ i , accesorului set i a s se transmite un parametru care reprezint noua valoare. a functie de prezenta sau absenta accesorilor, o proprietate este clasiIn cat dup cum urmeaz: a a a proprietate readwrite, dac are ambele tipuri de accesori; a proprietate readonly, dac are doar accesor de tip get; este o eroare de a compilare s se fac referire program la o proprietate sensul a a n n n care sar cere operarea cu un accesor de tip set; proprietate writeonly, dac este prezent doar accesorul de tip set; este a o eroare de compilare utilizarea unei proprieti at ntrun context care n ar necesar prezenta accesorului get. a Demn de mentionat este c ei pot folositi nu numai pentru a asigura a o sintax simplu de folosit pentru metodele traditionale de tip get/set, ci i a s pentru scrierea controalelor .NET utilizator, care se face extrem de uor. s gura 4.1 este dat reprezentarea unui control utilizator: In a Codul corespunztor este dat mai jos: a using using using using using using System; System.Collections; System.ComponentModel; System.Drawing; System.Data; System.Windows.Forms;

84

CURS 4. CLASE

Figura 4.1: Control denit de utilizator

namespace UserControlSample { public class UserControl1 : System.Windows.Forms.UserControl { private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox streetTextBox; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox numberTextBox; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox phoneTextBox; private System.ComponentModel.Container components = null; public UserControl1() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); } protected override void Dispose( bool disposing ) { if( disposing ) { if( components != null ) components.Dispose(); } base.Dispose( disposing ); } #region Component Designer generated code private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label();

4.6. PROPRIETATI

85

this.streetTextBox = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.numberTextBox = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); this.phoneTextBox = new System.Windows.Forms.TextBox(); this.SuspendLayout(); this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(8, 16); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(34, 13); this.label1.TabIndex = 0; this.label1.Text = "Street"; this.streetTextBox.Location = new System.Drawing.Point(56, 14); this.streetTextBox.Name = "streetTextBox"; this.streetTextBox.TabIndex = 1; this.streetTextBox.Text = ""; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(8, 48); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(44, 13); this.label2.TabIndex = 2; this.label2.Text = "Number"; this.numberTextBox.Location = new System.Drawing.Point(56, 44); this.numberTextBox.Name = "numberTextBox"; this.numberTextBox.TabIndex = 3; this.numberTextBox.Text = ""; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(8, 79); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(37, 13); this.label3.TabIndex = 4; this.label3.Text = "Phone"; this.phoneTextBox.Location = new System.Drawing.Point(56, 75); this.phoneTextBox.Name = "phoneTextBox"; this.phoneTextBox.TabIndex = 5; this.phoneTextBox.Text = ""; this.Controls.AddRange(new System.Windows.Forms.Control[] { this.phoneTextBox, this.label3, this.numberTextBox, this.label2,

86

CURS 4. CLASE this.streetTextBox, this.label1}); this.Name = "UserControl1"; this.Size = new System.Drawing.Size(168, 112); this.ResumeLayout(false); } #endregion [Category ("Data"), Description ("Contents of Street Control")] public string Street { get{ return streetTextBox.Text; } set{ streetTextBox.Text = value; } } [Category ("Data"), Description ("Contents of Number Control")] public string Number { get{ return numberTextBox.Text; } set{ numberTextBox.Text = value; } } [Category ("Data"), Description ("Contents of Phone Control")] public string Phone { get{ return phoneTextBox.Text; } set{ phoneTextBox.Text = value; } } }

Interesante sunt aici proprietile publice Street, Number i Phone care vor at s vizibile fereastra Properties atunci cnd acest control va adugat la o n a a form. Atributele cuprinse a ntre paranteze drepte sunt optionale, dar vor face ca aceste proprieti s e grupate sectiunea de date a ferestrei Properties, at a n i nu cea Misc. Pentru mai multe detalii despre componente utilizator, s n a se vedea [1], cap. 4.

4.7. INDEXATORI

87

4.7

Indexatori

Uneori are sens tratarea unei clase ca ind un array. Este o generalizare a supra arcrii operatorului [] din C++, ind o facilitate ce d o mare nc a a exibilitate. Declararea unui indexator se face felul urmtor: n a atributeopt modicatori-de-indexatoropt declarator-de-indexator {declaratiide-accesor} Modicatorii de indexator pot : new, public, protected, internal, private, virtual, sealed, override, abstract, extern. Declaratorul de indexator are forma: tip-de-retur this[lista-parametrilor-formali]. Lista parametrilor formali trebuie s contin cel putin un parametru i nu a a s poate s aibe vreun parametru de tip ref sau out. Declaratiile de accesor vor a contine accesor get sau accesor set, asemntor cu cei de la proprieti. a a at Exemple: 1. Exemplul 1: un indexator simplu: using System; class MyVector { private double[] v; public MyVector( int length ) { v = new double[ length ]; } public int Length { get { return length; } } public double this[int index] { get { return v[ index]; } set

88 { v[index] = value; } } }

CURS 4. CLASE

class Test { static void Main() { MyVector v = new MyVector( 10 ); v[0] = 0; v[1] = 1; for( int i=2; i<v.Length; i++) { v[i] = v[i-1] + v[i-2]; } for( int i=0; i<v.Length; i++) { Console.WriteLine(v[ + i.ToString() + ]= + v[i]); } } } 2. Exemplul 2: supra arcarea indexatorilor: nc using System; using System.Collections; class DataValue { public DataValue(string name, object data) { this.name = name; this.data = data; } public string Name { get { return(name); }

4.7. INDEXATORI set { name = value; } } public object Data { get { return(data); } set { data = value; } } string name; object data; } class DataRow { ArrayList row; public DataRow() { row = new ArrayList(); } public void Load() { row.Add(new DataValue("Id", 5551212)); row.Add(new DataValue("Name", "Fred")); row.Add(new DataValue("Salary", 2355.23m)); } public object this[int column] { get { return(row[column - 1]);

89

90 } set { row[column - 1] = value; } }

CURS 4. CLASE

int FindColumn(string name) { for (int index = 0; index < row.Count; index++) { DataValue dataValue = (DataValue) row[index]; if (dataValue.Name == name) return(index); } return(-1); } public object this[string name] { get { return this[FindColumn(name)]; } set { this[FindColumn(name)] = value; } } } class Test { public static void Main() { DataRow row = new DataRow(); row.Load(); DataValue val = (DataValue) row[0]; Console.WriteLine("Column 0: {0}", val.Data); val.Data = 12; // set the ID DataValue val = (DataValue) row["Id"];

4.7. INDEXATORI

91

Console.WriteLine("Id: {0}", val.Data); Console.WriteLine("Salary: {0}", ((DataValue) row["Salary"]).Data); ((DataValue)row["Name"]).Data = "Barney"; // set the name Console.WriteLine("Name: {0}", ((DataValue) row["Name"]).Data); } } 3. Exemplul 3: Indexator cu mai multi parametri: using System; namespace MyMatrix { class Matrix { double[,] matrix; public Matrix( int rows, int cols ) { matrix = new double[ rows, cols]; } public double this[int i, int j] { get { return matrix[i,j]; } set { matrix[i,j] = value; } } public int RowsNo { get { return matrix.GetLength(0); } }

92

CURS 4. CLASE

public int ColsNo { get { return matrix.GetLength(1); } } static void Main(string[] args) { MyMatrix m = new MyMatrix(2, 3); Console.WriteLine("Lines: {0}", m.RowsNo); Console.WriteLine("Columns: {0}", m.ColsNo); for(int i=0; i<m.RowsNo; i++) { for( int j=0; j<m.ColsNo; j++) { m[i,j] = i + j; } } for(int i=0; i<c.RowsNo; i++) { for( int j=0; j<c.ColsNo; j++) { Console.Write(c[i,j] + " "); } Console.WriteLine(); } } } }

4.8

Operatori

Un operator este un membru care denete semnicatia unei expresii ops erator care poate aplicat unei instante a unei clase. Corespunde supra arcrii a nc a operatorilor din C++. O declaratie de operator are forma: atributeopt modicatori-de-operator declaratie-de-operator corp-operator Se pot declara operatori unari, binari i de conversie. s

4.8. OPERATORI Urmtoarele reguli trebuie s e respectate pentru orice operator: a a 1. Orice operator trebuie s e declarat public i static. a s

93

2. Parametrii unui operator trebuie s e de tip valoare; nu se admite s a a e de tip ref sau out. 3. Acelai modicator nu poate aprea de mai multe ori antetul unui s a n operator

4.8.1

Operatori unari

Supra arcarea operatorilor unari are forma: nc tip operator operator-unar-supraincarcabil (tip identicator) corp Operatorii unari supra arcabili sunt: + - ! ++ true false. Urmtoarele nc a reguli trebuie s e respectate la supra arcarea unui operator unar (T a nc reprezint clasa care contine denitia operatorului): a 1. Un operator +, -, !, trebuie s preia un singur parametru de tip T i a s poate returna orice tip. 2. Un operator ++ sau trebuie s preia un singur parametru de tip T a i trebuie s returneze un rezultat de tip T. s a 3. Un operator unar true sau false trebuie s preia un singur parametru a de tip T i s returneze bool. s a Operatorii true i false trebuie s e ori ambii deniti, ori nici unul (altfel s a apare o eroare de compilare). Ei sunt necesari pentru cazuri de genul: if( a==true ) sau if( a==false ) Mai general, ei pot folositi ca expresii de control if, do, while i for, n s precum i operatorul ternar ? :. Dei pare paradoxal, nu este obligatoriu s n s ca if (a==true) s e echivalent cu if (!(a==false)), de exemplu pentru a a tipuri SQL care pot null, ceea ce nu nseam nici true, nici false, ci lips a a de informatie. Exemplu:

94 public struct DBBool { private int x; public static bool operator true(DBBool x) { return x.value > 0; } public static bool operator false(DBBool x) { return x.value <= 0; } ... }

CURS 4. CLASE

Exemplul de mai jos arat modul care se face supra arcarea operaa n nc torului ++, care poate folosit att ca operator de preincrementare ct i a a s ca operator de postincrementare: public class IntVector { public int Length { ... } // read-only property public int this[int index] { ... } // read-write indexer public IntVector(int vectorLength) { ... } public static IntVector operator++(IntVector iv) { IntVector temp = new IntVector(iv.Length); for (int i = 0; i < iv.Length; ++i) temp[i] = iv[i] + 1; return temp; } } class Test { static void Main() { IntVector iv1 = new IntVector(4); // vector of 4x0 IntVector iv2; iv2 = iv1++; // iv2 contains 4x0, iv1 contains 4x1 iv2 = ++iv1; // iv2 contains 4x2, iv1 contains 4x2 } }

4.8. OPERATORI

95

4.8.2

Operatori binari

Declararea unui operator binar se face astfel: tip operator operator-binar-supraincarcabil ( tip identicator, tip identicator) corp Operatorii binari supra arcabili sunt: + - * / % & | ^ << >> == != > < >= <=. nc Cel putin unul dintre cei doi parametri preluati trebuie s e de tipul contintor. a a Operatorii de shiftare trebuie s aib primul parametru de tipul clasei care a a n se declar, iar al doilea parametru de tip int. Unii operatori trebuie s se a a declare pereche: n 1. operatorii == i != s 2. operatorii > i < s 3. operatorii >= i <= s Pentru operaratorul ==, este indicat i denirea metodei Equals(), deoarece as tipul respectiv va putea astfel folosit i de ctre limbaje care nu suport s a a supra arcarea operatorilor, dar pot apela metoda polimorc Equals() denit nc a a clasa object. n Nu se pot supa arca operatorii + =, =, / =, =; dar pentru ca nc acetia s functioneze, este sucient s se supra s a a ncarce operatorii corespunztori: a +, , /, .

4.8.3

Operatori de conversie

O declaratie de operator de conversie trebuie introduce o conversie denit a de utilizator, care se va aduga (dar nu va suprascrie) la conversiile predea nite. Declararea unui operator de conversie se face astfel: implicit operator tip (tip parametru) corp explicit operator tip (tip parametru) corp Dup cum se poate deduce, conversiile pot implicite sau explicite. Un astfel a de operator va face conversia de la un tip surs, indicat de tipul parametrua lui din antet la un tip destinatie, indicat de tipul de retur. O clas poate s a a declare un operator de conversie de la un tip surs S la un tip destinatie T a cu urmtoarele conditii: a 1. S i T sunt tipuri diferite s 2. Unul din cele dou tipuri este clasa care se face denirea. a n 3. T i S nu sunt object sau tip interfat. s a

96 4. T i S nu sunt baze una pentru cealalt. s a

CURS 4. CLASE

Un bun design asupra operatorilor de conversie are vedere urmtoarele: n a Conversiile implicite nu ar trebui s duc la pierdere de informatie sau a a la aparitia de exceptii; Dac prima conditie nu este a ndeplinit, atunci neaprat trebuie declarat a a a ca o conversie explicit. a Exemplu: using System; public class Digit { byte value; public Digit(byte value) { if (value < 0 || value > 9) throw new ArgumentException(); this.value = value; } public static implicit operator byte(Digit d) { return d.value; } public static explicit operator Digit(byte b) { return new Digit(b); } } Prima conversie este implicit pentru c nu va duce la pierderea de informatie. a a Cea de doua poate s arunce o exceptie (via constructor) i de aceea este a s declarat ca i conversie explicit. a s a

4.8.4

Exemplu: clasa Fraction

using System; public class Fraction { public Fraction(int numerator, int denominator) { Console.WriteLine("In constructor Fraction(int, int)");

4.8. OPERATORI this.numerator=numerator; this.denominator=denominator; } public Fraction(int wholeNumber) { Console.WriteLine("In Constructor Fraction(int)"); numerator = wholeNumber; denominator = 1; }

97

public static implicit operator Fraction(int theInt) { System.Console.WriteLine("In conversie implicita la Fraction"); return new Fraction(theInt); } public static explicit operator int(Fraction theFraction) { System.Console.WriteLine("In conversie explicita la int"); return theFraction.numerator / theFraction.denominator; } public static bool operator==(Fraction lhs, Fraction rhs) { Console.WriteLine("In operator =="); if (lhs.denominator * rhs.numerator == rhs.denominator * lhs.numerator ) { return true; } return false; } public static bool operator !=(Fraction lhs, Fraction rhs) { Console.WriteLine("In operator !="); return !(lhs==rhs); } public override bool Equals(object o) { Console.WriteLine("In metoda Equals"); if (! (o is Fraction) ) {

98 return false; } return this == (Fraction) o;

CURS 4. CLASE

} public static Fraction operator+(Fraction lhs, Fraction rhs) { Console.WriteLine("In operator+"); // 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8 int firstProduct = lhs.numerator * rhs.denominator; int secondProduct = rhs.numerator * lhs.denominator; return new Fraction( firstProduct + secondProduct, lhs.denominator * rhs.denominator ); //ar mai trebui facuta reducerea termenilor } public override string ToString( ) { String s = numerator.ToString( ) + "/" + denominator.ToString( ); return s; } private int numerator; private int denominator; } public class Tester { static void Main( ) { Fraction f1 = new Fraction(3,4); Console.WriteLine("f1: {0}", f1.ToString( )); Fraction f2 = new Fraction(2,4); Console.WriteLine("f2: {0}", f2.ToString( )); Fraction f3 = f1 + f2; Console.WriteLine("f1 + f2 = f3: {0}", f3.ToString( )); Fraction f4 = f3 + 5; Console.WriteLine("f3 + 5 = f4: {0}", f4.ToString( )); Fraction f5 = new Fraction(2,4); if (f5 == f2) { Console.WriteLine("F5: {0} == F2: {1}",

4.9. CONSTRUCTORI DE INSTANTA f5.ToString( ), f2.ToString( )); } } }

99

4.9

Constructori de instant a

Un constructor de instant este un membru care implementeaz actiuni a a care sunt cerute pentru a initializa o instant a unei clase. Declararea unui a astfel de constructor se face felul urmtor: n a atributeopt modicatori-de-constructor declarator-de-constructor corp-constructor Un modicator de constructor poate : public, protected, internal, private, extern. Un declarator de constructor are forma: nume-clasa (lista-parametrilor-formaliopt ) initializator-de-constructoropt unde initializatorul-de-constructor are forma: : base( lista-argumenteopt ) sau : this( lista-argumenteopt ). Corp-constructor poate : un bloc de declaratii i instructiuni delimitat de s acolade sau caracterul punct i virgul. s a Un constructor are acelai nume ca i clasa din care face parte i nu s s s returneaz un tip. Constructorii de instant nu se motenesc. Dac o clas a a s a a nu contine nici o declaratie de constructor de instant, atunci compilatorul va a crea automat unul implicit. O clas care este motenit dintr-o alt clas ce a s a a a nu are constructori fr parametri va trebui s utilizeze un apel de constructor aa a de clas de baz pentru care s furnizeze parametrii potriviti; acest apel se a a a face prin intermediul initializatorului de constructor. Un constructor poate apela la un alt constructor al clasei din care face parte pentru a efectua initializi. Cnd exist cmpuri instant care au o expresie de initializare r a a a a afara constructorilor clasei respective, atunci aceste initializri se vor face n a nainte de apelul de constructor al clasei de baz. Pentru a nu se permite a crearea sau motenirea unei clase trebuie ca aceast clas s contin cel putin s a a a a un constructor denit de utilizator i toti constructorii s e declarati privati. s a

4.10

Constructori statici

Un constructor static este un membru care implementeaz actiunile cerute a pentru initializara unei clase. Declararea unui constructor static se face ca mai jos:

100

CURS 4. CLASE

atributeopt modicator-de-constructor-static identicator( ) corp Modicatorii de constructori statici se pot da sub forma: externopt static sau static externopt Constructorii statici nu se motenesc, nu se pot apela direct i nu se pot supras s arca. Un constructor static se va executa cel mult o dat nc a ntr-o aplicatie. Se garanteaz faptul c acest constructor se va apela a a naintea crerii vreunei a instante a clasei respective sau naintea accesului la un membru static. Acest apel este nedeterminist, necunoscnduse exact cnd sau dac se va apela. a a a Un astfel de constuctor nu are specicator de acces i poate s acceseze doar s a membri statici. Exemplu: class Color { public Color(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } int red; int green; int blue; public static readonly Color Red; public static readonly Color Green; public static readonly Color Blue; // constructor static static Color() { Red = new Color(255, 0, 0); Green = new Color(0, 255, 0); Blue = new Color(0, 0, 255); } } class Test { static void Main() { Color background = Color.Red;

4.11. CLASE INTERIOARE } }

101

4.11

Clase interioare

O clas contine membri, iar particular acetia pot i clase. a n s s Exemplul 1: using System; class A { class B { public static void F() { Console.WriteLine(A.B.F); } } static void Main() { A.B.F(); } } Exemplul 2: public class List { // Private data structure private class Node { public object Data; public Node Next; public Node(object data, Node next) { this.Data = data; this.Next = next; } } private Node first = null; private Node last = null;

102 //Interfata publica public void AddToFront(object o) {...} public void AddToBack(object o) {...} public object RemoveFromFront() {...} public object RemoveFromBack() {...} public int Count { get {...} } }

CURS 4. CLASE

Accesarea unei clase interioare se face prin NumeClasaExterioara.NumeClasaInterioara (aa cum este artat Exemplul 1), de unde se deduce c o s a n a clas interioar se comport ca un membru static al tipului contintor. O a a a a clas declarat interiorul unei alte clase poate avea unul din gradele de a a n accesibilitate public, protected internal, protected, internal, private (implicit este private). O clas declarat interiorul unei structuri poate declarat a a n a public, internal sau private (implicit private). Crearea unei instante a unei clase interioare nu trebuie s e precedat a a de crearea unei instante a clasei exterioare contintoare, aa cum se vede a s din Exemplul 1. O clas interioar nu au vreo relatie special cu membrul a a a predenit this al clasei contintoare. Altfel spus, nu se poate folosi this a n interiorul unei clase interioare pentru a accesa membri instant din tipul a contintor. Dac o clas interioar are nevoie s acceseze membri instant ai a a a a a a clasei contintoare, va trebui s primeasc prin constructor parametrul this a a a care s se refer la o astfel de instant: a a a using System; class C { int i = 123; public void F() { Nested n = new Nested(this); n.G(); } public class Nested { C this_c; public Nested(C c) { this_c = c; } public void G()

4.11. CLASE INTERIOARE { Console.WriteLine(this_c.i); } } } class Test { static void Main() { C c = new C(); c.F(); } }

103

Se observ de mai sus c o clas interioar poate manipula toti membrii care a a a a sunt accesibili interiorul clasei contintoare, indiferent de gradul lor de n a accesibilitate. cazul care clasa exterioar (contintoare) are membri In n a a statici, acetia pot utilizati fr a se folosi numele clasei contintoare: s aa a using System; class C { private static void F() { Console.WriteLine("C.F"); } public class Nested { public static void G() { F(); } } } class Test { static void Main() { C.Nested.G(); } }

104

CURS 4. CLASE

Clasele interioare se folosesc intens cadrul containerilor pentru care tren buie s se construiasc un enumerator. Clasa interioar va acest caz a a a n strns legat de container i va duce la o implementare uor de urmrit i de a a s s a s ntretinut.

Curs 5 Destructori. POO C# n


5.1 Destructori

Managementul memoriei este fcut sub platforma .NET mod automat, a n de ctre garbage collector, parte component a CLRului. Pentru o explicatie a a asupra modului cum actioneaz garbage collector-ul, a se vedea [5], pag. 225 a i urm. s general, acest garbage collector scutete programatorul de grija dealocrii In s a memoriei. Dar anumite situatii, se dorete s se fac management mann s a a ual al dealocrii resurselor (de exemplu al resurselor care in de sistemul de a t operare: iere, conexiuni la retea sau la baza de date, ferestre, etc, sau al s altor resurse al cror management nu se face de ctre CLR). C# exist a a In a posibilitatea de a lucra cu destructori, sau cu metode de tipul Dispose(), Close(). Un destructor se declar felul urmtor: a n a atributeopt externopt ~identicator() corp-destructor unde identicator este numele clasei. Un destructor nu are modicator de acces, nu poate apelat manual, nu poate supra arcat, nu este motenit. nc s Un destructor este o scurttur sintactic pentru metoda Finalize(), care a a a este denit clasa System.Object. Programatorul nu poate s suprascrie a n a sau s apeleze aceast metod. a a a Exemplu: ~MyClass() { // Perform some cleanup operations here. } Metoda de mai sus este automat translatat a n: 105

106

CURS 5. DESTRUCTORI. POO C# IN

protected override void Finalize() { try { // Perform some cleanup operations here. } finally { base.Finalize(); } } Problema cu destructorul este c el e chemat doar de ctre garbage colleca a tor, dar acest lucru se face nedeterminist (cu toate c apelarea de destructor a se face cele din urm, dac programatorul nu n a a mpiedic explicit acest a lucru). Exist cazuri care programatorul dorete s fac dealocarea manual, a n s a a astfel at s nu atepte ca garbage collectorul s apeleze destructorul. Pronc a s a gramatorul poate scrie o metoda care s fac acest lucru. Se sugereaz a a a denirea unei metode Dispose() care ar trebui s e explicit apelat atunci a a plus, clasa recnd resurse de sistem de operare trebuie s e eliberate. In a a spectiv ar trebui s implementeze interfata System.IDisposable, care contine a a aceast metod. a a acest caz, Dispose() ar trebui s inhibe executarea ulterioar de garbage In a a collector pentru instanta curent. Aceast manevr permite evitarea eliberrii a a a a unei resurse de dou ori. Dac clientul nu apeleaz explicit Dispose(), atunci a a a garbage collectorul va apela el destructorul la un moment dat. Intruct a utilizatorul poate s nu apeleze Dispose(), este necesar ca tipurile care ima plemeteaz aceast metod s deneasc de asemenea destructor. a a a a a Exemplu: public class ResourceUser: IDisposable { public void Dispose() { hwnd.Release();//elibereaza o fereastra in Win32 GC.SuppressFinalization(this);//elimina apel de Finalize() } ~ResourceUser() { hwnd.Release();

5.2. SPECIALIZAREA SI GENERALIZAREA } }

107

Pentru anumite clase C# se pune la dispozitie o metod numit Close() a a n locul uneia Dispose(): siere, socket-uri, ferestre de dialog, etc. Este indicat ca s se adauge o metod Close() care s fac doar apel de Dispose(): a a a a //in interiorul unei clase public void Close() { Dispose(); } Modalitatea cea mai indicat este folosirea unui bloc using, caz care se va a n elibera obiectul alocat (via metoda Dispose()) la sfritul blocului: as using( obiect ) { //cod }//aici se va apela automat metoda Dispose()

5.2

Specializarea i generalizarea s

Specializarea reprezint o tehnic de a obtine noi clase pornind de la cele a a existente. Deseori ntre clasele pe care le modelm putem observa relatii de a genul este un/o: un om este un mamifer, un salariat este un angajat, etc. Toate acestea duc la crearea unei ierarhii de clase, care din clase de baz n a (mamifer sau angajat) descind alte clase, care pe lng cmpuri din clasa de a a a baz mai au i caracteristici proprii. Obtinerea unei clase derivate plecnd a s a de la alt clas se numete specializare, operatia invers purtnd numele de a a s a a generalizare. O clas de baz denete un tip comun, compatibil cu oricare a a s din clasele derivate (direct sau indirect). C# o clas nu trebuie s moteneasc explicit din alt clas; acest In a a s a a a n caz se va considera c ea este implicit derivat din clasa predenit object a a a (tot una cu System.Object). C# nu permite motenire multipl, eliminnd s a a astfel complicatiile alnite C++. Ca alternativ, se permite totui im nt n a s plementarea de mai multe interfete (la fel ca Java). n

5.2.1

Specicarea motenirii s

C# se pentru o clas D se denete clasa de baz B folosind urmtoarea In a s a a formul: a

108 class D: B { //declaratii }

CURS 5. DESTRUCTORI. POO C# IN

Dac pentru o anumit clas nu se specic dou puncte urmate de numele a a a a a unei clase de baz atunci object va deveni baz pentru clasa cauz. a a n a Exemplu: //clasa de baza in C# public class Employee { protected string name; protected string ssn; } //clasa derivata in C# public class Salaried : Employee { protected double salary; public Salaried( string name, string ssn, double salary ) { this.name = name; this.ssn = ssn; this.salary = salary; } } Se observ c cmpurile name i ssn din clasa de baz sunt accesibile clasa a a a s a n derivat, datorit specicatorului de acces protected. a a

5.2.2

Apelul constructorilor din clasa de baz a

exemplul anterior nu sa denit nici un constructor clasa de baz In n a Employee; constructorul clasei derivate trebuie s fac initializrile cmpurilor a a a a conformitate cu parametrii transmii, chiar dac o parte din aceste cmpuri n s a a provin din clasa de baz. Mai logic ar ca clasa de baz s se gseasc un a n a a a a constructor care s initializeze cmpurile proprii: name i ssn. Intruct cona a s a structorii nu se motenesc, e nevoie ca clasa derivat s se fac un apel exs n a a a plicit al constructorului clasei de baz. Acest apel se face prin initializator de a constructor care are forma: dou puncte urmate de base(parametrii-efectivi). a

5.2. SPECIALIZAREA SI GENERALIZAREA

109

public class Employee { protected string name; protected string ssn; public Employee( string name, string ssn) { this.name = name; this.ssn = ssn; System.Console.WriteLine(Employee constructor: {0}, {1}, name, ssn); } } public class Salaried : Employee { protected double salary; public Salaried(string name, string ssn, double salary): base(name, ssn) { this.salary = salary; System.Console.WriteLine(Salaried constructor: {0}, salary); } } class Test { Salaried s = new Salaried(Jesse, 1234567890, 100000.00); } La rulare se va obtine: Employee constructor: Jesse, 1234567890 Salaried constructor: 100000.00 de unde se deduce c apelul de constructor de clas de baz se face a a a naintea executrii oricror alte instructiuni continute constructorul clasei derivate. a a n Dac o clas de baz nu are denit nici un constructor, atunci se va a a a crea unul implicit (fr parametri). Dac dup un constructor al unei clase aa a a derivate nu se specic un initializator de constructor, atunci va apelat a constructorul implicit (e creat automat de compilator, e scris de ctre a programator); dac nu exist nici un constructor implicit clasa de baz, a a n a atunci programatorul trebuie s specice un constructor din clasa de baz a a care va apelat, mpreun cu parametrii adecvati. a

110

CURS 5. DESTRUCTORI. POO C# IN

5.2.3

Operatorii is i as s

Operatorul is Operatorl is este folosit pentru a verica dac un anumit obiect este de a un anumit tip. Este folosit de obicei nainte operattiilor de downcasting (conversie explicit de la un tip de baz la unul derivat). Operatorul se a a folosete astfel: s instanta is NumeClasa rezultatul acestei operatii ind true sau false. Exemplu: Employee e = ...; if (e is Salaried) { Salaried s = (Salaried)e; } cazul care sar face conversia explicit iar obiectul nu este de tipul la In n a care se face conversia ar rezulta o exceptie: System.InvalidCastException. Operatorul as Acest operator este folosit pentru conversii explicite, returnnd un obiect a de tipul la care se face conversia sau null dac conversia nu se poate face a (nu se arunc exceptii). Determinarea validitii conversiei se face testnd a at a valoarea rezultat fat de null: dac rezultatul e null atunci conversia nu sa a a a putut face. Ca i precedentul operator se folosete special la downcasting. s s n Exemplu: Employee e = ...; Salaried s = e as Salaried; if (s != null) { //se lucreaza cu instanta valida de tip Salaried }

5.3

Clase sealed

Specicatorul sealed care se poate folosi naintea cuvntului cheie class a specic faptul c clasa curent nu se poate deriva. Este o eroare de compilare a a a ca o clas sealed s e declarat drept clas de baz. a a a a a

5.4. POLIMORFISMUL

111

5.4

Polimorsmul

Polimorsmul este capacitatea unei entiti de a lua mai multe forme. at limbajul C# polimorsmul este de 3 feluri: parametric, adhoc i de In s motenire. s

5.4.1

Polimorsmul parametric

Este cea mai slab form de polimorsm, ind regsit majoritatea a a a a n altor limbaje, ce nu sunt orientate pe obiecte: Pascal, C. Prin polimorsmul parametric se permite ca o implementare de functie s poat prelucra a a orice numr de parametri. Acest lucru se poate obtine prin folosirea unui a parametru de tip params (vezi 3.2).

5.4.2

Polimorsmul adhoc

Se mai numete i supra arcarea metodelor, mecanism prin care s s nc n cadrul unei clase se pot scrie mai multe metode, avnd acelai nume, dar a s tipuri i numere diferite de parametri de apel. Alegerea functiei care va s apelat se va face la compilare, pe baza corespondentei a ntre tipurile de apel i cele formale. s

5.4.3

Polimorsmul de motenire s

Este forma cea mai evoluat de polimorsm. Dac precedentele forme de a a polimorsm sunt aplicabile fr a se pune problema de motenire, acest aa s n caz este necesar s existe o ierarhie de clase. Mecanismul se bazeaz pe faptul a a c o clas de baz denete un tip care este compatibil din punct de vedere a a a s al atribuirii cu orice tip derivat, ca mai jos: class B{...} class D: B{...} class Test { static void Main() { B b = new D();//upcasting=conversie implicita catre baza } } Intrun astfel de caz se pune problema: ce se ampl cu metodele avnd nt a a aceeai list de parametri formali i care se regsesc cele dou clase? s a s a n a

112

CURS 5. DESTRUCTORI. POO C# IN

S considerm exemplul urmtor: avem o clas Shape care contine o a a a a metod public void Draw(); din Shape se deriveaz clasa Polygon care implea a menteaz aceeai metod mod specic. Problema care se pune este cum a s a n se rezolv un apel al metodei Draw context de upcasting: a n class Shape { public void Draw() { System.Console.WriteLine(Shape.Draw()); } } class Polygon: Shape { public void Draw() { System.Console.WriteLine(Polygon.Draw()); //desenarea s-ar face prin GDI+ } } class Test { static void Main() { Polygon p = new Polygon(); Shape s = p;//upcasting s.Draw(); p.Draw(); } } La compilarea acestui cod se va obtine un avertisment: warning CS0108: The keyword new is required on Polygon.Draw() because it hides inherited member Shape.Draw() dar despre specicatorul new vom vorbi mai jos (oricum, adugarea lui nu a va schimba cu nimic comportamentul de mai jos, doar va duce la disparitia de avertisment). Codul de mai sus va aa: s Shape.Draw() Polygon.Draw()

5.4. POLIMORFISMUL

113

Dac cea dea doua linie aat este conform cu intuitia, primul rezultat a s a a este discutabil, dar justicabil: apelul de metod Draw() este rezolvat a n ecare caz la compilare pe baza tipului declarat al obiectelor; ca atare apelul precedent este legat de corpul metodei Draw din clasa Shape, chiar dac s a a fost instantiat de fapt pe baza unui obiect de tip Polygon. Este posibil ca s se doreasc schimbarea acestui comportament: apelul de a a metod Draw s e rezolvat functie de tipul efectiv al obiectului care face a a n acest apel, i nu de tipul formal declarat. cazul precedent, apelul s.Draw() s In trebuie s se rezolve de fapt ca ind ctre metoda Draw() din Polygon, pentru a a c acesta este tipul la rulare al obiectului s. Cu alte cuvinte, apelul ar trebui a s e rezolvat la rulare i nu la compilare, functie de natura obiectelor. a s n Acest comportament polimorc este referit sub denumirea polimorsm de motenire. s

5.4.4

Virtual i override s

Pentru a asigura faptul c legarea apelului de metode se face la rulare a i nu la compilare, e necesar ca clasa de baz s se specice c metoda s n a a a Draw() este virtual, iar clasa derivat pentru aceeai metod trebuie s a n a s a a se spun c este o suprascriere a celei din baz: a a a class Shape{ public virtual void Draw(){...} } class Polygon{ public override void Draw(){...} } urma executrii metodei Main din clasa de mai sus, se va aa: In a s Polygon.Draw() Polygon.Draw() adic sa apelat metoda corespunztoare tipului efectiv de la rulare, ecare a a n caz. cazul care clasa Polygon este la rndul ei motenit i se dorete In n a s a s s ca polimorsmul s functioneze continuare va trebui ca aceast a treia a n n a clas s suprascrie (override) metoda Draw(). a a Un astfel de comportament polimorc este benec atunci cnd se folosete a s o colectie de obiecte de tipul unei clase de baz: a Shape[] painting = new Shape[10]; painting[0] = new Shape();

114

CURS 5. DESTRUCTORI. POO C# IN

painting[1] = new Polygon(); ... foreach( Shape s in painting) s.Draw();

5.4.5

Modicatorul new pentru metode

Modicatorul new se foloeste pentru a indica faptul c o metod dintr-o s a a clas derivat care are aceeai semntur cu una dintro clas de baz nu a a s a a a a este o suprascriere a ei, ci o nou metod. Este ca i cum metoda declarat a a s a new ar avea o semntur diferit. a a a S presupunem urmtorul scenariu: compania A creaz o clas A care a a a a are forma: public class A{ public void M(){ Console.WriteLine(A.M()); } } O alt companie B va crea o clas B care motenete clasa A. Compania a a s s B nu are nici o inuent asupra companiei A (sau asupra modului care a n aceasta va face modicri asupra clasei A). Ea va deni interiorul clasei B a n o metod M() i una N(): a s class B: A{ public void M(){ Console.WriteLine(B.M()); N(); base.M(); } protected virtual void N(){ Console.WriteLine(B.N()); } } Atunci cnd compania B compileaz codul, compilatorul C# va produce a a urmtorul avertisment: a warning CS0108: The keyword new is required on B.M() because it hides inherited member A.M()

5.4. POLIMORFISMUL

115

Acest avertisment va notica programatorul c clasa B denete o metod a s a M(), care va ascunde metoda M() din clasa de baz A. Aceast nou metod a a a a ar putea schimba elesul (semantica) lui M(), aa cum a fost creat initial nt s de compania A. Este de dorit astfel de cazuri compilatorul s avertizeze n a despre posibile nepotriviri semantice. Programatorii din B vor trebui s pun a a orice caz specicatorul new n naintea metodei B.M(). S presupunem c o aplicatie folosete clasa B() felul urmtor: a a s n a class App{ static void Main(){ B b = new B(); b.M(); } } La rulare se va aa: s B.M() B.N() A.M() S presupunem c A decide adugarea unei metode virtuale N() clasa sa, a a a n metod ce va apelat din M(): a a public class A { public void M() { Console.WriteLine(A.M()); N(); } protected virtual void N() { Console.WriteLine(A.N()); } } La o recompilare fcut de B, este dat urmtorul avertisment: a a a warning CS0114: B.N() hides inherited member A.N(). To make the current member override that implementation, add the override keyword. Otherwise, add the new keyword.

116

CURS 5. DESTRUCTORI. POO C# IN

acest mod compilatorul avertizeaz c ambele clase ofer o metod N() In a a a a a cror semantic poate s difere. Dac B decide c metodele N() nu sunt a a a a a semantic legate cele dou clase, atunci va specica new, informnd compin a a latorul de faptul c versiunea sa este una nou, care nu suprascrie polimorc a a metoda din clasa de baz. a Atunci cnd codul din clasa App este rulat, se va aa la ieire: a s s B.M() B.N() A.M() A.N() Ultima linie aat se explic tocmai prin faptul c metoda N() din B este s a a a declarat new i nu override (dac ar fost override, ultima linie ar fost a s a B.N(), din cauza polimorsmului). Se poate ca B s decid c metodele M() i N() din cele dou clase sunt a a a s a legate semantic. acest caz, ea poate terge denitia metodei B.M, iar In s pentru a semnala faptul c metoda B.N() suprascrie metoda omonim din a a clasa printe, va a nlocui cuvntul new cu override. acest caz, metoda a In App.Main va produce: A.M() B.N() ultima linie ind explicat de faptul c B.N() suprascrie o metod virtual. a a a a

5.4.6

Metode sealed

O metod de tip override poate declarat ca ind de tip sealed, astfel a a mpiedicnduse suprascrierea ei a ntro clas derivat din cea curent: a a a using System; class A { public virtual void F() { Console.WriteLine(A.F()); } public virtual void G() { Console.WriteLine(A.G()); } }

5.4. POLIMORFISMUL class B: A { sealed override public void F() { Console.WriteLine(B.F()); } override public void G() { Console.WriteLine(B.G()); } } class C: B { override public void G() { Console.WriteLine(C.G()); } }

117

Modicatorul sealed pentru B.F va mpiedica tipul C s suprascrie metoda a F.

5.4.7

Exemplu folosind virtual, new, override, sealed

S presupunem urmtoare ierarhie de clase, reprezentat Fig. 5.1; o a a a n clas X moteneste o clas Y dac sensul sgetii este de la X la Y. Fiecare a s a a a clas are o metod void f() care determin aarea clasei care este denit a a a s n a i pentru care se vor specica new, virtual, override, sealed. S presupunem s a c clasa de test arat astfel: a a public class Test { static void Main() { A[] x = new A[4]; x[0] = new A(); x[1] = new B(); x[2] = new C(); x[3] = new D(); A a = new A(); B b = new B();

118

CURS 5. DESTRUCTORI. POO C# IN


A

+foo(): void

+foo(): void

+foo(): void

+foo(): void

Figura 5.1: Ierarhie de clase C c = new C(); D d = new D(); /* secventa 1 */ for(int i=0; i<4; i++) { x[i].f(); } /* secventa 2 */ a.f(); b.f(); c.f(); d.f(); } } functie de specicatorii metodelor f() din ecare clas, se obtin ieirile din In a s tabelul 5.1:

5.5. CLASE SI METODE ABSTRACTE Tabelul 5.1: Efecte ale diferitilor specicatori.

119

Metoda Specicator Ieire secv. 1 s Ieire secv. 2 s Specicator Ieire secv. 1 s Ieire secv. 2 s Specicator Ieire secv. 1 s Ieire secv. 2 s Specicator Eroare de compilare deoarece C.f nu poate suprascrie metoda nevirtual B.f() a Specicator Ieire secv. 1 s Ieire secv. 2 s Avertisment la compilare deoarece B.f nlocuiete A.f s Specicator Eroare de compilare deoarece deoarece B.f nu poate suprascris de C.f a

A.f() virtual A.f A.f virtual A.f A.f virtual A.f A.f virtual

B.f() override B.f B.f override B.f B.f new A.f B.f new

C.f() override C.f C.f new B.f C.f new A.f C.f override

D.f() override D.f D.f override D.f D.f new A.f D.f override

virtual virtual A.f A.f A.f B.f

override override A.f D.f C.f D.f

virtual sealed override override override

5.5

Clase i metode abstracte s

Deseori pentru o anumit clas nu are sens crearea de instante, din cauza a a unei generaliti prea mari a tipului respectiv. Spunem c aceast clas este at a a a abstract, iar pentru a a mpiedica efectiv crearea de instante de acest tip, se va specica cuvntul abstract a naintea metodei. exemplele de mai sus, In clasele Employee i Shape ar putea gndite ca ind abstracte: ele contin s a prea putin informatie pentru a putea crea instante utile. a Analog, pentru o anumit metod din interiorul unei clase uneori nu se a a poate specica o implementare. De exemplu, pentru clasa Shape de mai sus,

120

CURS 5. DESTRUCTORI. POO C# IN

este imposibil s se dea o implementare la metoda Draw(), tocmai din cauza a generalitii acestei clase. Ar util dac pentru aceast metod programat a a a atorul ar obligat s dea implementri specice ale acestei metode pentru a a diversele clase derivate. Pentru a se asigura tratarea polimorc a acestui a tip abstract, orice metod abstract este automat i virtual. Orice metod a a s a a care este declarat abstract implic declararea clasei ca ind abstract. a a a a Exemplu: abstract class Shape { public abstract void Draw(); //de remarcat lipsa implementarii si semnul punct si virgula } Orice clas care este derivat dintro clas abstract va trebui e s nu aib a a a a a a nici o metod abstract motenit fr implementare, e s se declare ca ind a a s a aa a abstract. Existenta de metode neimplementate nu va permite instantierea a clasei respective.

5.6

Interfete

O interfat denete un contract. O clas sau o structur care implea s a a menteaz o interfat ader la acest contract. Relatia dintre o interfat i a a a a s un tip care o implementeaz este deosebit de cea existent a a a ntre clase (este un/o): este o relatie de implementare. O interfat poate contine metode, proprieti, evenimente, indexatori. Ea a at a nu va contine implementri pentru acestea, doar declaratii. Declararea ns a unei interfete se face astfel: atributeopt modicatori-de-interfatopt interface identicator baza-interfeteiopt a corp-interfat ;opt a Modicatorii de interfat sunt: new, public, protected, internal, private. O a interfat poate s moteneasc de la zero sau mai multe interfete. Corpul a a s a interfetei contine declaratii de metode, fr implementri. Orice metod are aa a a gradul de acces public. Nu se poate specica pentru o metod din interiorul a unei interfete: abstract, public, protected, internal, private, virtual, override, ori static. Exemplu: interface IStorable { void Read( );

5.6. INTERFETE void Write(); } O clas care implementeaz o astfel de interfat se declar ca mai jos: a a a a class Document: IStorable { public void Read(){/*cod*/} public void Write(){/*cod*/} //alte declaratii }

121

O clas care implementeaz o interfat trebuie s deneasc toate metodele a a a a a care se regsesc interfata respectiv. Urmtoarele reguli trebuie respectate a n a a la implementarea de interfete: 1. Tipul de retur al metodei din clas trebuie s coincid cu tipul de retur a a a al metodei din interfat a 2. Tipul parametrilor formali din metod trebuie s e acelai cu tipul a a s parametrilor formali din interfat a 3. Metoda din clas trebuie s e declarat public i nestatic. a a a as a Aceste implementri pot declarate folosind specicatorul virtual (deci suba clasele clasei curente pot folosi new i override). s Exemplu: using System; interface ISavable { void Read(); void Write(); } public class TextFile : ISavable { public virtual void Read() { Console.WriteLine("TextFile.Read()"); } public void Write() { Console.WriteLine("TextFile.Write()");

122 }

CURS 5. DESTRUCTORI. POO C# IN

} public class ZipFile : TextFile { public override void Read() { Console.WriteLine("ZipFile.Read()"); } public new void Write() { Console.WriteLine("ZipFile.Write()"); } } public class Test { static void Main() { Console.WriteLine("\nTextFile reference to ZipFile"); TextFile textRef = new ZipFile(); textRef.Read(); textRef.Write(); Console.WriteLine("\nISavable reference to ZipFile"); ISavable savableRef = textRef as ISavable; if(savableRef != null) { savableRef.Read(); savableRef.Write(); } Console.WriteLine("\nZipFile reference to ZipFile"); ZipFile zipRef = textRef as ZipFile; if(zipRef!= null) { zipRef.Read(); zipRef.Write(); } } } La ieire se va aa: s s TextFile reference to ZipFile ZipFile.Read()

5.6. INTERFETE TextFile.Write() ISavable reference to ZipFile ZipFile.Read() TextFile.Write() ZipFile reference to ZipFile ZipFile.Read() ZipFile.Write()

123

exemplul de mai sus se foloeste operatorul as pentru a obtine o referint la In s a general, se prefer ca apelul metodelor interfete, pe baza obiectelor create. In a care sunt implementate din interfat s se fac via o referint la interfata a a a a respectiv, obtinut prin intermediul operatorului as (ca mai sus) sau dup a a a o testare prealabil prin is urmat de conversie explicit, ca mai jos: a a a if (textRef is ISavable) { ISavable is = (ISavable)textRef; is.Read();//etc } general, dac se dorete doar rspunsul la In a s a ntrebarea este obiectul curent un implementator al interfetei I ?, atunci se recomand folosirea operatorului a is. Dac se tie c va trebui fcut i o conversie la tipul interfat, atunci a s a a as a este mai ecient folosirea lui as. Armatia se bazeaz pe studiul codului IL a a rezultat ecare caz. n S presupunem c exista o interfat I avnd metoda M() care este ima a a a plementat de o clas C, care denete metoda M(). Este posibil ca aceast a a s a metod s nui aib o semnicatie afara clasei C, ca atare a nu e util ca a a a n metoda M() s e declarat public. Mecanismul care permite acest lucru se a a a numete ascunderea numelor. Aceast tehnic permite ascunderea metodelor s a a motenite dintr-o interfat, acestea devenind private (calicarea lor ca ind s a publice este semnalat ca o eroare). a Metodele din interfete care sau implementat explicit (prin calicarea lor cu numele interfetei de origine) nu pot declarate abstract, virtual, override, new. Mai mult, asemenea metode nu pot accesate direct prin intermediul unui obiect (obiect.NumeMetoda), ci doar prin intermediul unei conversii ctre interfat respectiv, deoarece prin implementare explicit a metodelor a a a a aceste devin private i singura modalitate de acces a lor este upcasting-ul s ctre interfat. a a Exemplu:

124 using System; public interface IDataBound { void Bind(); }

CURS 5. DESTRUCTORI. POO C# IN

public class EditBox : IDataBound { // implementare de IDataBound void IDataBound.Bind() { Console.WriteLine("Binding to data store..."); } } class NameHidingApp { public static void Main() { Console.WriteLine(); EditBox edit = new EditBox(); Console.WriteLine("Apel EditBox.Bind()..."); //EROARE: Aceasta linie nu se compileaza deoarece metoda //Bind nu mai exista ca metoda publica in clasa EditBox edit.Bind(); Console.WriteLine(); IDataBound bound = (IDataBound)edit; Console.WriteLine("Apel (IDataBound) EditBox.Bind()..."); // Functioneaza deoarece s-a facut conversie la IDataBound bound.Bind(); } } Este posibil ca un tip s implementeze mai multe interfete. Atunci cnd a a dou interfete au o metod cu aceeai semntur, programatorul are mai a a s a a multe variante de lucru. Cel mai simplu, el poate s furnizeze o singur a a implementare pentru ambele metode, ca mai jos: interface IFriendly { void GreetOwner() ;

5.6. INTERFETE } interface IAffectionate { void GreetOwner() ; } abstract class Pet { public virtual void Eat() { Console.WriteLine( "Pet.Eat" ) ; } } class Dog : Pet, IAffectionate, IFriendly { public override void Eat() { Console.WriteLine( "Dog.Eat" ) ; } public void GreetOwner() { Console.WriteLine( "Woof!" ) ; } }

125

O alt modalitate este s se specice implicit care metod este implementat. a a a a class Dog : Pet, IAffectionate, IFriendly { public override void Eat() { Console.WriteLine( "Dog.Eat" ) ; } void IAffectionate.GreetOwner() { Console.WriteLine( "Woof!" ) ; } void IFriendly.GreetOwner() { Console.WriteLine( "Jump up!" ) ; } } public class Pets

126 {

CURS 5. DESTRUCTORI. POO C# IN

static void Main() { IFriendly mansBestFriend = new Dog() ; mansBestFriend.GreetOwner() ; (mansBestFriend as IAffectionate).GreetOwner() ; } } La ieire se va aa: s s Jump up! Woof! Dac a clasa Dog se adaug metoda a ns n a public void GreetOwner() { Console.WriteLine( "Woof!" ) ; } (care a fost initial denit), atunci se poate face apel de tipul dog.GreetOwner() a (dog este instant de Dog); apelurile de metode din interfat rmn de asemea a a a nea valide. Rezultatul este ca i la exemplul precedent: se aeaz mesajul s s a Woof.

5.6.1

Clase abstracte sau interfete?

Att interfetele ct i clasele abstracte au comportamente similare i pot a a s s folosite situatii similare. Dar totui ele nu se pot substitui reciproc. n s Cteva principii generale de utilizare a lor sunt date mai jos. a Dac o relatie se poate exprima mai degrab ca este un/o dect altfel, a a a atunci entitatea de baz ar trebui gndit ca o clas abstract. a a a a a Un alt aspect este bazat pe obiectele care ar folosi capabilitile din tipul at de baz. Dac aceste capabiliti ar folosite de ctre obiecte care nu sunt a a at a legate ntre ele, atunci ar indicat o interfat. a a Dezavantajul claselor abstracte este c nu poate dect baz unic pentru a a a a orice alt clas. a a

Curs 6 Delegati. Evenimente. Structuri


6.1 Tipul delegat

programare deseori apare urmtoarea situatie: trebuie s se execute o In a a anumit actiune, dar nu se tie de dinainte care anume, sau chiar ce obiect va a s trebui efectiv utilizat. De exemplu, un buton poate ti c trebuie s anunte s a a un obiect despre faptul c fost apsat, dar nu va ti aprioric pe cine. Mai a a s degrab dect s se lege butonul de un obiect particular, butonul va declara a a a un delegat, pentru care clasa interesat de evenimentul de apsare va da o a a implementare. Fiecare actiune pe care utilizatorul o execut pe o interfat grac de a a a claneaz un eveniment. Alte evenimente se pot declana independent de s a s actiunile utilizatorului: sosirea unui email, terminarea copierii unor iere, s sfritul unei interogri pe o baz de date, etc. Un eveniment este o as a a ncapsulare a ideii c se ampl ceva la care programul trebuie s rspund. a nt a a a a Evenimentele i delegatii sunt strns legate deoarece rspunsul la acest evenis a a ment se va face de ctre un event handler, care C# se implementeaz ca a n a un delegat. Un delegat este un tip referint folosit pentru a a ncapsula o metod cu un a anumit antet (tipul parametrilor formali i tipul de retur). Orice metod care s a are acelai antet poate legat la un anumit delegat. s a Intrun limbaj precum C++, acest lucru se rezolv prin intermediul pointerilor la funtii. Delegatii a rezolv aceeai problem, dar a s a ntro manier orientat obiect i cu garantii a a s asupra sigurantei codului rezultat, precum i cu o uoar generalizare (vezi s s a delegatii multicast). Un delegat este creat dup urmtoarea sintax: a a a 127

128

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

atributeopt modicatori-de-delegatopt delegate tip-retur identicator( listaparam-formaliopt ); Modicatorul de delegat poate : new, public, protected, internal, private. Un delegat se poate specica att interiorul unei clase, ct i exteriorul a n a s n ei, ind de fapt o declaratie de clas derivat din System.Delegate. Dac a a a este declarat interiorul unei clase, atunci este i static (a se vedea statutul n s claselor interioare). Exemplu: public delegate int WhichIsFirst(object obj1, object obj2);

6.1.1

Utilizarea delegatilor pentru a specica metode la runtime

S presupunem c se dorete crearea unei clase container simplu numit a a s Pair care va contine dou obiecte pentru care va putea face i sortare. Nu a s se va ti aprioric care va tipul obiectelor continute, deci se va folosi pentru s ele tipul object. Dar sortarea celor dou obiecte se va face diferit, functie a n de tipul lor efectiv: de exemplu pentru nite persoane (clasa Student cele s n ce urmeaz) se va face dup nume, pe cnd pentru animale (clasa Dog) se a a a va face dup alt criteriu: greutatea. Containerul Pair va trebui s fac fat a a a a acestor clase diferite. Rezolvarea se va da prin delegati. Clasa Pair va deni un delegat, WhichIsFirst. Metoda Sort de ordonare va prelua ca (unic) parametru o instant a metodei WhichIsFirst, care va a implementa relatia de ordine, functie de tipul obiectelor continute. Rezul n tatul unei comparatii ntre dou obiecte va de tipul enumerare Comparison, a denit de utilizator: public enum Comparison { theFirstComesFirst = 0, //primul obiect din colectie este primul in ordinea sortarii theSecondComesFirst = 1 //al doilea obiect din colectie este primul in ordinea sortarii } Delegatul (tipul de metod care realizeaz compararea) se declar astfel: a a a //declarare de delegat public delegate Comparison WhichIsFirst( object obj1, object obj2); Clasa Pair se declar dup cum urmeaz: a a a

6.1. TIPUL DELEGAT public class Pair { //tabloul care contine cele doua obiecte private object[] thePair = new object[2]; //constructorul primeste cele doua obiecte continute public Pair( object firstObject, object secondObject) { thePair[0] = firstObject; thePair[1] = secondObject; } //metoda publica pentru ordonarea celor doua obiecte //dupa orice criteriu public void Sort( WhichIsFirst theDelegatedFunc ) { if (theDelegatedFunc(thePair[0],thePair[1]) == Comparison.theSecondComesFirst) { object temp = thePair[0]; thePair[0] = thePair[1]; thePair[1] = temp; } } //metoda ce permite tiparirea perechii curente //se foloseste de polimorfism - vezi mai jos public override string ToString( ) { return thePair[0].ToString()+", "+thePair[1].ToString(); } } Clasele Student i Dog sunt: s public class Dog { public Dog(int weight) { this.weight=weight; } //Ordinea este data de greutate public static Comparison WhichDogComesFirst(

129

130

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI Object o1, Object o2)

{ Dog d1 = o1 as Dog; Dog d2 = o2 as Dog; return d1.weight > d2.weight ? Comparison.theSecondComesFirst : Comparison.theFirstComesFirst; } //pentru afisarea greutatii unui caine public override string ToString( ) { return weight.ToString( ); } private int weight; } public class Student { public Student(string name) { this.name = name; } //studentii sunt ordonati alfabetic public static Comparison WhichStudentComesFirst( Object o1, Object o2) { Student s1 = o1 as Student; Student s2 = o2 as Student; return (String.Compare(s1.name, s2.name) < 0 ? Comparison.theFirstComesFirst : Comparison.theSecondComesFirst); } //pentru afisarea numelui unui student public override string ToString( ) { return name; } private string name; } Clasa de test este:

6.1. TIPUL DELEGAT

131

public class Test { public static void Main( ) { //creaza cate doua obiecte //de tip Student si Dog //si containerii corespunzatori Student Stacey = new Student(Stacey); Student Jesse = new Student (Jess); Dog Milo = new Dog(10); Dog Fred = new Dog(5); Pair studentPair = new Pair(Stacey, Jesse); Pair dogPair = new Pair(Milo, Fred); Console.WriteLine(studentPair\t: {0}, studentPair); Console.WriteLine(dogPair\t: {0}, dogPair); //Instantiaza delegatii WhichIsFirst theStudentDelegate = new WhichIsFirst(Student.WhichStudentComesFirst); WhichIsFirst theDogDelegate = new WhichIsFirst(Dog.WhichDogComesFirst); //sortare folosind delegatii studentPair.Sort(theStudentDelegate); Console.WriteLine(Dupa sortarea pe studentPair\t: {0}, studentPair.ToString( )); dogPair.Sort(theDogDelegate); Console.WriteLine(Dupa sortarea pe dogPair\t\t: {0}, dogPair.ToString( )); } }

6.1.2

Delegati statici

Unul din aspectele neelegante ale exemplului anterior este c e necesar ca a clasa Test s se instantieze delegatii care sunt necesari pentru a ordona n a obiectele din Pair. O modalitate mai bun este s se obtin delegatii direct a a a din clasele Student i Dog. Acest lucru se obtine prin crearea unui delegat s static interiorul ecrei clase: n a public static readonly WhichIsFirst OrderStudents = new WhichIsFirst(Student.WhichStudentComesFirst);

132

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

(analog pentru clasa Dog; de remarcat c static readonly nu se poate a nlocui cu const, deoarece initilizatosul nu este considerat expresie con stant). Declaratia de mai sus se folosete astfel: a s ... studentpair.Sort(Student.OrderStudent); ... rezultatul ind identic. [2] este dat i o implementare de delegat ca proprietate static, aceasta In as a ducnd la crearea unui delegat doar cazul care este nevoie de el1 . a n n

6.1.3

Multicasting

Uneori este nevoie ca un delegat s poat apela mai mult de o singur a a a metod. De exemplu, atunci cnd un buton este apsat, se poate s vrei a a a a s efectuezi mai mult de o sigur actiune: s scrii a a a ntrun textbox un ir s de caractere i s s a nregistrezi ntrun ier faptul c sa apsat acel buton s a a (logging). Acest lucru sar putea rezolva prin construirea unui vector de delegati care s contin toate metodele dorite, a acest lucru ar duce la un a a ns cod greu de urmrit i inexibil; pentru un astfel de exemplu, a se vedea [2], a s pag. 261265. Mult mai simplu ar dac un delegat ar putea s contin a a a mai mult de o singur metod. Acest lucru se numete multicasting i este a a s s folosit intens la tratarea evenimentelor (vezi sectiunea urmtoare). a Orice delegat care returnez void este un delegat multicast, care poate a tratat i ca un delegat single-cast. Doi delegati multicast pot combinati s folosind semnul +. Rezultatul unei astfel de adunri este un nou delegat a multicast care la apelare va invoca metodele continute, ordinea care n n sa fcut adunarea. De exemplu, dac Writer i Logger sunt delegati care a a s returneaz void, atunci urmtoarea linie va produce combinarea lor a a ntrun singur delegat: myMulticastDelegate = Writer + Logger; Se pot aduga delegati multicast folosind operatorul +=, care va aduga a a delegatul de la dreapta operatorului la delegatul multicast aat stnga sa: n a myMulticastDelegate += Transmitter; presupunnd c Transmitter este compatibil cu myMulticastDelegate (are a a aceeai semntur). Operatorul = functioneaz invers fat de + =. s a a a a Exemplu:
1

Tehnic numit initializaer trzie (lazy initialization) a a a

6.1. TIPUL DELEGAT using System; //declaratia de delegat multicast public delegate void StringDelegate(string s); public class MyImplementingClass { public static void WriteString(string s) { Console.WriteLine("Writing string {0}", s); } public static void LogString(string s) { Console.WriteLine("Logging string {0}", s); } public static void TransmitString(string s) { Console.WriteLine("Transmitting string {0}", s); } } public class Test { public static void Main( ) { //defineste trei obiecte delegat StringDelegate Writer, Logger, Transmitter; //defineste alt delegat //care va actiona ca un delegat multicast StringDelegate myMulticastDelegate; //Instantiaza primii trei delegati //dand metodele ce se vor incapsula Writer = new StringDelegate( MyImplementingClass.WriteString); Logger = new StringDelegate( MyImplementingClass.LogString); Transmitter = new StringDelegate( MyImplementingClass.TransmitString); //Invoca metoda delegat Writer Writer("String passed to Writer\n"); //Invoca metoda delegat Logger

133

134

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI Logger("String passed to Logger\n"); //Invoca metoda delegat Transmitter Transmitter("String passed to Transmitter\n"); //anunta utilizatorul ca va combina doi delegati Console.WriteLine( "myMulticastDelegate = Writer + Logger"); //combina doi delegati, rezultatul este //asignat lui myMulticastDelagate myMulticastDelegate = Writer + Logger; //apelaeaza myMulticastDelegate //de fapt vor fi chemate cele doua metode myMulticastDelegate( "First string passed to Collector"); //Anunta utilizatorul ca se va adauga al treilea delegat Console.WriteLine( "\nmyMulticastDelegate += Transmitter"); //adauga al treilea delegat myMulticastDelegate += Transmitter; //invoca cele trei metode delagate myMulticastDelegate( "Second string passed to Collector"); //anunta utilizatorul ca se va scoate delegatul Logger Console.WriteLine( "\nmyMulticastDelegate -= Logger"); //scoate delegatul Logger myMulticastDelegate -= Logger; //invoca cele doua metode delegat ramase myMulticastDelegate( "Third string passed to Collector");

} } La ieire vom avea: s Writing string String passed to Writer Logging string String passed to Logger Transmitting string String passed to Transmitter myMulticastDelegate = Writer + Logger Writing string First string passed to Collector Logging string First string passed to Collector myMulticastDelegate += Transmitter Writing string Second string passed to Collector

6.2. EVENIMENTE Logging string Second string passed to Collector Transmitting string Second string passed to Collector myMulticastDelegate -= Logger Writing string Third string passed to Collector Transmitting string Third string passed to Collector

135

6.2

Evenimente

Interfetele grace actuale cer ca un anumit program s rspund la eveni a a a mente. Un eveniment poate de exemplu apsarea unui buton, terminarea a transferului unui ier, selectarea unui meniu, etc; pe scurt, se ampl ceva s nt a la care trebuie s se dea un rspuns. Nu se poate prezice ordinea care se a a n petrec evenimentele, iar la aparitia unuia se va cere tratarea sa. Alte clase pot interesate a rspunde la aceste evenimente. Modul n a care vor rspunde va extrem de particular, iar clasa care genereaz n a a evenimentul (ex: clasa Button, la apsarea unui buton) nu trebuie s tie a a s modul care se va rspunde. Butonul va comunica faptul c a fost apsat, n a a a iar clasele interesate acest eveniment vor reactiona consecint. n n a

6.2.1

Publicarea i subscrierea s

C#, orice obiect poate s publice un set de evenimente la care alte clase In a pot s subscrie. Cnd obiectul care a publicat evenimentul i semnaleaza, a a l s a acest toate obiectele care au subscris la acest eveniment sunt noticate. In mod se denete o dependent de tip onetomany s a ntre obiecte astfel at nc un obiect si schimb starea, toate celelate obiecte dependente sunt noticate a i modicate automat. s De exemplu, un buton poate s notice un numr oarecare de observatori a a atunci cnd a fost apsat. Butonul va numit publicator 2 deoarece publia a c evenimentul Click iar celelalte clase sunt numite abonati 3 deoarece ele a subscriu la evenimentul Click.

6.2.2

Evenimente i delegati s

Tratarea evenimentelor C# se face folosind delegati. Clasa ce public n a denete un delegat pe care clasele abonate trebuie s implementeze. Cnd s a l a evenimentul este declanat, metodele claselor abonate vor apelate prin s
2 3

Engl: publisher Engl: subscribers

136

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

intermediul delegatului (pentru care se prevede posibilitatea de a multicast, astfel at s se permit mai multi abonati). nc a a Metodele care rspund la un eveniment se numesc event handlers. Prin a conventie, un event handler .NET Framework returneaz void i preia doi n a s parametri: primul parametru este sursa evenimentului (obiectul publicator); al doilea parametru are tip EventArgs sau derivat din acesta. Declararea unui eveniment se face astfel: atributeopt modicatori-de-evenimentopt event tip numeeveniment Modicator-de-eveniment poate abstract, new, public, protected, internal, private, static, virtual, sealed, override, extern. Tip este un handler de eveniment (delegat multicast). Exemplu: public event SecondChangeHandler OnSecondChange; Vom da mai jos un exemplu care va construi urmtoarele: o clas Clock a a care folosete un eveniment (OnSecondChange) pentru a notica potentialii s abonati atunci cnd timpul local se schimb cu o secund. Tipul acestui a a a eveniment este un delegat SecondChangeHandler care se declar astfel: a public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation ); conformitate cu metodologia de declarare a unui event handler, pomenit n a mai sus. Tipul TimeInfoEventArgs este denit de noi ca o clas derivat din a a EventArgs: public class TimeInfoEventArgs : EventArgs { public TimeInfoEventArgs( int hour, int minute, int second ) { this.hour = hour; this.minute = minute; this.second = second; } public readonly int hour; public readonly int minute; public readonly int second; } Aceast clas va contine informatie despre timpul curent. Informatia este a a accesibil readonly. a Clasa Clock va contine o metod Run(): a

6.2. EVENIMENTE

137

public void Run() { for(;;) { //dormi 10 milisecunde Thread.Sleep(10); //obtine timpul curent System.DateTime dt = System.DateTime.Now(); //daca timpul s-a schimbat cu o secunda //atunci notifica abonatii if( dt.Second != second) //second este camp al clasei Clock { //creeaza obiect TimeInfoEventArgs //ce va fi transmis abonatilor TimeInfoEventArgs timeInformation = new TimeInfoEventArgs(dt.Hour, dt.Minute, dt.Second); //daca cineva este abonat, atunci anunta-l if (OnSecondChange != null) { OnSeconChange(this, timeInformation); } } //modifica timpul curent in obiectul Clock this.second = dt.Second; this.minute = dt.Minute; this.hour = dt.Hour; } } Metoda Run creeaz un ciclu innit care interogheaz periodic ceasul sistem. a a Dac timpul sa schimbat cu o secund fat de timpul precedent, se vor a a a notica toate obiectele abonate dup care si va modica starea, prin cele a trei atribuiri nale. Tot ce rmne de fcut este s se scrie nite clase care s subscrie la evenia a a a s a mentul publicat de clasa Clock. Vor dou clase: una numit DisplayClock a a care va aa pe ecran timpul curent i o alta numit LogCurrentTime care s s a ar trebui s a nregistreze evenimentul ntrun ier, dar pentru simplitate va s aa doar la dispozitivul curent de ieire informatia tranmsis: s s a public class DisplayClock {

138

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

public void Subscribe(Clock theClock) { theClock.OnSecondChange += new Clock.SecondChangeHandler(TimeHasChanged); } void TimeHasChanged( object theClock, TimeInfoEventArgs ti) { Console.WriteLine("Current Time: {0}:{1}:{2}", ti.hour.ToString( ), ti.minute.ToString( ), ti.second.ToString( )); } } public class LogCurrentTime { public void Subscribe(Clock theClock) { theClock.OnSecondChange += new Clock.SecondChangeHandler(WriteLogEntry); } //Aceasta metoda ar trebui sa scrie intr-un fisier //dar noi vom scrie la consola void WriteLogEntry( object theClock, TimeInfoEventArgs ti) { Console.WriteLine("Logging to file: {0}:{1}:{2}", ti.hour.ToString( ), ti.minute.ToString( ), ti.second.ToString( )); } } De remarcat faptul c evenimentele sunt adugate folosind operatorul +=. a a Exemplul n ntregime este dat mai jos: using System; using System.Threading; //o clasa care va contine informatie despre eveniment //in acest caz va contine informatie disponibila in clasa Clock public class TimeInfoEventArgs : EventArgs {

6.2. EVENIMENTE public TimeInfoEventArgs(int hour, int minute, int second) { this.hour = hour; this.minute = minute; this.second = second; } public readonly int hour; public readonly int minute; public readonly int second;

139

} //clasa care publica un eveniment: OnSecondChange //clasele care se aboneaza vor subscrie la acest eveniment public class Clock { //delegatul pe care abonatii trebuie sa il implementeze public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation ); //evenimentul ce se publica public event SecondChangeHandler OnSecondChange; //ceasul este pornit si merge la infinit //va declansa un eveniment pentru fiecare secunda trecuta public void Run( ) { for(;;) { //inactiv 10 ms Thread.Sleep(10); //citeste timpul curent al sistemului System.DateTime dt = System.DateTime.Now; //daca s-a schimbat fata de secunda anterior inregistrata //atunci notifica pe abonati if (dt.Second != second) { //creaza obiectul TimeInfoEventArgs //care va fi transmis fiecarui abonat TimeInfoEventArgs timeInformation = new TimeInfoEventArgs( dt.Hour,dt.Minute,dt.Second); //daca cineva a subscris la acest eveniment //atunci anunta-l if (OnSecondChange != null)

140 {

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

OnSecondChange(this,timeInformation); } } //modifica starea curenta this.second = dt.Second; this.minute = dt.Minute; this.hour = dt.Hour; } } private int hour; private int minute; private int second; } //un observator (abonat) //DisplayClock va subscrie la evenimentul lui Clock //DisplayClock va afisa timpul curent public class DisplayClock { //dandu-se un obiect clock, va subscrie //la evenimentul acestuia public void Subscribe(Clock theClock) { theClock.OnSecondChange += new Clock.SecondChangeHandler(TimeHasChanged); } //handlerul de eveniment de pe partea //clasei DisplayClock void TimeHasChanged( object theClock, TimeInfoEventArgs ti) { Console.WriteLine("Current Time: {0}:{1}:{2}", ti.hour.ToString( ), ti.minute.ToString( ), ti.second.ToString( )); } } //un al doilea abonat care ar trebui sa scrie intr-un fisier public class LogCurrentTime { public void Subscribe(Clock theClock)

6.2. EVENIMENTE { theClock.OnSecondChange += new Clock.SecondChangeHandler(WriteLogEntry); } //acest handler ar trebui sa scrie intr-un fisier //dar va scrie la standard output void WriteLogEntry( object theClock, TimeInfoEventArgs ti) { Console.WriteLine("Logging to file: {0}:{1}:{2}", ti.hour.ToString( ), ti.minute.ToString( ), ti.second.ToString( )); } } public class Test { static void Main( ) { //creaza un obiect de tip Clock Clock theClock = new Clock( ); //creaza un obiect DisplayClock care //va subscrie la evenimentul obiectului //Clock anterior creat DisplayClock dc = new DisplayClock( ); dc.Subscribe(theClock); //analog se creeaza un obiect de tip LogCurrentTime //care va subscrie la acelasi eveniment //ca si obiectul DisplayClock LogCurrentTime lct = new LogCurrentTime( ); lct.Subscribe(theClock); //porneste ceasul theClock.Run( ); } } La ieire se va aa: s s Current Logging Current Logging Time: 14:53:56 to file: 14:53:56 Time: 14:53:57 to file: 14:53:57

141

142 Current Logging Current Logging

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI Time: 14:53:58 to file: 14:53:58 Time: 14:53:59 to file: 14:53:59

6.2.3

Comentarii

Sar putea pune urmtoarea a ntrebare: de ce este nevoie de o astfel de redirectare de eveniment, cnd metoda Run() se poate aa direct pe ecran a n s sau ntrun ier informatia cerut? Avantajul abordrii anterioare este c s a a a se pot crea oricte clase care s e noticate atunci cnd acest eveniment se a a a declaneaz. Clasele abonate nu trebuie s tie despre modul care lucreaz s a as n a clasa Clock, iar clasa Clock nu trebuie s tie despre clasele care vor subscrie as la evenimentul su. Similar, un buton poate s publice un eveniment OnClick a a i orice numr de obiecte pot subscrie la acest eveniment, primind o noticare s a atunci cnd butonul este apsat. a a Publicatorul i abonatii sunt decuplati. Clasa Clock poate s modice s a modalitatea de detectare a schimbrii de timp fr ca acest lucru s impun a aa a a o schimbare clasele abonate. De asemenea, clasele abonate pot s si n a modice modul de tratare a evenimentului, mod transparent fat de clasa n a Clock. Toate aceste caracteristici fac ntretinerea codului extrem de facil. a

6.3

Structuri

Structurile reprezint tipuri de date asemntoare claselor, cu principala a a a diferent c sunt tipuri valoare (o astfel de variabil va contine direct vala a a oarea, i nu o adres de memorie). Sunt considerate versiuni uoare ale s a s claselor, sunt folosite predilect pentru tipuri pentru care aspectul comportamental este mai putin pronuntat. Declaratia unei structuri se face astfel: atributeopt modicatori-de-structopt struct identicator :interfeteopt corp ;opt Modicatorii de structur sunt: new, public, protected, internal, private. O a structur este automat derivat din System.ValueType, care la rndul ei este a a a derivat din System.Object; de asemenea, este automat considerat sealed a a (sect. 5.3). Poate a s implementeze una sau mai multe interfete. ns a O structur poate s contin declaratii de constante, cmpuri, metode, a a a a proprieti, evenimente, indexatori, operatori, constructori, constructori statici, at tipuri interioare. Nu poate contine destructor. La atribuire, se face o copiere a valorilor continute de ctre surs a a n destinatie (indiferent de tipul cmpurilor: valoare sau referint). a a

6.3. STRUCTURI Exemplu: using System; public struct Point { public Point(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 ToString( ) { return (String.Format({0}, {1}, xVal,yVal)); } public int xVal; public int yVal; }

143

144

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI

public class Tester { public static void myFunc(Point loc) { loc.X = 50; loc.Y = 100; Console.WriteLine(In MyFunc loc: {0}, loc); } static void Main( ) { Point loc1 = new Point(200,300); Console.WriteLine(Loc1 location: {0}, loc1); myFunc(loc1); Console.WriteLine(Loc1 location: {0}, loc1); } } Dup cum este dat exemplul de mai sus, crearea unei instante se face a n folosind operatorul new ; dar acest caz, nu se va crea o instant memoria n a n heap, ci pe stiv. Transmiterea lui loc1 ca parametru se face prin valoare, a adic metoda myFunc nu face dect s modice o copie de pe stiv a lui a a a a loc1. La revenire, se va aa tot valoarea original, deoarece loc1 a rmas s a a nemodicat: Loc1 location: 200, 300 In MyFunc loc: 50, 100 Loc1 location: 200, 300 Deseori pentru o structur se declar cmpurile ca ind publice, pentru a a a a nu mai necesare denirea accesorilor (simplicare implementrii). Alti a programatori consider a c accesarea membrilor trebuie s se fac precum a ns a a a la clase, folosind proprieti. Oricare ar alegerea, limbajul o sprijin. at a Alte aspecte demne de retinut: Cmpurile nu pot initializate la declarare; altfel spus, dac exema a n plul de mai sus se scria: public int xVal = 10; public int yVal = 20; s-ar semnalat o eroare la compilare.

6.3. STRUCTURI

145

Nu se poate deni un constructor implicit. Cu toate acestea, compilatorul va crea un astfel de constructor, care va initializa cmpurile la a valorile lor implicite (0 pentru tipuri numerice sau pentru enumerri, a false pentru bool, null pentru tipuri referint). a Pentru tipul Point de mai sus, urmtoarea secvent de cod este corect: a a a Point a = new Point(0, 0); Point b = new Point(); i duce la crearea a dou puncte cu abcisele i ordonatele 0. Un cons a s structor implicit este apelat atunci cnd se creeaz un tablou de struca a turi: Point[] points = new Points[10]; for( int i=0; i<points.Length; i++ ) { Console.WriteLine(points[i]); } va aa de 10 ori puncte de coordonate (0, 0). De asemenea este apelat s la initializarea membrilor unei clase (care se face nainte de orice constructor). De mentionat pentru exemplul anterior c se creeaz un obiect de tip a a tablou heap, dup care interiorul lui (i nu pe stiv!) se creeaz n a n s a a cele 10 puncte (alocare inline). Nu se poate declara destructor. Acestia se declar numai pentru clase. a Dac programatorul denete un constructor, atunci acesta trebuie s a s a dea valori initiale pentru cmpurile continute, altfel apare eroare la a compilare. Dac pentru instantierea unei structuri declarate interiorul unei a n metode sau bloc de instructiuni general nu se apeleaz new, atunci n a respectiva instant nu va avea asociat nici o valoare (constructorul ima a plicit nu este apelat automat!). Nu se poate folosi respectiva variabil a de tip structur dect dup ce i se initializeaz toate cmpurile: a a a a a Point p; Console.WriteLine(p); va duce la aparitia erorii de compilare:

146

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI Use of unassigned local variable p Dar dup nite asignri de tipul: a s a p.xVal=p.yVal=0; aarea este posibil (practic, orice apel de metod pe instant este s a a a acum acceptat).

Dac se cearc denirea unei structuri care contine un cmp de tipul a n a a structurii, atunci va aprea o eroare de compilare: a struct MyStruct { MyStruct s; } va genera un mesaj din partea compilatorului:

Struct member MyStruct.s of type MyStruct causes a cycle in the stru Dac o instant este folosit acolo unde un object este necesar, atunci a a a se va face automat o conversie implicit ctre System.Object (boxing). a a Ca atare, utilizarea unei structuri poate duce (dar nu obligatoriu, ci n functie de context) la un overhead datorat conversiei.

6.3.1

Structuri sau clase?

Structurile pot mult mai eciente alocarea memoriei atunci cnd sunt n a retinute ntrun tablou. De exemplu, crearea unui tablou de 100 de elemente de tip Point (de mai sus) va duce la crearea unui singur obiect (tabloul este un obiect), iar cele 100 de instante de tip structur ar alocate inline a n vectorul creat (i nu referinte ale acestora). Dac Point ar declarat ca i s a s clas, ar fost necesar crearea a 101 instante de obiecte heap (un obiect a a n pentru tablou, alte 100 pentru puncte), ceea ce ar duce la mai mult lucru pentru garbage collector. Dar cazul care structurile sunt folosite colectii (care stocheaz n n n a object), se va face automat un boxing, ceea ce duce la overhead (memorie i timp = resurse suplimentare). De asemenea, la transmiterea prin valoare s a unei structuri, se va face copierea tuturor cmpurilor continute pe stiv, a a ceea ce poate duce la un overhead semnicativ.

Curs 7 Tratarea exceptiilor. Atribute


7.1 Tratarea exceptiilor

C#, la fel ca alte limbaje, permite tratarea erorilor i a situatiilor des osebite prin exceptii. O exceptie este un obiect care ncapsuleaz informatie a despre o situatie anormal. Ea este folosit pentru a semnala contextul a a n care apare situatia deosebit a Un programator nu trebuie s confunde tratarea exceptiilor cu erorile sau a bugurile. Un bug este o eroare de programare care ar trebui s e xat a a nainte de livrarea codului. Exceptiile nu sunt gndite pentru a preveni bug a urile (cu toate c un bug poate s duc la aparitia unei exceptii), pentru c a a a a acestea din urm ar trebui s e eliminate. a a Chiar dac se scot toate bugurile, vor exista erori predictibile dar neprea venibile, precum deschiderea unui ier al crui nume este greit sau artiri s a s mp la 0. Nu se pot preveni astfel de situatii, dar se pot manipula astfel at nc nu vor duce la prbuirea programului. Cnd o metod alnete o situatie a s a a nt s exceptional, atunci se va arunca o exceptie; cineva va trebui s sesizeze (s a a a prind) aceast exceptie, sau eventual s lase o functie de nivel superior s a a a a o trateze. Dac nimeni nu trateaz aceast exceptie, atunci CLR o va face, a a a dar aceasta duce la oprirea threadului 1 .

7.1.1

Tipul Exception

C# se pot arunca ca exceptii obiecte de tip System.Exception sau In derivate ale acestuia. Exist o ierarhie de exceptii care se pot folosi, sau se a pot crea propriile tipuri exceptie.
1

Si nu neaprat a a ntregului proces!

147

148

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

Enumerm urmtoarele metode i proprieti relevante ale clasei Excepa a s at tion: public Exception(), public Exception(string), public Exception(string, Exception) - constructori; ultimul preia un obiect de tip Exception (sau de tip clas derivat) care va a a ncapsulat instanta curent; o exceptie n a poate deci s contin interiorul su o instant a altei exceptii (cea a a n a a care a fost de fapt semnalat initial). a public virtual string HelpLink {get; set;} obtine sau seteaz un link a ctre un ier help asociat acestei exceptii; poate de asemenea o a s adresa Web (URL) public Exception InnerException {get;} returnez exceptia care este a ncorporat exceptia curent a n a public virtual string Message {get;} obtine un mesaj care descrie exceptia curent a public virtual string Source {get; set;} obtine sau seteaz numele aplicatiei a sau al obiectului care a cauzat eroarea public virtual string StackTrace {get;} obtine o reprezetare string a apelurilor de metode care au dus la aparitia acestei exceptii public MethodBase TargetSite {get;} obtine metoda care a aruncat exceptia curent2 a

7.1.2

Aruncarea i prinderea exceptiilor s

Aruncarea cu throw Aruncarea unei exceptii se face folosind instructiunea throw. Exemplu: throw new System.Exception(); Aruncarea unei exceptii oprete executia metodei curente, dup care CLR s a ncepe s caute un manipulator de exceptie. Dac un handler de exceptie a a nu este gsit metoda curent, atunci CLR va cura stiva, ajungnduse a n a at a la metoda apelant. Fie undeva lantul de metode care au fost apelate a n se gsete un exception handler, e threadul curent este terminat de ctre a s a CLR. Exemplu:
MethodBase este o clas care pune la dispozitie informatii despre metodele i cona s structorii unei clase
2

7.1. TRATAREA EXCEPTIILOR using System; public class Test { public static void Main( ) { Console.WriteLine(Enter Main...); Test t = new Test( ); t.Func1( ); Console.WriteLine(Exit Main...); } public void Func1( ) { Console.WriteLine(Enter Func1...); Func2( ); Console.WriteLine(Exit Func1...); } public void Func2( ) { Console.WriteLine(Enter Func2...); throw new System.Exception( ); Console.WriteLine(Exit Func2...); } }

149

Se exemplic apelul de metode: Main() apeleaz Func1(), care apeleaz a a a Func2(); aceasta va arunca o exceptie. Deoarece lipsete un event handler s care s trateze aceast excepctie, se va a a ntrerupe threadul curent (i ind s singurul, i s ntregul proces) de ctre CLR, iar la ieire vom avea: a s Enter Main... Enter Func1... Enter Func2... Exception occurred: System.Exception: An exception of type System.Exception was thrown at Test.Func2( ) in ...Test.cs:line 24 at Test.Func1( ) in ...Test.cs:line 18 at Test.Main( ) in ...Test.cs:line 12 Deoarece este aruncat o exceptie, metoda Func2() nu se va mai executa a n ultima linie, ci CLRul va ncepe imediat cutarea event handlerului care a

150

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

s trateze exceptia. La fel, nu se execut nici ultima linie din Func1() sau a a din Main(). Prinderea cu catch Prinderea i tratarea exceptiei se poate face folosind un bloc catch, creat s prin intermediul instructiunii catch. Exemplu: using System; public class Test { public static void Main( ) { Console.WriteLine(Enter Main...); Test t = new Test( ); t.Func1( ); Console.WriteLine(Exit Main...); } public void Func1( ) { Console.WriteLine(Enter Func1...); Func2( ); Console.WriteLine(Exit Func1...); } public void Func2( ) { Console.WriteLine(Enter Func2...); try { Console.WriteLine(Entering try block...); throw new System.Exception( ); Console.WriteLine(Exiting try block...); } catch { Console.WriteLine(Exception caught and handled.); } Console.WriteLine(Exit Func2...); } }

7.1. TRATAREA EXCEPTIILOR

151

Se observ c sa folosit un bloc try pentru a delimita instructiunile care a a momentul care se arunc exceptia, restul vor duce la aparitia exceptiei. In n a instructiunilor din blocul try se ignor i controlul este preluat de ctre blocul as a catch. Deoarece exceptia a fost tratat, CLRul nu va mai opri procesul. La a ieire se va aa: s s Enter Main... Enter Func1... Enter Func2... Entering try block... Exception caught and handled. Exit Func2... Exit Func1... Exit Main... Se observ c blocul catch nu sa specicat tipul de exceptie care se a a n prinde; asta nseamn c se va prinde orice exceptie se va arunca, indiferent a a de tipul ei. Chiar dac exceptia este tratat, executia nu se va relua de la a a instructiunea care a produs exceptia, ci se continu cu instructiunea de dup a a blocul catch. Uneori, prinderea i tratatarea exceptiei nu se poate face functia apelat, s n a ci doar functia apelant. Exemplu: n a using System; public class Test { public static void Main( ) { Console.WriteLine(Enter Main...); Test t = new Test( ); t.Func1( ); Console.WriteLine(Exit Main...); } public void Func1( ) { Console.WriteLine(Enter Func1...); try { Console.WriteLine(Entering try block...); Func2( ); Console.WriteLine(Exiting try block...); }

152

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE catch { Console.WriteLine(Exception caught and handled.); } Console.WriteLine(Exit Func1...);

} public void Func2( ) { Console.WriteLine(Enter Func2...); throw new System.Exception( ); Console.WriteLine(Exit Func2...); } } La ieire se va aa: s s Enter Main... Enter Func1... Entering try block... Enter Func2... Exception caught and handled. Exit Func1... Exit Main... Este posibil ca ntro secvent de instructiuni s se arunce mai multe tipuri de a a acest caz, prinderea exceptiei exceptii, functie de natura strii aprute. In n a a printrun bloc catch generic, ca mai sus, nu este util; am vrea ca functie de a n natura exceptiei aruncate, s facem o tratare anume. Se sugereaza chiar s nu a a se foloseasc aceast constructie de prindere generic, deoarece majoritatea a a a n cazurilor este necesar s se cunoasc natura erorii (de exemplu pentru a a a scris a ntr-un ier de logging, pentru a consultat mai trziu). s a a Acest lucru se face specicnd tipul exceptiei care ar trebui tratate a n blocul catch: using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); }

7.1. TRATAREA EXCEPTIILOR

153

//incearca sa imparta doua numere public void TestFunc( ) { try { double a = 5; double b = 0; Console.WriteLine ({0} / {1} = {2}, a, b, DoDivide(a,b)); } //cel mai derivat tip de exceptie se specifica primul catch (System.DivideByZeroException) { Console.WriteLine(DivideByZeroException caught!); } catch (System.ArithmeticException) { Console.WriteLine(ArithmeticException caught!); } //Tipul mai general de exceptie este ultimul catch { Console.WriteLine(Unknown exception caught); } } //efectueaza impartirea daca se poate public double DoDivide(double a, double b) { if (b == 0) throw new System.DivideByZeroException( ); if (a == 0) throw new System.ArithmeticException( ); return a/b; } } exemplul de mai sus sa convenit ca o artire cu numitor 0 s duc la In mp a a o execeptie System.DivideByZeroException, iar o artire cu numrtor 0 mp aa s duc la aparitia unei exceptii de tip System.ArithmeticException. Este a a posibil specicarea mai multor blocuri de tratare a exceptiilor. Aceste a blocuri sunt parcurse ordinea care sunt specicate, iar primul tip care n n se potrivete cu exceptia aruncat ( sensul c tipul exceptie specicat este s a n a

154

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

e exact tipul obiectului aruncat, e un tip de baz al acestuia - din cauz a a de upcasting) este cel care va face tratarea exceptiei aprute. Ca atare, este a important ca ordinea exceptiilor tratate s e de la cel mai derivat la cel mai a general. exemplul anterior, System.DivideByZeroException este derivat In din clasa System.ArithmeticException. Blocul nally Uneori, aruncarea unei exceptii i golirea stivei pn la blocul de tratare s a a a exceptiei poate s nu e o idee bun. De exemplu, dac exceptia apare a a a atunci cnd un ier este deschis (i a s s nchiderea lui se poate face doar n metoda curent), atunci ar util ca s se a a nchid ierul a s nainte ca s e a preluat controlul de ctre metoda apelant. Altfel spus, ar trebui s existe o a a a garantie c un anumit cod se va executa, indiferent dac totul merge normal a a sau apare o exceptie. Acest lucru se face prin intermediul blocului nally, care se va executa orice situatie. Existenta acestui bloc elimin necesitatea n a existentei blocurilor catch (cu toate c i acestea pot s apar). as a a Exemplu: using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } public void TestFunc( ) { 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 line may or may not print); } finally { Console.WriteLine (Close file here.); }

7.1. TRATAREA EXCEPTIILOR } public double DoDivide(double a, double b) { if (b == 0) throw new System.DivideByZeroException( ); if (a == 0) throw new System.ArithmeticException( ); return a/b; } }

155

exemplul de mai sus, mesajul Close le here se va aa indiferent de ce In s parametri se transmit metodei DoDivide(). La aruncarea unei exceptii se poate particulariza obiectul care se arunc: a if (b == 0) { DivideByZeroException e = new DivideByZeroException( ); e.HelpLink = http://www.greselifatale.com; throw e; } iar cnd exceptia este prins, se poate prelucra informatia: a a catch (System.DivideByZeroException e) { Console.WriteLine( DivideByZeroException! goto {0} and read more, e.HelpLink); } Crearea propriilor exceptii cazul care suita de exceptii predenite nu este sucient, programIn n a atorul si poate construi propriile tipuri. Se recomand ca acestea s e a a derivate din System.ApplicationException, care este derivat direct din Sysa tem.Exception. Se indic aceast derivare deoarece astfel se face distinctie a a ntre exceptiile aplicatie i cele sistem (cele aruncate de ctre CLR). s a Exemplu: using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message): base(message)

156 { } }

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } public void TestFunc( ) { try { double a = 0; double b = 5; Console.WriteLine ({0} / {1} = {2}, a, b, DoDivide(a,b)); Console.WriteLine (This line may or may not print); } catch (System.DivideByZeroException e) { Console.WriteLine(DivideByZeroException! Msg: {0}, e.Message); Console.WriteLine(HelpLink: {0}, e.HelpLink); } catch (MyCustomException e) { Console.WriteLine(\nMyCustomException! Msg: {0}, e.Message); Console.WriteLine(\nHelpLink: {0}\n, e.HelpLink); } catch { Console.WriteLine(Unknown exception caught); } }

7.1. TRATAREA EXCEPTIILOR

157

public double DoDivide(double a, double b) { if (b == 0) { DivideByZeroException e = new DivideByZeroException( ); e.HelpLink= http://www.greselifatale.com; throw e; } if (a == 0) { MyCustomException e = new MyCustomException( Cant have zero divisor); e.HelpLink = http://www.greselifatale.com/NoZeroDivisor.htm; throw e; } return a/b; } } Rearuncarea exceptiilor Este perfect posibil ca ntrun bloc de tratare a exceptiilor s se se fac a a o tratare primar a exceptiei, dup care s se arunce mai departe o alt a a a a exceptie, de acelai tip sau de tip diferit (sau chiar exceptia original). Dac s a a se dorete ca aceast exceptie s pstreze cumva interiorul ei exceptia s a a a n original, atunci constructorul permite a nglobarea unei referinte la aceasta; aceast referint va accesibil prin intermediul proprietii InnerException: a a a at using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message,Exception inner): base(message,inner) { } } public class Test { public static void Main( ) { Test t = new Test( );

158 t.TestFunc( );

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

} public void TestFunc( ) { try { DangerousFunc1( ); } catch (MyCustomException e) { Console.WriteLine(\n{0}, e.Message); Console.WriteLine(Retrieving exception history...); Exception inner = e.InnerException; while (inner != null) { Console.WriteLine({0},inner.Message); inner = inner.InnerException; } } } public void DangerousFunc1( ) { try { DangerousFunc2( ); } catch(System.Exception e) { MyCustomException ex = new MyCustomException(E3 Custom Exception Situation!,e); throw ex; } } public void DangerousFunc2( ) { try { DangerousFunc3( ); } catch (System.DivideByZeroException e) {

7.1. TRATAREA EXCEPTIILOR Exception ex = new Exception(E2 - Func2 caught divide by zero,e); throw ex; }

159

} public void DangerousFunc3( ) { try { DangerousFunc4( ); } catch (System.ArithmeticException) { throw; } catch (System.Exception) { Console.WriteLine(Exception handled here.); } } public void DangerousFunc4( ) { throw new DivideByZeroException("E1 - DivideByZero Exception"); } }

7.1.3

Re ncercarea codului

Se poate pune ntrebarea: cum se procedeaz dac se dorecte revenirea a a s la codul care a produs exceptia, dup tratarea ei? Exist destule situatii a a n care reexecutarea acestui cod este dorit: s ne gndim de exemplu la cazul a a a care n ntr-o fereastr de dialog se specic numele unuei ier ce trebuie a a s procesat, numele este introdus greit si se dorete ca s se permit corectarea s s a a numelui. Un alt exemplu clasic este cazul care autorul unei metode tie c n s a o operatie poate s eueze periodic de exemplu din cauza unui timeout pe a s retea dar vrea s re a ncerce operatia de n ori nainte de a semnala eroare. aceast situatie se poate deni o etichet In a a naintea blocului try la care s se permit salutl printrun goto. Urmtorul exemplu permite unui utia a a lizator s specice de maxim trei ori numele unui ier ce se proceseaz, cu a s a revenire cazul erorii. n

160 using System; using System.IO; class Retry {

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

static void Main() { StreamReader sr; int attempts = 0; int maxAttempts = 3; GetFile: Console.Write("\n[Attempt #{0}] Specify file " + "to open/read: ", attempts+1); string fileName = Console.ReadLine(); try { sr = new StreamReader(fileName); Console.WriteLine(); string s; while (null != (s = sr.ReadLine())) { Console.WriteLine(s); } sr.Close(); } catch(FileNotFoundException e) { Console.WriteLine(e.Message); if (++attempts < maxAttempts) { Console.Write("Do you want to select " + "another file: "); string response = Console.ReadLine(); response = response.ToUpper(); if (response == "Y") goto GetFile;

7.1. TRATAREA EXCEPTIILOR

161

} else { Console.Write("You have exceeded the maximum " + "retry limit ({0})", maxAttempts); }

} catch(Exception e) { Console.WriteLine(e.Message); } Console.ReadLine(); } }

7.1.4

Compararea tehnicilor de manipulare a erorilor

Metoda standard de tratare a aerorilor a fost general returnarea unui n cod de eroare ctre metoda apelant. Ca atare, apelantul are sarcina de a a a descifra acest cod de eroare i s reactioneze consecint. a aa cum s a n a Ins s se arat mai jos, tratarea exceptiilor este superioar acestei tehnici din mai a a multe motive.

Neconsiderarea codului de retur Apelul unei functii care returneaz un cod de eroare poate fcut i fr a a s aa a utiliza efectiv codul returnat, scriind doar numele functiei cu parametrii de apel. Dac de exemplu pentru o anmit procesare se apeleaz metoda A a a a (de exemplu o deschidere de ier) dup care metoda B (citirea din ier), se s a s poate ca A s apar o eroare care nu este luat considerare; apelul lui B n a a a n este deja sortit eecului, pentru c buna sa functionare depinde de efectele lui s a A. Dac a metoda A arunc o exceptie, atunci nici mcar nu se mai ajunge a ns a a la apel de B, deoarece CLR-ul va pasa executia unui bloc catch/finally. Altfel spus, nu se permite o propagare a erorilor.

162

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

Manipularea erorii contextul adecvat n cazul care o metod A apeleaz alte metode B1 , . . . Bn , este posibil ca In n a a oricare din aceste n metode s cauzeze o eroare (i s returneze cod adecvat); a s a tratarea erorii din exteriorul lui A este dicil acest caz, deoarece ar trebui a n s se cerceteze toate codurile de eroare posibile pentru a determina motivul a aparitiei erorii. Dac se mai adauga i apelul de metod Bn+1 interiorul a s a n lui A, atunci orice apel al lui A trebuie s includ suplimentar i vericarea a a s pentru posibilitatea ca Bn+1 s cauzat o eroare. Ca atare, costul mentinerii a codului crete permanent, ceea ce are un impact negativ asupra TCO-ului3 s Folosind tratarea exceptiilor, aruncnd exceptii cu mesaje de eroare ex a plicite sau exceptii de un anumit tip (denit de programator) se poate trata mult mai convenabil o situatie deosebit. Mai mult dect att, introduc a a a erea apelului lui Bn+1 interiorul lui A nu reclam modicare suplimenn a tar, deoarece tipul de exceptie aruncat de Bn+1 este deja tratat (desigur, a se presupune c se denete un tip exceptie sau o ierarhie de exceptii creat a s a convenabil). Uurinta citirii codului s Pentru comparatie, se poate scrie un cod care realizeaz procesarea continutului a primul caz, unui ier folosind coduri de eroare returnate sau exceptii. In s solutia va contine cod de prelucrare a continutului mixat cu cod de vericare i reactie pentru diferitele cazuri de exceptie. Codul al doilea caz este mult s n mai scurt, mai uor de eles, de mentinut, de corectat i extins. s nt s Aruncarea de exceptii din constructori Nimic nu oprete ca o situatie deosebit s apar s a a a ntrun apel de constructor. Tehnica vericrii codului de retur nu mai functioneaz aici, deoarece a a un constructor nu returneaz valori. Folosirea exceptiilor este acest caz a n aproape de ne nlocuit.

7.1.5

Sugestie pentru lucrul cu exceptiile

Java, programatorii trebuei s declare c o metod poate arunca o In a a a exceptie i s o declare explicti s a ntro list astfel at un apelant s tie a nc a s c se poate se poate atepta la primirea ei. Aceast cunoatere avans a s a s n permite conceperea unui plan de lucru cu ecare dintre ele, preferabil dect a cazul .NET se sugereaz s se prind oricare dintre ele cu un catch generic. In a a a
3

Total Cost of Ownership.

7.2. ATRIBUTE

163

s se mentin o documentatie cu exceptiile care poti aruncate de ecare a a metod. a

7.2

Atribute

O aplicatie .NET contine cod, date i metadate. Metadata este o dat s a despre assembly (versiune, producator, etc), tipuri de date continute, etc stocate mpreun cu codul compilat. a Atributele sunt folosite pentru a da o extra-informatie compilatorului de .NET. Java folosete o combinatie de semne /** i @ pentru a include s s informatie relativ la clase, metode, cmpuri sau parametri. Aceste comentarii a a nu vor incluse bytecod-ul nal, doar eventuala documentatie. ns n n Folosind atributele, aceast informatie poate stocat codul compilat i a a n s reutilizat ulterior la runtime. a Unde ar utile aceste atribute? Exemple de utilizare a lor ar urmrirea a bugurilor care exist a ntrun sistem sau urmrirea stadiului proiectului. De a asemenea, anumite atribute predenite sunt utilizate pentru a specica dac a un tip este sau nu serializabil, care sunt portiunile de cod scoase din circulatie, informatii despre versiunea assembly-ului, etc.

7.2.1

Generaliti at

Atributele sunt de dou feluri: intrinseci (predenite) i denite de utia s lizator. Cele intrinseci sunt integrate platforma .NET i sunt recunoscute n s de CLR. Atributele denite de utilizator sunt create functie de dorintele n acestuia. Atributele se pot specica pentru: assemblyuri, clase, constructori, delegati, enumerri, evenimente, cmpuri, interfete, metode, module, parametri, proa a prieti, valori de retur, structuri. at Tinta unui atribut specic cui anume i se va aplica un atribut anume. a Tabelul 7.1 contine intele posibile i o scurt descriere a lor. t s a Tabelul 7.1: Tintele atributelor. Tint a All Assembly ClassMembers Class Constructor Descriere Orice element Un assembly Orice membru al unei clase O clas a Un constructor

164

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE Tabelul 7.1 (continuare) Descriere Un delegat O enumerare Un eveniment Un cmp a O interfat a O metod a Un modul Un parametru O proprietate O valoare de retur O structur a

Tint a Delegate Enum Event Field Interface Method Module Parameter Property ReturnValue Struct

Aplicarea atributelor se face prin specicarea numelui lor ntre paranteze drepte; mai multe atribute e se specicunul deasupra celuilalt, e a n interiorul acelorai paranteze, desprtite prin virgul. Exemplu: s a a [Serializable] [Webservice] echivalent cu: [Serializable, Webservice] cazul atributelor ce se specic pentru un assembly, forma lor este: In a [assembly:AssemblyDelaySign(false)] cazul acestor din urm atribute, specicarea lor se face dup toate declaratiile n a a using, dar naintea oricrui cod. general, specicarea unui atribut se face a In prin scrierea lui imediat naintea elementului asupra cruia se aplic: a a using System; [Serializable] class ClasaSerializabila { //definitia clasei }

7.2.2

Atribute predenite

Tabelul 7.2 prezint cteva dintre ele: a a

7.2. ATRIBUTE Tabelul 7.2: Atribute predenite. Atribut System.SerializableAttribute [Serializable]

165

Descriere Permite unei clase s e serializat a a pe disc sau ntro retea System.NonSerializedAttribute [NonSerialized] Permite unor membri s nu e a salvati pe retea sau pe disc System.Web.Services.WebServiceAttribute Permite specicarea unui nume i a s [WebService] unei descrieri pentru un serviciu Web System.Web.Services.WebMethodAttribute Marcheaz o metod ca ind expus a a a [WebMethod] ca parte a unui serviciu Web System.AttributeUsageAttribute Denete parametrii de utilizare s [AttributeUsage] pentru atribute System.ObsoleteAttribute [Obsolete] Marcheaz o secvent ca ind scoas a a a din uz System.Reection.AssemblyVersionAttribute Specic numrul de versiune al a a [AssemblyVersion] unui assembly System.Attribute.CLSCompliant Indic dac un element de program este a a [CLSCompliant] compatibil cu CLS System.Runtime.InteropServices. specic locatia DLL care contine a DllImportAttribute [DllImport] implementarea pentruo metod extern a a Intre parantezele drepte se arat cum se specic acel atribut. a a Exemplul de mai jos folosete atributul System.ObsoleteAttribute. s using System; namespace AttributeSample1 { class Class1 { [STAThread] static void Main(string[] args) { int s1 = AddTwoNumbers(2, 3); int s2 = AddNumbers(2, 3); int s3 = AddNumbers(2, 3, 4); } [Obsolete("obsolete: use AddNumbers instead")]

166

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

static int AddTwoNumbers(int a, int b) { return a + b; } static int AddNumbers(params int[] numbers) { int result = 0; foreach(int number in numbers) result += number; return result; } } } La compilarea codului se genereaz un avertisment care semnalizeaz faptul a a c se utilizeaz o metod care este scoas din uz. Mesajul specicat ca a a a a parametru al atributului este aat ca mesaj al avertismentului. Suplimentar, s pentru atributul Obsolete se poate specica dac respectivul avertisment este a sau nu interpretat ca o eroare, adugnd un parametru de tip bool: false ca la a a compilare s se genereze avertisment, true pentru ca utilizarea s e tratat a a a ca o eroare. Exemplul de mai jos exemplic serializarea i deserializarea unui obiect: a s using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; [Serializable] class Point2D { public int X; public int Y; } class MyMainClass { public static void Main() { Point2D My2DPoint = new Point2D(); My2DPoint.X = 100; My2DPoint.Y = 200; Stream WriteStream = File.Create("Point2D.bin"); BinaryFormatter BinaryWrite = new BinaryFormatter();

7.2. ATRIBUTE

167

BinaryWrite.Serialize(WriteStream, My2DPoint); WriteStream.Close(); Point2D ANewPoint = new Point2D(); Console.WriteLine("New Point Before Deserialization: ({0}, {1})", ANewPoint.X, ANewPoint.Y); Stream ReadStream = File.OpenRead("Point2D.bin"); BinaryFormatter BinaryRead = new BinaryFormatter(); ANewPoint = (Point2D)BinaryRead.Deserialize(ReadStream); ReadStream.Close(); Console.WriteLine("New Point After Deserialization: ({0}, {1})", ANewPoint.X, ANewPoint.Y); } }

7.2.3

Exemplicarea altor atribute predenite

Atributul Conditional Acest atribut se ataeaz la o metod pentru care se dorete ca atunci s a a s cnd compilatorul o alnete la un apel, dac un anumit simbol nu e denit a nt s a atunci nu va chemat. Este folosit pentru a omite anumite apeluri de a a metode ( de exemplu cele folosite la etapa de debugging). Exemplu: using System; using System.Diagnostics; namespace CondAttrib { class Thing { private string name; public Thing(string name) { this.name = name; SomeDebugFunc(); SomeFunc(); } public void SomeFunc() { Console.WriteLine("SomeFunc"); } [Conditional("DEBUG")]

168

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE public void SomeDebugFunc() { Console.WriteLine("SomeDebugFunc"); }

} public class Class1 { [STAThread] static void Main(string[] args) { Thing t = new Thing("T1"); } } } Denirea unui anumit simbol (de exemplu DEBUG) se poate face dou n a moduri: prin folosirea unei directive de preprocesor de tipul #dene: #define DEBUG prin folosirea parametrilor din linia de comanda sau a posibilitilor at mediului integrat de dezvoltare. De exemplu, pentru a se deni un anumit simbolul din Visual Studio se procedeaz astfel: clic dreapta a Solution Explorer pe numele proiectului, selectare Properties apoi n Conguration Properties. Pe linia Conditional Compilation Constants se adaug simbolul dorit. Acest lucru va avea ca efect folosirea optiunii a dene a compilatorului. Dac la compilare simbolul nu este denit atunci compilatorul va ignora a apelurile de metode calicate cu atributul Conditional. Acest lucru se paote verica att urmrind executia programului, ct i din inspectia codului IL a a a s rezultat la compilare. Atributul CLSCompliant Acest atribut se aplic pe assemblyuri. Dac un assembly este marcat a a ca ind CLSCompliant, orice tip expus public assembly care nu este comn patibil CLS trebuie s e marcat cu CLSCompliant(false). De exmplu, CLS a prevede faptul c tipurile de date a ntregi ar trebui s e cu semn. O clas a a poate s contin membri (cmpuri, metode) de tip unsigned, dar dac rea a a a spectiva clas este declarat ca ind CLSCompliant atunci acestea ar trebui a a

7.2. ATRIBUTE

169

declarate ca ind ne-vizibile din afara assemblyului. Dac ele sunt expuse a n afara assemblyului, unele limbaje .NET s-ar putea s nu le poat folosi efea a tiv. Pentru a ajuta dezvoltatorii .NET s evite asemenea situatii, platforma a pune la dispozitie atributul CLSCompliant cu care se controleaz rspunsul a a compilatorului la expunerea entitilor ne-compatibile cu CLS; astfel, se va at genera o eroare sau se vor ignora asemenea cazuri. Exemplu: [assembly:CLSCompliant(true)] namespace DemoCLS { public class ComplianceTest { //Tipul uint nu ste compatibil CLS //deaorece acest camp este privat, regulile CLS nu se aplica private uint a = 4; //deoarece acest camp uint este public, avem //incompatibilitate CLS public uint B = 5; //Acesta este modul corect de expunere a unui uint: //tipul long este compatibil CLS public long A { get { return a; } } } } Dac se compileaz assembly-ul de mai sus atunci apare o eroare: a a Type of DemoCLS.ComplianceTest.B is not CLS-compliant Pentru ca aceast eroare s nu mai apar putem s declarm B ca ind a a a a a invizibil din exteriorul assembly-ului sau s adugm inaintea lui B atributul: a a a [CLSCompliant(false)] Meritul acestui atribut este c anunt programatorul despre eventualele neconcordante a a cu Common Language Specications.

170

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

7.2.4

Atribute denite de utilizator

general, atributele predenite acoper marea majoritate a situatiilor In a care cer utilizarea de atribute. Eventualizatea ca utilizatorul s doreasc a a crearea propriilor sale atribute este prevzut de ctre platforma .NET, a a a dnduse posibilitatea denirii lor. a Exist cteva situatii care e benec denirea de noi atribute. Exema a n a plul cel mai des alnit este acela care pentru a se mentine informatii nt n despre un cod la care lucreaz mai multe echipe, se denete un atribut care a s s serveasc la pasarea de informatie relativ la portiuni din cod. Un alt a a exemplu este utilizarea unui sistem de urmrire a stadiului de dezvoltare a a codului, care ar folosi informatia stocat atribute. a n Un atribut utilizator este o clas denit ca pn acum. Se cere a a a a a derivat din System.Attribute e direct, e indirect. Sunt civa pai care a at s trebuie parcuri pentru realizara unui atribut: specicarea intei, derivarea ei s t adecvat, denumirea ei conformitate cu anumite reguli (recomandat, dar a n nu obligatoriu), denirea clasei. Pasul 1. Declararea intei t Primul pas crearea unui atribut utilizator este specicarea domeniului n su de aplicabilitate, adic a elementelor crora li se poate ataa. Tintele sunt a a a s cele din tabelul 7.1, care sunt de fapt valori din enumerarea AttributeTargets. Specicarea intei se face prin intermediul unui (meta)atribut AttributeUsage. t Pe lng inta propriuzis, se mai pot specica valorile proprietilor Inhera at a at ited i AllowMultiple. s Proprietatea Inherited este de tip boolean i precizeaz dac atributul s a a poate motenit de ctre clasele derivate din cele creia i se aplic. Valoarea s a a a implicit este true. a Proprietatea AllowMultiple este de asemenea de tip boolean i specic s a dac se pot utiliza mai multe instante ale atributului pe un acelai element. a s Valoarea implicit este false. a Vom deni mai jos inta atributului pe care vom construi ca ind t l assemblyul, clasa, metoda, cu posibilitate de repetare a sa: [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] Pasul 2. Declararea unei clase atribut Pentru declarearea unei clase atribut, urmtoarele reguli trebuie s e a a avute vedere: n

7.2. ATRIBUTE

171

(obligatoriu) O clas atribut trebuie s deriveze direct sau indirect Sysa a tem.Attribute (obligatoriu) O clas atribut trebuie s e declarat ca ind public a a a a (recomandat) Un atribut ar trebui s aib suxul Attribute. a a Vom declara clasa atribut CodeTrackerAttribute. Acest atribut s-ar putea folosi cazul care s-ar dori urmrirea dezvoltrii codului cadrul unui n n a a n proiect de dimensiuni foarte mari, la care particip mai multe echipe. Acest a atribut utilizator va inclus codul compilat i distribuit altor echipe (care n s nu vor avea acces la cod). O alternativ ar folosirea documentattiei XML a generate dup comentariile din cod. a [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] public class CodeTrackerAttribute : System.Attribute { //aici e codul dumneavoastra } Pasul 3. Declararea constructorilor i a proprietilor s at Acest pas const denirea efectiv membrilor clasei. Vom considera a n a ctributul pe care crem va purta trei informatii: numele programatorua l a lui care a actionat asupra respectivei uniti de cod, faza care se a, at n a optional note. Primele dou atribute sunt mandatorii i vor preluate prin a s constructor, al treilea poate setat prin intermediul unei proprieti. at using System; [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] public class CodeTrackerAttribute : System.Attribute { private string name; private string phase; private string notes; public CodeTrackerAttribute(string name, string phase) { this.name = name; this.phase = phase; }

172

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE

public virtual string Name { get{return name;} } public virtual string Phase { get{return phase;} } public virtual string Notes { get{return notes;} set{notes=value;} } } Pasul 4. Utilizarea atributului utilizator Atributele denite de utilizator sutn folosite acelai mod ca i cele n s s implicite. Codul de mai jso exemplic acest lucru: a [CodeTracker("Lucian Sasu", "implementing specification", Notes = "First attempt")] class AttribTest { public AttribTest() { Console.WriteLine("AttribTest instance"); } [CodeTracker("Lucian Sasu", "April 1st 2005")] public void SayHello(String message) { Console.WriteLine(message); } } O inspetie a codului IL rezultat arat c aceste atribute s-au salvat a a n codul compilat. Obtinerea acestor atribute i a valorilor lor se poate face s foarte uor prin reectare. s

Curs 8 ADO.NET
8.1 Ce reprezint ADO.NET? a

ADO.NET reprezint o parte component a lui .NET Framework ce pera a mite aducerea, manipularea i modicarea datelor. mod normal, o surs s In a de date poate s e o baz de date, dar de asemenea un ier text sau Exa a s cel sau XML sau Access. Lucrul se poate face e conectat, e deconectat de la sursa de date. Dei exist variate modaliti de lucru cu bazele de s a at date, ADO.NET se impune tocmai prin faptul c permite lucrul deconectat a de la baza de date, integrarea cu XML, reprezentarea comun a datelor cu a posibilitatea de a combina date din variate surse, toate pe baza unor clase .NET. Faptul c se permite lucrul deconectat de la sursa de date rezolv urmtoarele a a a probleme: mentinerea conexiunilor la baza de date este o operatie costisitoare. O bun parte a limii de band este mentinut ocupat pentru nite a at a a a s procesri care nu necesit neaprat conectare continu a a a a probleme legate de scalabilitatea aplicatiei: se poate ca serverul de baze de date s lucreze uor cu 5-10 conexiuni mentinute, dar dac numrul a s a a acestora crete aplicatia poate s reactioneze extrem de lent s a pentru unele servere se impun clauze asupra numrului de conexiuni ce a se pot folosi simultan. Toate acestea fac ca ADO.NET s e o tehnologie mai potrivit pentru deza a voltarea aplicatiilor Internet dect cele precedente (e.g. ADO, ODBC). a Pentru o prezentare a metodelor de lucru cu surse de date sub platform a Windows se poate consulta [7]. 173

174

CURS 8. ADO.NET

Vom exemplica cele ce urmeaz preponderent pe baz de date Min a a crosoft SQL 2000. Pentru lucrul cu MS SQL Server se poate instala gratuit versiunea gratuit de Microsoft SQL Server 2000 Desktop Engine1 . a

8.2

Furnizori de date ADO.NET n

Din cauza existentei mai multor tipuri de surse de date (de exemplu, a mai multor productori de servere de baze de date) e nevoie ca pentru ecare a tip major de protocol de comunicare s se foloseasc o bibliotec specializat a a a a de clase. Toate aceste clase implementeaz nite interfete bine stabilite, ca a s atare trecerea de la un SGBD la altul se face cu eforturi minore (dac codul a este scris innd cont de principiile POO). t a Exist urmtorii furnizori de date2 (lista nu este complet): a a a Tabelul 8.1: Furnizori de date. Nume furnizor Prex API ODBC Data Odbc Provider OleDb Data OleDb Provider Oracle Data Provider SQL Data Provider Borland Data Provider Oracle Sql Bdp Descriere Surse de date cu interfat ODBC a (baze de date vechi) Surse de date care expun o interfat a OleDb, de exemplu Access i Excel sau s SQL Sever versiune mai veche de 7.0 SGBD Oracle Pentru interactiune cu Microsoft SQL Server 7.0, 2000, 2005 sau MSDE Acces generic la SGBD-uri precum Interbase, SQL Server, IBM DB2, Oracle

Prexele trecute coloana Prex sunt folosite pentru clasele de lucru n specice unui anumit furnizor ADO.NET: de exemplu, pentru o connexiune SQL se va folosi clasa SqlConnection.

8.3

Componentele unui furnizor de date

Fiecare furnizor de date ADO.NET const patru componente: Cona n nection, Command, DataReader, DataAdapter . Arhitectura ADO.NET este
1 2

Adresa: http://www.microsoft.com/sql/msde/downloads/download.asp Engl: Data Providers

8.3. COMPONENTELE UNUI FURNIZOR DE DATE prezentat gura 8.1 a n

175

Figura 8.1: Principalele clase ADO.NET

Mai jos sunt date descrieri succinte ale claselor cel mai des utilizate.

8.3.1

Clasele Connection

Sunt folosite pentru a reprezenta o conexiune la surse de date specice. Ele contin date specice conexiunii, cum ar locatia sursei de date, nu mele i parola contului de acces, etc. plus au metode pentru deschiderea s In i s nchiderea conexiunilor, pornirea unei tranzactii sau setarea perioadei de time-out. Stau la baza oricrei a ncercri de conectare. a

176

CURS 8. ADO.NET

8.3.2

Clasele Command

Sunt folosite pentru a executa diferite comenzi pe baza de date (SELECT, INSERT, UPDATE, DELETE) i pentru a furniza un obiect de tip s DataReader sau DataSet. Pot folosite pentru apelarea de proceduri stocate aate pe server. Ele permit scrierea de interogri SQL parametrizate a sau specicarea parametrilor pentru procedurile stocate.

8.3.3

Clasele DataReader

Permit navigarea de tip forwardonly, readonly mod conectat la sursa n de date. Se obtin pe baza unui obiect de tip Command prin apelul metodei ExecuteReader(). Accesul rezultat este extrem de rapid cu minim de resurse consumate.

8.3.4

Clasele DataAdapter

Ultima component principal a unui furnizor de date .NET este DataAdapter. a a Functioneaz ca o punte a ntre sursa de date i obiecte de tip DataSet deconecs tate, permitnd efectuarea operatiilor pe baza de date. Contin referinte ctre a a obiecte de tip Connection i deschid / s nchid singure conexiunea la baza de date. plus, un DataAdapter contine referinte ctre patru comenzi pentru In a selectare, tergere, modicare i adugare la baza de date. s s a

8.3.5

Clasa DataSet

Aceast clas nu este parte a unui furnizor de date .NET ci independent a a a de particularitile de conectare i lucru cu o baz de date anume. Prezint at s a a marele avantaj c poate sa lucreze deconectat de la sursa de date, facilitnd a a stocarea i modicarea datelor local, apoi reectarea acestor modicri s a n baza de date. Un obiect DataSet este de fapt un container de tabele i relatii s ntre tabele. Folosete serviciile unui obiect de tip DataAdapter pentru as i procura datele i a trimite modicrile s s a napoi ctre baza de date. Datele a sunt stocate de un DataSet format XML; acelai format este folosit pentru n s transportul datelor.

8.4

Obiecte Connection

Clasele de tip Connection pun la dispozitie tot ceea ce e necesar pentru conectarea la baze de date. Este primul obiect cu care un programator ia contact atunci cnd a ncearc s foloseasc un furnizor de date .NET. a a a Inainte

8.4. OBIECTE CONNECTION

177

ca o comand s e executat pe o baz de date trebuie stabilit o conexiune a a a a a la ea. Orice clas de tip conexiune (din orice furnizor de date) implementeaz a a intefata IDbConnection. De exemplu, clasele SqlConnection (folosit pen a tru conectare la server Microsoft SQL Server 2000) sau OleDbConnection (folosit pentru conectare la iere .mdb din Access sau .xls) implementeaz a s a IDbConnection. Pentru deschiderea unei conexiuni se poate proceda ca mai jos: using System.Data.SqlClient;//spatiul de nume SqlClient ... SqlConnection cn = new SqlConnection("Data Source=serverBD; Database=Northwind;User ID=sa;Password=parola"); cn.Open(); ... Mai sus sa specicat numele calculatorului pe care se a instalat severul a MSSQL de baze de date (serverBD), baza de date la care se face conectarea (Northwind), contul SQL cu care se face accesul (sa) i parola pentru s acest cont (parola). Pentru conectarea la un ier Access Northwind.mdb aat directorul s n c:\lucru se folosete un obiect de tipul OleDbConnection sub forma: s using System.Data.OleDb;//spatiul de nume OleDb ... OleDbConnection cn = new OleDbConnection( @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source= C:\Lucru\Northwind.mdb"); cn.Open(); ... S-a specicat furnizorul de date (Microsoft.Jet.OLEDB.4.0 pentru ier Acs cess) precum i locul unde se a sursa de date (C:\Lucru\Northwind.mdb). s a Vom enumera principalele proprieti, metode i evenimente pentru un at s obiect de tip Connection.

8.4.1

Proprieti at

1. ConnectionString: de tip String, cu accesori de get i set; aceast pros a prietate denete un string valid care permite identicarea tipului i s s locatiei sursei de date la care se face conectarea i eventual contul si s

178

CURS 8. ADO.NET parola de acces. Acest string contine lista de parametri necesari pen tru conectare sub forma numeParametru=valoare, separati prin punct i virgul. Parametrii sunt: s a provider : se specic furnizorul de date pentru conectarea la sursa a de date. Acest furnizor trebuie precizat doar dac se folosete OLE a s DB .NET Data Provider, a nu se specic pentru conectare la ns a SQL Server. Data Source: se specic numele serverului de baze de date sau a numele ierului de date. s Initial Catalog (sinonim cu Database): specic numele baze de a date. Baza de date trebuie s se gseasc pe serverul dat Data a a a n Source. User ID: specica un nume de utilizator care are acces de loginare la server. Password : specic parola contului de mai sus. a

2. ConnectionTimeout: de tip int, cu accesor de get, valoare implicit a 15; specic numrul de secunde pentru care un obiect de conexiune a a ar trebui s atepte pentru realizarea conectrii la server a s a nainte de a se genera o exceptie. Se poate specica o valoare diferit de 15 a n ConnectionString folosind parametrul Connect Timeout: SqlConnection cn = new SqlConnection("Data Source=serverBD; Database=Northwind;User ID=sa;Password=parola; Connect Timeout=30"); Se poate specica pentru Connect Timeout valoarea 0 cu semnicatia ateapt orict, dar se sugereaz s nu se procedeze acest mod. s a a a a n 3. Database: atribut de tip string, read-only, returneaz numele bazei de a date la care sa fcut conectarea. Folosit pentru a arta unui utilizator a a a care este baza de date pe care se face operarea. 4. Provider : atribut de tip string, read-only, returneaz furnizorul OLE a DB. 5. ServerVersion: atribut de tip string, read-only, returneaz versiunea a de server la care sa fcut conectarea. a 6. State: atribut de tip enumerare ConnectionState , read-only, returneaz a starea curent a conexiunii. Valorile posibile sunt: Broken, Closed, a Connecting, Executing, Fetching, Open.

8.4. OBIECTE CONNECTION

179

8.4.2

Metode

1. Open(): deschide o conexiune la baza de date 2. Close(), Dispose(): nchid conexiunea i elibereaz toate resursele alos a cate pentru ea 3. BeginTransaction(): pentru executarea unei tranzactii pe baza de date; la sfrit se apeleaz Commit() sau Rollback(). as a 4. ChangeDatabase(): se modic baza de date la care se vor face conexiua nile. Noua baz de date trebuie s existe pe acelai server ca precedenta. a a s 5. CreateCommand(): creeaz un obiect de tip Command valid (care ima plementeaz interfata IDbCommand ) asociat cu conexiunea curent. a a

8.4.3

Evenimente

Un obiect de tip conexiune poate semnala dou evenimente: a evenimentul StateChange: apare atunci cnd se schimb starea conexa a iunii. Event-handlerul este de tipul delegat StateChangeEventHandler, care spune care sunt strile a ntre care sa fcut tranzitia. a evenimentul InfoMessage: apare atunci cnd furnizorul trimite un avera tisment sau un mesaj informational ctre client. a

8.4.4

Stocarea stringului de conexiune ier de conn s gurare

general este contraindicat ca stringul de conexiune s e scris direct In a n cod; modicarea datelor de conectare (de exemplu parola pe cont sau sursa de date) ar nsemna recompilarea codului. .NET Framework permite mentinerea unor perechi de tipul chei-valoare, specice aplicatiei; ierul este de tip XML. Pentru aplicatiile Web ierul se s s numete web.cong, pentru aplicatiile de tip consol ierul de congurare are s a s extensia cong i numele aplicatiei, iar pentru aplicatiile Windows acest ier s s nu exist implicit, dar se adaug prin click dreapta pe proiect Solution a a n Explorer->Add->Add New Item->Application Conguration File, implicit acesta avnd numele App.cong. e care caz, structura ierului XML de a In s congurare este similar. Implicit, acesta arat astfel: a a

180 <?xml version="1.0" encoding="utf-8" ?> <configuration> </configuration>

CURS 8. ADO.NET

interiorrul elementului rdcin conguration se va introduce elementul In a a a appSettings, care va contine oricte perechi cheie-valoare interiorrul unui a n atribut numit add, precum mai jos: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="constring" value="Data Source=serverBD;database=Northwind;User ID=sa; pwd=parola"/> </appSettings> </configuration> Clasele neceasre pentru accesarea ierului de congurare se gsesc spatiul s a n de nume System.Conguration. Utilizarea stringului de conexiune denit anterior se face astfel: using System; using System.Data; using System.Data.SqlClient; using System.Configuration; public class UsingConfigSettings { public static void Main() { SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); //se lucreaza cu conexiunea con.Close(); } }

8.4.5

Gruparea conexiunilor

Gruparea conexiunilor3 reprezint reutilizarea resurselor de tip conexia une la o baz de date. Atunci cnd se creeaz o grupare de conexiuni se a a a genereaz automat mai multe obiecte de tip conexiune, acest numr ind a a
3

Engl: connection pooling

8.5. OBIECTE COMMAND

181

egal cu minimul setat pentru gruparea respectiv. O nou conexiune este a a creat dac toate conexiunile sunt ocupate i se cere o nou conexiune. Dac a a s a a dimensiunea maxim setat a gruprii este atins, atunci nu se va mai crea a a a o conexiune nou, ci se va pune a ntro coad de ateptare. Dac ateparea a s a s dureaz mai mult dect este precizat valoarea de Timeout se va arunca o a a n exceptie. Pentru a returna o conexiune la grupare trebuie apelat metoda a Close() sau Dispose() pentru acea conexiune. Sursele de date .NET administreaz automat acest aspect, degrevndu-l a a pe programator de aceast aspect ce nu ine de logica aplicatiei. La dorint t a comportamentul implicit se poate modica prin intemediul continutului stringu lui de conectare.

8.5

Obiecte Command

Un clas de tip Command dat de un furnizor .NET trebuie s implea a a menteze interfata IDbCommand, ca atare toate vor asigura un set de servicii bine specicat. Un asemenea obiect este folosit pentru a executa comenzi pe baza de date: SELECT, INSERT, DELETE, UPDATE sau apel de proceduri stocate (dac SGBD-ul respectiv tie acest lucru). Comanda se poate a s executa numai dac s-a deschis o conexiune la baza de date. a Exemplu: SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", con); Codul care utilizeaz o comand pentru lucrul cu siere mdb sau xls ar a a foarte asemntor, cu diferenta c loc de SqlCommand se folosete OleDa a a n s bCommand, din spatiul de nume System.Data.OleDb. Nu trebuie modicat altceva, doarece locatia sursei de date se specic doar la conexiune. a Se observ c obiectul de conexiune este furnizat comenzii create. Enua a merm mai jos principalele proprieti i metode ale unui obiect de tip coa at s mand. a

8.5.1

Proprieti at

1. CommandText: de tip String, cu ambii accesori; contine comanda SQL sau numele procedurii stocate care se execut pe sursa de date. a 2. CommandTimeout: de tip int, cu ambii accesori; reprezint numrul a a de secunde care trebuie s e ateptat pentru executarea interogrii. a s a Dac se depeste acest timp, atunci se arunc o exceptie. a as a

182

CURS 8. ADO.NET

3. CommandType: de tip CommandType (enumerare), cu ambii accesori; reprezint tipul de comand care se execut pe sursa de date. Valorile a a a pot : CommandType.StoredProcedure - interpreteaz comanda continut a a proprietatea CommandText ca o un apel de procedur stocat n a a denit baza de date. a n CommandType.Text - interpreteaz comanda ca ind o comand a a SQL clasic. a CommandType.TableDirect - momentan disponibil numai pentru furnizorul OLE DB (deci pentru obiect de tip OleDbCommand); dac proprietatea CommandType are aceast valoare, atunci proa a prietatea CommandText este interpretat ca numele unui tabel a pentru care se aduc toate liniile i coloanele la momentul exes cutrii. a 4. Connection - proprietate de tip System. Data. [.NET Data Provider]. PrexConnection, cu ambii accesori; contine obiectul de tip conexiune folosit pentru legarea la sursa de date; Prex este prexul asociat furnizorului respectiv (a se vedea tabelul 8.1). 5. Parameters - proprietate de tip System.Data.[.NET Data Provider]. PrexParameterCollection, read-only; returneaz o colectie de parametri a care s-au transmis comenzii; aceast list a fost creat prin apelul a a a metodei CreateParameter().Prex reprezint acelasi lucru ca mai sus. a 6. Transaction - proprietate de tip System.Data.[.NET Data Provider]. PrexTransaction, read-write; permite accesul la obiectul de tip tranzactie care se cere a executat pe sursa de date.

8.5.2

Metode

1. Constructori - un obiect de tip comand poate creat i prin intera s mediul apelului de constructor; de exemplu un obiect SqlCommand se poate obtine astfel: SqlCommand cmd; cmd = new SqlCommand(); cmd = new SqlCommand(string CommandText); cmd = new SqlCommand(string CommandText, SqlConnection con ); cmd = new SqlCommand(string CommandText, SqlConnection con, SqlTransaction trans);

8.5. OBIECTE COMMAND

183

2. Cancel() - ncearc s opreasc o comand dac ea se a executie. a a a a a a n Dac nu se a executie, sau aceast a a n a ncercare nu are nici un efect nu se intmpl nimic. a a 3. Dispose() - distruge obiectul comand. a 4. ExecuteNonQuery() - execut o comand care nu returneaz un set de a a a date din baza de date; dac comanda a fost de tip INSERT, UPDATE, a DELETE, se returneaz numrul de a a nregistrri afectate. Dac nu este a a denit conexiunea la baza de date sau aveasta nu este deschis, se a a arunc o exceptie de tip InvalidOperationException. a Exemplu: SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand(); cmd.CommandText = "DELETE FROM Customers WHERE CustomerID = SEVEN"; cmd.Connection = con; con.Open(); Console.WriteLine(cmd.ExecuteNonQuery().ToString()); con.Close() exemplul de mai sus se returneaz numrul de In a a nregistrri care au a fost terse. s 5. ExecuteReader() - execut comanda continut proprietatea Coma a n mandText i se returneaz un obiect de tip IDataReader (e.g. Sqls a DataReader sau OleDbDataReader ). Exemplu: se obtine continutul tabelei Customers ntrun obiect de tip SqlDataReader (se presupune c baza de date se stocheaz pe un server a a MSSQL): SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand(); cmd.CommandText = "SELECT * FROM Customers"; cmd.Connection = con; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) {

184 Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); con.Close();

CURS 8. ADO.NET

Metoda ExecuteReader() mai poate lua un argument optional de tip enumerare CommandBehavior care descrie rezultatele i efectul asupra s bazei de date: CommandBehavior.CloseConnection - conexiunea este nchis atunci a cnd obiectul de tip IDataReader este a nchis. CommandBehavior.KeyInfo - comanda returnez informatie dea spre coloane i cheia primar. s a CommandBehavior.SchemaOnly - comanda returnez doar informatie a despre coloane. CommandBehavior.SequentialAccess - d posibilitatea unui DataReader a s manipuleze a nregistrri care contin cmpuri cu valori binare de a a mare ntindere. Acest mod permite arcarea sub forma unui ux nc de date folosind GetChars() sau GetBytes(). CommandBehavior.SingleResult - se returneaz un singur set de a rezultate CommandBehavior.SingleRow - se returneaz o singur linie. De a a exemplu, dac codul anterior a n nainte de while obtinerea obiec tului reader sar face cu: SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleRow); atunci sar returna doar prima nregistrare din setul de date. 6. ExecuteScalar() - execut comanda continut proprietatea Coma a n mandText; se returneaz valoarea primei coloane de pe primul rnd a a a setului de date rezultat; folosit pentru obtinerea unor rezultate de tip agregat (SELECT COUNT(*) FROM CUSTOMERS, de exemplu). 7. ExecuteXmlReader() - returneaz un obiect de tipul XmlReader obtinut a din rezultatul interogrii pe sursa de date. a Exemplu:

8.5. OBIECTE COMMAND

185

SqlCommand custCMD=new SqlCommand("SELECT * FROM Customers FOR XML AUTO, ELEMENTS", con); System.Xml.XmlReader myXR = custCMD.ExecuteXmlReader();

8.5.3

Utilizarea unei comenzi cu o procedur stocat a a

Pentru a se executa pe server o procedur stocat denit baza respeca a a n tiv, este necesar ca obiectul comand s aibe proprietatea CommandType la a a a valoarea CommandType.StoredProcedure iar proprietatea CommandText s a contin numele procedurii stocate: a SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand("Ten Most Expensive Products", con); cmd.CommandType = CommandType.StoredProcedure; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetDecimal(1)); } reader.Close(); con.Close(); Observatie: se remarc toate exemplele de mai sus faptul c ecare conex a n a iune se nchide manual, printr-un apel de tipul con.Close(). Daca conexiunea a fost folosit pentru un obiect de tip DataRead atunci acesta din urm a a trebuie s e i el a s nchis, naintea nchiderii conexiunii. Dac nu se face acest a apel atunci conexiunea va inut ocupat i nu va putea reutilizat. t a as a

8.5.4

Folosirea comenzilor parametrizate

Exist posibilitatea de a rula cod SQL (interogri, proceduri stocate) a a pe server care s e parametrizate. Orice furnizor de date .NET permite a crearea obiectelor parametru care pot adugate la o colectie de parametri a ai comenzii. Valoarea cestor parametrii se specic e prin numele lor (cazul a SqlParameter ), e prin poziia lor (cazul OleDbParameter ). t Exemplu: vom aduce din tabela Customers toate nregistrrile care au a n cmpul Country valoarea USA. a SqlConnection con = new SqlConnection(

186

CURS 8. ADO.NET

ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand("SELECT * FROM Customers WHERE Country=@country",con); SqlParameter param = new SqlParameter("@country", SqlDbType.VarChar); param.Value = "USA"; cmd.Parameters.Add( param ); con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); con.Close(); Pentru parametrul creat sa setat tipul lui (ca ind tip SQL ir de caracs tere) i valoarea. De retinut faptul c numele parametrului se prexeaz cu s a a caracterul @. cazul care un parametru este de ieire, acest lucru trebuie spus In n s explicit folosind proprietatea Direction a parametrului respectiv: SqlCommand cmd = new SqlCommand( "SELECT * FROM Customers WHERE Country = @country; " + "SELECT @count = COUNT(*) FROM Customers WHERE Country = @country", con); SqlParameter param = new SqlParameter("@country", SqlDbType.VarChar); param.Value = "USA"; cmd.Parameters.Add( param ); cmd.Parameters.Add(new SqlParameter("@count", SqlDbType.Int)); cmd.Parameters["@count"].Direction = ParameterDirection.Output; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); Console.WriteLine("{0} - {1}", "Count", cmd.Parameters["@count"].Value.ToString());

8.6. OBIECTE DATAREADER con.Close(); Remarcm urmtoarele: a a este posibil ca ntro comand s se execute mai multe interogri a a a

187

pentru parametrul de ieire numit @count trebuie fcut declarare s a a de directie; implicit un parametru este de intrare parametrii de ieire sunt accesibili doar dup s a nchiderea obiectului de tip DataReader

8.6

Obiecte DataReader

Un obiect de tip DataReader este folosit pentru a citi date dintr-o surs a de date. Caracteristicile unei asemenea clase sunt: 1. implementeaz a ntotdeauna interfata IDataReader 2. se lucreaz conectat la sursa de date - pe toat perioada ct este accesat a a a un DataReader necesit conexiune activ a a 3. este de tip read-only; dac se dorete modicarea datelor se poate folosi a s un DataSet + DataAdapter 4. este de tip forward-only - metoda de modicare a pozitiei curente este doar directia n nainte; orice re ntoarcere presupune reluarea nregistrrilor a (dac programatorul nu implementeaz el singur un mecanism de coad) a a a Avantajele utilizrii acestui tip de obiecte sunt: accesul conectat, performantele a bune, consumul mic de resurse i tipizarea puternic. s a

8.6.1

Proprieti at

1. IsClosed - proprietate read-only, returneaz true daca obiectul este a deschis, false altfel 2. HasRows - proprietate boolean read-only care spune dac readerul a a contine cel putin o nregistrare 3. Item - indexator care d acces la cmpurile unei a a nregistrri a 4. FieldCount - d numrul de cmpuri din a a a nregistrarea curent a

188

CURS 8. ADO.NET

8.6.2

Metode

1. Close() - nchide obiectul de citire i elibereaz resursele client. Este s a obligatoriu apelul acestei metode inaintea nchiderii conexiunii. 2. GetBoolean(), GetByte(), GetChar(), GetDateTime(), GetDecimal(), GetDouble(), GetFloat(), GetInt16(), GetInt32(), GetInt64(), GetValue(), GetString() returneaz valorile cmpurilor din a a nergistrarea curent. a Preiau ca parametru indicele coloanei a crei valoare se cere. Geta Value() returneaz un obiect de tip Object, pentru celelalte tipul retura nat este descris de numele metodelor. 3. GetBytes(), GetChars() - returneaz numrul de octeti / caractere cititi a a dintrun cmp ce stocheaz o structur de dimensiuni mari; primete a a a s ca parametri indicele de coloan (int), pozitia din acea coloan de unde a a se va ncepe citirea, vectorul care se face citirea, pozitia buer de n n la care se depun datele citite, numrul de octeti/caractere ce urmeaz a a a cititi. 4. GetDataTypeName() - returneaz tipul coloanei specicat prin indice a 5. GetName() - returneaz numele coloanei a 6. IsDBNull() - returneaz true dac cmpul specicat prin index este a a n a o valoare de NULL (din baza de date) 7. NextResult() - determin trecerea la urmtorul rezultat, dac aceasta a a a exist; acest caz returneaz true, altfel false (este posibil ca a n a ntr un DataReader s vin mai multe rezultate, provenind din interogri a a a diferite) 8. Read() - determin trecerea la urmtoarea a a nregistrare, dac aceasta exa ista; acest caz ea returneaz true. Metoda trebuie chemat cel putin a n a a o dat, deoarece initial pozitia curent este a a naintea primei nregistrri. a

8.6.3

Crearea i utilizarea unui DataReader s

Nu este posibil s se creeze un obiect de tip DataReader cu ajutorul a unui constructor, ci prin intermediul unui obiect de tip Command, folosind apelul ExecuteReader() (a se vedea sectiunea 8.3.2). Pentru comanda re spectiv se specic instructiunea care determin returnarea setului de date a a a precum i obiectul de conexiune. Aceast conexiune trebuie s e deschis s a a a naintea apelului ExecuteReader(). Trecerea la urmtoarea a nregistrare se

8.6. OBIECTE DATAREADER

189

face folosind metoda Read(). Cnd se dorete a s ncetarea lucrului se nchide readerul i conexiunea. Omiterea s nchiderii obiectului de tip reader va duce la imposibilitatea reutilizrii conexiunii initiale. Dup ce se a a nchide acest DataReader este necesar i chiderea explicit a conexiunii (acest lucru nu a s n a mai e mandatoriu doar dac la apelul metodei ExecuteReader sa specicat a CommandBehavior.CloseConnection). Dac se a ncearc refolosirea conexia unii fr ca readerul s fost aa a nchis se va arunca o exceptie InvalidOpera tionException. Exemplu: SqlConnection conn = new SqlConnection ( ConfigurationSettings.AppSettings["constring"]); SqlCommand selectCommand = new SqlCommand("select * from ORDERS", conn); conn.Open (); OleDbDataReader reader = selectCommand.ExecuteReader ( ); while ( reader.Read () ) { object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } reader.Close (); conn.Close (); Este perfect posibil ca un obiect de tip DataReader s aduc datele prin a a apelul unei proceduri stocate (de fapt invocarea acestei proceduri este fcut a a de ctre obiectul de tip Command ). a Urmtoarele observatii trebuie luate considerare atunci cnd se lua n a creaz cu un obiect DataReader : a Metoda Read() trebuie s e a ntotdeauna apelat a naintea oricrui acces a la date; pozitia curent la deschidere este a naintea primei nregistrri. a Intotdeauna apelati metoda Close() pe un DataReader i pe conexiunea s asociat ct mai repede posibil; caz contrar conexiunea nu poate a a n reutilizat a Procesarea datelor citite trebuie s se fac dup a a a nchiderea conexiunii; felul acesta conexiunea se las liber pentru a putea reutilizat. n a a a

190

CURS 8. ADO.NET

8.6.4

Utilizarea de seturi de date multiple

Este posibil ca ntrun DataReader s se aduc mai multe seturi de date. a a Acest lucru ar micora numrul de apeluri pentru deschiderea unei conexiuni s a la stratul de date. Obiectul care permite acest lucru este chiar cel de tip Command : string select = "select * from Categories; select * from customers"; SqlCommand command = new SqlCommand ( select, conn ); conn.Open (); SqlDataReader reader = command.ExecuteReader (); Trecerea de la un set de date la altul se face cu metoda NextResult() a obiectului de tip Reader : do { while ( reader.Read () ) { Console.WriteLine ( "{0}\t\t{1}", reader[0], reader[1] ); } }while ( reader.NextResult () );

8.6.5

Accesarea datelor ntro manier sigur din punct a a de vedere a tipului

S considerm urmtoarea secvent de cod: a a a a while ( reader.Read () ) { object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } Dup cum se observ, este posibil ca valorile cmpurilor dintro a a a nregistrare s e accesate prin intermediul numelui coloanei (sau a indicelui ei, pornind a de la 0). Dezavantajul acestei metode este c tipul datelor returnate este a pierdut (ind returnate obiecte de tip Object), trebuind fcut un downcasta a ing pentru a utiliza din plin facilitile tipului respectiv. Pentru ca acest at lucru s nu se ample se pot folosi metodele GetXY care returneaz un tip a nt a specic de date:

8.6. OBIECTE DATAREADER

191

while ( reader.Read () ) { int id = reader.GetInt32 ( 0 ); DateTime date = reader.GetDateTime ( 3 ); decimal freight = reader.GetDecimal ( 7 ); Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } Avantajul secventei anterioare este c dac se a a ncearc aducerea valorii unui a cmp pentru care tipul nu este dat corect se arunc o exceptie InvalidCasa a tException; altfel spus, accesul la date se face sigur din punct de verere al tipului datelor. Pentru a evita folosirea unor constante magice ca indici de coloan a (precum mai sus: 0, 3, 7), se poate folosi urmtoarea strategie: indicii se a obtin folosind apel de metod GetOrdinal la care se specic numele coloanei a a dorite: private int OrderID; private int OrderDate; private int Freight; ... OrderID = reader.GetOrdinal("OrderID"); OrderDate = reader.GetOrdinal("OrderDate"); Freight = reader.GetOrdinal("Freight"); ... reader.GetDecimal ( Freight ); ...

192

CURS 8. ADO.NET

Curs 9 ADO.NET (2)


9.1 Obiecte DataAdapter

La fel ca i Connection, Command, DataReader, obiectele de tip DataAdapter s fac parte din furnizorul de date .NET specic ecrui tip de surs de date. a a Scopul ei este s permit umplerea unui obiect DataSet cu date i reectarea a a s schimbrilor efectuate asupra acestuia a napoi baza de date (DataSet pern mite lucrul deconectat de la baza de date). Orice clas de tipul DataAdapter (de ex SqlDataAdapter i OleDbDataAdapter) a s este derivat din clasa DbDataAdapter (clas abstract). Pentru orice obiect a a a de acest tip trebuie specicat minim comanda de tip SELECT care care a s populeze un obiect de tip DataSet; acest lucru este stabilit prin intera mediul proprietii SelectCommand de tip Command (SqlCommand, OleDat bCommand, . . . ). cazul care se dorete i modicarea informatiilor din In n s s sursa de date (inserare, modicare, tergere) trebuie specicate obiecte de tip s comand via proprietile: InsertCommand, UpdateCommand, DeleteComa at mand. Exemplu: mai jos se preiau nregistrrile din 2 tabele: Authors i TitleAua s thor i se trec s ntrun obiect de tip DataSet pentru a procesate ulterior. using System; using System.Data; using System.Data.SqlClient; class DemoDataSource { static void Main() { SqlConnection conn = new SqlConnection( 193

194

CURS 9. ADO.NET (2) ConfigurationSettings.AppSettings["constring"]); SqlDataAdapter daAuthors = new SqlDataAdapter("SELECT au_id, au_fname, au_lname FROM authors",conn); daAuthors.Fill(ds,"Author"); SqlDataAdapter daTitleAuthor = new SqlDataAdapter("SELECT au_id, title_id FROM titleauthor", conn); daTitleAuthor.Fill(ds,"TitleAuthor");

} } Prezentm mai jos cele mai importante componente ale unei clase de tip a DataAdapter.

9.1.1

Metode

1. Constructori de la cei impliciti pn la cei care se specic o comand a a n a a de tip SELECT i conexiunea la sursa de date. Pentru un obiect de s tip SqlDataAdapter se poate crea o instant urmtoarele moduri: a n a SqlDataAdapter da = new SqlDataAdapter(); sau: SqlCommand cmd = new SqlCommand("SELECT * FROM Employees"); SqlDataAdapter da = new SqlDataAdapter(cmd); sau: String strCmd = "SELECT * FROM Employees"; String strConn = "..."; SqlDataAdapter da = new SqlDataAdapter(strCmd, strConn);

2. Fill() metod polimorc, permitnd umplerea unei tabele dintrun a a a obiect de tip DataSet cu date. Permite specicarea obiectului DataSet care se depun datele, eventual a numelui tablei din acest DataSet, n numrul de a nregistrare cu care s se a nceap popularea (prima avnd a a indicele 0) i numrul de s a nregistrri care urmeaz a aduse. Rea a turneaz de ecare dat numrul de a a a nregistrri care au fost aduse din a clipa care se apeleaz Fill() se procedeaz astfel: baz. In a n a a

9.2. CLASA DATASET (a) Se deschide conexiunea (dac ea nu a fost explicit deschis) a a

195

(b) Se aduc datele i se populeaz un obiect de tip DataTable din s a DataSet (c) Se nchide conexiunea (dac ea nu a fost explicit deschis!) a a De remarcat c un DataAdapter si poate deschide i a s nchide singur conexiunea, dar dac aceasta a fost deschis a a naintea metodei Fill() atunci tot programatorul trebuie s o a nchid. a 3. Update() metod polimorc, permitnd reectarea modicrilor efeca a a a tuate ntreun DataSet. Pentru a functiona are nevoie de obiecte de tip comand adecvate: proprietile InsertCommand, DeleteCommand a at i UpdateCommand trebuie s indice ctre comenzi valide. Returneaz s a a a de ecare dat numrul de a a nregistrri afectate. a

9.1.2

Proprieti at

1. DeleteCommand, InsertCommand, SelectCommand, UpdateCommand de tip Command, contin comenzile ce se execut pentru selectarea a sau modicarea datelor sursa de date. Mcar proprietatea Selectn a Command trebuie s indice ctre un obiect valid, pentru a se putea a a face popularea setului de date. 2. MissingSchemaAction de tip enumerare MissingSchemaAction, determin ce se face atunci cnd datele care sunt aduse nu se potrivesc a a peste schema tablei care sunt depuse. Poate avea urmtoarele valori: n a Add - implicit, DataAdapter adaug coloana la schema tablei a

AddWithKey - ca mai sus, dar adaug i informatii relativ la cine as este cheia primar a Ignore - se ignor lipsa coloanei respective, ceea ce duce la pierdere a de date pe DataSet Error - se genereaz o exceptie de tipul InvalidOperationExcepa tion.

9.2

Clasa DataSet

Clasa DataSet nu mai face parte din biblioteca unui furnizor de date ADO.NET, ind standard. Ea poate s contin reprezentri tabelare ale a a a datelor din baz precum i diferite restrictii i relatii existente. Marele ei a s s

196

CURS 9. ADO.NET (2)

avantaj este faptul c permite lucrul deconectat de la sursa de date, eliminnd a a felul acesta, necesitatea unei conexiuni permanente precum la DataReader. In un server de aplicatii sau un client oarecare poate apela la serverul de baze de date doar cnd preia datele sau cnd se dorete salvarea lor. Functioneaz a a s a strns legtur cu clasa DataAdapter care actioneaz ca o punte n a a a a a ntre un DataSet i sursa de date. Remarcabil este faptul c un DataSet poate face s a abstractie de sursa de date, procesarea datelor desfurnduse independent as a de ea. Figura 9.1 contine o vedere partial asupra clasei DataSet. a

Figura 9.1: Structura unui DataSet

9.2.1

Continut

Prezentm succint continutul unui DataSet: a 1. Colectia Tables contine 0 sau mai multe obiecte DataTable. Fiecare DataTable este compus dintr-o colectie de linii i coloane. a s 2. Colectia Relations contine 0 sau mai multe obiecte de tip DataRelation, folosite pentru marcarea legturilor printecopil. a a 3. Colectia ExtendedProperties contine proprieti denite de utilizator. at

9.2.2

Clasa DataTable

Datele sunt continute ntr-un DataSet sub forma unor tabele de tip DataTable. Aceste obiecte pot folosite att independent, ct i interiorul unui a a s n

9.2. CLASA DATASET

197

DataSet ca elemente ale colectiei Tables. Un DataTable contine o colectie Columns de coloane, Rows de linii i Constraints de constrngeri. s a DataColumn Un obiect DataColumn denete numele i tipul unei coloane care face s s parte sau se adaug unui obiect DataTable. Un obiect de acest tip se obtine a prin apel de constructor sau pe baza metodei DataTable.Columns.Add. Exemplu: DataColumn myColumn = new DataColumn("title", Type.GetType("System.String")); Denirea unei coloane ca ind de tip autonumber ( vederea stabilirii ei n ca i cheie pe o tabel) se face astfel: s a DataColumn idColumn = new DataColumn("ID", Type.GetType("System.Int32")); idColumn.AutoIncrement = true; idColumn.AutoIncrementSeed = 1; idColumn.ReadOnly = true; DataRow Un obiect de tip DataRow reprezint o linie dintr-un obiect DataTable. a Orice obiect DataTable contine o proprietate Rows ce da acces la colectia de obiecte DataRow continut. Pentru crearea unei linii se poate apela metoda a NewRow pentru o tabel a crei schem se cunoate. Mai jos este dat a a a s a secventa de cod care creeaz o linie nou pentru o tabel i o adaug acestesia: a a as a DataRow tempRow; tempRow = myTable.NewRow(); tempRow["ID"] = 1; tempRow["Name"] = "Book"; tempRow["Category"] = 1; myTable.Rows.Add(tempRow); Constrngeri a Constrngerile sunt folosite pentru a descrie anumite restrictii aplicate a asupra valorilor din coloane. ADO.NET exist dou tipuri de constrngeri: In a a a de unicitate i de cheie strin. Toate obiectele de constrngere se a s a a a a n colectia Constraints a unei tabele.

198

CURS 9. ADO.NET (2)

UniqueConstraint - precizeaz c a a ntr-o anumit coloan valorile sunt a a unice. Incercarea de a seta valori duplicate pe o coloana pentru care s-a precizat restrictia duce la aruncarea unei exceptii. Este necesar a o asemenea coloan clipa care se folosete metoda Find pentru a n n s proprietatea Rows: acest caz trebuie s se specice o coloan pe care n a a avem unicitate. ForeignKeyConstraint - specic actiunea care se va efectua atunci a cnd se terge sau modic valoarea dintro anumit coloan. De exa s a a a emplu se poate decide c dac se sterge o a a nregistrare dintr-o tabel a atunci s se tearg i a s a s nregistrrile copil. Valorile care se pot seta a pentru o asemenea constrngere se specic proprietile ForeignKa a n at eyConstraint.DeleteRule i ForeignKeyConstraint.UpdateRule: s Rule.Cascade - actiunea implicit, terge sau modic a s a nregistrrile a afectate Rule.SetNull - se seteaz valoare de null pentru a nregistrrile afeca tate Rule.SetDefault - se seteaz valoarea implicit denit baz a a a n a pentru cmpul respectiv a Rule.None - nu se execut nimic a Exemplu: ForeignKeyConstraint custOrderFK=new ForeignKeyConstraint ("CustOrderFK",custDS.Tables["CustTable"].Columns["CustomerID"], custDS.Tables["OrdersTable"].Columns["CustomerID"]); custOrderFK.DeleteRule = Rule.None; //Nu se poate sterge un client care are comenzi facute custDS.Tables["OrdersTable"].Constraints.Add(custOrderFK); Mai sus s-a declarat o relatie de tip cheie strin a a ntre dou tabele a (CustTable i OrdersTable, care fac parte dintr-un DataSet). Restrictia s se adaug la tabla copil. a Stabilirea cheii primare O cheie primar se denete ca un vector de coloane care se atribuie a s proprietii PrimaryKey a unei tabele (obiect DataTable). at DataColumn[] pk = new DataColumn[1]; pk[0] = myTable.Columns["ID"]; myTable.PrimaryKey = pk;

9.2. CLASA DATASET

199

Proprietatea Rows a clasei DataTable permite cutarea unei anumite linii a din colectia continut dac se specic un obiect sau un array de obiecte a a a folosite pe post de cheie: object key = 17;//cheia dupa care se face cautarea DataRow line = myTable.Rows.Find(key); if ( line != null ) //proceseaza linia

9.2.3

Relatii ntre tabele

Proprietatea Relations a unui obiect de tip DataSet contine o colectie de obiecte de tip DataRelation folosite pentru a gura relatiile de tip printe a copil ntre dou tabele. Aceste relatii se precizeaz esent ca niste perechi a a n a de array-uri de coloane sau chiar coloane simple din cele dou tabele care se a relationeaz, de exemplu sub forma: a myDataSet.Relations.Add(DataColumn, DataColumn); //sau myDataSet.Relations.Add(DataColumn[], DataColumn[]); concret: myDataSet.Relations.Add( myDataSet.Tables["Customers"].Columns["CustomerID"], myDataSet.Tables["Orders"].Columns["CustomerID"]);

9.2.4

Popularea unui DataSet

Dei un obiect DataSet se poate popula prin crearea dinamic a obiectelor s a DataTable, cazul cel mai des alnit este acela care se populeaz prin nt n a intermediul unui obiect DataAdapter. O dat obtinut un asemenea obiect a (care contine cel putin o comand de tiop SELECT ) se poate apela metoda a Fill() care primete ca parametru DataSet-ul care se umple i optional numele s s tabelei care va contine datele: //defineste comanda de selectare din baza de date String mySqlStmt ="SELECT * FROM Customers"; String myConString = ConfigurationSettings.AppSettings["constring"]; //Construieste obiectele de conexiune + comanda SELECT SqlConnection myConnection = new SqlConnection(myConString); SqlCommand myCommand = new SqlCommand(mySqlStmt, myConnection); //Construieste obiectul DataAdapter

200

CURS 9. ADO.NET (2)

SqlDataAdapter myDataAdapter = new SqlDataAdapter(); //seteaza proprietatea SelectCommand pentru DataAdapter myDataAdapter.SelectCommand = myCommand; //construieste obiectul DataSet si il umple cu date DataSet myDataSet = new DataSet(); myDataAdapter.Fill(myDataSet, "Customers"); Datele aduse mai sus sunt depuse ntr-un obiect de tip DataTable din interiorul lui DataSet, numit Customers. Accesul la acest tabel se face prin constructia myDataSet.Tables["Customers"] sau folosind indici ntregi (prima tabel are indicele 0). Acelai DataSet se a s poate popula continuare cu alte tabele pe baza aceluiai sau a altor obiecte n s DataAdapter.

9.2.5

Propagarea modicrilor ctre baza de date a a

Pentru a propaga modicrile efectuate asupra continutului tabelelor a dintr-un DataSet ctre baza de date este nevoie s se deneasc adecvat a a a obiecte comand de tip INSERT, UPDATE, DELETE. Pentru cazuri sima ple se poate folosi clasa SqlCommandBuilder care va construi singur aceste a comenzi. Clasa CommandBuilder Un obiect de tip CommandBuilder (ce provine din furnizorul de date) va analiza comanda SELECT care a adus datele DataSet i va construi cele n s 3 comenzi de update functie de aceasta. E nevoie s se satisfac 2 conditii n a a atunci cnd se uzeaz de un astfel de obiect: a a 1. Trebuie specicat o comand de tip SELECT care s aduc datele a a a a dintr-o singur tabel a a 2. Trebuie specicat cel putin cheia primar sau o coloan cu constrngere a a a a de unicitate comanda SELECT. n Pentru cea de a doua conditie se poate proceda felul urmtor: comanda n a n SELECT se specic i aducerea cheii, iar pentru obiectul DataAdapter care as face aducerea din baz se seteaz proprietatea MissingSchemaAction pe vala a oarea MissingSchemaAction.AddWithKey (implicit este doar Add ). Fiecare linie modicat din colectia Rows a unei tabele va avea moda icat valoarea proprietii RowState astfel: DataRowState.Added pentru a at

9.2. CLASA DATASET

201

o linie nou adugat, DataRowState.Deleted dac e tears i DataRowa a a a s a s State.Modied dac a fost modicat. Apelul de update pe un dataReader a a va apela comanda necesar pentru ecare linie care a fost modicat, a a n functie de starea ei. Artm mai jos modul de utilizare a clasei SqlCommandBuilder pentru aa adugarea, modicarea, tergerea de a s nregistrri din baza de date. a SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); da = new SqlDataAdapter("SELECT id, name, address FROM customers",conn); da.Fill(ds); SqlCommandBuilder cb = new SqlCommandBuilder(da); //determina liniile care au fost schimbate DataSet dsChanges = ds.GetChanges(); if (dsChanges != null) { // modifica baza de date da.Update(dsChanges); //accepta schimbarile din dataset ds.AcceptChanges(); } clipa care se creeaz obiectul SqlCommandBuilder automat se vor comIn n a pleta proprietile InsertCommand, DeleteCommand, UpdateCommand ale at dataAdapter-ului. Se determin apoi liniile care au fost modicate (prin a interogarea strii lor) i se obtine un nou DataSet care le va contine doar a s pe acestea. Comanda de Update se d doar pentru acest set de modicri, a a reducnd astfel tracul spre serverul de baze de date. a Update folosind comenzi SQL Atunci cnd interogrile de aducere a datelor sunt mai complexe (de a a exemplu datele sunt aduse din mai multe table, printr-un join) se pot specica propriile comenzi SQL prin intermediul proprietilor InsertCommand, at DeleteCommand UpdateCommand ale obiectului DataAdapter. Pentru ecare linie dintr-o tabel care este modicat/adugat/tears se va apela coa a a a s a manda SQL corespunztoare.Aceste comenzi pot fraze SQL parametrizate a sau pot denumi proceduri stocate aate baz. n a S presupunem c s-a denit un DataAdapter legat la o baz de date. a a a Instructiunea de selectie este

202

CURS 9. ADO.NET (2)

SELECT CompanyName, Address, Country, CustomerID FROM Customers unde CustomerID este cheia. Pentru inserarea unei noi nregistrri sar putea a scrie codul de mai jos: //da=obiect DataAdapter da.InsertCommand.CommandText = @"INSERT INTO Customers ( CompanyName, Address, Country) VALUES (@CompanyName, @Address, @Country); SELECT CompanyName, Address, Country, CustomerID FROM Customers WHERE (CustomerID = @@Identity)"; Update-ul efectiv se face prin prima instructiune de tip Update. Valorile pentru aceti parametri se vor da la runtime, de exemplu prin alegerea lor s dintr-un tabel. Valoarea pentru cheia CustomerID nu s-a specicat, deoarece ( acest caz) ea este de tip AutoNumber (SGBD-ul este cel care face managen mentul valorilor acestor cmpuri, nu programatorul). @@Identity reprezint a a id-ul noii nergistrri adugate tabel. Ultima instructiune va duce la a a n a reactualizarea obiectului DataSet, pentru ca acesta s contin modicarea a a efectuat (de exemplu ar putea aduce valorile implicite puse pe anumite a coloane). Pentru modicarea continutului unei linii se poate declara instructiunea de UPDATE astfel: da.UpdateCommand.CommandText = @"UPDATE Customers SET CompanyName = @CompanyName, Address = @Address, Country = @Country WHERE (CustomerID = @ID)"; Comanda de update este ca la cazul precedent.

9.3

Tranzactii ADO.NET n

O tranzactie este un set de operatii care se efectueaz e a n ntregime, e deloc. S presupunem c se dorete trecerea unei anumite sume de bani a a s dintr-un cont altul. Operatia presupune 2 pai: n s 1. scade suma din primul cont 2. adaug suma la al doilea cont a Ar inadmisibil ca primul pas s reueasc iar al doilea s eueze. Tranzactiile a s a a s satisfac nite proprieti strnse sub numele ACID: s at a

9.3. TRANZACTII ADO.NET IN

203

atomicitate - toate operatiile din tranzactie ar trebui s aib succes a a sau s eueze a s mpreun a consistent - tranzactia duce baza de date dintr-o stare stabil alta a a n izolare - nici o tranzactie nu ar trebui s afecteze o alta care ruleaz a a acelai timp n s durabilitate - schimbrile care apar tipul tranzactiei sunt permaa n nent stocate pe un mediu. Sunt trei comenzi care se folosesc context de tranzactii: n BEGIN - nainte de executarea unei comenzi SQL sub o tranzactie, aceasta trebuie s e initializat a a COMMIT - se spune c o tranzactie este terminat cnd toate schimbrile a a a a cerute sunt trecute baza de date n ROLLBACK - dac o parte a tranzactiei eueaz, atunci toate operatiile a s a efectuate de la nceputul tranzactiei vor neglijate Schema de lucru cu tranzactiile sub ADO.NET este: 1. deschide conexiunea la baza de date 2. ncepe tranzactia 3. execut comenzi pentru tranzactie a 4. dac tranzactia se poate efectua (nu sunt exceptii sau anumite conditii a sunt ndeplinite), efectueaz COMMIT, altfel efectueaz ROLLBACK a a 5. nchide conexiunea la baza de date Sub ADO.NET acest lucru s-ar face astfel: SqlConnection myConnection = new SqlConnection(myConnString); myConnection.Open(); SqlCommand myCommand1 = myConnection.CreateCommand(); SqlCommand myCommand2 = myConnection.CreateCommand(); SqlTransaction myTrans; myTrans = myConnection.BeginTransaction(); //Trebuie asignate ambele obiecte: conexiune si tranzactie

204

CURS 9. ADO.NET (2)

//unui obiect de tip comanda care va participa la tranzactie myCommand1.Transaction = myTrans; myCommand2.Transaction = myTrans; try { myCommand1.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, Description)"; myCommand1.ExecuteNonQuery(); myCommand2.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, Description)"; myCommand2.ExecuteNonQuery(); myTrans.Commit(); Console.WriteLine("Ambele inregistrari au fost scrise."); } catch(Exception e) { myTrans.Rollback(); } finally { myConnection.Close(); } Comanda de ROLLBACK se poate executa i alte situatii, de exemplul s n comanda efectuat deptete stocul disponibil. a as s

Curs 10 Fire de executie


Firele de executie1 sunt responsabile cu multitaskingul interiorul unei n aplicatii. Clasele i interfetele responsabile pentru crearea aplicatiilor mul s tithreading se gsesc spatiul de nume System.Threading. Vom discuta a n n cele ce urmeaz despre managementul threadurilor i despre sincronizare. a s De cele mai multe ori crearea de threaduri este necesar pentru a da a utilizatorului impresia c programul execut mai multe actiuni simultan, a a n cadrul unei aplicatii. Pentru ca o interfat utilizator s poat folosit fr a a a a aa a se atepta s ncheierea unei anumite secvente de instructiuni, e nevoie de mai multe re de executie (unele pentru procesarea efectiv a informatiei, a altele care s rspund actiunilor utilizatorului). Pentru probleme de calcul a a a numeric, exprimarea procesului de calcul se poate face foarte natural sub form de re de exectie. a

10.1
10.1.1

Managementul threadurilor
Pornirea threadurilor

Cea mai simpl metod de a crea un r de exectie este de a crea o instant a a a a clasei Thread, al crei constructor preia un singur argument de tip delea gat. BCL este denit tipul delegat ThreadStart, care este folosit ca proIn totip pentru orice metod care se vrea a lansat a a ntrun r de executie. Declaratia de ThreadStart este: public delegate void ThreadStart(); Crearea unui r de executie se face pe baza unei metode care returneaz a void i nu preia nici un parametru: s
1

Engl: threads; vom folosi alternativ termenii thread i r de executie s

205

206

CURS 10. FIRE DE EXECUTIE

ThreadStart ts = new ThreadStart(MyFunc); Thread myThread = new Thread( ts ); Un exemplu simplu este: using System; using System.Threading; class SimpleThreadApp { public static void WorkerThreadMethod() { Console.WriteLine("[WorkerThreadMethod] Worker " + "thread started"); } public static void Main() { ThreadStart worker = new ThreadStart(WorkerThreadMethod); Console.WriteLine("[Main] Creating worker thread"); Thread t = new Thread(worker); t.Start(); Console.WriteLine( "[Main] Have requested the start of worker thread"); Console.ReadLine(); } } Pn la linia t.Start() exist un singur thread: cel dat de pornirea metodei a a a Main. Dup t.Start() sunt 2 threaduri: cel anterior i t. Mesajul din a s Main va tiprit a naintea celui din threadul t; functie de cum anume n se planic threadurile pentru executie, mesajul de dup t.Start() poate s a a a apar a nainte sau dup mesajul tiprit de metoda WorkerThreadMethod(). a a De remarcat c simpla creere a rului de executie nu determin i pornirea a as lui: acest lucru se ampl dupa apelul metodei Start denit clasa nt a a n Thread. Exemplul urmtor pornete dou re de executie. Functiile care contin a s a codul ce se va executa cte un thread sunt Increment() i Decrement(): n a s using System; using System.Threading; class Tester

10.1. MANAGEMENTUL THREADURILOR { static void Main( ) { Tester t = new Tester( ); t.DoTest( ); } public void DoTest( ) { // creeaza un thread pentru Thread t1 = new Thread( new // creeaza un thread pentru Thread t2 = new Thread( new // porneste threadurile t1.Start( ); t2.Start( ); }

207

Incrementer ThreadStart(Incrementer) ); Decrementer ThreadStart(Decrementer) );

public void Incrementer( ) { for (int i = 1;i<=1000;i++) { Console.WriteLine( "Incrementer: {0}", i); } } public void Decrementer( ) { for (int i = 1000;i>0;i--) { Console.WriteLine("Decrementer: {0}", i); } } } La ieire se vor mixa mesajele tiprite de primul thread cu cele tiprite s a a de cel deal doilea thread: ... Incrementer: Incrementer: Incrementer: Incrementer: 102 103 104 105

208 Incrementer: Decrementer: Decrementer: Decrementer: Decrementer: ... 106 1000 999 998 997

CURS 10. FIRE DE EXECUTIE

Perioada de timp alocat ecrui thread este determinatu de ctre plania a a a catorul de re de executie2 i depinde de factori precum viteza procesorului, s gradul lui de ocupare, etc. O alt modalitate de obtinere a unei referinte la un thread este prin apelul a metodei statice Thread.CurrentThread pentru rul de executie curent. In exemplul de mai jos se obtine obiectul Thread asociat rului de executie curent, i se seteaz numele (proprietate de tip read/write) i se aeaz pe a s s a ecran: Thread current = Thread.CurrentThread; current.Name = "My name"; Console.WriteLine("nume={0}", current.Name );

10.1.2

Metoda Join()

Exist situatii care, a n naintea unei instructiuni trebuie s se asigure fap a tul c un alt r de executie t s-a terminat; acest lucru se va face folosind apelul a t.Join() naintea instructiunii cauz; acest moment rul de executie care n a n a apelat t.Join() intr ateptare. a n s Dac de exemplu metoda Main se lanseaz o colectie de threaduri a n a (stocat myThreads), atunci pentru a se continua executia numai dup ce a n a toate rele din colectie sau terminat, se procedeaz astfel: a foreach( Thread myThread in myThreads ) { myThread.Join(); } Console.WriteLine(All my threads are done); Mesajul nal se va tipri doar cnd toate rele de executie sau terminat. a a
2

Engl: thread scheduler

10.1. MANAGEMENTUL THREADURILOR

209

10.1.3

Suspendarea relor de executie

Se poate ca anumite cazuri s se doreasc suspendarea unui r de n a a executie pentru o scurt perioad de timp. Clasa Thread ofer o metod a a a a static supra arcat Sleep(), care poate prelua un parametru de tip int a nc a reprezentnd milisecundele de adormire, iar a doua variant preia un ara a gument de tip TimeSpan, care reprezint cea mai mic unitate de timp a a care poate specicat, egal cu 100 nanosecunde. Pentru a cere rului a a de executie curent s se suspende pentru o secund, se execut cadrul a a a n acestuia: Thread.Sleep(1000); acest fel se semnaleaz planicatorului de re de executie c poate In a a lansa un alt thread. Dac exemplul de mai sus se adaug un apel Thread.Sleep(1) dup a n a a ecare WriteLine(), atunci ieirea se schimb dramatic: s a Iesire (extras) Incrementer: 0 Incrementer: 1 Decrementer: 1000 Incrementer: 2 Decrementer: 999 Incrementer: 3 Decrementer: 998 Incrementer: 4 Decrementer: 997 Incrementer: 5 Decrementer: 996 Incrementer: 6 Decrementer: 995

10.1.4

Omorrea threadurilor a

De obicei, un r de executie moare dup ce se termin de executat. Se a a poate totui cere unui r de executie s si s a nceteze executia folosind metoda Abort(). Acest lucru va duce la aruncarea unei exceptii interiorul rului n de executie cruia i se cere suspendarea: ThreadAbortedException, pe care a rul respectiv o poate prinde i procesa, permitndui eliberarea de resurse s a alocate. Ca atare, se recomand ca o metod care se va lansa ca r de a a

210

CURS 10. FIRE DE EXECUTIE

executie s se compun dintrun bloc try care contine instructiunile utile, a a dup care un bloc catch i/sau nally care vor efectua eliberarea de resurse. a s Exemplu: using System; using System.Threading; class Tester { static void Main( ) { Tester t = new Tester( ); t.DoTest( ); } public void DoTest( ) { // creeaza un vector de threaduri Thread[] myThreads = { new Thread( new ThreadStart(Decrementer) ), new Thread( new ThreadStart(Incrementer) ), new Thread( new ThreadStart(Incrementer) ) }; // porneste fiecare thread int ctr = 1; foreach (Thread myThread in myThreads) { myThread.IsBackground=true; myThread.Start( ); myThread.Name = "Thread" + ctr.ToString( ); ctr++; Console.WriteLine("Started thread {0}", myThread.Name); Thread.Sleep(50); } // dupa ce firele se pornesc, // comanda oprirea threadului 1 myThreads[1].Abort( ); // asteapta ca fiecare thread sa se termine foreach (Thread myThread in myThreads) { myThread.Join( ); }

10.1. MANAGEMENTUL THREADURILOR Console.WriteLine("All my threads are done.");

211

} // numara descrescator de la 1000 public void Decrementer( ) { try { for (int i = 1000;i>0;i--) { Console.WriteLine(Thread {0}. Decrementer: {1}, Thread.CurrentThread.Name, i); Thread.Sleep(1); } } catch (ThreadAbortedException) { Console.WriteLine( Thread {0} interrupted! Cleaning up..., Thread.CurrentThread.Name); } finally { Console.WriteLine(Thread {0} Exiting. , Thread.CurrentThread.Name); } } // numara cresacator pana la 1000 public void Incrementer( ) { try { for (int i =1;i<=1000;i++) { Console.WriteLine(Thread {0}. Incrementer: {1}, Thread.CurrentThread.Name, i); Thread.Sleep(1); } } catch (ThreadAbortedException) { Console.WriteLine( Thread {0} interrupted! Cleaning up...,

212 Thread.CurrentThread.Name);

CURS 10. FIRE DE EXECUTIE

} finally { Console.WriteLine( Thread {0} Exiting. , Thread.CurrentThread.Name); } } } Ieire: s Started thread Thread1 Thread Thread1. Decrementer: 1000 Thread Thread1. Decrementer: 999 Thread Thread1. Decrementer: 998 Started thread Thread2 Thread Thread1. Decrementer: 997 Thread Thread2. Incrementer: 0 Thread Thread1. Decrementer: 996 Thread Thread2. Incrementer: 1 Thread Thread1. Decrementer: 995 Thread Thread2. Incrementer: 2 Thread Thread1. Decrementer: 994 Thread Thread2. Incrementer: 3 Started thread Thread3 Thread Thread1. Decrementer: 993 Thread Thread2. Incrementer: 4 Thread Thread2. Incrementer: 5 Thread Thread1. Decrementer: 992 Thread Thread2. Incrementer: 6 Thread Thread1. Decrementer: 991 Thread Thread3. Incrementer: 0 Thread Thread2. Incrementer: 7 Thread Thread1. Decrementer: 990 Thread Thread3. Incrementer: 1 Thread Thread2 interrupted! Cleaning up... Thread Thread2 Exiting. Thread Thread1. Decrementer: 989 Thread Thread3. Incrementer: 2 Thread Thread1. Decrementer: 988 Thread Thread3. Incrementer: 3

10.1. MANAGEMENTUL THREADURILOR Thread Thread Thread Thread // ... Thread Thread Thread1. Thread3. Thread1. Thread3. Decrementer: Incrementer: Decrementer: Incrementer: 987 4 986 5

213

Thread1. Decrementer: 1 Thread3. Incrementer: 997

10.1.5

Sugerarea prioritilor relor de executie at

Un r de executie se lanseaz implicit cu prioritatea ThreadPriorityLevel.Normal. a Dar schedulerul poate inuentat activitatea sa prin setarea de diferite n nivele de prioritate pentru re; aceste nivele fac parte din enumerarea ThreadPriorityLevel : ThreadPriorityLevel.TimeCritical, ThreadPriorityLevel.Highest, ThreadPriorityLevel.AboveNormal, ThreadPriorityLevel.Normal, ThreadPriorityLevel.BelowNormal, ThreadPriorityLevel.Lowest, ThreadPriorityLevel.Idle. Prioritatea este descresctoare lista prezentat. Prioritatea este descresctoare a n a a lista prezentat. Pe baza prioritii procesului care contine rele de n a at executie i a prioritii relor, se calculeaz un nivel de propritate (de ex. s at a pe maini Intel valori s ntre 0 i 31) care determin prioritatea ansamblul s a n sistemului de operare a rului respectiv. Setarea unei anumite prioriti se face folosind proprietatea Priority: at myThread.Priority = ThreadPriorityLevel.Highest;

10.1.6

Fire fundal i re prim-plan n s n

Relativ la proprietatea boolean IsBackground, trebuie fcut precizarea a a a c un r de executie poate s se execute fundal (background) sau prim a a n n plan (foreground). Diferenta dintre cele dou posibiliti o constituie faptul a at c dac un proces are mcar un r de executie foreground, CLR va mentine a a a n aplicatia executie. O dat ce toate rele de executie de tip foreground n a se termin, CLR va executa Abort() pentru ecare r de executie de tip a background (dac mai exist aa ceva) i termin procesul. a a s s a Exemplu: using System; using System.Threading; class Test { static void Main()

214 {

CURS 10. FIRE DE EXECUTIE

BackgroundTest shortTest = new BackgroundTest(10); Thread foregroundThread = new Thread(new ThreadStart(shortTest.RunLoop)); foregroundThread.Name = "ForegroundThread"; BackgroundTest longTest = new BackgroundTest(50); Thread backgroundThread = new Thread(new ThreadStart(longTest.RunLoop)); backgroundThread.Name = "BackgroundThread"; backgroundThread.IsBackground = true; foregroundThread.Start(); backgroundThread.Start(); } } class BackgroundTest { int maxIterations; public BackgroundTest(int maxIterations) { this.maxIterations = maxIterations; } public void RunLoop() { String threadName = Thread.CurrentThread.Name; for(int i = 0; i < maxIterations; i++) { Console.WriteLine("{0} count: {1}", threadName, i.ToString()); Thread.Sleep(250); } Console.WriteLine("{0} finished counting.", threadName); } } Firul din foreground va mentine procesul executie pn cnd se termin n a a a a

10.2. SINCRONIZAREA

215

ciclul su while. Cnd acesta se termin, procesul este oprit, chiar dac a a a a ciclul while din rul de executie din background nu i-a terminat executia. s

10.2

Sincronizarea

Sincronizarea se ocup cu controlarea accesului la resurse partajate de mai a multe re de executie. De exemplu, se poate cere ca utilizarea unei resurse anume s se fac la un moment dat de ctre un singur r de executie. Vom a a a discuta aici trei mecanisme de sincronizare: clasa Interlock, instructiunea C# lock i clasa Monitor. Exemplele se vor baza pe acces la o resurs partajat, s a a ca mai jos: public void Incrementer( ) { try { while (counter < 1000) { int temp = counter; temp++; // increment // simuleza o sarcina oarecare in acest thread Thread.Sleep(1); // atribuie valoarea incrementata // variabilei counter // si afiseaza rezultatul counter = temp; Console.WriteLine( Thread {0}. Incrementer: {1}, Thread.CurrentThread.Name, counter); } } catch (ThreadAbortedException) { Console.WriteLine( Thread {0} interrupted! Cleaning up..., Thread.CurrentThread.Name); } finally { Console.WriteLine( Thread {0} Exiting. , Thread.CurrentThread.Name); } }

216

CURS 10. FIRE DE EXECUTIE

Cmpul counter se initializeaz cu 0. S presupunem c pornim dou a a a a a re de executie pe baza metodei Incrementer() de mai sus. Este posibil s se ample urmtoarele: primul thread va citi valoarea lui counter (0) a nt a i o va atribui unei variabile temporare, pe care o va incrementa apoi. Al s doilea r se activeaz i el, va citi valoarea (nemodicat) lui counter i va as a s atribui aceast valoare unei variabile temporare. Primul r de executie si a termin munca, apoi asigneaz valoarea variabilei temporare (1) lui counter a a i o aeaz. Al doilea thread face exact acelai lucru. Se tiprete astfel 1, s s a s a s 1. La urmtoarele iteratii se va aa 2, 2, 3, 3, etc, locul lui 1, 2, 3, 4. a s n Exemplu: using System; using System.Threading; class Tester { private int counter = 0; static void Main( ) { Tester t = new Tester( ); t.DoTest( ); } public void DoTest( ) { Thread t1 = new Thread( new ThreadStart(this.Incrementer) ); t1.IsBackground=true; t1.Name = ThreadOne; t1.Start( ); Console.WriteLine(Started thread {0}, t1.Name); Thread t2 = new Thread( new ThreadStart(this.Incrementer) ); t2.IsBackground=true; t2.Name = ThreadTwo; t2.Start( ); Console.WriteLine(Started thread {0}, t2.Name); t1.Join( ); t2.Join( ); } // numara crescator pana la 1000 public void Incrementer( ) { //la fel ca la inceputul sectiunii }

10.2. SINCRONIZAREA } Ieire: s Started thread ThreadOne Started thread ThreadTwo Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer:

217

1 2 3 3 4 4 5 5 6 6

Trebuie deci s se realizeze o excludere reciproc a threadurilor pentru aca a cesul la counter.

10.2.1

Clasa Interlocked

Incrementarea i decrementarea unei valori este o situatie att de des s a alnit, at C# pune la dispozitie o clas special Interlocked pentru o nt a nc a a rezolvare rapid. Clasa include dou metode statice, Increment() i Decrea a s ment(), care incrementeaz sau decrementeaz o valoare, a sub un control a a ns sincronizat. Putem modica metoda Incrementer() de mai sus astfel: public void Incrementer( ) { try { while (counter < 1000) { Interlocked.Increment(ref counter); // simuleaza o sarcina in aceasta metoda Thread.Sleep(1); // asigura valoarea decrementata // si afiseaza rezultatul Console.WriteLine( "Thread {0}. Incrementer: {1}",

218 Thread.CurrentThread.Name, counter); }

CURS 10. FIRE DE EXECUTIE

} //blocurile catch si finally raman neschimbate } Ieirea este cea dorit: s a Started thread ThreadOne Started thread ThreadTwo Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer:

1 2 3 4 5 6 7 8

10.2.2

Instructiunea lock

Exist situatii cnd vrem s blocm alte variabile dect cele de tip int. a a a a a Un lock marcheaz o sectiune critic a codului, producnd astfel sincronizare a a a pentru un obiect. La utilizare, se specic un obiect pentru care se stabilete a s un lock, dup care o instrutiune sau un grup de instructiuni. Lockul este a aturat la sfritul instructiunii/blocului de instrutiuni. Sintaxa este: nl as lock(expresie) { instructiuni } Exemplu: metoda Incrementer() se va modica dup cum urmeaz: a a public void Incrementer( ) { try { while (counter < 1000) { lock (this) {

10.2. SINCRONIZAREA int temp = counter; temp++; Thread.Sleep(1); counter = temp; } // atribuie valoarea decrementata // si afiseaza rezultatul Console.WriteLine( "Thread {0}. Incrementer: {1}", Thread.CurrentThread.Name, counter); } } //blocurile catch si finally raman neschimbate } Rezultatele sunt aate exact ca la sectiunea 10.2.1. s

219

10.2.3

Clasa Monitor

Clasa Monitor contine metode pentru a controla sincronizarea relor de executie, permitnd declararea unei zone critice care la un moment dat a n doar un thread trebuie s opereze. a Atunci cnd se dorete s se a s a nceap sincronizarea, se va apela metoda a Enter, dnd obiectul pentru care se va face blocarea: a Monitor.Enter( obiect ); Dac monitorul este nedisponibil, atunci a nseamn c un alt thread este a a ntro regiune critic a obiectului respectiv. Se mai poate folosi de asemenea a metoda Wait(), care elibereaz monitorul, dar blocheaz threadul, informnd a a a CLR c atunci cnd monitorul devine din nou liber, threadul curent ar vrea a a s si continue executia (este adugat a a ntro coad de ateptare format din a s a re de executie blocate pe obiect). Terminarea zonei critice se face folosind metoda Exit() a clasei Monitor. Metoda Pulse() semnaleaz c a avut loc a a o schimbare de stare, urma creia este posibil ca un alt r de executie n a care ateapt va putea s e continuat (ordinea de selectare a relor ce vor s a a executate ind ordinea introducerii coada de ateptare). n s Inrudit este a metoda PulseAll care anunt toate obiectele blocate pe un anumit obiect de a schimbarea de stare. S presupunem c avem o clas MessageBoard unde re individuale pot a a a citi i scrie mesaje. Vom sincroniza accesul la aceast clas astfel at doar s a a nc un thread s poat actiona la un moment dat. Clasa MessageBoard va avea a a o metod Reader() i una Writer(). a s

220

CURS 10. FIRE DE EXECUTIE

Metoda Reader() determin dac stringul message contine vreun mesaj a a valabil, iar metoda Writer() va scrie acest string. Dac nu sunt mesaje n a timpul citirii, threadul Reader() va intra stare de ateptare folosind n n s Wait() pn cnd metoda Writer() scrie un mesaj i transmite un mesaj via a a a s Pulse() pentru a trezi alte threaduri. using System; using System.Threading; class MessageBoard { private String messages = no messages ; public void Reader() { try { Monitor.Enter(this); //daca nu e nici un mesaj atunci asteapta if (messages == no messages) { Console.WriteLine({0} {1}, Thread.CurrentThread.Name, messages); Console.WriteLine({0} waiting..., Thread.CurrentThread.Name); Monitor.Wait(this); } //inseamna ca mesajul s-a schimbat Console.WriteLine({0} {1}, Thread.CurrentThread.Name, messages); } finally { Monitor.Exit(this); } } public void Writer() { try { Monitor.Enter(this); messages = Greetings Caroline and Marianne!; Console.WriteLine({0} Done writing message...,

10.2. SINCRONIZAREA

221

Thread.CurrentThread.Name); //semnaleaza threadului de asteptare ca s-a schimbat mesajul Monitor.Pulse(this); } finally { Monitor.Exit(this); } } public static void Main() { MessageBoard myMessageBoard = new MessageBoard(); Thread reader = new Thread(new ThreadStart(myMessageBoard.Reader)); reader.Name = ReaderThread:; Thread writer = new Thread( new ThreadStart(myMessageBoard.Writer)); writer.Name = WriterThread:; reader.Start(); writer.Start(); } } Ieirea este: s ReadrThread: no messages ReaderThread: waiting... WriterThread: Done writing message... ReaderThread: Greetings Caroline and Marianne! Dup cum se vede mai sus, threadul reader este pornit primul, el blocheaz a a clasa obeictul de tip MessageBoard, ceea ce nseamn c are acces exclusiv la a a variabila message. Dar deoarece message nu contine nici un mesaj, thread-ul reader elibereaz obiectul pe care la blocat mai a nainte prin apelul Wait(). Threadul writer va putea acum s blocheze obiectul pentru a scrie mesajul. a Apoi el cheam metoda Pulse() pentru a semnala rului reader c a aprut a a a o schimbare de stare a obiectului indicat wde this.

222

CURS 10. FIRE DE EXECUTIE

Curs 11 ASP.NET
ASP.NET (actualmente versiunea 1.1) permite crearea de aplicatii Web folosind platforma .NET Framework; este una din cele 3 tipuri de aplicatii creabile sub aceast platform, alturi de Windows Forms i Web Services. a a a s Este destina a nlocui vechiul ASP (Active Server Pages), ind obiectual i integrat cu toate elementele constitutive ale lui .NET Framework: CLR, s BCL, CTS, FCL, etc. Se poate rula pe servere de aplicatii IIS (versiuni 5, 6), pe serverul open-source Cassini i de asemenea poate integrat cu s Apache (folosind Cassini). Rezultatele cele mai bune se obtin totui pe IIS s 6.0 (integrat Windows 2003 Server). n Enuntm mai jos o list neexhaustiv a trsturilor ASP.NET: a a a aa unul din marile avantaje ale lui ASP.NET fat de ASP este faptul c a a codul este pstrat compilat (sub form de cod .NET PE, executat de a a ctre CLR) loc ca pagina s e interpretat la ecare rulare; a n a a modul de realizare este orientat pe obiecte, o aplicatie ind compus din a dou prti: pagina aat browser i pagina de code-behind, care a a s a n s este o clas; acest code-behind are posibilitatea de accesare a bazelor a de date sau chiar a codului unmanaged (COM-uri i DLL-uri anterior s existente); plus, se realizeaz separarea prtii de interfat utilizator n a a a de ceea ce este executat pe server; pune la dispozitie un mod de tratare a erorilor unicat (tratarea de exceptii) i folosete controale Web predenite care se pot executa pe s s server sau pe client; are un mecanism ncorporat care permite pstrarea strii paginii (chiar a a dac prototlul de comunicare HTTP este fr stare, stateless); a aa 223

224

CURS 11. ASP.NET

ASP.NET permite includerea de mai multe funtionaliti pe controalele at ASP.NET iar managementul strii lor este mult a mbuntit a at permite programarea bazat pe evenimente, cu diferente minime fat a a de programarea aplicatiilor windows; este senzitiv la browserul pentru care se face trimiterea codului HTML, alegnd automat codul optimizat pentru o colectie de browsere a erorile grave aprute aplicatie ASP.NET nu vor duce la oprirea a n serverului Web (dar pot duce la imposibilitatea de a mai executa pagini ASP.NET pn la repornirea serverului) a a permite scrierea codului care se va executa pe server orice limbaj n .NET: C#, VB.NET, etc

11.1

Anatomia unei pagini ASP.NET

O pagin ASP.NET se poate crea a nntr-o multitudine de feluri, de exemplu prin Notepad, Web Matrix sau VS.NET (varianta recomandat, de a altfel). Se poate crea o pagin simpl pe baza urmtorului continut: a a a <html> <head> </head> <body> Hello World </body> </html> salvat sub numele de HelloWorld.aspx directorul rdcin al site-ului web n a a a implicit (de obicei c:\Inetpub\wwwroot, creat automat la instalarea IISului). Pagina se ncarc browser folosind adresa http://localhost/HelloWorld.aspx. a n

11.1.1

Adugarea unui control Web a

Intr-o pagin Web se poate aduga un control pe pagin care s e generat a a a a de ctre serverul IIS mod adecvat i care poate accesat programatic prin a n s cod. Astfel, pagina de mai sus se adaug astfel at s devin: n a nc a a <html> <head> </head>

11.1. ANATOMIA UNEI PAGINI ASP.NET <body> <form runat=server> <asp:Label text="Hellow World" runat=server /> </form> </body> </html>

225

Dac se mai a ncarc o dat pagina, se va genera urmtorul cod HTML pentru a a a browser (vizibil prin optiunea View Source): <html> <head> </head> <body> <form name="_ct10" method="post" action="HelloWorld2.aspx" id="_ct10"> <input type="hidden" name="_VIEWSTATE" value="dDw5MjMzODA0MjI7Oz4="/> <span>Hello World</span> </form> </body> </html> sursa s-a In ncorporat un control Web <asp:Label> care prezint propria etatea Text cu textul care va afat pagin. Atributul runat are ca valoare s n a cuvntul server (singura optiune posibil) care spune c respectivul control a a a se va procesa pe server i nu pe client. Dup cum se vede sursa HTML s a n generat, acest control va avea ca rezultat un tag de tip span. De remara cat c controlul Web a fos scris interiorul unui formular; acest lucru este a n mandatoriu pentru toate controalele Web sau HTML.

11.1.2

Adugarea scriptului inline a

Vom aduga la pagina anterioar un buton a crui apsare va duce la a a a a schimbarea textului etichetei anterioare; aceast schimbare va fcut pe a a a server prin apelul unei metode scrise C#: n <html> <head> </head> <body> <form method="post" runat="server"> <asp:Label id=lblHelloWorld text="Hello World" runat="Server"/> <br>

226

CURS 11. ASP.NET

<asp:Button onclick="ClickedIt" text="Submit" runat=Server /> </form> </body> <script Language=C# runat=server> void ClickedIt(object sender, System.EventArgs e) { lblHelloWorld.Text = "Text schimbat"; } </script> </html> La apsarea butonului se va face submiterea formularului ctre server, spre a a aceeai pagin HelloWorld.aspx (lucru care se poate verica din inspectarea s a codului HTML generat). Pentru a se putea accesa controlul de tip Label pe partea de server, trebuie ca acestuia s i se asigneze un ID (lblHela loWorld cazul nostru). Pentru controlul Button (reprezentat de tag-ul n <asp:Button\>) s-au asignat textul i metoda care se va apela pe server s (ClickedIt). Codul pentru ClickedIt este scris inline aceeai pagin ASP.NET n s a (pentru simplitate, dar vom vedea c aceast metod are o variant mai a a a a bun). Remarcm c dac s-a apsat pe buton apoi se face refresh pe browser a a a a a se va retransmite Text schimbat, deoarece serverul mentine starea con troalelor. Cea mai important este observatia c la codul HTML generat pe client sa a a adugat un element input de tip hidden cu numele VIEWSTATE. Acesta a este mecanismul prin care ASP.NET mentine starea unei pagini de la o trim itere la alta ctre server. Prima dat cnd se cere pagina, ASP.NET va a a a codica valoarea tuturor controalelor i le va stoca acest input ascuns. La s n ecare cerere urmtoare a paginii acest input va folosit pentru a reinitializa a felul acesta este mentinut starea unor concontroalele Web i HTML. In s a troale (de exemplu continutul unui input de tip text sau de tip select, pentru care s-a fcut alegerea unei optiuni). a

11.1.3

Code-behind

Se recomand evitarea scrierii codului inline, deoarece asta duce la amestea carea prtii de design al paginii cu procesarea evenimentelor; plus, este a n posibil ca procesarea s e extrem de consistent iar codul rezultat devine a a greu de urmrit i mentinut. Erorile de sintax care se fac codul inline a s a n sunt observabile doar la runtime, precompilarea ind acest caz imposibil. n a ASP.NET are o modalitate extrem de simpl pentru separarea prtii de a a interfat utilizator de cea de cod propriu-zis. E util separarea a a ntre cele dou a

11.1. ANATOMIA UNEI PAGINI ASP.NET

227

aspecte, deoarece de partea de interfat se poate ocupa un web-designer, iar a de cea programativ cineva cu cunotinte de .NET. Codul executat se va a s scrie ntr-un ier cu extensia aspx.cs sau cs (dac e vorba de cod C#) care s a va compilat sub forma unui ier dll depus directorul bin al aplicatiei s n Web. Pentru a se introduce partea de code-behind pentru pagina anterioar, se a terge scriptul inline i se modic astfel: s s a <%@ Page Inherits="Test1.HelloWorldCB" %> <html> <head> </head> <body> <form method= post runat= server > <asp:Label id="lblHelloWorld" text="Hello World" runat="Server" /> <br> <asp:Button onclick="ClickedIt" text="Submit" runat="Server" /> </form> </body> </html> Pe primul rnd s-a scris o directiv ASP.NET (ele pot scrise oriunde, dar se a a obi uiete trecerea lor la n s nceputul documentului). Directiva Page precizeaz a acest caz care este clasa din care se deriveaz pagina curent. Clasa de n a a baz se poate declara aa: a s namespace Test1 { using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public class HelloWorldCB: System.Web.UI.Page { protected Label lblHelloWorld; protected void ClickedIt(object sender, System.EventArgs e) { lblHelloWorld.Text = "Text schimbat" ; } } } Pagina aspx rezultat va moteni clasa Test1.HelloWorldCB. a s

228

CURS 11. ASP.NET

11.2

Clasa Page

Clasa Page este motenit direct sau indirect de orice pagin ASP.NET; s a a ea este responsabil de asigurarea unei functionaliti de baz pentru procea at a sarea de pagini pe server. Ciclul de viat a unei pagin ASP.NET este urmtorul: a a 1. Pagina este initializat. Aceasta include crearea instantelor controalelor a i setarea event-handlerelor. s 2. Partea de viewstate este procesat, populndu-se controalele cu date. a a 3. Evenimentul de Load este apelat. In event-handlerul pentru acest eveniment se ncepe programarea logicii paginii. 4. Event-handlerele pentru controale sunt apelate (apsri de butoane, aa schimbarea selectiei curente dintr-un dropdown list, etc) 5. Se salveaz viewstate a 6. Pagina este tradus HTML (randare) a n Dm mai jos cele mai importante evenimente din clasa Page. a

11.2.1

Evenimentul Init

Acest eveniment este primul apelat ciclul de viat al unei pagini. n a In interiorul event-handlerului se creeaz toate controalele de pe pagin i de a as asemenea se face asigneaz event-handlerele pentru acestea. a

11.2.2

Evenimentul Load

Se apeleaz dup Init. Are acces la partea de viewstate a paginii (ina a acceibil pentru Init). Aceast metod poate decide dac este cerut de a a a a a ctre client pentru prima dat sau este rezultatul unui postback, pe baza a a proprietii boolene IsPostBack). Schita unei metode Load ar : at private void Page_Load(object sender, System.EventArgs e) { //cod care se executa la fiecare cerere if(!IsPostBack) { //cod care trebuie executat doar la prima cerere a paginii //ex: aducerea unor date din baza si popularea unor controale

11.3. CREAREA UNEI PAGINI ASP.NET FOLOSIND VISUAL STUDIO.NET229 } else { //cod care se executa doar daca s-a facut postback //ex: verificarea unor intrari de pe forma } }

11.2.3

Evenimentul Unload

Este apelat la descrcarea paginii din memoria serverului. Aici se dealoc a a resurse precum conexiuni la baza de date.

11.3

Crearea unei pagini ASP.NET folosind Visual Studio.NET

Dei se poate scrie ASP.NET i de mn, este de preferat folosirea s s a a VS.NET pentru c permite crearea simpl a ierelor aspx i a celor de codea a s s behind. plus, se creeaz nite iere de setri care ar bine s se gseasc In a s s a a a a folderul aplicatiei. n Fiierele care se creeaz automat sunt Web.cong, Global.asax, Global.asax.cs. s a Ultimul este util pentru cazul care se dorete procesarea evenimentelor de n s pornire a aplicatiei sau de creare a sesiunii utilizator. Fiierul Web.cong contine setri (de securitate, legate de sesiune) specis a ce aplicatiei. De asemenea s-a creat i un web form (alt nume pentru pagin ASP.NET) s a numit implicit Webform1.aspx; din partea de Solution Explorer se poate modica numele lui cu unul adecvat, de exemplu HelloWorld.aspx (se folosete s aceast denumire cele ce urmeaz). a n a Pe prima linie a ierului HelloWorld.aspx apare: s <%@ Page language="c#" Codebehind="HelloWorld.aspx.cs" AutoEventWireup="false" Inherits="HelloWorld.WebForm1"%> Pentru directiva Page s-au specicat urmtoarele atribute: a language cu valoarea c#; este limbajul care se scrie toate blocurile n n de cod de tip server; Codebehind specic numele ierului surs; acesta se creeaz automat a s a a de ctre VS.NET; a

230

CURS 11. ASP.NET

Inherits seteaz clasa care este motenit de forma web; aceast clas a s a a a trebuie s e derivat direct sau indirect din clasa System.Web.UI.Page. a a Se trage de pe toolbox un control Label pe forma ASP.NET, iar partea n de proprieti se seteaz pentru ID valoarea lblHelloWorld, iar pentru text at a valoarea Hello World. Codul generat de designer va : <%@ Page language="c#" Codebehind="HelloWorld.aspx.cs" AutoEventWireup="false" Inherits="HelloWorld.WebForm1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <title>WebForm1</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name="vs_defaultClientScript" content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> <asp:Label id="lblHelloWorld" style="Z-INDEX: 101; LEFT: 32px; POSITION: absolute; TOP: 24px" runat="server">Hello World </asp:Label> </form> </body> </HTML> Se trage apoi un control de tip buton pe form pentru care ID-ul implicit este a Button1 iar textul va setat la Buton; pe partea de HTML a formularului codul va : <form id="Form1" method="post" runat="server"> <asp:Label id="lblHelloWorld" style="Z-INDEX: 101; LEFT: 32px; POSITION: absolute; TOP: 24px" runat="server">Hello World</asp:Label> <asp:Button id="Button1" style="Z-INDEX: 102; LEFT: 128px; POSITION: absolute; TOP: 24px" runat="server" Text="Buton"></asp:Button> </form> Dac toolbox, la partea de evenimente ataate butonului se d dublu click a n s a pe evenimentul Click partea de code-behind se va genera automat metoda: n private void Button1_Click(object sender, System.EventArgs e)

11.4. CONTROALE SERVER { }

231

atunci cnd butonul este apsat se face o submitere a formularului ctre a a a server i codul continut metoda de mai sus va executat. Codul va : s n private void Button1_Click(object sender, System.EventArgs e) { lblHelloWorld.Text = "Text schimbat"; } La apsarea butonului textul din componenta lblHelloWorld se va schimba, a iar pagina nou va trimis ctre browser. Re a a a mprosptri succesive ale aa paginii (refresh) vor duce la aarea aceluiai Text schimbat, deoarece s s viewstate-ul contine valoarea modicat de apsarea butonului. a a

11.4
11.4.1

Controale server
Postback

Postback apare de cte ori se transmite pagina de la client a napoi la server. Exist dou modaliti de realizarea a acestei trimiteri a a at napoi: prin folosirea unor elemente de tip <input type="submit"> sau <input type="image"> respectiv prin script executat pe client. De exemplu, dac se trage pe form a a un buton, codul generat de designer va : <form method="post" runat="server" ID="Form1"> <asp:Button id="Button1" runat="server" Text="Button 1"></asp:Button> </form> iar pe browser se va genera: <form name="Form1" method="post" action="MyASPNETPage.aspx" id="Form1"> <input type="submit" name="Button1" value="Button 1" id="Button1" /> </form> Dac pentru un formular nu se specic partea de action, aceasta implicit va a a acceai pagin. s a Trimiterea formularului prin cod client s-ar face astfel: <script language="javascript"> function SubmitTheForm()

232 { document.forms["Form1"].submit(); } </script>

CURS 11. ASP.NET

iar proprietatea onclick a unui contro de tip HTML se seteaz la valoarea a SubmitTheForm(): <INPUT style="Z-INDEX: 103; LEFT: 80px; POSITION: absolute; TOP: 104px" type="button" value="Button" onclick="SubmitTheForm()"> cazul controalelor Web de tip server a cror modicare de stare trebuie s In a a duc la postback (de exemplu la selectarea unui item dintr-un combobox), se a seteaz proprietatea AutoPostBack pe true, se seteaz o metod pentru evenia a a mentul de schimbare a continutului ( cazul nostru: SelectedIndexChanged ) n iar pagina HTML generat pe client va contine o functie javaScript de suba mitere a formularului: function __doPostBack(eventTarget, eventArgument) { var theform; if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) { theform = document.Form1; } else { theform = document.forms["Form1"]; } theform.__EVENTTARGET.value = eventTarget.split("$").join(":"); theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } unde EVENTTARGET i EVENTARGUMENT sunt dou cmpuri ass a a cunse. Combobox-ul arat astfel codul HTML client: a n <select name="DropDownList1" onchange="__doPostBack(DropDownList1,)" language="javascript" id="DropDownList1" style="Z-INDEX: 104; LEFT: 312px; POSITION: absolute; TOP: 120px"> <option value="1">opt 1</option> <option value="2">opt 2</option> </select>

11.5. CONTROALE WEB

233

11.4.2

Data Binding

Data Binding permite legarea unei proprieti a unui control la o surs at a de date, precum un cmp baza de date. Aceast legare se poate face a n a scriind expresii interiorrul unei declaratii de control ierul .aspx sau n n s n code-behind. Pentru a se lega direct ierul .aspx se folosete tag-ul <%# %> (expresie n s s Data Binding). Aceast metod se aplic pentru orice control care nu este a a a list precum Label, TextBox, CheckBox. Cnd Data Binding se face pentru a a un control de tip list (DropDownList sau CheckBoxList) se va folosi propria etatea DataSource a controlului la o surs de date oarecare (e.g. DataSet). a Aceasta paote fcut partea de code-behind sau a a n ntr-un bloc de tip script din ierul .aspx. s Toate controalele care au posibiliti de Data Binding implementeaz o at a metod numit DataBind(). Aceasta se ocup de evaluarea expresiilor de a a a biding ierul .aspx sau pentru sursele de date specicate proprietatea n s n DataSource. Este important de retinut c expresiile Data Binding i pro a s prietile DataSource nu sunt evaluate pn cnd nu se apeleaz metoda at a a a a DataBind() ale controalelor cu care sunt asociate. Deoarece Page este el si un control, are o implementare de metod DataBind(), care cnd este nsu a a apelat va duce la apelul tuturor metodelor DataBind() pentru controalele a copil.

11.5

Controale Web

Controalele Web reprezint o modalitate uoar de a interationa cu serverul. a s a Nu exist o relatie univoc a a ntre controale web i elemente Html, ci cons troalele web vor randa pe parte de client cod Html specic browserului. Clasa Page are o proprietate nestatic numit ClientTarget care permite denirea a a browserului client. Valorile pentru proprietate sunt: Auto - se va face autodetectarea browserului DownLevel - se presupune c browserul tie doar Html 3.2 sau mai a s putin UpLevel - browserul tie Html 4 sau mai mult, CSS, EcmaScript, Mis crosoft Document Object Model Orice control Web este derivat din System.Web.UI.WebControls care este derivat din System.Web.UI.Control. Aceasta din urm pune la dispozitie a partea de Data Binding, viewstate, evenimente precum Init, Load, Unload,

234

CURS 11. ASP.NET

etc. Clasa WebControl contine proprieti de stil precum bacgkround, height, at width.

11.5.1

Label

Contine text de tip read-only, asignat static sau prin Data Binding. Se genereaz pe browser ca ind un element de tip span. Pentru crearea unei a etichete cu text predenit se trage un control Web de pe toolbar i se pot s seta proprieti ale lui, ceea ce ar putea genera urmtorul cod designer: at a n <asp:Label id=Label1 runat="server" Width="70px" Height="50px"> This is a label </asp:Label> Dac se dorete folosirea DataBinding, atunci se poate scrie: a s <asp:Label id=Label1 style="Z-INDEX: 101; LEFT: 10px; POSITION: absolute; TOP: 10px" runat="server" text="<%# DateTime.Now.ToString() %>"> </asp:Label> Pentru ca valoarea s e efectiv calculat i aat pe parte de browser a a s s a trebuie chemat metoda DataBind() pentru obiectul etichet; acest lucru se a a poate face partea de arcare a paginii: n nc protected void Page_Load(object sender, System.EventArgs e) { Label1.DataBind(); }

11.5.2

Button

Permite crearea unui buron de tip submit i care poate s determine s a executarea unei metode C# pe parte de client, pe baza unui event handler denit code-behind. n cazul care se dorete crearea mai multor butoane care s e procesate In n s a pe server, e nevoie de un mecanism care s fac diferentierea lor, astfel at a a nc s se poat determina care a fost butonul apsat. Acest lucru se face prin a a a proprietile CommandName i CommandArgument de tip string. Event at s handlerul pentru butoane poate s e: a private void Button_Command(object sender, System.Web.UI.WebControls.CommandEventArgs e)

11.5. CONTROALE WEB { //This Is a Command event handler }

235

unde obiectul e are proprietile CommandName i CommandArgument care at s vor da valorile setate pentru controale.

11.5.3

LinkButton

Se comport ca i un buton; codul Html generat va de forma <a> iar la a s apsare va apela functia Javascript __doPostBack prezentat mai sus. a a

11.5.4

TextBox

D posibilitatea de a introduce text a ntr-un formular. Se poate folosi n trei moduri: SingleLine, MultiLine sau Password. Prezint proprieti de a at tipul Columns, Rows, Wrap, MaxLength, ReadOnly, Text, AutoPostBack i s un eveniment TextChanged. Dac proprietatea AutoPostBack este setat la a a true, pagina va automat retransmis la server dac se iese din controlul a a curent i textul a fost schimbat; mecanismul se bazeaz pe acceai funtie s a s Javascript ca la LinkButton.

11.5.5

CheckBox

Determin aparitia unui checkbox; proprietile de interes sunt Checked, a at Text, TextAlign, AutoPostBack iar evenimentul detectat este CheckedChanged.

11.5.6

RadioButton

Derivat din CheckBox, are plus proprietatea GroupName care permite a n gruparea mai multor astfel de butoane.

11.5.7

DropDownList

Permite crearea unei liste de optiuni stocate sub numele de nume i val s oare. Toate item-urile continute sunt accesibile via proprietatea Items de tip ListItemCollection. Fiecare element al unei astfel de coletii este de tip Lis tItem i are proprietile Text, Value de tip String i Selected de tip boolean. s at s Lista de optiuni se poate crea e prin introducere direct proprietatea a n Items, e prin Data Binding. Evenimentul care se pate detecta este SelectedIndexChanged i se poate procesa pe server. s

236

CURS 11. ASP.NET

Curs 12 ASP.NET (2)


12.1 Controale de validare

Controalele de validare sunt utilizate pentru a face vericari asupra ndeplinirii unor conditii pentru continutul controalelor de introducere a datelor. Aceast a validare se poate face pe server sau (de preferat majoritatea cazurilor) pe n client (dac acesta suport DHTML). Validri suplimentare mai complexe a a a pot aprea i pe server, chiar dac au fost efectuate validri primare pe a s a a client. Unui control de preluare a datelor i se pot asocia oricte controale a de validare. De exemplu, se poate folosi un control de tipul RequiredFieldValidator i altul care s valideze continutul folosind expresii regulate; doar s a primul control poate determina dac s-a introdus ceva, celelalte returnnd a a automat valoare de adevr dac controlul de intrare nu contine nimic. a a Orice astfel de control de validare este derivat din clasa BaseValidator, care prezint proprietatea ControlToValidate care se depune ca valoare IDa n ul controlului pentru care se face validarea. Proprietatea Display determin a dac se rezerv loc pe pagin pentru a se aa mesajul de eroare. Poate avea a a a s valorile Static i Dynamic: primul caz se rezerv sucient spatiu pentru s n a mesajul de eroare, chiar dac acesta nu este vizibil; al doilea caz dac a n a mesajul de eroare este artat, atunci pot aprea deplasri ale componentelor a a a de pe pagin pentru a face loc textului de eroare. Mesajul de eroare se a denete proprietatea ErrorMessage. s n Proprietatea EnableClientScript setat pe true va duce la generarea de a cod scripting pentru client (JavaScript sau VBScript); dac proprietatea este a setat pe false se va face validarea doar pe server. Trebuie retinut faptul a c validarea se face a ntotdeauna i pe server. Pentru un client de tip nons DHTML, pentru un asemenea control nu se va genera nici un cod scripting client, ci atunci cnd formularul este trimis ctre server se va face testare vaa a 237

238

CURS 12. ASP.NET (2)

liditii datelor introduse; dac ceva nu corespunde criteriilor se va retrimite at a pagina ctre client a mpreun cu mesajele de eroare scrise. a acest context spunem c clasa Page contine o proprietate IsValid care In a va setat la true dac toate validatoarele sunt a a ndeplinite pe parte de server sau false caz contrar. n Controalele de validare sunt: RequiredFieldValidator, RegularExpressionValidator, ValidationSummary, RangeValidator, CompareValidator, CustomValidator.

12.1.1

RequiredFieldValidator

Forteaz utilizatorul s introduc ceva a a a ntr-un control de introducere. Dac se a ncearc submiterea formularului iar cmpul pentru care s-a setat a a vaidatorul nu are valoare introdus se va semnala eroare prin aparitia mesajua lui denit de utilizator proprietatea ErrorMessage. Codul JavaScript care n se efectueaz aceste validri este scris ierul surs C:\Inetpub\wwwroot\ a a n s a aspnet client\system web\1 1 4322\WebUIValidation.js.

12.1.2

RegularExpressionValidator

Veric pe baza unei expresii regulate dac valoarea introdus a a a ntr-un cmp este corect. Pe parte de client se va folosi sintax de regex-uri de a a a tip JavaScript, pe parte de server se va folosi clasa Regex 1 . Visual Studio In sunt denite cteva expresii regulate care se pot aplica pentru diferite scopuri: a adrese de email, numere de telefoane din diverse ri, URL, etc. ta

12.1.3

RangeValidator

Veric dac valoarea dintr-o intrare este cuprins a a a ntre un minim i un s maxim. Pentru continut i extreme tipul poate String, Integer, Double, s Date i Currency, specicat proprietatea Type a controlului. Valorile s n minime i maxime admise se dau proprietile MinimumValue i Maxis n at s mumValue.

12.1.4

CompareValidator

Se folosete pentru a compara continutul unui control cu continutul als tui control sau cu o valoare x, predenit. Dac se dorete compararea cu a a a s
1

Pentru o coletie bogat de expresii regulate, a se vedea i www.regexlib.com a s

12.1. CONTROALE DE VALIDARE

239

valoarea dintr-un alt control, se seteaz adecvat proprietatea ControlToComa pare, iar dac se face compararea cu o valoare predenit atunci se seteaz a a a proprietatea ValueToCompare. De asemenea se poate specica tipul de dat a pentru care se face compararea, ca la RangeValidator. Proprietatea Operator specic operatorul folosit la comparare: Equal (implicit), NotEqual, a GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, i DataTypes Check (operator unar, care veric validitatea datei calendaristice specia cate).

12.1.5

CustomValidator

Permite crearea de validatoare de ctre programator, care se pot executa a att pe client ct i pe server. Pentru validarea pe server trebuie dat un a a s delegat de tipul: void HandlerName (object source, ServerValidateEventArgs args) Al doilea parametru are proprietatea Value care d acces pe server la valoarea a introdus pe client i proprietatea IsValid de tip bool care trebuie setat pe a s a true dac validarea s-a fcut cu succes sau false caz contrar. Aceast a a n a valoare afecteaz valoarea proprietii IsValid a obiectului de tip Page. a at Motivul pentru care s-ar folosi un asemenea validator ar c vericarea a se poate face numai pe server (de exemplu se verica dac numele si parola a unui utilizator se regasesc in baza de date). Dac a se dorete validarea i a ns s s pe client folosind JavaScript, atunci se va seta mod adecvat proprietatea n ClientValidationFunction. Exemplu: pentru validarea pe server a valorii introduse ntr-un cmp, se a poate scrie codul C#: private void vldUserIDCstm_ServerValidate(object source, ServerValidateEventArgs args) { if (args.Value == "JoeHealy" || args.Value == "SpikePierson" || args.Value == "AndyJohnston") { args.IsValid = false; } else { args.IsValid = true; } }

240

CURS 12. ASP.NET (2)

Pentru a se face aceeai vericare i pe client, se poate scrie codul urmtor s s a la sfritul paginii aspx: as <script language="JavaScript"> function vldUserIDCstm_ClientValidate(source, args) { if (args.Value = "JoeHealy" || args.Value = "SpikePierson" || args.Value = "AndyJohnston") { args.IsValid=false; } else { args.IsValid=true; } } </script> Numele functiei JavaScript este setat proprietatea ClientValidationFunc n tion.

12.1.6

ValidationSummary

Nu este un control de validare propriu zis, ind derivat din clasa WebControl. Este folosit pentru a aa mesajele de eroare pentru toate controalele s de validare ntr-un singur loc. Proprietatea DisplayMode este folosit pentru a a controla formatul care se vor aa mesajele de eroare, avnd valorile n s a posibile: List, BulletList, SingleParagraph. De asemenea exist proprietatea a HeaderText care permite aarea unui antet deasupra listei de erori. s Mesajele de eroare pot aate s ntr-unul din 2 moduri sau ambele. n Ele pot aate pagin de orice browser, e el UpLevel e DownLevel. s n a plus, un browser UpLevel poate s aeze mesajul (i) In a s s ntr-o fereastr de a eroare creat de browser. Dac proprietatea de tip bool ShowSummary este a a setat pe true, atunci se vor aa mesajele de eroare pagina HTML. Dac a s n a proprietatea de tip bool ShowMessageBox este pus pe true, atunci va aprea a a o fereastr cu lista de erori; acest din urm caz, pentru un browser de tip a n a DownLevel mesajul nu va artat fereastr. a n a

12.2

Comunicarea cu browserul

Pentru trimiterea de date ctre client se poate folosi pe parte de server a obiectul predenit Response de tip HttpResponse. Dei ASP.NET pune la s

12.2. COMUNICAREA CU BROWSERUL

241

dispozitie mecanisme sucient de complexe pentru a nu necesita folosiera masiv a acestui mecanism, Response este util pentru: a 1. pagini foarte simple sau debugging; 2. modicarea mecanismului de buering; 3. redirectarea clientului; 4. managementul mecanismului de caching

12.2.1

Generarea programatic continutului a

unele cazuri rspunsul care se trimite de la server la browserul client In a poate sucient de simplu pentru a nu necesita utilizarea de controale. Utilitatea unei asemenea metode este limitat, dar exist situatii care calea a a n se dovedete a bun. s a Exemplu: protected void Page_Load(object sender, System.EventArgs e) { Response.Write(string.Format("The date is: {0}<br>", DateTime.Now.ToShortDateString())); Response.Write(string.Format("<i>The time is: <b>{0}</b></i><br>", DateTime.Now.ToShortTimeString())); } De asemenea se permite trimiterea continutului unor iere cte browser, via s a metoda WriteFile.

12.2.2

Redirectarea

Se folosete pentru cazul care pagina care se trimite clientului nu este s n cea curent, ci o alta. De exemplu, pentru cazul care se dorete redirectarea a n s utilizatorului cter o pagin de login. Redirectarea trebuie fcut a a a a nainte ca vreun header s e trimis ctre client. a a Exemplu: Response.Redirect("http://mysite.com/myBusiness"); Aceast redirectare are loc de pe server, fr a se trimite cod ctre browser a aa a care s determine pe acesta s cear o alt resurs. a l a a a a

242

CURS 12. ASP.NET (2)

12.3

Cookies

Cookie-urile reprezint informatie stocat pe client. Dei nu reprezint a a s a un mecanism garantat pentru lucrul cu clientul, unele cazuri pot sucient n de utile pentru o aplicatie. Exist 4 clase de lucru cu cookie-urile: a HttpCookie HttpCookieCollection HttpResponse HttpRequest Obiectul Response are o colectie numit Cookies. Cnd un browser face o a a cerere ctre un server, va trimite prin intermediul headerelor toat colectia a a de cookie-uri care apartin acelui site. Aceast colectie se a ncarc Cookies. a n Atunci cnd se trimite un cookie de pe server pe client se poate prea ciza dac acesta va valabil doar pe perioada ct este deschis browserul a a (aa-numitele cookie-uri de sesiune) sau i dup ce browserul este s s a nchis (cookie-uri persistente - rezist pn la o dat a expirrii setat sau pentru a a a a a a totdeauna). Primul tip de cookie-uri sunt utile medii care nu exist n n a intimitate a utilizatorului, un cont ind folosit de ctre mai multi; al doilea a tip se poate folosi pentru stocarea informatiilor (precum preferintele) de la o vizit la alta. a Diferenta dintre cele dou din punct de vedere programatic const a a n absenta sau prezenta unui timp de expirare. Cele de tip sesiune nu au setat nici un timp de expirare (i se retin memoria RAM a calculatorului), pe s n cnd celelalte pot avea un timp setat viitor (ind persistate pe disc). a n Exemplu de creare a unui cookie de tip sesiune: protected void Page_Load(object sender, System.EventArgs e) { HttpCookie tempcookie = new HttpCookie("myCookie"); tempcookie.Values.Add("userid", "1250"); Response.Cookies.Add(tempcookie); } Pentru crearea unui cookie persistent, se poate proceda ca mai jos: protected void Page_Load(object sender, System.EventArgs e) { HttpCookie perscookie = new HttpCookie("myCookie"); perscookie.Expires = Convert.ToDateTime("24/05/05 16:00");

12.4. FISIERUL DE CONFIGURARE AL APLICATIEI WEB perscookie.Values.Add("userid", "1250"); Response.Cookies.Add(perscookie); }

243

Accesarea acestui cookie se face pe server prin intermediul obiectului Request: HttpCookie cookie = Request.Cookies["myCookie"]; if (cookie != null) { string s = cookie.Values["userid"].ToString(); ... }

12.4

Fiierul de congurare al aplicatiei Web s

Diverse setri ale aplicatiei pot mentinute a ntr-un ier de tip XML nus mit Web.cong. Structura i modul de accesare a continutului acestuia este s asemntoare cu cea prezentat sectiunea 8.4.4. Deosebit este a fapa a a n ns tul pentru o aplicatie Web aceast congurare respect o anumit ierarhie. a a a La rdcina acestei ierarhii se a ierul machine.cong aat dioreca a a s n torul %windir%\Microsoft.NET\Framework\Version\CONFIG. Setrile de a aici pot suprascrise de setri aate directoarele Web.cong unei aplicatii a n ASP.NET.

12.5

Fiierul global.asax s

Este un ier optional structurat XML care prezint setri relative la s a a aplicatia ASP.NET (mai exact, pentru obiectul reprezentnd aplicatia). Fiierul a s se gsete rdcina aplicatiei, este compilat ca o clas care este derivat a s n a a a a din HttpApplication, ind recompilat automat la ecare modicare a sa. a Metodele care se pot implementa aceast clas sunt: n a a Application Start - apelat atunci cnd un client cere pentru prima a a oar o pagin de la aceast aplicatie; folosit pentru initializarea unor a a a a resurse (grupuri de conexiuni, valori din regitri, etc care sunt accesate s de ctre prti din aplicatie). a a Session Start - ori de cte ori se pornete o sesiune a s Application BeginRequest - apelat ori de cte ori un client cere o pagin a a oarecare de la server; s-ar putea folosi la contorizarea numrului de a accese la pagin a

244

CURS 12. ASP.NET (2)

Application EndRequest - apelat la sfritul oricrei cereri ctre server as a a Application AuthenticateRequest - apelat la ecare cerere de autentia care a unui utilizator Application Error - apelat ori de cte ori apare o eroare care nu a fost a a procesat a Session End - la terminarea unei sesiuni prin expirare Application End - apare la o resetare a serverului sau atunci cnd se a recompileaz aplicatia ASP.NET. a

12.6

Managementul sesiunii

Sesiunea este un mecansim prin care se creeaz impresia unei conexiuni a permanente de la client la server. O sesiune este stocat pe server i va a s retine toate obiectele necesare bunei functionri pe perioada respectiv (de a a exemplu poate contine coul de cumprturi pentru un client, ID-ul acelui s aa client, etc). Ori de cte ori un client cere prima pagin a unei aplicatii Web, a a se creeaz un obiect de sesiune pe server identicat printr-un numr unic a a care este de asemenea trimis clientului printr-un cookie. Aceast sesiune a este ataat doar browserului care a cerut crearea ei; dac acesta se s a a nchide, atunci vechea sesiune este inaccesibil. De ecare dat cnd se cere o pagin a a a a de la server (dup ce sesiunea a fost creat), se cere id-ul stocat cookie-ul a a n de pe client i se a care este sesiunea ataat. s a s a Stocarea unei valori sesiune trebuie s se fac folosind o cheie (unic n a a a pe acea sesiune) i o valoare ataat (de ori ce tip). Exemplu: s s a Session["username"] = "jsmiley"; Dac cheia username nu exist, se va crea i i se va ataa string-ul numit; a a s s dac exist, valoarea sa se va suprascrie. Recuperarea valorii anterioare se a a face cu: String uname = (string)Session["username"]; Mentionm c sesiune se pot stoca orice obiecte, nu doar iruri de ca a a n s ractere. Dac serverul web se repornete, toate valorile din acea sesiune se a s pierd; exist alternativa de a stoca sesiunile pe un alt server sau chiar pe un a server SQL. Stergerea unei chei i a valori ataate din sesiune se face folosind s s metodele Session.Remove(String key), Session.RemoveAt(int index). Golirea sesiunii se face cu Session.RemoveAll() sau Session.Clear().

12.7. VIEWSTATE

245

12.7

ViewState

ViewState reprezint o colectie de valori care se stocheaz pe client, dar a a nu prin intermediul cookie-urilor, ci pe baza unui cmp de tip hidden din a pagina HTML trimis clientului. Este folosit special pentru a retine a a n valorile controalelor Web de-a lungul mai multor post-back-uri. Spre deosebire de obiectul Session care poate stoca orice tip de obiect, obiectul ViewState (de tip StateBag) poate s contin doar string-uri. a a Exemplu: ViewState["uname"] = "Joe"; .. String suname = (string)ViewState["uname"]; Se poate terge selectiv din obiectul ViewState prin metoda Remove(String s cheie).

12.7.1

Application

Obiectul Application este foarte asemntor cu Session, cu deosebirea c a a a nu este unic pentru ecare sesiune utilizator, ci pentru ntreag aplicatie. Este utilizat pentru mentinearea unor obiecte cu caracter global, utile pentru orice sesiune (valori din regitri sau valori imuabile care nu trebuie citite de ecare s dat). Stocarea de valori se face prin: a Application["x"] = new MyObject(); iar accesarea lui prin: MyObject x = Application["x"];

12.8

Alte puncte de interes aplicatii ASP.NET n

crearea controalelor utilizator - a paginilor care sunt convertite conn troale pentru a permite reutilizarea lor mai simpl - ex: header, footer; a separarea codului de prezentare - pentru a permite separarea muncii ntre web designer i programator; separarea pe mai multe nivele uureaz s s a mentinerea codului i schimbarea lui; este sugerat folosirea a cel putin s a 3 nivele: interfata utilizator, nivelul de business logic, nivelul de acces la date;

246

CURS 12. ASP.NET (2)

crearea de aplicatii web pentru dispozitive mobile; dei la ora actual s a piata de dispozitive mobile este relativ redus, exist totui un seg a a s ment bine denit care cere astfel de aplicatii. Design-ul specic i s capabilitile unui astfel de browser pot ridica probleme noi; at politici de caching - pentru optimizarea accesului la resurse; autenticarea - diverse forme de securitate: formulare, .NET Passport; folosirea protocoalelor de comunicatie securizate (SSL) folosirea serviciilor Web - servicii care se pot accesa Internet; bazate n pe XML i protocoale de comunicatie larg rspndite, un serviciu Web s a a este independent de platform. a

Curs 13 Servicii Web


13.1 Generaliti at

Un serviciu Web (SW) expune o interfat de invocare a unei activiti de a at ctre un client. Un client poate accesa un serviciu Web folosind standarde a Internet. Un serviciu web rezolv urmtoarele probleme: a a interoperabilitate - un SW trebuie s permit comunicarea a a ntre orice platforme; interfete puternic tipizate - nu trebuie s existe ambiguitate relativ la a tipul de date trimise sau primite spre/de la un SW. Aceste tipuri de date ar trebui s se poat mapa rezonabil peste tipurile de date folosite a a de limbajele procedurale existente; folosirea standardelor Internet - implementarea unui SW trebuie s a se bazeze pe standardele i protocoalele de comunicare deja existente, s pentru a evita reinventarea rotii acces multilimbaj - un SW nu trebuie s reduc aria limbajelor care le a a pot utiliza; transparenta trebuie s se manifeste att la nivelul limba a a jului care s-a creat SW, ct i la nivelul clientului; n a s posibilitate de integrare orice infrastructur distribuit - un SW nu n a a trebuie s e strns cuplat de o anumit infrastructur de comunicare. a a a a

13.1.1

Componentele unui WS

Principalele blocuri componente ale unui serviciu Web sunt redate n gura 13.1. 247

248

CURS 13. SERVICII WEB

Descoperirea - dac se dorete conectarea la un serviciu Web, este necea s sar o modalitate de a determina locatia serviciului - proces numit a descoperire. Descoperirea poate realizat prin intemediul unor direca toare centralizate sau prin alte metode ad-hoc. Descrierea - dup ce s-a rezolvat problema de la punctul precedent, a clientul va avea nevoie de o descriere a modului de interactiune cu SW. Descrierea reprezint metadate structurate despre interfata ce urmeaz a a a consumat, precum i documentatie scris despre SW continnd a s a a detalii sau exemple de utilizare. Formatul mesajelor - necesar a precizat pentru a se putea codica ntr-un mod convenabil toat conversatia a ntre client i SW. Acest lucru s permite abstractizarea protocoalelor de comunicare i concentrarea pe s logica problemei. Codicarea - datele trimise ntre cei 2 parteneri de conversatie trebuie codicate ntr-un mod care s permit procesarea lor de ctre orice a a a limbaj. XML este alegerea potrivit pentru aa ceva, ind structurat a s i de tip text. s Transportul - modul prin care datele serializate care compun un mesaj sunt trimise de la un capt la cellalt. S-a cerut explicit functionarea a a unui serviciu web peste protocoale prietenoase, care s nu necesite a setri speciale de securitate. a

13.2
13.2.1

Discutii asupra blocurilor componente


Protocolul de transport

O aplicatie bazat pe SW poate s e folosit att LAN ct i a a a a n a s ntr-o retea de mare ntindere. Protocoale precum HTTP sau SMTP sunt testate din punct de vedere al serviciilor pe care ofer. Faptul c HTTP este fr l a a aa stare permite crearea unor aplicatii care s permit continuarea servirii unor a a clienti chiar dac serverul initial a czut (sarcina sa ind preluat de alte a a a servere). plus, SMTP i HTTP sunt portocoale care nu cer o congurare In s special a rewall-urilor care delimiteaz o retea. a a

13.2.2

Schema de codicare

XML reprezint alegerea natural pentru codicarea mesajelor dintre a a client i server, deoarece este structurat (fcnd procesarea sa uoar) i s a a s a s

13.2. DISCUTII ASUPRA BLOCURILOR COMPONENTE

249

Figura 13.1: Componentele unui serviciu Web acelai timp neutru din punct de vedere al platformei. acest fel dispar n s In problemele legate de modul de reprezentare a datelor pe diverse arhitecturi de sisteme: textul poate procesat la fel de ctre oricine. plus, exist a In a o multitudine de parsere XML care pot utilizate chiar i pe platforme cu s putere de procesare redus. a

13.2.3

Conventia de formatare

Alturi de corpul mesajului se transmit de cele mai multe ori i metadate. a s Simple Object Access Protocol (SOAP) este un protocol care descrie regulile i formatul pentru comunicare. Prin intermediul lui SOAP se pot transmite s documente i se pot face apeluri de metode: s <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <PurchaseOrder ID="3245">

250

CURS 13. SERVICII WEB

<From>Jim</From> <To>Frank</To> <Items> <Item ID="111">Dingbats</Item> <Item ID="222">Widgets</Item> </Items> </PurchaseOrder> </soap:Body> </soap:Envelope> exemplul de mai sus se transmite continutul unei comenzi de la Jim la In Frank. Pentru apelul unei metode la distant s-ar putea folosi: a <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <m:GetStockPrice xmlns:m="http://contoso.com/Stock"> <symbol>MSFT</symbol> </m:GetStockPrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope> corespunztor apelului de metod C#: a a GetStockPrice( "msft" ); Structura general a unui mesaj SOAP este un plic1 care este format din a header-e (metadate asociate mesajului) i continut propriu-zis. s

13.2.4

Mecanismul de descriere

Web Services Description Language (WSDL) denete un document XML s care permite descrierea serviciului Web int. Pemite descrierea continutului t a mesajului dintre client i serviciu, realizarea de tipuri de date utilizator, s formatul de comunicare ntre client i server. s

13.2.5

Mecanisme de descoperire

Universal Description, Discovery, and Integration (UDDI) este mecansimul prin care se aduce la cunotint existenta unor servicii web disponibile s a
1

Engl: envelope

13.3. CREAREA UNUI SERVICIU WEB SIMPLU

251

oricrui client. UDDI este un director centralizat care poate folosit pentru a anuntarea de SW, pentru care se poate face cutarea dup criterii: cate a a goria, tipul de SW, compania, etc. De asemenea se poate folosi i DISCO, s un format XML proprietar Microsoft, avnd ca principal consumator Visual a Studio .NET.

13.3

Crearea unui serviciu Web simplu

Mai jos exemplicm crearea unui SW i a unor clienti care s interactioneze a s a cu acestea.

13.3.1

Adunarea a dou numere a

Vom crea un serviciu Web care preia dou numere a ntregi i returneaz s a suma lor. Manual, acest lucru se face prin crearea unui director virtual (de ex. Test), se creeaz un ier cu extensia asmx acest director (e el Test.asmx) a s n i se scrie codul: s <%@ WebService Class="TestClass" Language="C#" %> using System; using System.Web.Services; public class TestClass { [WebMethod] public int Add( int a, int b) { return a+b; } } Drept client se poate folosi browser-ul IE cu adresa: http://localhost/Test/Test.asmx. Rspunsul este un document XML: a <?xml version="1.0" encoding="utf-8" ?> <int xmlns="http://tempuri.org/">5</int> Descrierea pentru acest serviciu (documentul WSDL) este obtinut prin a adresa: http://localhost/Test/Test.asmx?WSDL.

252

CURS 13. SERVICII WEB

13.3.2

Validarea unui numr de card de credit a

S presupunem c avem de implementat un site de tip B2C (de exemplu a a vnzare de crti). Atunci cnd se efectueaz plata prin card de credit se a a a a cere de la utilizator numrul de card i data expirrii. Se presupune c a s a a aceast validare nu este fcut de ctre aplicatie, ci pe baza unui serviciu a a a a Web specializat. Vom exemplica cele ce urmeaz prin Visual Studio, dei a s se poate folosi i alt modalitate. s a Pentru Web Service se poate proceda astfel: File->New->Project->se alege ASP.NET Web Service->se completeaz numele PlataWS. Din Solution a Explorer se modic numele ierului PlataWS.asmx. partea de cod se a s n In scrie metoda: [WebMethod] public bool ValidateCardNo(String cardNo, String date) { return int.Parse(cardNo) %2 == 0; } (o validare simpl, pe baza paritii numrului de card). a at a Pentru partea de site se poate crea o form windows de tipul celei din a gura 13.2, care s se poat introduce numrul de card i data expirrii. n a a a s a

Figura 13.2: Preluarea datelor unei crti de credit a Pentru a se putea accesa serviciul web, se intr Solution Explorer i a n s se adaug la References un Web Reference la serviciul local PlataWS numit a

13.3. CREAREA UNUI SERVICIU WEB SIMPLU

253

localhost. Aceasta permite crearea unui obiect de tipul PlataWS pentru care se va apela metoda de validare. Codul apelat pe server la apsarea butonului a de submitere este: private void Button1_Click(object sender, System.EventArgs e) { localhost.PlataWS validator = new localhost.PlataWS(); if (!validator.ValidateCardNo(NrCarte.Text, Calendar1.SelectedDate.ToString())) { Validitate.Text = "Invalid"; Validitate.Visible = true; } else { Validitate.Visible = false; } } unde Validitate este un Label invizibil de pe form. Validitatea este deci a testat prin intermediul unui serviciu Web care returneaz true sau false, a a n functie de care se aeaz sau nu eroare. s a

13.3.3

SW pentru transmitere de continut binar

Folosind SOAP se poate transmite orice fel de mesaj, indiferent de sursa de provenient a acestuia. Vom arta mai jos modul care se poate face a a n transmiterea unui ier binar folosind servicii Web2 . s Pentru SW se va scrie codul: [WebMethod] public FileContent GetFile(String name) { FileStream fileStream = File.OpenRead(name); FileContent fileContent = new FileContent(); fileContent.Name = name; fileContent.Content = new byte[fileStream.Length]; fileStream.Read(fileContent.Content, 0, fileContent.Content.Length); fileStream.Close(); return fileContent; }
2

Cu toate c FTP este mai potrivit pentru aa ceva a s

254

CURS 13. SERVICII WEB

unde FileContent este o structur denit acelai spatu de nume: a a n s namespace FileTransfer { public struct FileContent { public string Name; public byte[] Content; } } Aplicatia client poate s e de tip form Windows, ce permite specicarea cii a a a absolute a unui ier aat pe server i care salveaz continutul obiectului de s s a tip FileTransfer pe discul local. Pentru accesarea serviciului Web se adaug a o referint Web la serviciul creat anterior (denumit mai jos site), astfel a a putnd-se referi orice tip de date declarat public interiorul spatiului de a n nume. Secventa de cod pentru apelarea serviciului Web este: private void button1_Click(object sender, System.EventArgs e) { site.FileTransfer transfer = new site.FileTransfer(); site.FileContent fileContent = transfer.GetFile(textBox1.Text); Stream sw = File.OpenWrite(@"c:\temp\output.bin"); sw.Write( fileContent.Content, 0, fileContent.Content.Length ); sw.Close(); }

13.4

SOAP

SOAP (Simple Object Access Protocol) specic modul de a mpachetare a mesajelor. Cteva avantaje ale SOAP-ului sunt: a nu este puternic cuplat cu vreun limbaj de programare, nu se specic a un API. SOAP se poate folosi din orice limbaj sau de pe orice platform a nu este legat de vreaun protocol de transport anume. Deoarece un mesaj SOAP etse un document XML, pentru el se poate folosi orice fel de protocol care tie s transmit text s a a nu este legat de nici o infrastructur distribuit (DCOM, Corba, etc) a a se folosete de standarde existente - XML, XML schema, HTTP, SMTP, s etc

13.4. SOAP

255

interoperabilitate ntre platforme: SOAP poate folosit de ctre PCa uri, dispozitive mobile, mainframe-uri, etc Un mesaj SOAP const a ntr-un plic compus principal din header-e i n s corp.

13.4.1

Header-e

Elementele de header sunt folosite pentru a transmite continut care nu are legtur propriu-zis cu corpul mesajului. De exemplu, dac se folosete a a a a s un anumit algoritm de compresie a continutului mesajului atunci receptorul trebuie s tie care este acesta. De asemenea se pot include: as autenticare - se poate cere receptorului s se autentice a nainte de a procesat mesajul informatie de tip rezumat sau semntur digital pentru a se asigura a a a c continutul mesajului nu afost alterat a informatie de rutare - dac mesajul trebuie s ajung la mai multe a a a destinatii Atributul mustUnderstand Headerele pot ignorate de ctre receptor. Dar pentru o parte a acesora a se poate cere ca s se asigure elegerea unui anumit header s e garantat a nt a a de ctre client sau ca a ntregul mesaj s e ignorat. Intr-un asemenea caz se a specic pentru acel element de header atributul mustUnderstand cu valoarea a 1. <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <TransactionId soap:mustUnderstand="1">123</TransactionId> </soap:Header> <soap:Body> <UpdateAccountInfo> <email>sshort@microsoft.com</email> <firstName>Scott</firstName> <lastName>Short</lastName> </UpdateAccountInfo> </soap:Body> </soap:Envelope>

256 Atributul actor

CURS 13. SERVICII WEB

Un mesaj SOAP paote rutat prin mai multi intermediari. Se folosete s un atribut actor care specic adresa intermediarului. Acest intermediar a trebuie s elimine informatia de intermediar care privete, dar poate s a l s a adauge la rndul lui alt informatie de acest tip. a a

13.4.2

Elementul body

Un mesaj SOAP poate avea un singur element de tip body al crui a continut poate s e structurat XML sau simple iruri de caractere, orice a s care nu invalideaz continutul documentului XML contintor. Pe baza acesa a tui continut, un mesaj SOAP poate privit ca un mesaj orientat pe proce dur sau ca unul de tip document. a Pentru primul caz, s presupunem c SW cere apelarea unei metode de a a adunare a 2 numere: public int Add(int x, int y) { return x + y; } Mesajul SOAP la apelarea acestei metode este: <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Add> <x>1</x> <y>2</y> </Add> </soap:Body> </soap:Envelope> Rspunsul generat de serviciul Web este: a <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResult> <result>3</result>

13.4. SOAP </AddResult> </soap:Body> </soap:Envelope>

257

Exist suport pentru transmiterea tipurilor de date denite de utilizator, a a array-urilor, denirea parametrilor ca ind de tip valoare/referint/ieire, a a s grafurilor de obiecte, etc.

258

CURS 13. SERVICII WEB

Bibliograe
[1] C# .NET. Web Developers Guide, Turtschi Adrian, DotThatCom.com, Werry Jasson, Hack Greg, Albahari Joseph, Nandu Saurabh, Lee Wei Meng; Syngress Publishing, 2002 [2] Programming C#, Jesse Liberty; OReilly, 2001 [3] .NET Framework Essentials, Thia Thuan, Lam Hoang Q.; OReilly, 2001 [4] C# Language Specication, ECMA TC39/TG2, Octombrie 2002 [5] A Programmers Introduction to C#, Eric Gunnerson, APress, 2001 [6] A Programmerss Introduction to C#, Syngress Publishing, 2002 [7] Professional ADO.NET Programming, Wrox, 2001

259

You might also like