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 Tr as aturi ale platformei .NET . . . . . . 2 Tipuri predenite, tablouri, string-uri 2.1 Vedere general a asupra limbajului C# 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 S iruri de caractere . . . . . . . . . . . 2.4.1 Expresii regulate . . . . . . . . 3 Clase, instruct iuni, spat ii 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 Spat ii 3.4.1 3.4.2 Conversiile implicite ale expresiilor constante Conversii explicite . . . . . . . . . . . . . . Boxing si unboxing . . . . . . . . . . . . . . Declarat ii de variabile si constante . . . . . Declarat ii de etichete . . . . . . . . . . . . . Instruct iuni de select ie . . . . . . . . . . . . Instruct iuni de ciclare . . . . . . . . . . . . Instruct iuni de salt . . . . . . . . . . . . . . Instruct iunile try, throw, catch, nally . . . Instruct iunile checked si unchecked . . . . . Instruct iunea lock . . . . . . . . . . . . . . . Instruct iunea using . . . . . . . . . . . . . . de nume . . . . . . . . . . . . . . . . . . . . Declarat ii de spat ii 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 C ampuri . . . . . . . . . . . . . . 4.3.1 C ampuri instant e . . . . . 4.3.2 C ampuri statice . . . . . . 4.3.3 C ampuri readonly . . . . . 4.3.4 C ampuri volatile . . . . . 4.3.5 Init ializarea c ampurilor . . 4.4 Constante . . . . . . . . . . . . . 4.5 Metode . . . . . . . . . . . . . . . 4.5.1 Metode statice si nestatice 4.5.2 Metode externe . . . . . . 4.6 Propriet a ti . . . . . . . . . . . . . 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 n C# 5.1 Destructori . . . . . . . . . . . . . . . . . . . . . . . 5.2 Specializarea si generalizarea . . . . . . . . . . . . . . 5.2.1 Specicarea mo stenirii . . . . . . . . . . . . . 5.2.2 Apelul constructorilor din clasa de baz a . . . 5.2.3 Operatorii is si as . . . . . . . . . . . . . . . 5.3 Clase sealed . . . . . . . . . . . . . . . . . . . . . . . 5.4 Polimorsmul . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Polimorsmul parametric . . . . . . . . . . . . 5.4.2 Polimorsmul adhoc . . . . . . . . . . . . . . 5.4.3 Polimorsmul de mo stenire . . . . . . . . . . . 5.4.4 Virtual si override . . . . . . . . . . . . . . . 5.4.5 Modicatorul new pentru metode . . . . . . . 5.4.6 Metode sealed . . . . . . . . . . . . . . . . . . 5.4.7 Exemplu folosind virtual, new, override, sealed 5.5 Clase si metode abstracte . . . . . . . . . . . . . . . 5.6 Interfet e . . . . . . . . . . . . . . . . . . . . . . . . . 5.6.1 Clase abstracte sau interfet e? . . . . . . . . .

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 Delegat i. Evenimente. Structuri 6.1 Tipul delegat . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 Utilizarea delegat ilor pentru a specica metode la runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.2 Delegat i statici . . . . . . . . . . . . . . . . . . . . . 6.1.3 Multicasting . . . . . . . . . . . . . . . . . . . . . . . 6.2 Evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2.1 Publicarea si subscrierea . . . . . . . . . . . . . . . . 6.2.2 Evenimente si delegat i . . . . . . . . . . . . . . . . . 6.2.3 Comentarii . . . . . . . . . . . . . . . . . . . . . . . 6.3 Structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Structuri sau clase? . . . . . . . . . . . . . . . . . . . 7 Tratarea except iilor. Atribute 7.1 Tratarea except iilor . . . . . . . . . . . . . . . 7.1.1 Tipul Exception . . . . . . . . . . . . . 7.1.2 Aruncarea si prinderea except iilor . . . 7.1.3 Re ncercarea codului . . . . . . . . . . 7.1.4 Compararea tehnicilor de manipulare a 7.1.5 Sugestie pentru lucrul cu except iile . . 7.2 Atribute . . . . . . . . . . . . . . . . . . . . . 7.2.1 Generalit a ti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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 a ADO.NET? . . . . . . . . . . . . . . . . . . . . 173 8.2 Furnizori de date n ADO.NET . . . . . . . . . . . . . . . . . 174 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 Propriet a ti . . . . . . . . . . . . . . . . . . . . . . . . . 177 8.4.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 179 8.4.3 Evenimente . . . . . . . . . . . . . . . . . . . . . . . . 179 8.4.4 Stocarea stringului de conexiune n sier de congurare 179 8.4.5 Gruparea conexiunilor . . . . . . . . . . . . . . . . . . 180 8.5 Obiecte Command . . . . . . . . . . . . . . . . . . . . . . . . 181 8.5.1 Propriet a ti . . . . . . . . . . . . . . . . . . . . . . . . . 181 8.5.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 182 8.5.3 Utilizarea unei comenzi cu o procedur a stocat a . . . . . 185 8.5.4 Folosirea comenzilor parametrizate . . . . . . . . . . . 185 8.6 Obiecte DataReader . . . . . . . . . . . . . . . . . . . . . . . 187 8.6.1 Propriet a ti . . . . . . . . . . . . . . . . . . . . . . . . . 187 8.6.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 188 8.6.3 Crearea si utilizarea unui DataReader . . . . . . . . . . 188 8.6.4 Utilizarea de seturi de date multiple . . . . . . . . . . . 190 8.6.5 Accesarea datelor ntro manier a sigur a din punct de vedere a tipului . . . . . . . . . . . . . . . . . . . . . . 190 9 ADO.NET (2) 9.1 Obiecte DataAdapter . . . . . . 9.1.1 Metode . . . . . . . . . 9.1.2 Propriet a ti . . . . . . . . 9.2 Clasa DataSet . . . . . . . . . . 9.2.1 Cont inut . . . . . . . . . 9.2.2 Clasa DataTable . . . . 9.2.3 Relat ii ntre tabele . . . 9.2.4 Popularea unui DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 . 193 . 194 . 195 . 195 . 196 . 196 . 199 . 199

CUPRINS

9.3

9.2.5 Propagarea modic arilor c atre baza de date . . . . . . 200 Tranzact ii n ADO.NET . . . . . . . . . . . . . . . . . . . . . 202 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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 execut ie 10.1 Managementul threadurilor . . . . . . . . . . . 10.1.1 Pornirea threadurilor . . . . . . . . . . 10.1.2 Metoda Join() . . . . . . . . . . . . . . 10.1.3 Suspendarea relor de execut ie . . . . . 10.1.4 Omor area threadurilor . . . . . . . . . 10.1.5 Sugerarea priorit a tilor relor de execut ie 10.1.6 Fire n fundal si re n prim-plan . . . . 10.2 Sincronizarea . . . . . . . . . . . . . . . . . . . 10.2.1 Clasa Interlocked . . . . . . . . . . . . . 10.2.2 Instruct iunea lock . . . . . . . . . . . . . 10.2.3 Clasa Monitor . . . . . . . . . . . . . . .

11 ASP.NET 11.1 Anatomia unei pagini ASP.NET . . . . . . . . . . . . . . . 11.1.1 Ad augarea unui control Web . . . . . . . . . . . . . 11.1.2 Ad augarea scriptului inline . . . . . . . . . . . . . 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 a cont inutului 12.2.2 Redirectarea . . . . . . . . . . . . . . Cookies . . . . . . . . . . . . . . . . . . . . Fi sierul de congurare al aplicat iei Web . . . Fi sierul global.asax . . . . . . . . . . . . . . Managementul sesiunii . . . . . . . . . . . . ViewState . . . . . . . . . . . . . . . . . . . 12.7.1 Application . . . . . . . . . . . . . . Alte puncte de interes n aplicat ii ASP.NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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 Generalit a ti . . . . . . . . . . . . . . . . . . . . 13.1.1 Componentele unui WS . . . . . . . . . 13.2 Discut ii asupra blocurilor componente . . . . . . 13.2.1 Protocolul de transport . . . . . . . . . . 13.2.2 Schema de codicare . . . . . . . . . . . 13.2.3 Convent ia 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 a numere . . . . . . . . . 13.3.2 Validarea unui num ar de card de credit . 13.3.3 SW pentru transmitere de cont inut 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 aplicat iile de tip forme Windows, aplicat ii WEB si servicii WEB. Ea const a n trei p art i principale: Common Language Runtime, clase pentru platform a si ASP.NET. O infrastructur a ajut atoare, Microsoft .NET Compact Framework este un set de interfet e de programare care permite dezvoltatorilor realizarea de aplicat ii pentru dispozitive mobile precum telefoane inteligente si PDA-uri1 . .NET Framework constituie un nivel de abstractizare ntre aplicat ie si kernel-ul sistemului de operare (sau alte programe), pentru a asigura portabilitatea codului; de asemenea integreaz a tehnologii care au fost lansate de catre Microsoft incep and cu mijlocul anilor 90 (COM, DCOM, ActiveX, etc) sau tehnologii actuale (servicii Web, XML). Platforma const a c ateva grupe de produse: 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 a cuprinz atoare de clase pentru crearea serviciilor Web (Web Ser2 vices) , aplicat iilor Web (Web Forms) si aplicat iilor Windows (Windows 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

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

10

CURS 1. PLATFORMA MICROSOFT .NET dispozit ie funct ionalit a ti diverse pentru stocarea bazelor de date, email, 3 aplicat ii 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 si o parol a pentru toate. De si nu este omiprezent, multe site-uri l folosesc pentru a u sura accesul utilizatorilor. 4. Dispozitive - noi dispozitive nonPC, programabile prin .NET Compact Framework, o versiune redus a a lui .NET Framework: Pocket PC Phone 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, accentu anduse urm atoarele direct ii: 1. Aplicat iile distribuite - sunt din ce n ce mai numeroase aplicat iile de tip client / server sau cele pe mai multe nivele (ntier). Tehnologiile distribuite actuale cer de multe ori o mare anitate fat a de produc ator si prezint a o carent a acut a a interoper arii cu Web-ul. Viziunea actual a se dep arteaz a de cea de tip client/server c atre una n care calculatoare, dispozitive inteligente si servicii conlucreaz a pentru atingerea scopurilor propuse. Toate acestea se fac deja folosind standarde Internet neproprietare (HTTP, XML, SOAP). 2. Dezvoltarea orientat a pe componente - este de mult timp cerut a simplicarea integr arii componentelor software dezvoltate de diferit i produc atori. COM (Component Object Model) a realizat acest deziderat, dar dezvoltarea si distribuirea aplicat iilor COM este prea complex a. Microsoft .NET pune la dispozit ie un mod mai simplu de a dezvolta si a distribui componente. 3. Modic ari ale paradigmei Web - de-a lungul timpului sau adus mbun at a tiri tehnologiilor Web pentru a simplica dezvoltarea aplicat iilor. In ultimii ani, dezvoltarea aplicat iilor Web sa mutat de la prezentare (HTML si adiacente) c atre capacitate sporit a de programare (XML si SOAP). 4. Alt i factori de maturizare a industriei software - reprezint a con stientizarea 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 a de comert ul electronic ntre client si afacere (Bussiness to Consumer B2C).
3

1.2. ARHITECTURA PLATFORMEI MICROSOFT .NET

11

1.2

Arhitectura platformei Microsoft .NET

Figura 1.1: Arhitectura .NET Figura 1.1 schematizeaz a arhitectura platformei Microsoft .NET. Orice program scris ntr-unul din limbajele .NET este compilat n Microsoft Intermediate Language (MSIL), n concordant a cu Common Language Specication (CLS). Aceste limbaje sunt sprijinite de o bogat a colect ie de biblioteci de clase, ce pun la dispozit ie facilit a ti pentru dezvoltarea de Web Forms, Windows Forms si Web Services. Comunicarea dintre aplicat ii si servicii se face pe baza unor clase de manipulare XML si a datelor, ceea ce sprijin a dezvoltarea aplicat iilor cu arhitectur a n-tier. Base Class Library exist a pentru a asigura funct ionalitate de nivel sc azut, precum operat ii de I/O, re de execut ie, lucrul cu siruri de caractere, comunicat ie prin ret ea, etc. Aceste clase sunt reunite sub numele de .NET Framework Class Library, ce permite dezvoltarea rapid a a aplicat iilor. La baza tuturor se a a cea mai important a component a a lui .NET Framework - Common Language Runtime, care r aspunde de execut ia ec arui program. Mediul de dezvoltare Visual Studio .NET nu este absolut necesar pentru a dezvolta aplicat ii (exist a si alternative open-source pentru el), dar datorit a extensibilit a tii este cel mai

12

CURS 1. PLATFORMA MICROSOFT .NET

bine adaptat pentru crearea aplicat iilor. Evident, nivelul inferior este rezervat sistemului de operare. Trebuie spus c a platforma .NET nu este exclusiv dezvoltat a pentru sistemul de operare Microsoft Windows, ci si pentru arome 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 a ferim utilizatorul de detalii, s a punem la dispozit ia altora o interfat a simpl a, care s a permit a atingerea scopului, f ar a a necesare cunoa sterea tuturor detaliilor. Dac a interfat a r am ane neschimbat a, se pot modica toate detaliile interne, f ar a a afecta act iunile celorlat i beneciari ai codului. In cazul limbajelor de programare, s-a ajuns treptat la crearea unor nivele de abstractizare a codului rezultat la compilare, precum p-code (cel produs de compilatorul Pascal-P) si bytecode (binecunoscut celor care au lucrat n Java). Bytecode-ul Java, generat prin compilarea unui sier surs a, este cod scris ntrun limbaj intermediar care suport a POO. Bytecod-ul este n acela si timp o abstractizare care permite executarea codului Java, indiferent de platforma tint a, at ata timp c at aceast a platform a are implementat a o ma sin a virtual a Java, capabil a s a traduc a mai departe sierul class n cod nativ. Microsoft a realizat si el propria sa abstractizare de limbaj, aceasta numindu-se Common Intermediate Language. De si exist a mai multe limbaje de programare de nivel nalt (C#, Managed C++, Visual Basic .NET, etc), la compilare toate vor produce cod n acela si limbaj intermediar: Microsoft Intermediate Language (MSIL, sau IL pe scurt). Asem an ator cu bytecod-ul, IL are tr as aturi OO, precum abstractizarea datelor, mo stenirea, polimorsmul, sau concepte care s-au dovedit a extrem de necesare, precum except iile sau evenimentele. De remarcat c a aceast a abstractizare de limbaj permite rularea aplicat iilor independent de platform a (cu aceea si condit ie ca la Java: s a existe o ma sin a virtual a pentru acea platform a).

1.3.2

Common Language Specication

Unul din scopurile .NET este de a sprijini integrarea limbajelor astfel nc at programele, de si scrise n diferite limbaje, pot interopera, folosind din plin mo stenirea, polimorsmul, ncapsularea, except iile, etc. Dar limbajele nu
4

www.go-mono.com

1.3. COMPONENTE ALE LUI .NET FRAMEWORK

13

sunt identice: unele suport a supra nc arcarea operatorilor (Managed C++, C#), altele nu (Visual Basic .NET); unele sunt case sensitive, altele nu. Pentru a se asigua totu si interoperabilitatea codului scris n diferite limbaje, Microsoft a publicat Common Language Specication (CLS), un subset al lui CTS (Common Type System, vezi 1.3.4), cont in and specicat ii de reguli necesare pentru integrarea limbajelor. CLS dene ste un set de reguli pentru compilatoarele .NET, asigur and faptul c a ecare compilator va genera cod care interfereaz a cu platforma (mai exact, cu CLRul vezi mai jos) ntr-un mod independent de limbajul surs a. Obiectele si tipurile create n diferite limbaje pot interact iona f ar a probleme suplimentare. Combinat ia CTS/CLS realizeaz a de fapt interoperarea limbajelor. Concret, se poate ca o clas a scris a n C# s a e mo stenit a de o clas a scris a n Visual Basic care arunc a except ii ce sunt prinse de cod scris n C++ sau J#.

1.3.3

Common Language Runtime

CLR este de departe cea mai important a parte component a a lui .NET Framework. Este responsabil cu managementul si execut ia codului scris n limbaje .NET, aat n format IL; este foarte similar cu Java Virtual Machine. CLR instant iaz a obiectele, face veric ari de securitate, depune obiectele n memorie, disponibilizeaz a memoria prin garbage collection. In urma compil arii unei aplicat ii rezult a un sier cu extensia exe, dar 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 c atre CLR, ntocmai cum un sier class este rulat n cadrul JVM. CLR folose ste tehnologia compil arii JIT - o implementare de ma sin a virtual a, n care o metod a sau o funct ie, n momentul n care este apelat a pentru prima oar a, este tradus a n cod ma sin a. Codul translatat este depus ntr-un cache, evit and-se astfel recompilarea ulterioar a. Exist a 3 tipuri de compilatoare JIT: 1. Normal JIT - a se vedea descrierea de mai sus. 2. Pre-JIT - compileaz a ntregul cod n cod nativ singur a dat a. In mod normal este folosit la instal ari. 3. Econo-JIT - se folo se ste pe dispozitive cu resurse limitate. Compileaz a codul IL bit cu bit, eliber and resursele folosite de codul nativ ce este stocat n cache.

14

CURS 1. PLATFORMA MICROSOFT .NET

In esent a, activitatea unui compilator JIT este destinat a a mbun at a ti performant a execut iei, ca alternativ a la compilarea repetat a a aceleia si buc a ti de cod n cazul unor apel ari multiple. Unul din avantajele mecanismului JIT apare n clipa n care codul, o dat a ce a fost compilat, se execut a pe diverse procesoare; dac a ma sina virtual a este bine adaptat a la noua platform a, atunci acest cod va benecia de toate optimiz arile posibile, f ar a a mai nevoie recompilarea lui (precum n C++, de exemplu).

1.3.4

Common Type System

Pentru a asigura interoperabilitatea limbajelor din .NET Framework, o clas a scris a n C# trebuie s a e echivalent a cu una scris a n VB.NET, o interfat a scris a n Managed C++ trebuie s a e perfect utilizabil a n Managed Cobol. Toate limbajele care fac parte din pleiada .NET trebuie s a aibe un set comun de concepte pentru a putea integrate. Modul n care acest deziderat s-a transformat n realitate se nume ste Common Type System (CTS); orice limbaj trebuie s a recunoasc a si s a poat a manipula ni ste tipuri comune. O scurt a descriere a unor facilit a ti comune (ce vor exhaustiv enumerate si tratate n cadrul prezent arii limbajului C#): 1. Tipuri valoare - n general, CLR-ul (care se ocup a de managementul si execut ia codului IL, vezi mai jos) suport a dou a tipuri diferite: tipuri valoare si tipuri referint a. Tipurile valoare reprezint a tipuri alocate pe stiv a si nu pot avea valoare de null. Tipurile valoare includ tipurile primitive, structuri si enumer ari. Datorit a faptului c a de regul a au dimensiuni mici si sunt alocate pe stiv a, se manipuleaza ecient, reduc and overhead-ul cerut de mecanismul de garbage collection. 2. Tipuri referint a - se folosesc dac a variabilele de un anumit tip cer resurse de memorie semnicative. Variabilele de tip referint a cont in adrese de memorie heap si pot null. Transferul parametrilor se face rapid, dar referint ele induc un cost suplimentar datorit a mecanismului de garbage collection. 3. Boxing si unboxing - motivul pentru care exist a tipuri primitive este acela si ca si n Java: performant a. Ins a orice variabil a n .NET este compatibil a cu clasa Object, r ad acina ierarhiei existente n .NET. De exemplu, int este un alias pentru System.Int32, care se deriveaz a din System.ValueType. Tipurile valoare se stocheaz a pe stiv a, dar pot oric and convertite ntr-un tip referint a memorat n heap; acest mecanism se nume ste boxing. De exemplu:

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

15

C and se face boxing, se obt ine un obiect care poate gestionat la fel ca oricare altul, f ac anduse abstract ie de originea lui. Inversa boxing-ului este unboxing-ul, prin care se poate converti un obiect n tipul valoare echivalent, ca mai jos: int j = (int)box; unde operatorul de conversie este sucient pentru a converti de la un obiect la o variabil a de tip valoare. 4. Clase, propriet a ti, indexatori - platforma .NET suport a pe deplin programarea orientat a pe obiecte, concepte legate de obiecte ( ncapsularea, mo stenirea, polimorsmul) sau tr as aturi legate de clase (metode, c ampuri, membri statici, vizibilitate, accesibilitate, tipuri interioare, etc). De asemenea se includ tr as aturi precum propriet a ti, indexatori, evenimente. 5. Interfet e - reprezint a acela si concept precum clasele abstracte din C++ (dar cont in and doar funct ii virtuale pure), sau interfet ele Java. O clas a care se deriveaz a dintr-o interfat a trebuie s a implementeze toate metodele acelei interfet e. Se permite implementarea simultan a a mai multor interfet e ( n rest mo stenirea claselor este simpl a). 6. Delegat i - inspirat i de pointerii la funct ii din C, ce permit programarea generic a. Reprezint a versiunea sigur a a pointerilor c atre funct ii din C/C++ si sunt mecanismul prin care se trateaz a evenimentele. Exist a numeroase componente ale lui CLR care l fac cea mai important a parte a lui .NET Framework: metadata, assemblies, assembly cache, reection, garbage collection.

1.3.5

Metadata

Metadata nseamn a n general date despre date. In cazul .NET, ea reprezint a informat ii destinate a citite de c atre ma sin a, nu de utilizatorul uman. Este stocat a mpreun a cu codul pe care l descrie. Pe baza metadatei, CLR stie cum s a instant ieze obiectele, cum s a le apeleze metodele, cum s a acceseze propriet a tile. Printr-un mecanism numit reection, o aplicat ie (nu

16

CURS 1. PLATFORMA MICROSOFT .NET

neap arat CLR) poate s a interogheze aceast a metadat a si s a ae ce expune un obiect. Mai pe larg, metadata cont ine o declarat ie a ec arui tip si c ate o declarat ie pentru ecare metod a, c amp, proprietate, eveniment al tipului respectiv. Pentru ecare metod a implementat a, metadata cont ine informat ie care permite nc arc atorului clasei respective s a localizeze corpul metodei. De asemena mai poate cont ine declarat ii despre cultura aplicat iei respective, adic a despre localizarea ei (limba folosit a n partea de interfat a utilizator). Mecanismul prin care aceste informat ii se folosesc pe parcursul rul arii de c atre aplicat ii se nume ste reection.

1.3.6

Assemblies

Un assembly reprezint a un bloc funct ional al unei aplicat ii .NET. El formeaz a unitatea fundamental a de distribuire, versionare, reutilizare si permisiuni de securitate. La runtime, un tip de date exist a n interiorul unui assembly ( si nu poate exista n exteriorul acestuia). Un assembly cont ine metadate care sunt folosite de c atre CLR. Scopul acestor assemblies este s a se asigure dezvoltarea softului n mod plug-and-play. Dar metadatele nu sunt suciente pentru acest lucru; mai sunt necesare si manifestele. Un manifest reprezint a metadate despre assembly-ul care g azduie ste tipurile de date. Cont ine numele assembly-ului, informat ia despre versiune, referiri la alte assemblies, o list a a tipurilor n assembly, permisiuni de securitate si altele. Un assembly care este mp art it ntre mai multe aplicat ii are de asemenea un shared name. Aceast a informat ie care este unic a este opt ional a, neap ar and n manifestul unui assembly daca acesta nu a fost g andit ca o aplicat ie partajat a. Deoarece un assembly cont ine date care l descriu, instalarea lui poate f acut a copiind assemblyul n directorul destinat ie dorit. C and se dore ste rularea unei aplicat ii cont inute n assembly, maniferstul va instrui mediul .NET despre modulele care sunt cont inute n assembly. Sunt folosite de asemenea si referint ele c atre orice assembly extern de care are nevoie aplicat ia. Versionarea este un aspect deosebit de important pentru a se evita a sa numitul DLL Hell. Scenariile precedente erau de tipul: se instaleaza o aplicat ie care aduce ni ste siere .dll necesare pentru functionare. Ulterior, o alt a aplicat ie care se instaleaz a suprascrie aceste siere (sau m acar unul din ele) cu o versiune mai nou a, dar cu care vechea aplicat ie nu mai funct ioneaz a corespunz ator. Reinstalarea vechii aplicat ii nu rezolv a problema, deoarece a doua aplicat ie nu va funct iona. De si sierele dll cont in informat ie relativ la versiune n interiorul lor, ea nu este folosit a de c atre sistemul de operare,

ATURI 1.4. TRAS ALE PLATFORMEI .NET

17

ceea ce duce la probleme. O solut ie la aceast a dilem a ar instalarea sierelor dll n directorul aplicat iei, dar n acest mod ar disp area reutilizarea acestor biblioteci.

1.3.7

Assembly cache

Assembly cache este un director aat n mod normal n directorul \Winnt \Assemblies. Atunci c and un assembly este instalat pe o ma sin a, el va ad augat n assembly cache. Dac a un assembly este desc arcat de pe Internet, el va stocat n acest assembly cache, ntr-o zon a tranzient a. Aplicat iile instalate vor avea assemblies ntr-un assembly cache global. In acest assembly cache vor exista versiuni multiple ale aceluia si assembly. Dac a programul de instalare este scris corect, va evita suprascrierea assembly-urilor deja existente ( si care funct ioneaz a perfect cu acplicat iile instalate), ad aug and doar noul assembly. Este un mod de rezolvare a problemei DLL Hell, unde suprascrierea unei biblioteci dinamice cu o variant a mai nou a putea duce la nefunct ionarea corespunz atoare a aplicat iilor anterior instalate. CLR este cel care decide, pe baza informat iilor din manifest, care este versiunea corect a de assembly de care o aplicat ie are nevoie. Acest mecanism pune cap at unei epoci de trist a amintire pentru programatori si utilizatori.

1.3.8

Garbage collection

Managementul memoriei este una din sarcinile cele mai consumatoare de resurse umane. Garbage collection este mecanismul care se declan seaz a atunci c and alocatorul de memorie r aspunde negativ la o cerere de alocare de memorie. Implementarea este de tip mark and sweep: se presupune init ial c a toat a memoria alocat a se poate disponibiliza, dupa care se determin a care din obiecte sunt referite de variabilele aplicat iei; cele care nu mai sunt referite sunt dealocate, celelalte zone de memorie sunt compactate. Obiectele a c aror dimensiune de memorie este mai mare dec at un anumit prag nu mai sunt mutate, pentru a nu cre ste semnicativ penalizarea de performant a. In general, CLR este cel care se ocup a de apelarea mecanismului de garbage collection. Totu si, la dorint a, programatorul poate cere rularea lui.

1.4

Tr as aturi ale platformei .NET

Prin prisma celor prezentate p ana acum putem rezuma urm atoarele tr as aturi:

18

CURS 1. PLATFORMA MICROSOFT .NET Dezvoltarea multilimbaj: Deoarece exist a mai multe limbaje pentru aceast a platform a, este mai u sor de implementat p art i specice n limbajele cele mai adecvate. Numarul limbajelor curent implementate este mai mare dec at 10. Aceast a dezvoltare are n vedere si debugging-ul aplicat iilor dezvoltate n mai multe limbaje. Independent a de procesor si de platform a: IL este independent de procesor. O dat a scris a si compilat a, orice aplicat ie .NET (al c arei management este f acut de c atre CLR) poate rulat a pe orice platform a. Datorit a CLR-ului, aplicat ia este izolat a de particularit a tile hardware sau ale sistemului de operare. Managementul automat al memoriei: Problemele de tipul memory leakage nu mai trebuie s a preocupe programatorii; overhead-ul indus de c atre mecanismul de garbage collection este suportabil, ind implementat n sisteme mult mai timpurii. Suportul pentru versionare: Ca o lect ie nv a tat a din perioada de DLL Hell, versionarea este acum un aspect de care se tine cont. Dac a o aplicat ie a fost dezvoltat a si testat a folosind anumite componente, instalarea unei componente de versiune mai nou a nu va atenta la buna funct ionare a aplicat iei n discut ie: cele dou a versiuni vor coexista pa snic, alegerea lor ind f acut a pe baza manifestelor. Sprijinirea standardelor deschise: Nu toate dispozitivele ruleaz a sisteme de operare Microsoft sau folosesc procesoare Intel. Din aceast a cauz a orice este str ans legat de acestea este evitat. Se folose ste XML si cel mai vizibil descendent al acestuia, SOAP. Deoarece SOAP este un protocol simplu, bazat pe text, foarte asem an ator cu HTTP5 , el poate trece u sor de rewall-uri, spre deosebire de DCOM sau CORBA. Distribuirea u soar a: Actualmente instalarea unei aplicat ii sub Windows nseamn a copierea unor siere n ni ste directoare anume, modicarea unor valori n regi stri, instalare de componente COM, etc. Dezinstalarea complet a a unei aplicat ii este in majoritatea cazurilor o utopie. Aplicat iile .NET, datorit a metadatelor si reect arii trec de aceste probleme. Se dore ste ca instalarea unei aplicat ii s a nu nsemne mai mult dec at copierea sierelor necesare ntr-un director, iar dezinstalarea aplicat iei s a se fac a prin stergerea acelui director. Arhitectur a distribuit a: Noua losoe este de a asigura accesul la servicii Web distribuite; acestea conlucreaz a la obt inerea informat iei
5

Folosit la transmiterea paginilor Web pe Internet

ATURI 1.4. TRAS ALE PLATFORMEI .NET

19

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

20

CURS 1. PLATFORMA MICROSOFT .NET

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

C#1 este un limbaj de programare modern, simplu, obiectorientat. Este foarte asem an ator cu Java si C++, motiv pentru care curba de nv a tare este foarte lin a. Un prim exemplu de program, care cont ine o serie de elemente ce se vor nt alni n continuare, este: using System; class HelloWorld { public static void Main() { Console.WriteLine(Hello world!); } } Prima linie using System; este o directiv a care specic a faptul c a se vor 2 folosi clasele care sunt incluse n spat iul de nume System ; un spat iu de nume este o colect ie de tipuri sau o grupare de alte spat ii de nume care pot
1 2

# se pronunt a sharp Eng: namespace

21

22

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

folosite ntr-un program (detalii vor date mai t arziu). In cazul de fat a, clasa care este folosit a din acest spat iu de nume este Console. Mai departe, orice program este cont inut ntro clas a, n cazul nostru HelloWorld. Punctul de intrare n aplicat ie este metoda Main, care nu preia n acest exemplu nici un argument din linia de comand a si nu returneaz a explicit un indicator de stare a termin arii. Pentru operat iile de intrareie sire cu consola, se folose ste clasa Console din spat iul de nume System ; pentru aceast a clas a se apeleaz a metoda static a WriteLine, care a seaz a pe ecran mesajul, dup a care face trecerea la linie nou a. O variant a u sor modicat a a programului de mai sus, n care se salut a persoanele ale c aror nume este transmis prin linia de comand 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]); } } } In exemplul de mai sus metoda principal a preia o list a de parametri transmi si din linia de comand a (un sir de obiecte de tip String ) si va a sa pentru ecare nume Hello urmat de numele de indice i (numerotarea parametrilor ncepe de la 0). Construct ia {0} va nlocuit a cu primul argument care urmeaz a dup a Hello {0}. La executarea programului de mai sus n forma: HelloWorld Ana Dan, se va a sa pe ecran: Hello Ana Hello Dan Metoda Main poate s a returneze o valoare ntreag a, care s a e folosit a de c atre sistemul de operare pentru a semnala dac a procesul sa ncheiat cu succes sau nu3 . Ment ion am faptul c a limbajul este casesensitive4 . Ca metode de notare Microsoft recomand a folosirea urm atoarelor dou a convent ii:
3 4

De exemplu n siere de comenzi prin testarea variabilei de mediu ERRORLEVEL Face distinct ie ntre litere mari si mici

2.2. TIPURI DE DATE

23

Conventie Pascal, n care prima liter a a ec arui cuv ant se scrie ca liter a mare convent ia tip c amil a este la fel ca precedenta, dar primul caracter al primului cuv ant nu este scris ca liter a mare. In general, convent ia tip Pascal este folosit a pentru tot ce este vizibil din exteriorul unei clase, precum clase, metode, propriet a ti, etc. Doar parametrii metodelor se scriu cu convent ia c amil a. Se recomand a evitarea folosirii notat iei ungare (numele unei entit a ti trebuie s a se refere la semantica ei, nu la tipul de reprezentare). Cuvintele cheie (cuvinte rezervate care nu pot folosite ca si identicatori) din C# sunt date n tabelul 2.1: Tabelul 2.1: Cuvinte cheie n C# 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 a dou a tipuri de date: tipuri valoare si tipuri referint a. Tipu5 rile valoare includ tipurile simple (ex. char, int, oat ) , tipurile enumerare
5

De fapt acestea sunt structuri, prezentate n alt curs

24

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

si structur a si au ca principale caracteristici faptul c a ele cont in direct datele referite si sunt alocate pe stiv a sau inline ntrun struct. Tipurile referint a includ tipurile clas a, interfat a, delegat si tablou, toate av and proprietatea c a variabilele de acest tip stocheaz a referint e c atre obiecte. Demn de remarcat este c a toate tipurile de date sunt derivate (direct sau nu) din tipul System.Object, pun and astfel la dispozit ie un mod unitar de tratare a lor.

2.2.1

Tipuri predenite

C# cont ine un set de tipuri predenite, pentru care nu este necesar a includerea vreunui spat iu de nume via directiva using: string, object, tipurile ntregi cu semn si f ar a semn, tipuri numerice n virgul a mobil a, tipurile bool si decimal. Tipul string este folosit pentru manipularea sirurilor de caractere codicate Unicode; cont inutul obiectelor de tip string nu se poate modi6 ca . Clasa object este r ad acina ierarhiei de clase din .NET, la care orice tip (inclusiv un tip valoare) poate convertit. Tipul bool este folosit pentru a reprezenta valorile logice true si false. Tipul char este folosit pentru a reprezenta caractere Unicode, reprezentate pe 16 bit i. Tipul decimal este folosit pentru calcule n care erorile determinate de reprezentarea n virgul a mobil a sunt inacceptabile, el pun and la dispozit ie 28 de cifre zecimale semnicative. Tabelul de mai jos cont ine lista tipurilor predenite, ar at and totodat a cum se scriu valorile corespunz atoare: Tabelul 2.2: Tipuri predenite.

Tip object string sbyte short int long byte ushort uint

Descriere r adacina oric arui tip o secvent a de caractere Unicode tip ntreg cu semn, pe 8 bit i tip ntreg cu semn, pe 16 bit i tip ntreg cu semn, pe 16 bit i tip ntreg cu semn, pe 64 bit i tip ntreg f ar a semn, pe 8 bit i tip ntreg f ar a semn, pe 16 bit i tip ntreg f ar a semn, pe 32 bit i

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 a este invariabil - engl. immutable

2.2. TIPURI DE DATE Tabelul 2.2 (continuare) Tip Descriere Exemplu ulong tip ntreg f ar a semn, pe 64 de bit i ulong val1=12; ulong val2=34U; ulong val3=56L; ulong va;4=76UL; oat tip cu virgul a mobil a, simpl a precizie oat val=1.23F; double tip n virgul a mobil a, dubl a precizie 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 dispozit ie 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 dispozit ie tipuri valoare, care sunt e structuri, e enumer ari. Exist a un set predenit de structuri numite tipuri simple, identicate prin cuvinte rezervate. Un tip simplu este e de tip numeric7 , e boolean. Tipurile numerice sunt tipuri ntregi, n virgul a mobil a sau decimal. Tipurile intregi sunt sbyte, byte, short, ushort, int, uint, long, ulong, char; cele n virgul a mobil a sunt oat si double. Tipurile enumerare se pot deni de c atre utilizator. Toate tipurile valoare deriveaz a din clasa System.ValueType, care la r andul ei este derivat a din clasa object (alias pentru System.Object). Nu este posibil ca dintrun tip valoare s a se deriveze. O variabil a de tip valoare ntotdeauna trebuie s a cont in a o valoare (nu poate null, spre deosebire de tipurile referint a). Atribuirea pentru un astfel de tip nseamn a copierea valorii (clonarea) dintro parte n alta. Structuri Un tip structur a este un tip valoare care poate s a cont in a declarat ii de constante, c ampuri, metode, propriet a ti, indexatori, operatori, constructori sau tipuri imbricate. Vor descrise n ntro sect iune urm atoare.
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 corespunz atoare din spat iul de nume System; corespondent a este dat a n tabelul de mai jos: Tabelul 2.3: Tipuri simple si corespondent ele lor cu tipurile din spat iul de nume System. Tabelul 2.3 Cuv ant rezervat Tipul alias 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, urm atoarele declarat ii sunt legale: 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 a nou a tipuri ntregi: sbyte, byte, short, ushort, int, uint, long, ulong si char. Acestea au urm atoarele dimensiuni si domeniu de valori: sbyte reprezint a tip cu semn pe 8 bit i, cu valori de la -128 la 127;

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

27

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

28

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

In cazul n care o operat ie aritmetic a produce un rezultat care nu poate reprezentat n tipul destinat ie, comportamentul depinde de utilizarea operatorilor sau a declarat iilor checked si unchecked (care se pot utiliza n surs a sau din linia de compilare): n context checked, o eroare de dep a sire duce la arun carea unei except ii de tip System.OverowException. In context unchecked, eroarea de dep a sire este ignorat a, iar bit ii semnicativi care nu mai ncap n reprezentare sunt eliminat i. 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 cont in operatorii ++, - -, +, - (unar si binar), *, / si care nu sunt cont inute n interiorul unui bloc de tip checked, comportamentul este specicat prin intermediul opt iunii /checked[+|] dat din linia de comand a pentru compilator. Tipuri n virgul a mobil a Sunt prezente 2 tipuri numerice n virgul a mobil a: oat si double. Tipurile sunt reprezentate folosind precizie de 32, respectivi 64 de bit i, folosind formatul IEC 60559, care permit reprezentarea valorilor de 0 pozitiv si 0 negativ (se comport a la fel, dar anumite operat ii duc la obt inerea acestor dou a valori), + si (obt inute prin mp art irea unui num ar strict pozitiv, respectiv strict negativ la 0), a valorii NotaNumber (NaN) inut a (obt prin operat ii n virgul a mobil a invalide, de exemplu 0/0 sau 1), precum si un set nit de numere8 . Tipul oat poate reprezenta valori cuprinse ntre 45 38 1.5 10 si 3.4 10 ( si din domeniul negativ corespunz ator), cu o precizie de 7 cifre. Double poate reprezenta valori cuprinse ntre 5.0 10324 si 308 1.7 10 cu o precizie de 15-16 cifre. Operat iile cu oating point nu duc niciodat a la aparit ia de except ii, dar ele pot duce, n caz de operat ii invalide, la valori 0, innit sau NaN.
8

Pentru detalii vezi [4], pag. 93.

2.2. TIPURI DE DATE

29

Literalii care specic a un num ar reprezentat n virgul a mobil a au forma: 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 a nici un sux de tip real nu este specicat, atunci literalul dat este de tip double. Suxul f sau F specic a tip oat, d sau D specic a double. Dac a literalul specicat nu poate reprezentat n tipul precizat, apare eroare de compilare. Tipul decimal Este un tip de date reprezentat pe 128 de bit i, g andit a folosit n calcule nanciare sau care necesit a precizie mai mare. Poate reprezenta valori aate n intervalul 1.0 1028 si 7.9 1028 , cu 28 de cifre semnicative. Acest tip nu poate reprezenta zero cu semn, innit sau NaN. Dac a n urma operat iilor, un num ar este prea mic pentru a putea reprezentat ca decimal, atunci el este f acut 0, iar dac a este prea mare, rezult a o except ie. Diferent a principal a fat a de tipurile n virgul a mobil a este c a are o precizie mai mare, dar un domeniu de reprezentare mai mic. Din cauza aceasta, nu se fac conversii implicite ntre nici un tip n virgul a mobil a si decimal si nu este posibil a mixarea variabilelor de acest tip ntr-o expresie, f ar a conversii explicite. Literalii de acest tip se exprim a folosind ca sux-de-tip-real caracterele m sau M. Dac a valoarea specicat a nu poate reprezentat a prin tipul decimal, apare o eroare la compilare. Tipul bool Este folosit pentru reprezentarea valorilor de adev ar true si false. Literalii care se pot folosi sunt true si false. Nu exist a conversii standard ntre bool si nici un alt tip.

2.2.3

Tipul enumerare

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

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 n enum. Ca atare, programatorul trebuie s a fac a valid ari suplimentare pentru a determina consistent a valorilor. In cazul de fat a, la apelul de metod a se arunc a o except ie (except iile vor tratate pe larg ntr-un curs viitor). Ca si mod de scriere a enumer arilor, se sugereaz a folosirea convent iei Pascal at at pentru numele tipului c at si pentru numele valorilor cont inute, precum si evitarea folosirii prexelor sau suxelor pentru acestea. Enumer arile nu pot declarate abstracte si nu pot derivate. Orice enum este derivat automat din System.Enum, care este la r andul lui derivat din System.ValueType); astfel, metodele mo stenite de la tipurile p arinte sunt utilizabile de c atre orice variabil a de tip enum. Fiecare tip enumerare care este folosit are un tip de reprezentare9 , pentru a se cunoa ste c at spat iu de memorie trebuie s a e alocat unei variabile de acest tip. Dac a nu se specic a nici un tip de reprezentare (ca mai sus), atunci se presupune implicit tipul int. Specicarea unui tip de reprezentare (care poate orice tip integral, except and tipul char) se face prin enunt area tipului dup a numele enumer arii: enum MyEnum : byte { small, large } Specicarea este folosit a atunci c and dimensiunea n memorie este important a, sau c and se dore ste crearea unui tip de indicator (un tip ag) al c arui num ar de st ari difer a de num arul de bit i alocat i tipului int (modelare de 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; ... In mod implicit, valoarea primului membru al unei structuri este 0, si ecare variabl a care urmeaz a are valoarea mai mare cu o unitate dec at precedenta. La dorint a, valoarea ec arei variabile poate specicat a explicit: enum Values { a = 1, b = 2, c = a + b } Urm atoarele observat ii se impun relativ la lucrul cu tipul enumerare: 1. valorile specicate ca init ializatori trebuie s a e reprezentabile prin tipul de reprezentare a enumer arii, altfel apare o eroare la compilare: enum Out : byte { A = -1 }//eroare semnalata la compilare 2. mai mult i membri pot avea aceea si valoare (manevr a dictat a de semantica tipului construit): enum ExamState { passed = 10, failed = 1, rejected = failed } 3. dac a pentru un membru nu este dat a o valoare, acesta va lua valoarea membrului precedent + 1 (cu except ia primului membru vezi mai sus)

2.2. TIPURI DE DATE 4. nu se permit referint e 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 a cont in a un membru cu valoarea 0, pentru c a n anumite contexte valoarea implicit a pentru o variabil a enum este 0, ceea ce poate duce la inconsistent e si bug-uri greu de depanat enum Months { InvalidMonth,//are valoarea implicita 0, fiind primul element January, February, //etc } Tipurile enumerare pot convertite c atre tipul lor de baz a si napoi, folosind o conversie explicit a (cast): 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 a c atre un enum f ar a cast:

34

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

Urm atorul cod arat a c ateva din articiile care pot aplicate tipului enumerare: obt inerea tipului unui element de tip enumerare precum si a tipului clasei de baz a, a tipului de reprezentare, a valorilor cont inute (ca nume simbolice si ca valori), conversie de la un string la un tip enumerare 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

In cele mai multe cazuri nu se cunoa ste aprioric num arul de obiecte care se vor folosi la momentul rul arii. O solut ie pentru stocarea elementelor av and acela si tip o reprezint a tablourile. Sintaxa de declarare este asem an atoare cu cea din Java sau C++, dar ecare tablou este un obiect, derivat din clasa abstract a System.Array. Accesul la elemente se face prin intermediul indicilor care ncep de la 0 si se termin a la num arul de elemente-1; orice dep a sire a indicilor duce la aparit ia unei except ii: System.IndexOutOfRangeException. O variabil a de tip tablou poate avea valoare de null sau poate s a indice c atre o instant a valid a.

2.3.1

Tablouri unidimensionale

Declararea unui tablou unidimensional se face prin plasarea de paranteze drepte ntre numele tipului tabloului si numele s au, ca mai jos10 : int[] sir; Declararea de mai sus nu duce la alocare de spat iu pentru memorarea sirului; instat ierea 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 a nu se poate 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

In acest exemplu se folose ste proprietatea11 Length, care returneaz a num arul tuturor elementelor vectorului (lucru vizibil la tablourile multidimensionale rectangulare). De ment ionat c a n acest context n si sir nu se pot declara la un loc, adic a declarat ii de genul int[] sir, n; sau int n, []sir; sunt incorecte (prima este corect a din punct de vedere sintactic, dar ar rezulta c a n este si el un tablou; n al doilea caz, declarat ia nu este corect a sintactic). Se pot face init ializ ari ale valorilor cont inute ntrun tablou: int[] a = new int[] {1,2,3}; sau n forma mai scurt a: int[] a = {1,2,3};

2.3.2

Tablouri multidimensionale

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

Pentru not iunea 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. Instant ierea se face: tab = new int[2,3]; rezult and un tablou cu 2 linii si 3 coloane. Referirea la elementul aat pe linia i si coloana j se face cu tab[i, j ]. La declararea tabloului se poate face si init ializare: 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 a ce se a seaz a tabla nmult irii p an a la 10, se va a sa: tabInm.Length=100, deoarece proprietatea Length d a num arul total de elemente aat n tablou (pe toate dimensiunile). Am folosit ns a metoda GetLength(d) care returneaz a

2.3. TABLOURI

39

num arul de elemente aate pe dimensiunea num arul d (num ararea dimensiunilor ncepe cu 0). Determinarea num arului de dimensiuni pentru un tablou rectangular la runtime se face folosind proprietatea Rank a clasei de baz a System.Array. 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 ap area: t1.Rank=1 t2.Rank=2 t3.Rank=3 Tablouri neregulate Un tablou neregulat13 reprezint a un tablou de tabouri. Declararea unui tablou neregulat cu dou a dimensiuni se face ca mai jos: int[][] tab; Referirea la elementul de indici i si j se face prin tab[i][j]. 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 a sat a se explic a prin faptul c a un tablou neregulat este un vector care cont ine referint e, deci este unidimensional. Init ializarea 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. S IRURI DE CARACTERE

41

2.4

S iruri de caractere

Tipul de date folosit pentru reprezentarea sirurilor de caractere este clasa System.String (pentru care se poate folosi aliasul string; reamintim c a este un tip predenit). Obiectele de acest tip sunt imutabile (caracterele cont inute nu se pot schimba, dar pe baza unui sir se poate obt ine un alt sir). S irurile pot cont ine secvent e escape si pot de dou a tipuri: regulate si de tip verbatim14 . S irurile regulate sunt demarcate prin ghilimele si necesit a secvent e escape pentru reprezentarea caracterelor escape. Exemplu: String a = "string literal"; String versuri = "vers1\nvers2"; String caleCompleta = "\\\\minimax\\protect\\csharp"; Pentru situat ia n care se utilizeaz a masiv secvent e escape, se pot folosi sirurile verbatim. Un literal de acest tip are simbolul @ naintea ghilimelelor de nceput. Pentru cazul n care ghilimelele sunt nt alnite n interiorul sirului, ele se vor dubla. Un sir de caractere poate reprezentat pe mai multe r anduri f ar a a folosi caracterul \n. S irurile verbatim sunt folosite pentru a face referiri la siere sau chei n regi stri, sau pentru prelucrarea sierelor. 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 == si != pentru dou a siruri de caractere se comport a n felul urm ator: dou a siruri de caractere se consider a egale dac a sunt e am andou a null, e au aceea si lungime si caracterele de pe acelea si pozit ii coincid; != d a negarea relat iei de egalitate. Pentru a se compara dou a siruri din punct de vedere al referint elor, trebuie ca m acar unul dintre obiecte s a e convertit la object: 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 ie sire se va a sa: a==b: true (object)a==b: false Alt operator care se poate utiliza este +, reprezent and concatenarea: String s = "Salut" + " " + "lume"; s += "!!!!"; Clasa String pune la dispozit ie metode pentru: comparare (Compare, CompareOrdinal, CompareTo), c autare (EndsWith, StartsWith, IndexOf, LastIndexOf), modicare (a se nt elege obt inerea altor obiecte pe baza celui curent - Concat, CopyTo, Insert, Join, PadLeft, PadRight, Remove, Replace, Split, Substring, ToLower, ToUpper, Trim, TrimEnd, TrimStart)15 . Accesarea unui caracter aat pe o pozit ie i a unui sir s se face prin folosirea parantezelor drepte, cu acelea si restrict ii asupra indicelui ca si pentru tablouri: s[i]. In clasa object se a a metoda ToString() care este suprascris a n ecare clas a ale c arei instant e pot tip arite. Pentru obt inerea unei reprezent ari diferite se folose ste metoda String.Format(). Un exemplu de folosire a funct iei Split() pentru desp art irea unui sir n funct ie 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. S IRURI DE CARACTERE } } } va a sa pe ecran: Token: Token: Token: Token: Token: Token: Token: Oh I hadnt thought of that!

43

De remarcat c a pentru caracterul apostrof nu este obligatorie secvent a escape n cazul sirurilor de caractere. Al doilea lucru care trebuie explicat este c a al doilea token este cuv antul vid, care apare ntre cei doi separatori al aturat i: virgula si spat iul. Metoda Split() nu face gruparea mai multor separatori, lucru care ar de dorit n prezent a a doi separatori al aturat i. Pentru aceasta se folosesc expresii regulate. Pentru a lucra cu siruri de caractere care permit modicarea lor se folose ste clasa StringBuilder, din namespaceul System.Text.

2.4.1

Expresii regulate

In cazul n care funct iile din clasa String nu sunt sucient de puternice, namespaceul System.Text.RegularExpresions pune la dispozit ie o clas a de lucru cu expresii regulate numit a Regex. Expresiile regulate reprezint a o metod a extrem de facil a de a opera c aut ari/ nlocuiri pe text. Forma expresiilor regulate este cea din limbajul Perl. Aceast a clas a folose ste o tehnic a interesant a pentru m arirea performan telor: dac a programatorul vrea, se scrie o secvent a din mers pentru a implementa potrivirea expresiei regulate, dup a care codul este rulat16 . Exemplul anterior poate rescris corect din puct de vedere al funct ionalit a tii prin folosirea unei expresii regulate, pentru a prinde si cazul separatorilor multipli adiacent i: class ExpresieRegulata { static void Main(string[] args) {
16

Codul este scris direct n IL.

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 generalit a ti. Instruct iuni. Spat ii de nume


3.1 Clase vedere general a

Clasele reprezint a tipuri referint a denite de utilizator. O clas a poate s a mo steneasc a o singur a clas a si poate implementa mai multe interfet e. Clasele pot cont ine constante, c ampuri, metode, propriet a ti, evenimente, indexatori, operatori, constructori de instant a, destructori, constructori de clas a, tipuri imbricate. Fiecare membru poate cont ine un nivel de accesare, care controleaz a gradul de acces la el. O descriere este dat a n tabelul 3.1: Tabelul 3.1: Modicatori de acces ai membrilor unei clase Accesor public protected Semnicat ie Acces nelimitat Acces limitat la clasa cont in atoare 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, INSTRUCT IUNI, SPAT II 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 a o valoare constant a, care poate evaluat a la compilare. Constantele pot depinde de alte constante, at ata timp c at nu se creeaz a dependent e circulare. Ele sunt considerate automat membri statici (dar este interzis s a se foloseasc a specicatorul static n fat a lor). Ele pot accesate exclusiv prin intermediul numelui de clas a (MyClass.MyConst), si nu prin intermediul vreunei instant e (a.MyConst). C ampul este un membru reprezent and o variabil a asociat a unui obiect.

48

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

Metoda este un membru care implementeaz a un calcul sau o act iune care poate efectuat a asupra unui obiect sau asupra unei clase. Metodele statice (care au n antet cuv antul cheie static) sunt accesate prin intermediul numelui de clas a, pe c and cele nestatice (metode instant a) sunt apelate prin intermediul unui obiect. Proprietatea este un membru care d a acces la o caracteristic a a unui obiect sau unei clase. Exemplele folosite p an a acum includeau lungimea unui vector, num arul de caractere ale unui sir de caractere, etc. Sintaxa pentru accesara c ampurilor si a propriet a tilor este aceea si. Reprezint a o alt a modalitate de implementare a accesorilor pentru obiecte. Evenimentul este un membru care permite unei clase sau unui obiect s a pun a la dispozit ia altora notic ari asupra evenimentelor. Tipul acestei declarat ii trebuie s a e un tip delegat. O instant a a unui tip delegat ncapsuleaz a una sau mai multe entit a ti apelabile. Exemplu: 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 a Button1 Click ca tratare de eveniment1 pentru evenimentul Click al lui Button1. In metoda Disconnect(), acest event handler este nl aturat. Operatorul este un membru care dene ste semnicat ia (supra nc arcarea) unui operator care se aplic a instant elor unei clase. Se pot supra nc arca operatorii binari, unari si de conversie. Indexatorul este un membru care permite unui obiect s a e indexat n acela si mod ca un tablou (pentru programatorii C++: supra nc arcarea operatorului []). Constructorii instant a sunt membri care implementeaz a act iuni cerute pentru init ializarea ec arui obiect. Destructorul este un membru special care implementeaz a act iunile cerute pentru a distruge o instant a a unei clase. Destructorul nu are parametri, nu poate avea modicatori de acces, nu poate apelat i explicit si poate apelat automat de c atre garbage collector. Constructorul static este un membru care implementeaz a act iuni necesare pentru a init ializa o clas a, mai exact membrii statici ai clasei. Nu pot avea parametri, nu pot avea modicatori de acces, nu sunt apelat i explicit, ci automat de c atre sistem. Mo stenirea este de tip simplu, iar r ad acina ierarhiei este clasa object (alias System.Object).

3.2

Transmiterea de parametri

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

Engl: event handler

50

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

valoarea parametrului actual transmis, iar la revenire din metod a aceast a valoare va stears a. Exemplic am mai jos acest lucru pentru tipurile valoare si referint 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 a, pe stiv a se depune tot o copie a valorii obiectului. Ins a pentru un asemenea tip de variabil a acest lucru nseamn a c a pe stiv a se va depune ca valoare adresa de memorie la care eset stocat obiectul respectiv. Ca atare, metoda apelat a poate s a modice starea obiectului care 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

Totu si, chiar si n cazul tipului referint a ncercarea de a recrea n interiorul 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, INSTRUCT IUNI, SPAT II 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);

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

Exist a situat ii n care acest comportament nu este cel dorit: am vrea ca efectul asupra unui parametru s a se ment in a si dup a ce metoda apelat a sa terminat. Un parametru referint a este folosit tocmai pentru a rezolva problema transmiterii prin valoare, folosind referint a (un alias) pentru entitatea dat a de c atre metoda apelant a. Pentru a transmite un parametru prin referint a, se prexeaz a cu cuv antul cheie ref la apel sau la declarare de metod 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 si b. Una din tr as aturile specice parametrilor referint a este c a valorile pentru care se face apelul trebuie s a e init ializate. Neasignarea de valori pentru x si y n exemplul de mai sus duce o eroare de compilare. Mai clar, exemplul 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 a cazuri n care dorim s a obt inem acela si efect ca la parametrii referint a, dar f ar a a trebui s a init ializ am argumentele date de c atre metoda apelant a (de exemplu c and valoarea acestui parametru se calculeaz a n in2 teriorul metodei apelate). Pentru aceasta exist a parametrii de ie sire , similar cu parametrii referint a, cu deosebirea c a nu trebuie asignat a o valoare 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, INSTRUCT IUNI, SPAT II DE NUME area = l * l; }

} Pentru toate tipurile de parametri de mai sus exist a o mapare 1 la 1 ntre 3 parametrii actuali si cei formali. Un parametru vector permite o relat ie de tipul unul-la-mult i: mai mult i parametri actuali pot referite prin intermediul unui singur parametru formal. Un astfel de parametru se declar a folosind modicatorul params. Pentru o implementare de metod a, putem avea cel mult un parametru de tip vector si acesta trebuie s a e ultimul 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 folose ste si de c atre metoda WriteLine (sau Write) a clasei Console, i.e. exist a n aceast a clas a o metod a de tipul: 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 a e tratat a ca ind de alt tip. Conversiile pot implicite sau explicite, aceasta specic and de fapt dac a un operator de cast (conversie) este sau nu necesar.

3.3.1

Conversii implicite

Sunt clasicate ca si conversii implicite urm atoarele: conversiile identitate conversiile numerice implicite conversiile implicite de tip enumerare conversiile implicite de referint e boxing conversiile implicite ale expresiilor constante conversii implicite denite de utilizator Conversiile implicite pot ap area ntro varietate de situat ii, de exemplu apeluri de funct ii sau atribuiri. Conversiile implicite predenite nu determin a niciodat a aparit ia de except ii. Conversiile indentitate O conversie identitate converte ste de la un tip oarecare c atre acela si tip. 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, INSTRUCT IUNI, SPAT II 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 si cele de la long sau ulong la double pot duce la o pierdere a preciziei, dar niciodat a la o reducere a ordinului de m arime. Alte conversii numerice implicite niciodat a nu duc la pierdere de informat ie. Conversiile de tip enumerare implicite O astfel de conversie permite ca literalul 0 s a e convertit la orice tip enumerare (chiar dac a acesta nu cont ine valoarea 0) - a se vedea 2.2.3, pag. 29. Conversii implicite de referint e Conversiile implicite de referint e implicite sunt: de la orice tip referint a la object; de la orice tip clas a B la orice tip clas a A, dac a B este derivat din A; de la orice tip clas a A la orice interfat a B, dac a A implementeaz a B; de al orice interfat a A la orice interfat a B, dac a A este derivat a din B; de la orice tip tablou A cu tipul AE la un tip tablou B av and tipul BE , cu urm atoarele condit ii: 1. A si B au acela si num ar de dimensiuni; 2. at at AE c at si BE sunt tipuri referint a; 3. exist a o conversie implicit a de tip referint a de la AE la BE 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 a de tip referint a;

3.3. CONVERSII Conversie de tip boxing

57

Permite unui tip valoare s a e implicit convertit c atre tipul object sau System.ValueType sau c atre orice tip interfat a pe care tipul valoare l implementeaz a. O descriere mai am anunt it a este dat a n sect iunea 3.3.4.

3.3.2

Conversiile implicite ale expresiilor constante

Permit urm atoarele tipuri de conversii: o expresie constant a de tip int poate convertit a c atre tipurile sbyte, byte, short, ushort, uint, ulong, cu condit ia ca valoarea expresiei constante s a se ae n domeniul tipului destinat ie; o expresie constant a de tip long poate convertit a la tipul ulong, dac a valoarea ce se converte ste nu este negativ a. Conversii implicite denite de utilizator Constau ntro conversie implicit a standard opt ional a, urmat a de execut ia unui operator de conversie implicit a utilizator urmat a de alt a conversie implicit a standard opt ional a. Regulile exacte sunt descrise n [4].

3.3.3

Conversii explicite

Urm atoarele conversii sunt clasicate ca explicite: toate conversiile implicite conversiile numerice explicite conversiile explicite de enumer ari conversiile explicite de referint e unboxing conversii explicite denite de utilizator Din cauz a c a orice conversie implicit a este de asemenea si una explicit a, aplicarea operatorului de cast este redundant a: int x = 0; long y = (long)x;//(long) este redundant

58

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

Conversii numerice explicite Sunt conversii de la orice tip numeric la un alt tip numeric pentru care nu exist a conversie numeric a implicit 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 a n astfel de conversii pot ap area pierderi de informat ie, exist a dou a contexte n care se fac aceste conversii: checked si unchecked. In context checked, conversia se face cu succes dac a valoarea care se converte ste este reprezentabil a de c atre tipul c atre care se face conversia. In cazul n care conversia nu se poate face cu succes, se va arunca except ia System.OverowException. In context unchecked, conversia se face ntotdeauna, dar se poate ajunge la pierdere de informat ie sau la valori ce nu sunt bine nedenite (vezi [4], pag. 115116). Conversii explicite de enumer ari Conversiile explicite de enumer ari sunt: 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 ec arui tip enumerare ca ind tipul ntreg de reprezentare, dup a care se efectueaz a o conversie implict a sau explicit a ntre tipuri (ex: dac a se dore ste conversia de la un tip enumerare E care are tipul de reprezentare int la un tip byte, se va face o conversie explicit a de la int la byte; invers, se va face o conversie implict a de la byte la int). Conversii explicite de referint e Conversiile explicite de referint e sunt: de la object la orice tip referint a; de la orice tip clas a A la orice tip clas a B, cu condit ia ca A s a e clas a de baz a pentru B; de la orice tip clas a A la orice tip interfat a B, dac a A nu este nederivabil a si A nu implementeaz a pe B; de la orice tip interfat a A la orice tip clas a B, dac a B nu este nederivabil a sau cu condit ia ca B s a implementeze A; de la orice tip interfat a A la orice tip interfat a B, dac a A nu este derivat din B; de la un tip tablou A cu elemente de tip AE la un tip tablou B cu elemente BE , cu condit iile: 1. A si B au acela si num ar de dimensiuni; 2. AE si BE sunt tipuri referint a; 3. exist a o conversie de referint a explicit a de la AE al BE de la System.Array si interfet ele pe care le implementeaz a la orice tip tablou; de la System.Delegate si interfet ele pe care le implementeaz a la orice tip delegat. Acest tip de conversii cer vericare la runtime. Dac a o astfel de conversie e sueaz a, se va arunca o except ie de tipul System.InvalidCastException.

60 Unboxing

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

Unboxing-ul permite o conversie explicit a de la object sau System.ValueType la orice tip valoare, sau de la orice tip interfat a la orice tip valoare care implementeaz a tipul interfat a. Mai multe detalii se vor da n sect iunea 3.3.4. Conversii explicite denite de utilizator Constau ntro conversie standard explicit a opt ional a, urmat a de execut ia unei conversii explicite, urmat a de o alt a conversie standard explicit a opt ional a.

3.3.4

Boxing si unboxing

Boxingul si unboxingul reprezint a modalitatea prin care C# permite utilizarea simpl a a sistemului unicat de tipuri. Spre deosebire de Java, unde exist a tipuri primitive (care nu pot cont ine metode) si tipuri referint a, n C# 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 r andul ei este derivat a din clasa object (alias System.Object). Ca atare, orice int este compatibil cu object. Boxing Conversia de tip boxing permite oric arui tip valoare s a e implicit convertit c atre tipul object sau c atre un tip interfat a implementat de tipul valoare. Boxingul unei valori const a n alocarea unei variabile de tip obiect si copierea valorii init iale n acea instant a. Procesul de boxing al unei valori sau variabile de tip valoare se poate nt elege ca o simulare de creare de clas a pentru acel tip: sealed class T_Box { T value; public T_Box(T t) { value = t; } } Astfel, declarat iile: int i = 123; object box = i;

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

61

procesul se desf a soar a ca n gura 3.1: la linia 1, se declar a si se init ializeaz a o variabil a de tip valoare, care va cont ine valoarea 10; valoarea va stocat a pe stiv a. La linia 2 se va crea o referint a o c atre un obiect alocat n heap, care va cont ine at at valoarea 10, c at si o informat ie despre tipul de dat a cont inut ( n cazul nostru, System.Int32). Unboxingul se face printrun cast explicit, ca n linia 3.
i 10

o 10 j 10

System.Int32

Figura 3.1: Boxing si unboxing Determinarea tipului pentru care sa f acut 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 cont inut a. secvent a: int i = 10; object o = i; i++; Console.WriteLine("in o: {0}", o); va a sa valoarea nealterat a 10. Altfel spus,

62

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

3.3.5

Declarat ii de variabile si constante

Variabilele si constantele trebuie declarate n C#. Opt ional, pentru variabile se poate specica valoarea init ial a, iar pentru constante acest lucru este obligatoriu. O variabil a trebuie s a aib a valoarea asignat a denit a nainte ca valoarea ei s a e utilizat a, n cazul n care este declarat a n interiorul unei metode. Este o eroare ca ntrun sub-bloc s a se declare o variabil a cu acela si nume ca n blocul cont in ator: 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 init iale care trebuie s a se poat a evalua la compilare.

3.3.6

Declarat ii de etichete

O etichet a poate prexa o instruct iune. Ea este vizibil a n ntregul bloc si toate sub-blocurile cont inute. O etichet a poate referit a de c atre o instruct iune 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

Instruct iuni de select ie

Instruct iunea if Instruct iunea if execut a o instruct iune n funct ie de valoarea de adev ar a unei expresii logice. Are formele: if (expresie logica) instructiune; if (expresie logica) instructiune; else instructiune; Instruct iunea switch Permite executarea unei instruct iuni n funct ie de valoarea unei expresii, care se poate reg asi sau nu ntro list a de valori candidat: switch (expresie) { case eticheta: instructiune; case eticheta: instructiune; ... default: instructiune; } O etichet a reprezint a o expresie constant a. O instruct iune poate s a si lipseasc a si n acest caz se va executa instruct iunea de la case ul urm ator, sau de la default. Sect iunea default poate s a lipseasc a. Dac a o instruct iune este nevid a, atunci obligatoriu va avea la sf ar sit o instruct iune break sau goto case expresieConstanta sau goto default. Expresia dup a care se face select ia poate de tip sbyte, byte, short, ushort, int, uint, long, ulong, char, string, enumerare. Dac a valoarea expresiei se reg ase ste printre valorile specicate la clauzele case, atunci instruct iunea corespunz atoare va executat a; dac a nu, atunci instruct iunea de la clauza default va executat a (dac a ea exist a). Spre deosebire de C si C++, e interzis s a se foloseasc a fenomenul de c adere de la o etichet a la alta; continuarea se face folosind explicit goto. Subliniem c a se permite folosirea unui selector de tip string. switch (i) { case 0: Console.WriteLine("0"); break; case 1:

64

CURS 3. CLASE, INSTRUCT IUNI, SPAT II 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

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

3.3.8

Instruct iuni de ciclare

Exist a 4 instruct iuni de ciclare: while, do, for, foreach. Instruct iunea while Permite executarea unei instruct iuni at ata timp c at valoarea unei expresii logice este adev arat a (ciclu cu test anterior). Sintaxa: while (expresie logica) instructiune; In interiorul unei astfel de instruct iuni se poate folosi o instruct iune de salt de tip break sau continue. while { r = a = b = } (r != 0) a%b; b; a;

3.3. CONVERSII Instruct iunea do

65

Execut a o instruct iune o dat a sau de mai multe ori, c at timp o condit ie logic a este adev arat a (ciclu cu test posterior). Exemplu: do { S += i++; }while(i<=n) Poate cont ine instruct iuni break sau continue. Instruct iunea for Execut a o secvent a de init ializare, dup a care va executa o instruct iune at ata timp c at o condit ie este adev arat a (ciclu cu test anterior); poate s a cont in a un pas de reinit ializare (trecerea la pasul urm ator). Se permite folosirea instruct iunilor break si continue. Exemplu: for (int i=0; i<n; i++) { Console.WriteLine("i={0}", i); } Instruct iunea foreach Enumer a elementele dintro colet ie, execut and o instruct iune pentru ecare element. Colect ia poate s a e orice instant a a unei clase care implementeaz a interfat a 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 si nu se poate aplica un operator care s a i schimbe valoarea).

66

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

3.3.9

Instruct iuni de salt

Permit schimbarea ordinii de execut ie a instruct iunilor. Ele sunt: break, continue, goto, return, throw. Instruct iunea break Produce ie sirea fort at a dintrun ciclu de tip while, do, for, foreach. Instruct iunea continue Porne ste o nou a iterat ie n interiorul celui mai apropiat ciclu cont in ator de tip while, do, for, foreach. Instruct iunea goto Goto permite saltul al o anumit a instruct iune. Are 3 forme: goto eticheta; goto case expresieconstanta; goto default; Cerint a este ca eticheta la care se face saltul s a e denit a n cadrul funct iei curente si saltul s a nu se fac a n interiorul unor blocuri de instruct iuni, deoarece nu se poate reface ntotdeauna contextul acelui bloc. Se recomand a evitarea utiliz arii intense a acestui cuv ant cheie, n caz contrar se poate ajunge la fenomenul de spagetti code. Pentru o argumentare consistent a a acestei indicat ii, a se vedea articolul clasic al lui Edsger W. Dijkstra, Go To Statement Considered Harmful: http://www.acm.org/classics/oct95/ Instruct iunea return Determin a cedarea controlului funt iei apelante de c atre funct ia apelat a. Dac a funct ia apelat a are tip de retur, atunci instruct iunea return trebuie s a e urmat a de o expresie care suport a o conversie implicit a c atre tipul de retur.

3.3.10

Instruct iunile try, throw, catch, nally

Permit tratarea except iilor. Vor studiate n detaliu la capitolul de except ii.

3.3. CONVERSII

67

3.3.11

Instruct iunile checked si unchecked

Controleaz a contextul de vericare de dep a sire a domeniului pentru aritmetica pe ntregi si conversii. Au forma: checked { //instructiuni } unchecked { //instructiuni } Vericare se va face la runtime.

3.3.12

Instruct iunea lock

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

3.3.13

Instruct iunea using

Determin a obt inerea a unei sau mai multor resurse, execut a o instruct iune si apoi disponibilizeaz a resursa: using ( achizitie de resurse ) instructiune O resurs a este o clas a sau o structur a care implementeaz a interfat a System.IDisposable, care include o sigur a metod a f ar a parametri Dispose(). Achizit ia de resurse se poate face sub form a de variabile locale sau a unor expresii; toate acestea trebuie s a e implicit convertibile la IDisposable. Variabilele locale alocate ca resurse sunt readonly. Resursele sunt automat dealocate (prin apelul de Dispose ) la sf ar situl instruct iunii (care poate bloc de instruct iuni). Motivul pentru care exist a aceast a instruct iune este unul simplu: uneori se dore ste ca pentru anumite obiecte care det in resurse importante s a se apeleze automat metod a Dispose() de dezalocare a lor, c at mai repede cu putint a. Exemplu:

68

CURS 3. CLASE, INSTRUCT IUNI, SPAT II 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

Spat ii de nume

In cazul cre arii de tipuri este posibil s a se foloseasc a un acela si nume pentru tipurile noi create de c atre dezvoltatorii de soft. Pentru a putea folosi astfel de clase care au numele comun, dar responsabilit a ti diferite, trebuie prev azut a o modalitate de a le adresa n mod unic. Solut ia la aceast a 4 problem a este crearea spat iilor de nume care rezolv a, printro adresare complet a astfel de ambiguit a ti. Astfel, putem folosi de exemplu clasa Buer din spat iul System (calicare complet a: System.Buer), al aturi de clasa Buer din spat iul de nume Curs3: Curs3.Buer. Crearea unui spat iu de nume se face prin folosirea cuv antului namespace: using System; namespace Curs3 { public class Buffer { public Buffer() { Console.WriteLine("Bufferul meu!"); } } } Se pot de asemenea crea spat ii de nume imbricate. Altfel spus, un spat iu de nume este o colect ie de tipuri sau de alte spat ii de nume.
4

engl: namespaces

3.4. SPAT II DE NUME

69

3.4.1

Declarat ii de spat ii de nume

O declarat ie de spat iu de nume const a n cuv antul cheie namespace, urmat de identicatorul spat iului de nume si de blocul spat iului de nume, delimitat de acolade. Spat iile de nume sunt implicit publice si acest tip de acces nu se poate modica. In interiorul unui spat iu de nume se pot utiliza alte spat ii de nume, pentru a se evita calicarea complet a a claselor. Identicatorul unui spat iu de nume poate simplu sau o secvent a de identicatori separat i prin .. Cea de a doua form a permite denirea de spat ii de nume imbricate, f ar a a se imbrica efectiv: namespace N1.N2 { class A{} class B{} } este echivalent a cu: namespace N1 { namespace N2 { class A{} class B{} } } Dou a declarat ii de spat ii de nume cu aceea si denumire contribuie la declararea unui acela si spat iu de nume: namespace N1.N2 { class A{} } namespace N1.N2 { class B{} } este echivalent a cu cele dou a declarat ii anterioare.

70

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME

3.4.2

Directiva using

Directiva using faciliteaz a n primul r and utilizarea spat iilor de nume si a tipurilor denite n acestea; ele nu creeaz a membri noi n cadrul unit a tii de program n care sunt folosite, ci au rol de a u sura referirea tipurilor. Nu se pot utiliza n interiorul claselor, structurilor, enumer aririlor. Exemplu: e mai u sor de nteles un cod de forma: using System; class A { static void Main() { Console.WriteLine("Mesaj"); } } dec at: class A { static void Main() { System.Console.WriteLine("Mesaj"); } } Directiva using poate de fapt folosit a at at pentru importuri simbolice, c at si pentru crearea de aliasuri. Directiva using pentru import simbolic O directiv a using permite importarea simbolic a a tuturor tipurilor cont inute direct ntrun spat iu de nume, i.e. folosirea lor f ar a a necesar a o calicare complet a. Acest import nu se refer a si la spat iile de nume cont inute: namespace N1.N2 { class A{} } namespace N3.N4 { class B{};

3.4. SPAT II 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 spat ii de nume nu trebuie s a duc a la ambiguit a ti: 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

In situat ia de mai sus, conictul (care poate foarte u sor n cazul n care se folosesc tipuri produse de dezvoltatori diferit i) poate rezolvat de o calicare complet a: namespace N3 { using N1; using N2; class B {

72

CURS 3. CLASE, INSTRUCT IUNI, SPAT II DE NUME N1.A a1 = null; N2.A a2 = null;//ok, nu mai este ambiguitate }

} Tipurile declarate n interiorul unui spat iu de nume pot avea modicatori de acces public sau internal (modicatorul implicit). Un tip internal nu poate folosit prin import n afara assembly-ului, pe c and unul public, da. Directiva using ca alias Introduce un identicator care serve ste drept alias pentru un spat iu de nume sau pentru un tip. Exemplu: namespace N1.N2 { class A{} } namespace N3 { using A = N1.N2.A; class B { A a = null; } } Acela si efect se obt ine cre nd un alias la spat iul de nume N1.N2: namespace N3 { using N = N1.N2; class B { N.A a = null; } } Identicatorul dat unui alias trebuie s a e unic, adic a n interiorul unui namespace nu trebuie s a existe tipuri si aliasuri cu acela si nume:

3.4. SPAT II DE NUME

73

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

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

adic a directiva de alias nu este tranzitiv a. Situat ia de mai sus se poate rezolva prin declarearea aliasului n afara spat iului de nume: 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 c atre alte declarat ii care folosesc acela si identicator n interiorul unui bloc:

74

CURS 3. CLASE, INSTRUCT IUNI, SPAT II 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 inuent eaz a reciproc: 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 n felul urm ator: atributeopt modicatori-de-clasaopt class identicator clasa-de-bazaopt corpclasa ;opt Modicatorii de clas a sunt: public - clasele publice sunt accesibile de oriunde; poate folosit at at pentru clase interioare, c at si pentru clase care sunt cont inute n spat ii de nume; internal - se poate folosi at at pentru clase interioare, c at si pentru clase care sunt cont inute n spat ii de nume (este modicatorul implicit pentru clase care sunt cont inute n spat ii de nume). Semnic a acces permis doar n clasa sau spat iul de nume care o cuprinde; protected - se poate specica doar pentru clase interioare; tipurile astfel calicate sunt accesibile n clasa curent a sau n cele derivate (chiar dac a clasa derivat a face parte din alt spat iu de nume); private - doar pentru clase interioare; semnic a acces limitat la clasa con tin atoare; este modicatorul implicit; protected internal - folosibil doar pentru clase interioare; tipul denit este accesibil n spat iul de nume curent, n clasa cont in atoare sau n tipurile derivate din clasa cont in atoare; new - permis pentru clasele interioare; clasa astfel calicat a ascunde un membru cu acela si nume care este mo stenit; 75

76

CURS 4. CLASE

sealed - o clas a sealed nu poate mo stenit a; poate clas a interioar a sau nu; abstract - clasa care este incomplet denit a si care nu poate instant iat a; folosibil a pentru clase interioare sau cont inute n spat ii de nume;

4.2

Membrii unei clase

Corpul unei clase se specic a n felul urm ator: { declaratii-de-membri };opt Membrii unei clase sunt mp art iti n urm atoarele categorii: constante c ampuri metode propriet a ti evenimente indexatori operatori constructori (de instant a) destructor constructor static tipuri Acestor membri le pot ata sat i modicatorii de acces: public - membrul este accesibil de oriunde; protected - membrul este accesabil de c atre orice membru al clasei cont in atoare si de c atre clasele derivate; internal - membrul este accesabil doar n assembly-ul curent; protected internal - reuniunea precedentelor dou a; private accesabil doar n clasa cont in atoare; este specicatorul implicit.

4.3. CAMPURI

77

4.3

C ampuri

Un c amp reprezint a un membru asociat cu un obiect sau cu o clas a. Modicatorii de c amp care se pot specica opt ional naintea unui c amp sunt cei de mai sus, la care se adaug a modicatorii new, readonly, volatile, static, ce vor prezentat i mai jos. Pentru orice c amp este necesar a precizarea unui tip de date, ce trebuie s a aibe gradul de accesibilitate cel put in cu al c ampului ce se declar a. Opt ional, c ampurile pot init ializate cu valori compatibile. Un astfel de c amp se poate folosi e prin specicarea numelui s au, e printr-o calicare bazat a pe numele clasei sau al unui obiect. 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

C ampuri instant e

Dac a o declarat ie de c amp nu include modicatorul static, atunci acel c amp se va reg asi n orice obiect de tipul clasei curente care va instant iat. Modic ari ale acestor c ampuri se vor face independent pentru ecare obiect. Deoarece un astfel de c amp are o valoare specic a ec arui obiect, accesarea lui se va face prin calicarea cu numele obiectului: objA.a = 1; (dac a modicatorii de acces permit a sa ceva). Un c amp special este this (cuv ant cheie) care reprezint a o referint a la obiectul curent.

4.3.2

C ampuri statice

C and o declarat ie de c amp include un specicator static, c ampul respectiv nu apat ine ec arei instant e n particular, ci clasei ns a si. Accesarea unui c amp static din exteriorul clasei se face exclusiv prin intermediul numelui de clas a:

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

CURS 4. CLASE

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

4.3.3

C ampuri readonly

Declararea unui c amp de tip readonly (static sau nu) se face prin specicarea cuv antului readonly n declarat ia sa: class A { public readonly string salut = Salut; public readonly string nume; public class A(string nume) { this.nume = nume; } } Atribuirea asupra unui c amp de tip readonly se poate face doar la declararea sa (ca n exemplu de mai sus) sau prin intermediul unui constructor. Valoarea unor astfel de c ampuri nu se presupune a cunoscut a la compilare.

4.3.4

C ampuri volatile

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

4.4. CONSTANTE

79

Pentru c ampuri nevolatile, tehnicile de optimizare care reordoneaz a instruct iunile pot duce la rezultate nea steptate sau nepredictibile n programe multithreading care acceseaz a c ampurile f ar a sincronizare (efectuabil a cu instruct iunea lock). Aceste optimiz ari pot f acute de c atre compilator, de c atre sistemul 1 de rulare sau de c atre hardware. Urm atoarele tipuri de optimiz ari sunt afectate n prezent a unui modicator volatile: citirea unui c amp volatile este garantat a c a se va nt ampla nainte de orice referire la c amp care apare dup a citire; orice scriere a unui c amp volatile este garantat a c a se va petrece dup a orice instruct iune anterioar a care se refer a la c ampul respectiv.

4.3.5

Init ializarea c ampurilor

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

4.4

Constante

O constant a este un c amp a c arui valoare poate calculat a la compilare. O constant a poate prexat a de urm atorii modicatori: new, public, protected, internal, protected internal,private. Cuvantul new poate s a se combine cu unul din ceilalt i 4 modicatori de acces. Pentru un c amp constant e obligatoriu s a se asigneze o valoare calculabil a la compilare: 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 a. Valoarea care se asignez a unei constante trebuie s a admit a o conversie implicit a c atre tipul constantei. Tipul unei constante trebuie s a e cel put in la fel de accesibil ca si constanta ns a si. Orice c amp constant este automat un c amp static. Un c amp constant difer a de un c amp static readonly : const -ul are o valoare cunoscut a la compilare, pe c and valoarea unui readonly poate init ializat a la runtime n interiorul constructorului (cel mai t arziu, de altfel).

4.5

Metode

O metod a este un membru care implementeaz a un calcul sau o act iune care poate efectuat a de c atre un obiect sau o clas a. Antetul unei metode se declar a n felul urm ator: 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 a care este cel put in la fel de accesibil ca si metoda ns a si sau void (absent a informat iei returnate); nume poate un identicator de metod a din clasa curent a sau un identicator calicat cu numele unei interfet e pe care o implementeaz a (NumeInterfata.numeMetoda ); parametrii pot de tip ref, out, params, sau f ar a nici un calicator; corpmetoda este un bloc cuprins ntre acolade sau doar caracterul ; (dac a este vorba de o metod a ce nu se implementeaz a n tipul curent). Despre calicatorii virtual, override, sealed, new, abstract se va discuta mai pe larg ntro sect iune viitoare.

I 4.6. PROPRIETAT

81

4.5.1

Metode statice si nestatice

O metod a se declar a a static a dac a numele ei este prexat cu modicatorul de metod a static. O astfel de metod a nu opereaz a asupra unei instant e anume, ci doar asupra clasei. Este o eroare ca o metod a static a s a fac a referire la un membru nestatic al unei clase. Apelul unei astfel de metode se face prin NumeClasa.NumeMetoda sau direct NumeMetoda dac a este apelat a din context static al aceleia si clase. O metod a nestatic a nu are cuv antul static specicat; ea este apelabil a n conjunct ie cu un obiect anume.

4.5.2

Metode externe

Metodele externe se declar a folosind modicatorul extern; acest tip de metode sunt implementate extern, de obicei n alt limbaj dec at C#. Deoarece o astfel de metod a nu cont ine o implementare, corpul acestei metode este ;. Exemplu: se utilizeaz a metoda MessageBox importat a din biblioteca dll 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

Propriet a ti

O proprietate este un membru care permite acces la un atribut al unui obiect sau al unei clase. Exemple de propriet a ti sunt: lungimea unui sir, numele unui client, textul continut ntrun control de tip TextBox. Propriet a tile sunt extensii naturale ale c ampurilor, cu deosebirea c a ele nu presupun alocarea de memorie. Ele sunt de fapt ni ste metode (accesori) care permit citirea sau setarea unor atribute ale unui obiect sau clase; reprezint a modalitatea de scriere a unor metode de tip get/set pentru clase sau obiecte.

82

CURS 4. CLASE

Declararea unei propriet a ti se face astfel: 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 propriet a ti specic a tipul de proprietate introdus de declarat ie, i.e. ce valori vor putea atribuite propriet a tii respective (dac a accesorul de tip set a fost denit), respectiv care este tipul valorii returnate de aceast a proprietate (corespunz ator accesorului de tip get ). 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()

I 4.6. PROPRIETAT { 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 f ar a parametri, care returneaz a o valoare de tipul propriet a tii. C and o proprietate este folosit 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 propriet a tii si tip de retur void. Acest parametru implicit al lui set este numit ntotdeauna value. C and o proprietate este folosit a ca destinatar ntro atribuire, sau c and se folosesc operatorii ++ si , accesorului set i se transmite un parametru care reprezint a noua valoare. In funct ie de prezent a sau absent a accesorilor, o proprietate este clasicat a dup a cum urmeaz a: proprietate readwrite, dac a are ambele tipuri de accesori; proprietate readonly, dac a are doar accesor de tip get ; este o eroare de compilare s a se fac a referire n program la o proprietate n sensul n care sar cere operarea cu un accesor de tip set ; proprietate writeonly, dac a este prezent doar accesorul de tip set ; este o eroare de compilare utilizarea unei propriet a ti ntrun context n care ar necesar a prezent a accesorului get. Demn de ment ionat este c a ei pot folosit i nu numai pentru a asigura o sintax a simplu de folosit pentru metodele tradit ionale de tip get/set, ci si pentru scrierea controalelor .NET utilizator, care se face extrem de u sor. In gura 4.1 este dat a reprezentarea unui control utilizator: Codul corespunz ator este dat mai jos: 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();

I 4.6. PROPRIETAT

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 propriet a tile publice Street, Number si Phone care vor vizibile n fereastra Properties atunci c and acest control va ad augat la o form a. Atributele cuprinse ntre paranteze drepte sunt opt ionale, dar vor face ca aceste propriet a ti s a e grupate n sect iunea de date a ferestrei Properties, si nu n cea Misc. Pentru mai multe detalii despre componente utilizator, 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 nc arc arii operatorului [] din C++, ind o facilitate ce d a o mare exibilitate. Declararea unui indexator se face n felul urm ator: 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 a cont in a cel put in un parametru si nu poate s a aibe vreun parametru de tip ref sau out. Declarat iile de accesor vor cont ine accesor get sau accesor set, asem an ator cu cei de la propriet a ti. 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 nc arcarea indexatorilor: 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 mult i 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 dene ste semnicat ia unei expresii operator care poate aplicat a unei instant e a unei clase. Corespunde supra nc arc arii operatorilor din C++. O declarat ie de operator are forma: atributeopt modicatori-de-operator declaratie-de-operator corp-operator Se pot declara operatori unari, binari si de conversie.

4.8. OPERATORI Urm atoarele reguli trebuie s a e respectate pentru orice operator: 1. Orice operator trebuie s a e declarat public si static.

93

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

4.8.1

Operatori unari

Supra nc arcarea operatorilor unari are forma: tip operator operator-unar-supraincarcabil (tip identicator) corp Operatorii unari supra nc arcabili sunt: + - ! ++ true false. Urm atoarele reguli trebuie s a e respectate la supra nc arcarea unui operator unar (T reprezint a clasa care cont ine denit ia operatorului): 1. Un operator +, -, !, trebuie s a preia un singur parametru de tip T si poate returna orice tip. 2. Un operator ++ sau trebuie s a preia un singur parametru de tip T si trebuie s a returneze un rezultat de tip T. 3. Un operator unar true sau false trebuie s a preia un singur parametru de tip T si s a returneze bool. Operatorii true si false trebuie s a e ori ambii denit i, ori nici unul (altfel apare o eroare de compilare). Ei sunt necesari pentru cazuri de genul: if( a==true ) sau if( a==false ) Mai general, ei pot folosit i ca expresii de control n if, do, while si for, precum si n operatorul ternar ? :. De si pare paradoxal, nu este obligatoriu ca if (a==true) s a e echivalent a cu if (!(a==false)), de exemplu pentru tipuri SQL care pot null, ceea ce nu nseam a nici true, nici false, ci lips a de informat ie. 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 a modul n care se face supra nc arcarea operatorului ++, care poate folosit at at ca operator de preincrementare c at si 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 nc arcabili sunt: + - * / % & | ^ << >> == != > < >= <=. Cel put in unul dintre cei doi parametri preluat i trebuie s a e de tipul cont in ator. Operatorii de shiftare trebuie s a aib a primul parametru de tipul clasei n care se declar a, iar al doilea parametru de tip int. Unii operatori trebuie s a se declare n pereche: 1. operatorii == si != 2. operatorii > si < 3. operatorii >= si <= Pentru operaratorul ==, este indicat a si denirea metodei Equals(), deoarece tipul respectiv va putea astfel folosit si de c atre limbaje care nu suport a supra nc arcarea operatorilor, dar pot apela metoda polimorc a Equals() denit a n clasa object. Nu se pot supa nc arca operatorii + =, =, / =, =; dar pentru ca ace stia s a funct ioneze, este sucient s a se supra ncarce operatorii corespunz atori: +, , /, .

4.8.3

Operatori de conversie

O declarat ie de operator de conversie trebuie introduce o conversie denit a de utilizator, care se va ad auga (dar nu va suprascrie) la conversiile predenite. Declararea unui operator de conversie se face astfel: implicit operator tip (tip parametru) corp explicit operator tip (tip parametru) corp Dup a cum se poate deduce, conversiile pot implicite sau explicite. Un astfel de operator va face conversia de la un tip surs a, indicat de tipul parametrului din antet la un tip destinat ie, indicat de tipul de retur. O clas a poate s a declare un operator de conversie de la un tip surs a S la un tip destinat ie T cu urm atoarele condit ii: 1. S si T sunt tipuri diferite 2. Unul din cele dou a tipuri este clasa n care se face denirea. 3. T si S nu sunt object sau tip interfat a.

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

CURS 4. CLASE

Un bun design asupra operatorilor de conversie are n vedere urm atoarele: Conversiile implicite nu ar trebui s a duc a la pierdere de informat ie sau la aparit ia de except ii; Dac a prima condit ie nu este ndeplinit a, atunci neap arat trebuie declarat 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 a pentru c a nu va duce la pierderea de informat ie. Cea de doua poate s a arunce o except ie (via constructor) si de aceea este declarat a ca si conversie explicit 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 INSTANT A f5.ToString( ), f2.ToString( )); } } }

99

4.9

Constructori de instant a

Un constructor de instant a este un membru care implementeaz a act iuni care sunt cerute pentru a init ializa o instant a a unei clase. Declararea unui astfel de constructor se face n felul urm ator: 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 declarat ii si instruct iuni delimitat de acolade sau caracterul punct si virgul a. Un constructor are acela si nume ca si clasa din care face parte si nu returneaz a un tip. Constructorii de instant a nu se mo stenesc. Dac a o clas a nu cont ine nici o declarat ie de constructor de instant a, atunci compilatorul va crea automat unul implicit. O clas a care este mo stenit a dintr-o alt a clas a ce nu are constructori f ar a parametri va trebui s a utilizeze un apel de constructor de clas a de baz a pentru care s a furnizeze parametrii potrivit i; acest apel se face prin intermediul init ializatorului de constructor. Un constructor poate apela la un alt constructor al clasei din care face parte pentru a efectua init ializ ri. C and exist a c ampuri instant a care au o expresie de init ializare n afara constructorilor clasei respective, atunci aceste init ializ ari se vor face nainte de apelul de constructor al clasei de baz a. Pentru a nu se permite crearea sau mo stenirea unei clase trebuie ca aceast a clas a s a cont in a cel put in un constructor denit de utilizator si tot i constructorii s a e declarat i privat i.

4.10

Constructori statici

Un constructor static este un membru care implementeaz a act iunile cerute pentru init ializara 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 mo stenesc, nu se pot apela direct si nu se pot supra nc arca. Un constructor static se va executa cel mult o dat a ntr-o aplicat ie. Se garanteaz a faptul c a acest constructor se va apela naintea cre arii vreunei instant e a clasei respective sau naintea accesului la un membru static. Acest apel este nedeterminist, necunosc anduse exact c and sau dac a se va apela. Un astfel de constuctor nu are specicator de acces si poate s a acceseze doar 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 a cont ine membri, iar n particular ace stia pot si clase. 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 (a sa cum este ar atat n Exemplul 1), de unde se deduce c a o clas a interioar a se comport a ca un membru static al tipului cont in ator. O clas a declarat a n interiorul unei alte clase poate avea unul din gradele de accesibilitate public, protected internal, protected, internal, private (implicit este private ). O clas a declarat a n interiorul unei structuri poate declarat a public, internal sau private (implicit private ). Crearea unei instant e a unei clase interioare nu trebuie s a e precedat a de crearea unei instant e a clasei exterioare cont in atoare, a sa cum se vede din Exemplul 1. O clas a interioar a nu au vreo relat ie special a cu membrul predenit this al clasei cont in atoare. Altfel spus, nu se poate folosi this n interiorul unei clase interioare pentru a accesa membri instant a din tipul cont in ator. Dac a o clas a interioar a are nevoie s a acceseze membri instant a ai clasei cont in atoare, va trebui s a primeasc a prin constructor parametrul this care s a se refer a la o astfel de instant 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 a de mai sus c a o clas a interioar a poate manipula tot i membrii care sunt accesibili n interiorul clasei cont in atoare, indiferent de gradul lor de accesibilitate. In cazul n care clasa exterioar a (cont in atoare) are membri statici, ace stia pot utilizat i f ar a a se folosi numele clasei cont in atoare: 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 n cadrul containerilor pentru care trebuie s a se construiasc a un enumerator. Clasa interioar a va n acest caz str ans legat a de container si va duce la o implementare u sor de urm arit si de ntret inut.

Curs 5 Destructori. POO n C#


5.1 Destructori

Managementul memoriei este f acut sub platforma .NET n mod automat, de c atre garbage collector, parte component a a CLRului. Pentru o explicat ie asupra modului cum act ioneaz a garbage collector-ul, a se vedea [5], pag. 225 si urm. In general, acest garbage collector scute ste programatorul de grija dealoc arii memoriei. Dar n anumite situat ii, se dore ste s a se fac a management manual al dealoc arii resurselor (de exemplu al resurselor care tin de sistemul de operare: siere, conexiuni la ret ea sau la baza de date, ferestre, etc, sau al altor resurse al c aror management nu se face de c atre CLR). In C# exist a posibilitatea de a lucra cu destructori, sau cu metode de tipul Dispose(), Close(). Un destructor se declar a n felul urm ator: atributeopt externopt ~identicator() corp-destructor unde identicator este numele clasei. Un destructor nu are modicator de acces, nu poate apelat manual, nu poate supra nc arcat, nu este mo stenit. Un destructor este o scurt atur a sintactic a pentru metoda Finalize(), care este denit a n clasa System.Object. Programatorul nu poate s a suprascrie sau s a apeleze aceast a metod a. Exemplu: ~MyClass() { // Perform some cleanup operations here. } Metoda de mai sus este automat translatat a n: 105

106

CURS 5. DESTRUCTORI. POO IN C#

protected override void Finalize() { try { // Perform some cleanup operations here. } finally { base.Finalize(); } } Problema cu destructorul este c a el e chemat doar de c atre garbage collector, dar acest lucru se face nedeterminist (cu toate c a apelarea de destructor se face n cele din urm a, dac a programatorul nu mpiedic a explicit acest lucru). Exist a cazuri n care programatorul dore ste s a fac a dealocarea manual, astfel nc at s a nu a stepte ca garbage collectorul s a apeleze destructorul. Programatorul poate scrie o metoda care s a fac a acest lucru. Se sugereaz a denirea unei metode Dispose() care ar trebui s a e explicit apelat a atunci c and resurse de sistem de operare trebuie s a e eliberate. In plus, clasa respectiv a ar trebui s a implementeze interfat a System.IDisposable, care cont ine aceast a metod a. In acest caz, Dispose() ar trebui s a inhibe executarea ulterioar a de garbage collector pentru instant a curent a. Aceast a manevr a permite evitarea eliber arii unei resurse de dou a ori. Dac a clientul nu apeleaz a explicit Dispose(), atunci garbage collectorul va apela el destructorul la un moment dat. Intruc at utilizatorul poate s a nu apeleze Dispose(), este necesar ca tipurile care implemeteaz a aceast a metod a s a deneasc a de asemenea destructor. 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 S I GENERALIZAREA } }

107

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

5.2

Specializarea si generalizarea

Specializarea reprezint a o tehnic a de a obt ine noi clase pornind de la cele existente. Deseori ntre clasele pe care le model am putem observa relat ii de genul este un/o: un om este un mamifer, un salariat este un angajat, etc. Toate acestea duc la crearea unei ierarhii de clase, n care din clase de baz a (mamifer sau angajat) descind alte clase, care pe l ang a c ampuri din clasa de baz a mai au si caracteristici proprii. Obt inerea unei clase derivate plec and de la alt a clas a se nume ste specializare, operat ia invers a purt and numele de generalizare. O clas a de baz a dene ste un tip comun, compatibil cu oricare din clasele derivate (direct sau indirect). In C# o clas a nu trebuie s a mo steneasc a explicit din alt a clas a; n acest caz se va considera c a ea este implicit derivat a din clasa predenit a object (tot una cu System.Object ). C# nu permite mo stenire multipl a, elimin and astfel complicat iile nt alnite n C++. Ca alternativ a, se permite totu si implementarea de mai multe interfet e (la fel ca n Java).

5.2.1

Specicarea mo stenirii

In C# se pentru o clas a D se dene ste clasa de baz a B folosind urm atoarea formul a:

108 class D: B { //declaratii }

CURS 5. DESTRUCTORI. POO IN C#

Dac a pentru o anumit a clas a nu se specic a dou a puncte urmate de numele unei clase de baz a atunci object va deveni baz a pentru clasa n cauz 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 a c a c ampurile name si ssn din clasa de baz a sunt accesibile n clasa derivat a, datorit a specicatorului de acces protected.

5.2.2

Apelul constructorilor din clasa de baz a

In exemplul anterior nu sa denit nici un constructor n clasa de baz a Employee ; constructorul clasei derivate trebuie s a fac a init ializ arile c ampurilor n conformitate cu parametrii transmi si, chiar dac a o parte din aceste c ampuri provin din clasa de baz a. Mai logic ar ca n clasa de baz a s a se g aseasc a un constructor care s a init ializeze c ampurile proprii: name si ssn. Intruc at constructorii nu se mo stenesc, e nevoie ca n clasa derivat a s a se fac a un apel explicit al constructorului clasei de baz a. Acest apel se face prin init ializator de constructor care are forma: dou a puncte urmate de base(parametrii-efectivi).

5.2. SPECIALIZAREA S I 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 obt ine: Employee constructor: Jesse, 1234567890 Salaried constructor: 100000.00 de unde se deduce c a apelul de constructor de clas a de baz a se face naintea execut arii oric aror alte instruct iuni cont inute n constructorul clasei derivate. Dac a o clas a de baz a nu are denit nici un constructor, atunci se va crea unul implicit (f ar a parametri). Dac a dup a un constructor al unei clase derivate nu se specic a un init ializator de constructor, atunci va apelat constructorul implicit (e creat automat de compilator, e scris de c atre programator); dac a nu exist a nici un constructor implicit n clasa de baz a, atunci programatorul trebuie s a specice un constructor din clasa de baz a care va apelat, mpreun a cu parametrii adecvat i.

110

CURS 5. DESTRUCTORI. POO IN C#

5.2.3

Operatorii is si as

Operatorul is Operatorl is este folosit pentru a verica dac a un anumit obiect este de un anumit tip. Este folosit de obicei nainte operatt iilor de downcasting (conversie explicit a de la un tip de baz a la unul derivat). Operatorul se folose ste astfel: instanta is NumeClasa rezultatul acestei operat ii ind true sau false. Exemplu: Employee e = ...; if (e is Salaried) { Salaried s = (Salaried)e; } In cazul n care sar face conversia explicit a iar obiectul nu este de tipul la care se face conversia ar rezulta o except ie: System.InvalidCastException. Operatorul as Acest operator este folosit pentru conversii explicite, return and un obiect de tipul la care se face conversia sau null dac a conversia nu se poate face (nu se arunc a except ii). Determinarea validit a tii conversiei se face test and valoarea rezultat a fat a de null: dac a rezultatul e null atunci conversia nu sa putut face. Ca si precedentul operator se folose ste n special la downcasting. 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 cuv antului cheie class specic a faptul c a clasa curent a nu se poate deriva. Este o eroare de compilare ca o clas a sealed s a e declarat a drept clas a de baz a.

5.4. POLIMORFISMUL

111

5.4

Polimorsmul

Polimorsmul este capacitatea unei entit a ti de a lua mai multe forme. In limbajul C# polimorsmul este de 3 feluri: parametric, adhoc si de mo stenire.

5.4.1

Polimorsmul parametric

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

5.4.2

Polimorsmul adhoc

Se mai nume ste si supra nc arcarea metodelor, mecanism prin care n cadrul unei clase se pot scrie mai multe metode, av and acela si nume, dar tipuri si numere diferite de parametri de apel. Alegerea funct iei care va apelat a se va face la compilare, pe baza corespondent ei ntre tipurile de apel si cele formale.

5.4.3

Polimorsmul de mo stenire

Este forma cea mai evoluat a de polimorsm. Dac a precedentele forme de polimorsm sunt aplicabile f ar a a se pune problema de mo stenire, n acest caz este necesar s a existe o ierarhie de clase. Mecanismul se bazeaz a pe faptul c a o clas a de baz a dene ste un tip care este compatibil din punct de vedere 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 nt ampl a cu metodele av and aceea si list a de parametri formali si care se reg asesc n cele dou a clase?

112

CURS 5. DESTRUCTORI. POO IN C#

S a consider am exemplul urm ator: avem o clas a Shape care cont ine o metod a public void Draw() ; din Shape se deriveaz a clasa Polygon care implementeaz a aceea si metod a n mod specic. Problema care se pune este cum se rezolv a un apel al metodei Draw n context de upcasting: 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 obt ine 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, ad augarea lui nu va schimba cu nimic comportamentul de mai jos, doar va duce la disparit ia de avertisment). Codul de mai sus va a sa: Shape.Draw() Polygon.Draw()

5.4. POLIMORFISMUL

113

Dac a cea dea doua linie a sat a este conform a cu intuit ia, primul rezultat este discutabil, dar justicabil: apelul de metod a Draw() este rezolvat 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 asa fost instant iat de fapt pe baza unui obiect de tip Polygon. Este posibil ca s a se doreasc a schimbarea acestui comportament: apelul de metod a Draw s a e rezolvat n funct ie de tipul efectiv al obiectului care face acest apel, si nu de tipul formal declarat. In cazul precedent, apelul s.Draw() trebuie s a se rezolve de fapt ca ind c atre metoda Draw() din Polygon, pentru c a acesta este tipul la rulare al obiectului s. Cu alte cuvinte, apelul ar trebui s a e rezolvat la rulare si nu la compilare, n funct ie de natura obiectelor. Acest comportament polimorc este referit sub denumirea polimorsm de mo stenire.

5.4.4

Virtual si override

Pentru a asigura faptul c a legarea apelului de metode se face la rulare si nu la compilare, e necesar ca n clasa de baz a s a se specice c a metoda Draw() este virtual a, iar n clasa derivat a pentru aceea si metod a trebuie s a se spun a c a este o suprascriere a celei din baz a: class Shape{ public virtual void Draw(){...} } class Polygon{ public override void Draw(){...} } In urma execut arii metodei Main din clasa de mai sus, se va a sa: Polygon.Draw() Polygon.Draw() adic a sa apelat metoda corespunz atoare tipului efectiv de la rulare, n ecare caz. In cazul n care clasa Polygon este la r andul ei mo stenit a si se dore ste ca polimorsmul s a funct ioneze n continuare va trebui ca n aceast a a treia clas a s a suprascrie (override ) metoda Draw(). Un astfel de comportament polimorc este benec atunci c and se folose ste o colect ie de obiecte de tipul unei clase de baz a: Shape[] painting = new Shape[10]; painting[0] = new Shape();

114

CURS 5. DESTRUCTORI. POO IN C#

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

5.4.5

Modicatorul new pentru metode

Modicatorul new se folo seste pentru a indica faptul c a o metod a dintr-o clas a derivat a care are aceea si semn atur a cu una dintro clas a de baz a nu este o suprascriere a ei, ci o nou a metod a. Este ca si cum metoda declarat a new ar avea o semn atur a diferit a. S a presupunem urm atorul scenariu: compania A creaz a o clas a A care are forma: public class A{ public void M(){ Console.WriteLine(A.M()); } } O alt a companie B va crea o clas a B care mo stene ste clasa A. Compania B nu are nici o inuent a asupra companiei A (sau asupra modului n care aceasta va face modic ari asupra clasei A). Ea va deni n interiorul clasei B o metod a M() si una N() : class B: A{ public void M(){ Console.WriteLine(B.M()); N(); base.M(); } protected virtual void N(){ Console.WriteLine(B.N()); } } Atunci c and compania B compileaz a codul, compilatorul C# va produce urm atorul avertisment: 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 a clasa B dene ste o metod a M(), care va ascunde metoda M() din clasa de baz a A. Aceast a nou a metod a ar putea schimba nt elesul (semantica) lui M(), a sa cum a fost creat init ial de compania A. Este de dorit n astfel de cazuri compilatorul s a avertizeze despre posibile nepotriviri semantice. Programatorii din B vor trebui s a pun a n orice caz specicatorul new naintea metodei B.M(). S a presupunem c a o aplicat ie folose ste clasa B() n felul urm ator: class App{ static void Main(){ B b = new B(); b.M(); } } La rulare se va a sa: B.M() B.N() A.M() S a presupunem c a A decide ad augarea unei metode virtuale N() n clasa sa, metod a ce va apelat a din M() : public class A { public void M() { Console.WriteLine(A.M()); N(); } protected virtual void N() { Console.WriteLine(A.N()); } } La o recompilare f acut a de B, este dat urm atorul avertisment: 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 IN C#

In acest mod compilatorul avertizeaz a c a ambele clase ofer a o metod a N() a c aror semantic a poate s a difere. Dac a B decide c a metodele N() nu sunt semantic legate n cele dou a clase, atunci va specica new, inform and compilatorul de faptul c a versiunea sa este una nou a, care nu suprascrie polimorc metoda din clasa de baz a. Atunci c and codul din clasa App este rulat, se va a sa la ie sire: B.M() B.N() A.M() A.N() Ultima linie a sat a se explic a tocmai prin faptul c a metoda N() din B este declarat a new si nu override (dac a ar fost override, ultima linie ar fost B.N(), din cauza polimorsmului). Se poate ca B s a decid a c a metodele M() si N() din cele dou a clase sunt legate semantic. In acest caz, ea poate sterge denit ia metodei B.M, iar pentru a semnala faptul c a metoda B.N() suprascrie metoda omonim a din clasa p arinte, va nlocui cuv antul new cu override. In acest caz, metoda App.Main va produce: A.M() B.N() ultima linie ind explicat a de faptul c a B.N() suprascrie o metod a virtual a.

5.4.6

Metode sealed

O metod a de tip override poate declarat a ca ind de tip sealed, astfel mpiedic anduse suprascrierea ei ntro clas a derivat a din cea curent 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 a suprascrie metoda F.

5.4.7

Exemplu folosind virtual, new, override, sealed

S a presupunem urm atoare ierarhie de clase, reprezentat a n Fig. 5.1; o clas a X mo steneste o clas a Y dac a sensul s aget ii este de la X la Y. Fiecare clas a are o metod a void f() care determin a a sarea clasei n care este denit a si pentru care se vor specica new, virtual, override, sealed. S a presupunem c a clasa de test arat a astfel: 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 IN C#


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(); } } In funct ie de specicatorii metodelor f() din ecare clas a, se obt in ie sirile din tabelul 5.1:

5.5. CLASE S I METODE ABSTRACTE Tabelul 5.1: Efecte ale diferit ilor specicatori.

119

Metoda Specicator Ie sire secv. 1 Ie sire secv. 2 Specicator Ie sire secv. 1 Ie sire secv. 2 Specicator Ie sire secv. 1 Ie sire secv. 2 Specicator Eroare de compilare deoarece C.f nu poate suprascrie metoda nevirtual a B.f() Specicator Ie sire secv. 1 Ie sire secv. 2 Avertisment la compilare deoarece B.f nlocuie ste A.f Specicator Eroare de compilare deoarece deoarece B.f nu poate suprascris a de C.f

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 si metode abstracte

Deseori pentru o anumit a clas a nu are sens crearea de instant e, din cauza unei generalit a ti prea mari a tipului respectiv. Spunem c a aceast a clas a este abstract a, iar pentru a mpiedica efectiv crearea de instant e de acest tip, se va specica cuv antul abstract naintea metodei. In exemplele de mai sus, clasele Employee si Shape ar putea g andite ca ind abstracte: ele cont in prea put in a informat ie pentru a putea crea instant e utile. Analog, pentru o anumit a metod a din interiorul unei clase uneori nu se poate specica o implementare. De exemplu, pentru clasa Shape de mai sus,

120

CURS 5. DESTRUCTORI. POO IN C#

este imposibil s a se dea o implementare la metoda Draw(), tocmai din cauza generalit a tii acestei clase. Ar util dac a pentru aceast a metod a programatorul ar obligat s a dea implement ari specice ale acestei metode pentru diversele clase derivate. Pentru a se asigura tratarea polimorc a a acestui tip abstract, orice metod a abstract a este automat si virtual a. Orice metod a care este declarat a abstract a implic a declararea clasei ca ind abstract a. Exemplu: abstract class Shape { public abstract void Draw(); //de remarcat lipsa implementarii si semnul punct si virgula } Orice clas a care este derivat a dintro clas a abstract a va trebui e s a nu aib a nici o metod a abstract a mo stenit a f ar a implementare, e s a se declare ca ind abstract a. Existent a de metode neimplementate nu va permite instant ierea clasei respective.

5.6

Interfet e

O interfat a dene ste un contract. O clas a sau o structur a care implementeaz a o interfat a ader a la acest contract. Relat ia dintre o interfat a si un tip care o implementeaz a este deosebit a de cea existent a ntre clase (este un/o): este o relat ie de implementare. O interfat a poate cont ine metode, propriet a ti, evenimente, indexatori. Ea ns a nu va cont ine implement ari pentru acestea, doar declarat ii. Declararea unei interfet e se face astfel: atributeopt modicatori-de-interfat aopt interface identicator baza-interfet eiopt corp-interfat a ;opt Modicatorii de interfat a sunt: new, public, protected, internal, private. O interfat a poate s a mo steneasc a de la zero sau mai multe interfet e. Corpul interfet ei cont ine declarat ii de metode, f ar a implement ari. Orice metod a are gradul de acces public. Nu se poate specica pentru o metod a din interiorul unei interfet e: abstract, public, protected, internal, private, virtual, override, ori static. Exemplu: interface IStorable { void Read( );

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

121

O clas a care implementeaz a o interfat a trebuie s a deneasc a toate metodele care se reg asesc n interfat a respectiv a. Urm atoarele reguli trebuie respectate la implementarea de interfet e: 1. Tipul de retur al metodei din clas a trebuie s a coincid a cu tipul de retur al metodei din interfat a 2. Tipul parametrilor formali din metod a trebuie s a e acela si cu tipul parametrilor formali din interfat a 3. Metoda din clas a trebuie s a e declarat a public a si nestatic a. Aceste implement ari pot declarate folosind specicatorul virtual (deci subclasele clasei curente pot folosi new si override ). 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 IN C#

} 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 ie sire se va a sa: TextFile reference to ZipFile ZipFile.Read()

5.6. INTERFET E TextFile.Write() ISavable reference to ZipFile ZipFile.Read() TextFile.Write() ZipFile reference to ZipFile ZipFile.Read() ZipFile.Write()

123

In exemplul de mai sus se folo seste operatorul as pentru a obt ine o referint a la interfet e, pe baza obiectelor create. In general, se prefer a ca apelul metodelor care sunt implementate din interfat a s a se fac a via o referint a la interfat a respectiv a, obt inut a prin intermediul operatorului as (ca mai sus) sau dup a o testare prealabil a prin is urmat a de conversie explicit a, ca mai jos: if (textRef is ISavable) { ISavable is = (ISavable)textRef; is.Read();//etc } In general, dac a se dore ste doar r aspunsul la ntrebarea este obiectul curent un implementator al interfet ei I ?, atunci se recomand a folosirea operatorului is. Dac a se stie c a va trebui f acut a si o conversie la tipul interfat a, atunci este mai ecient a folosirea lui as. Armat ia se bazeaz a pe studiul codului IL rezultat n ecare caz. S a presupunem c a exista o interfat a I av and metoda M() care este implementat a de o clas a C, care dene ste metoda M(). Este posibil ca aceast a metod a s a nui aib a o semnicat ie n afara clasei C, ca atare a nu e util ca metoda M() s a e declarat a public a. Mecanismul care permite acest lucru se nume ste ascunderea numelor. Aceast a tehnic a permite ascunderea metodelor mo stenite dintr-o interfat a, acestea devenind private (calicarea lor ca ind publice este semnalat a ca o eroare). Metodele din interfet e care sau implementat explicit (prin calicarea lor cu numele interfet ei 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 c atre interfat a respectiv a, deoarece prin implementare explicit a a metodelor aceste devin private si singura modalitate de acces a lor este upcasting-ul c atre interfat a. Exemplu:

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

CURS 5. DESTRUCTORI. POO IN C#

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 a implementeze mai multe interfet e. Atunci c and dou a interfet e au o metod a cu aceea si semn atur a, programatorul are mai multe variante de lucru. Cel mai simplu, el poate s a furnizeze o singur a implementare pentru ambele metode, ca mai jos: interface IFriendly { void GreetOwner() ;

5.6. INTERFET E } 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 a modalitate este s a se specice implicit care metod a este implementat 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 IN C#

static void Main() { IFriendly mansBestFriend = new Dog() ; mansBestFriend.GreetOwner() ; (mansBestFriend as IAffectionate).GreetOwner() ; } } La ie sire se va a sa: Jump up! Woof! Dac a ns a n clasa Dog se adaug a metoda public void GreetOwner() { Console.WriteLine( "Woof!" ) ; } (care a fost init ial denit a), atunci se poate face apel de tipul dog.GreetOwner() (dog este instant a de Dog); apelurile de metode din interfat a r am an de asemenea valide. Rezultatul este ca si la exemplul precedent: se a seaz a mesajul Woof.

5.6.1

Clase abstracte sau interfet e?

At at interfet ele c at si clasele abstracte au comportamente similare si pot folosite n situat ii similare. Dar totu si ele nu se pot substitui reciproc. C ateva principii generale de utilizare a lor sunt date mai jos. Dac a o relat ie se poate exprima mai degrab a ca este un/o dec at altfel, atunci entitatea de baz a ar trebui g andit a ca o clas a abstract a. Un alt aspect este bazat pe obiectele care ar folosi capabilit a tile din tipul de baz a. Dac a aceste capabilit a ti ar folosite de c atre obiecte care nu sunt legate ntre ele, atunci ar indicat a o interfat a. Dezavantajul claselor abstracte este c a nu poate dec at baz a unic a pentru orice alt a clas a.

Curs 6 Delegat i. Evenimente. Structuri


6.1 Tipul delegat

In programare deseori apare urm atoarea situat ie: trebuie s a se execute o anumit a act iune, dar nu se stie de dinainte care anume, sau chiar ce obiect va trebui efectiv utilizat. De exemplu, un buton poate sti c a trebuie s a anunt e un obiect despre faptul c a fost ap asat, dar nu va sti aprioric pe cine. Mai degrab a dec at s a se lege butonul de un obiect particular, butonul va declara un delegat, pentru care clasa interesat a de evenimentul de ap asare va da o implementare. Fiecare act iune pe care utilizatorul o execut a pe o interfat a grac a declan seaz a un eveniment. Alte evenimente se pot declan sa independent de act iunile utilizatorului: sosirea unui email, terminarea copierii unor siere, sf ar situl unei interog ari pe o baz a de date, etc. Un eveniment este o ncapsulare a ideii c a se nt ampl a ceva la care programul trebuie s a r aspund a. Evenimentele si delegat ii sunt str ans legate deoarece r aspunsul la acest eveniment se va face de c atre un event handler, care n C# se implementeaz a ca un delegat. Un delegat este un tip referint a folosit pentru a ncapsula o metod a cu un anumit antet (tipul parametrilor formali si tipul de retur). Orice metod a care are acela si antet poate legat a la un anumit delegat. Intrun limbaj precum C++, acest lucru se rezolv a prin intermediul pointerilor la funt ii. Delegat ii rezolv a aceea si problem a, dar ntro manier a orientat a obiect si cu garant ii asupra sigurant ei codului rezultat, precum si cu o u soar a generalizare (vezi delegat ii multicast). Un delegat este creat dup a urm atoarea sintax a: 127

128

CURS 6. DELEGAT I. 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 at at n interiorul unei clase, c at si n exteriorul ei, ind de fapt o declarat ie de clas a derivat a din System.Delegate. Dac a este declarat n interiorul unei clase, atunci este si static (a se vedea statutul claselor interioare). Exemplu: public delegate int WhichIsFirst(object obj1, object obj2);

6.1.1

Utilizarea delegat ilor pentru a specica metode la runtime

S a presupunem c a se dore ste crearea unei clase container simplu numit Pair care va cont ine dou a obiecte pentru care va putea face si sortare. Nu se va sti aprioric care va tipul obiectelor cont inute, deci se va folosi pentru ele tipul object. Dar sortarea celor dou a obiecte se va face diferit, n funct ie de tipul lor efectiv: de exemplu pentru ni ste persoane (clasa Student n cele ce urmeaz a) se va face dup a nume, pe c and pentru animale (clasa Dog ) se va face dup a alt criteriu: greutatea. Containerul Pair va trebui s a fac a fat a acestor clase diferite. Rezolvarea se va da prin delegat i. Clasa Pair va deni un delegat, WhichIsFirst. Metoda Sort de ordonare va prelua ca (unic) parametru o instant a a metodei WhichIsFirst, care va implementa relat ia de ordine, n funct ie de tipul obiectelor cont inute. Rezultatul unei comparat ii ntre dou a obiecte va de tipul enumerare Comparison, 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 a care realizeaz a compararea) se declar a astfel: //declarare de delegat public delegate Comparison WhichIsFirst( object obj1, object obj2); Clasa Pair se declar a dup a cum urmeaz 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 si Dog sunt: public class Dog { public Dog(int weight) { this.weight=weight; } //Ordinea este data de greutate public static Comparison WhichDogComesFirst(

129

130

CURS 6. DELEGAT I. 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

Delegat i statici

Unul din aspectele neelegante ale exemplului anterior este c a e necesar ca n clasa Test s a se instant ieze delegat ii care sunt necesari pentru a ordona obiectele din Pair. O modalitate mai bun a este s a se obt in a delegat ii direct din clasele Student si Dog. Acest lucru se obt ine prin crearea unui delegat static n interiorul ec arei clase: public static readonly WhichIsFirst OrderStudents = new WhichIsFirst(Student.WhichStudentComesFirst);

132

CURS 6. DELEGAT I. EVENIMENTE. STRUCTURI

(analog pentru clasa Dog ; de remarcat c a static readonly nu se poate nlocui cu const, deoarece init ilizatosul nu este considerat expresie constant a). Declarat ia de mai sus se folose ste astfel: ... studentpair.Sort(Student.OrderStudent); ... rezultatul ind identic. In [2] este dat a si o implementare de delegat ca proprietate static a, aceasta duc and la crearea unui delegat doar n cazul n care este nevoie de el1 .

6.1.3

Multicasting

Uneori este nevoie ca un delegat s a poat a apela mai mult de o singur a metod a. De exemplu, atunci c and un buton este ap asat, se poate s a vrei s a efectuezi mai mult de o sigur a act iune: s a scrii ntrun textbox un sir de caractere si s a nregistrezi ntrun sier faptul c a sa ap asat acel buton (logging). Acest lucru sar putea rezolva prin construirea unui vector de delegat i care s a cont in a toate metodele dorite, ns a acest lucru ar duce la un cod greu de urm arit si inexibil; pentru un astfel de exemplu, a se vedea [2], pag. 261265. Mult mai simplu ar dac a un delegat ar putea s a cont in a mai mult de o singur a metod a. Acest lucru se nume ste multicasting si este folosit intens la tratarea evenimentelor (vezi sect iunea urm atoare). Orice delegat care returnez a void este un delegat multicast, care poate tratat si ca un delegat single-cast. Doi delegat i multicast pot combinat i folosind semnul +. Rezultatul unei astfel de adun ari este un nou delegat multicast care la apelare va invoca metodele cont inute, n ordinea n care sa f acut adunarea. De exemplu, dac a Writer si Logger sunt delegat i care returneaz a void, atunci urm atoarea linie va produce combinarea lor ntrun singur delegat: myMulticastDelegate = Writer + Logger; Se pot ad auga delegat i multicast folosind operatorul +=, care va ad auga delegatul de la dreapta operatorului la delegatul multicast aat n st anga sa: myMulticastDelegate += Transmitter; presupun and c a Transmitter este compatibil cu myMulticastDelegate (are aceea si semn atur a). Operatorul = funct ioneaz a invers fat a de + =. Exemplu:
1

Tehnic a numit a init ializaer t arzie (lazy initialization)

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. DELEGAT I. 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 ie sire vom avea: 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

Interfet ele grace actuale cer ca un anumit program s a r aspund a la evenimente. Un eveniment poate de exemplu ap asarea unui buton, terminarea transferului unui sier, selectarea unui meniu, etc; pe scurt, se nt ampl a ceva la care trebuie s a se dea un r aspuns. Nu se poate prezice ordinea n care se petrec evenimentele, iar la aparit ia unuia se va cere tratarea sa. Alte clase pot interesate n a r aspunde la aceste evenimente. Modul n care vor r aspunde va extrem de particular, iar clasa care genereaz a evenimentul (ex: clasa Button, la ap asarea unui buton) nu trebuie s a stie modul n care se va r aspunde. Butonul va comunica faptul c a a fost ap asat, iar clasele interesate n acest eveniment vor react iona n consecint a.

6.2.1

Publicarea si subscrierea

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

6.2.2

Evenimente si delegat i

Tratarea evenimentelor n C# se face folosind delegat i. Clasa ce public a dene ste un delegat pe care clasele abonate trebuie s a l implementeze. C and evenimentul este declan sat, metodele claselor abonate vor apelate prin
2 3

Engl: publisher Engl: subscribers

136

CURS 6. DELEGAT I. EVENIMENTE. STRUCTURI

intermediul delegatului (pentru care se prevede posibilitatea de a multicast, astfel nc at s a se permit a mai mult i abonat i). Metodele care r aspund la un eveniment se numesc event handlers. Prin convent ie, un event handler n .NET Framework returneaz a void si preia doi 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 urm atoarele: o clas a Clock care folose ste un eveniment (OnSecondChange ) pentru a notica potent ialii abonat i atunci c and timpul local se schimb a cu o secund a. Tipul acestui eveniment este un delegat SecondChangeHandler care se declar a astfel: public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation ); n conformitate cu metodologia de declarare a unui event handler, pomenit a mai sus. Tipul TimeInfoEventArgs este denit de noi ca o clas a derivat a din 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 a clas a va cont ine informat ie despre timpul curent. Informat ia este accesibil a readonly. Clasa Clock va cont ine o metod a Run() :

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 a un ciclu innit care interogheaz a periodic ceasul sistem. Dac a timpul sa schimbat cu o secund a fat a de timpul precedent, se vor notica toate obiectele abonate dup a care si va modica starea, prin cele trei atribuiri nale. Tot ce r am ane de f acut este s a se scrie ni ste clase care s a subscrie la evenimentul publicat de clasa Clock. Vor dou a clase: una numit a DisplayClock care va a sa pe ecran timpul curent si o alta numit a LogCurrentTime care ar trebui s a nregistreze evenimentul ntrun sier, dar pentru simplitate va a sa doar la dispozitivul curent de ie sire informat ia tranmsis a: public class DisplayClock {

138

CURS 6. DELEGAT I. 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 a evenimentele sunt ad augate folosind operatorul +=. 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. DELEGAT I. 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 ie sire se va a sa: 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. DELEGAT I. 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 urm atoarea ntrebare: de ce este nevoie de o astfel de redirectare de eveniment, c and n metoda Run() se poate a sa direct pe ecran sau ntrun sier informat ia cerut a? Avantajul abord arii anterioare este c a se pot crea oric ate clase care s a e noticate atunci c and acest eveniment se declan seaz a. Clasele abonate nu trebuie s a stie despre modul n care lucreaz a clasa Clock, iar clasa Clock nu trebuie s a stie despre clasele care vor subscrie la evenimentul s au. Similar, un buton poate s a publice un eveniment OnClick si orice num ar de obiecte pot subscrie la acest eveniment, primind o noticare atunci c and butonul este ap asat. Publicatorul si abonat ii sunt decuplat i. Clasa Clock poate s a modice modalitatea de detectare a schimb arii de timp f ar a ca acest lucru s a impun a o schimbare n clasele abonate. De asemenea, clasele abonate pot s a si modice modul de tratare a evenimentului, n mod transparent fat a de clasa Clock. Toate aceste caracteristici fac ntret inerea codului extrem de facil a.

6.3

Structuri

Structurile reprezint a tipuri de date asem an atoare claselor, cu principala diferent a c a sunt tipuri valoare (o astfel de variabil a va cont ine direct valoarea, si nu o adres a de memorie). Sunt considerate versiuni u soare ale claselor, sunt folosite predilect pentru tipuri pentru care aspectul comportamental este mai put in pronunt at. Declarat ia unei structuri se face astfel: atributeopt modicatori-de-structopt struct identicator :interfet eopt corp ;opt Modicatorii de structur a sunt: new, public, protected, internal, private. O structur a este automat derivat a din System.ValueType, care la r andul ei este derivat a din System.Object ; de asemenea, este automat considerat a sealed (sect. 5.3). Poate ns a s a implementeze una sau mai multe interfet e. O structur a poate s a cont in a declarat ii de constante, c ampuri, metode, propriet a ti, evenimente, indexatori, operatori, constructori, constructori statici, tipuri interioare. Nu poate cont ine destructor. La atribuire, se face o copiere a valorilor cont inute de c atre surs a n destinat ie (indiferent de tipul c ampurilor: valoare sau referint 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. DELEGAT I. 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 a cum este dat n exemplul de mai sus, crearea unei instant e se face folosind operatorul new ; dar n acest caz, nu se va crea o instant a n memoria heap, ci pe stiv a. Transmiterea lui loc1 ca parametru se face prin valoare, adic a metoda myFunc nu face dec at s a modice o copie de pe stiv a a lui loc1. La revenire, se va a sa tot valoarea original a, deoarece loc1 a r amas nemodicat: Loc1 location: 200, 300 In MyFunc loc: 50, 100 Loc1 location: 200, 300 Deseori pentru o structur a se declar a c ampurile ca ind publice, pentru a nu mai necesare denirea accesorilor (simplicare implement arii). Alt i programatori consider a ns a c a accesarea membrilor trebuie s a se fac a precum la clase, folosind propriet a ti. Oricare ar alegerea, limbajul o sprijin a. Alte aspecte demne de ret inut: C ampurile nu pot init ializate la declarare; altfel spus, dac a n exemplul 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 init ializa c ampurile la valorile lor implicite (0 pentru tipuri numerice sau pentru enumer ari, false pentru bool, null pentru tipuri referint a). Pentru tipul Point de mai sus, urm atoarea secvent a de cod este corect a: Point a = new Point(0, 0); Point b = new Point(); si duce la crearea a dou a puncte cu abcisele si ordonatele 0. Un constructor implicit este apelat atunci c and se creeaz a un tablou de structuri: Point[] points = new Points[10]; for( int i=0; i<points.Length; i++ ) { Console.WriteLine(points[i]); } va a sa de 10 ori puncte de coordonate (0, 0). De asemenea este apelat la init ializarea membrilor unei clase (care se face nainte de orice constructor). De ment ionat pentru exemplul anterior c a se creeaz a un obiect de tip tablou n heap, dup a care n interiorul lui ( si nu pe stiv a!) se creeaz a cele 10 puncte (alocare inline). Nu se poate declara destructor. Acest ia se declar a numai pentru clase. Dac a programatorul dene ste un constructor, atunci acesta trebuie s a dea valori init iale pentru c ampurile cont inute, altfel apare eroare la compilare. Dac a pentru instant ierea unei structuri declarate n interiorul unei metode sau bloc de instruct iuni n general nu se apeleaz a new, atunci respectiva instant a nu va avea asociat a nici o valoare (constructorul implicit nu este apelat automat!). Nu se poate folosi respectiva variabil a de tip structur a dec at dup a ce i se init ializeaz a toate c ampurile: Point p; Console.WriteLine(p); va duce la aparit ia erorii de compilare:

146

CURS 6. DELEGAT I. EVENIMENTE. STRUCTURI Use of unassigned local variable p Dar dup a ni ste asign ari de tipul: p.xVal=p.yVal=0; a sarea este posibil a (practic, orice apel de metod a pe instant a este acum acceptat).

Dac a se n cearc a denirea unei structuri care cont ine un c amp de tipul structurii, atunci va ap area o eroare de compilare: 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 a o instant a este folosit a acolo unde un object este necesar, atunci se va face automat o conversie implicit a c atre System.Object (boxing). Ca atare, utilizarea unei structuri poate duce (dar nu obligatoriu, ci n funct ie de context) la un overhead datorat conversiei.

6.3.1

Structuri sau clase?

Structurile pot mult mai eciente n alocarea memoriei atunci c and sunt ret inute 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 instant e de tip structur a ar alocate inline n vectorul creat ( si nu referint e ale acestora). Dac a Point ar declarat ca si clas a, ar fost necesar a crearea a 101 instant e de obiecte n heap (un obiect pentru tablou, alte 100 pentru puncte), ceea ce ar duce la mai mult lucru pentru garbage collector. Dar n cazul n care structurile sunt folosite n colect ii (care stocheaz a object ), se va face automat un boxing, ceea ce duce la overhead (memorie si timp = resurse suplimentare). De asemenea, la transmiterea prin valoare a unei structuri, se va face copierea tuturor c ampurilor cont inute pe stiv a, ceea ce poate duce la un overhead semnicativ.

Curs 7 Tratarea except iilor. Atribute


7.1 Tratarea except iilor

C#, la fel ca alte limbaje, permite tratarea erorilor si a situat iilor deosebite prin except ii. O except ie este un obiect care ncapsuleaz a informat ie despre o situat ie anormal a. Ea este folosit a pentru a semnala contextul n care apare situat ia deosebit a Un programator nu trebuie s a confunde tratarea except iilor cu erorile sau bugurile. Un bug este o eroare de programare care ar trebui s a e xat a nainte de livrarea codului. Except iile nu sunt g andite pentru a preveni bug urile (cu toate c a un bug poate s a duc a la aparit ia unei except ii), pentru c a acestea din urm a ar trebui s a e eliminate. Chiar dac a se scot toate bugurile, vor exista erori predictibile dar neprevenibile, precum deschiderea unui sier al c arui nume este gre sit sau mp art iri la 0. Nu se pot preveni astfel de situat ii, dar se pot manipula astfel nc at nu vor duce la pr abu sirea programului. C and o metod a nt alne ste o situat ie except ional a, atunci se va arunca o except ie; cineva va trebui s a sesizeze (s a prind a) aceast a except ie, sau eventual s a lase o funct ie de nivel superior s a o trateze. Dac a nimeni nu trateaz a aceast a except ie, atunci CLR o va face, dar aceasta duce la oprirea threadului 1 .

7.1.1

Tipul Exception

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

S i nu neap arat a ntregului proces!

147

148

CURS 7. TRATAREA EXCEPT IILOR. ATRIBUTE

Enumer am urm atoarele metode si propriet a ti relevante ale clasei Exception : public Exception(), public Exception(string), public Exception(string, Exception) - constructori; ultimul preia un obiect de tip Exception (sau de tip clas a derivat a) care va ncapsulat n instant a curent a; o except ie poate deci s a cont in a n interiorul s au o instant a a altei except ii (cea care a fost de fapt semnalat a init ial). public virtual string HelpLink {get; set;} obt ine sau seteaz a un link c atre un sier help asociat acestei except ii; poate de asemenea o adresa Web (URL) public Exception InnerException {get;} returnez a except ia care este ncorporat a n except ia curent a public virtual string Message {get;} obt ine un mesaj care descrie except ia curent a public virtual string Source {get; set;} obt ine sau seteaz a numele aplicat iei sau al obiectului care a cauzat eroarea public virtual string StackTrace {get;} obt ine o reprezetare string a apelurilor de metode care au dus la aparit ia acestei except ii public MethodBase TargetSite {get;} obt ine metoda care a aruncat except ia curent a2

7.1.2

Aruncarea si prinderea except iilor

Aruncarea cu throw Aruncarea unei except ii se face folosind instruct iunea throw. Exemplu: throw new System.Exception(); Aruncarea unei except ii opre ste execut ia metodei curente, dup a care CLR ncepe s a caute un manipulator de except ie. Dac a un handler de except ie nu este g asit n metoda curent a, atunci CLR va cur a ta stiva, ajung anduse la metoda apelant a. Fie undeva n lant ul de metode care au fost apelate se g ase ste un exception handler, e threadul curent este terminat de c atre CLR. Exemplu:
MethodBase este o clas a care pune la dispozit ie informat ii despre metodele si constructorii unei clase
2

7.1. TRATAREA EXCEPT IILOR 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 a apelul de metode: Main() apeleaz a Func1(), care apeleaz a Func2() ; aceasta va arunca o except ie. Deoarece lipse ste un event handler care s a trateze aceast a excepct ie, se va ntrerupe threadul curent ( si ind singurul, si ntregul proces) de c atre CLR, iar la ie sire vom avea: 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 a o except ie, n metoda Func2() nu se va mai executa ultima linie, ci CLRul va ncepe imediat c autarea event handlerului care

150

CURS 7. TRATAREA EXCEPT IILOR. ATRIBUTE

s a trateze except ia. La fel, nu se execut a nici ultima linie din Func1() sau din Main(). Prinderea cu catch Prinderea si tratarea except iei se poate face folosind un bloc catch, creat prin intermediul instruct iunii 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 EXCEPT IILOR

151

Se observ a c a sa folosit un bloc try pentru a delimita instruct iunile care vor duce la aparit ia except iei. In momentul n care se arunc a except ia, restul instruct iunilor din blocul try se ignor a si controlul este preluat de c atre blocul catch. Deoarece except ia a fost tratat a, CLRul nu va mai opri procesul. La ie sire se va a sa: Enter Main... Enter Func1... Enter Func2... Entering try block... Exception caught and handled. Exit Func2... Exit Func1... Exit Main... Se observ a c a n blocul catch nu sa specicat tipul de except ie care se prinde; asta nseamn a c a se va prinde orice except ie se va arunca, indiferent de tipul ei. Chiar dac a except ia este tratat a, execut ia nu se va relua de la instruct iunea care a produs except ia, ci se continu a cu instruct iunea de dup a blocul catch. Uneori, prinderea si tratatarea except iei nu se poate face n funct ia apelat a, ci doar n funct ia apelant a. 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...); try { Console.WriteLine(Entering try block...); Func2( ); Console.WriteLine(Exiting try block...); }

152

CURS 7. TRATAREA EXCEPT IILOR. 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 ie sire se va a sa: Enter Main... Enter Func1... Entering try block... Enter Func2... Exception caught and handled. Exit Func1... Exit Main... Este posibil ca ntro secvent a de instruct iuni s a se arunce mai multe tipuri de except ii, n funct ie de natura st arii ap arute. In acest caz, prinderea except iei printrun bloc catch generic, ca mai sus, nu este util a; am vrea ca n funct ie de natura except iei aruncate, s a facem o tratare anume. Se sugereaza chiar s a nu se foloseasc a aceast a construct ie de prindere generic a, deoarece n majoritatea cazurilor este necesar s a se cunoasc a natura erorii (de exemplu pentru a scris a ntr-un sier de logging, pentru a consultat a mai t arziu). Acest lucru se face specic and tipul except iei care ar trebui tratate n blocul catch : using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); }

7.1. TRATAREA EXCEPT IILOR

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; } } In exemplul de mai sus sa convenit ca o mp art ire cu numitor 0 s a duc a la o execept ie System.DivideByZeroException, iar o mp art ire cu num ar ator 0 s a duc a la aparit ia unei except ii de tip System.ArithmeticException. Este posibil a specicarea mai multor blocuri de tratare a except iilor. Aceste blocuri sunt parcurse n ordinea n care sunt specicate, iar primul tip care se potrive ste cu except ia aruncat a ( n sensul c a tipul except ie specicat este

154

CURS 7. TRATAREA EXCEPT IILOR. ATRIBUTE

e exact tipul obiectului aruncat, e un tip de baz a al acestuia - din cauz a de upcasting) este cel care va face tratarea except iei ap arute. Ca atare, este important ca ordinea except iilor tratate s a e de la cel mai derivat la cel mai general. In exemplul anterior, System.DivideByZeroException este derivat din clasa System.ArithmeticException. Blocul nally Uneori, aruncarea unei except ii si golirea stivei p an a la blocul de tratare a except iei poate s a nu e o idee bun a. De exemplu, dac a except ia apare atunci c and un sier este deschis ( si nchiderea lui se poate face doar n metoda curent a), atunci ar util ca s a se nchid a sierul nainte ca s a e preluat controlul de c atre metoda apelant a. Altfel spus, ar trebui s a existe o garant ie c a un anumit cod se va executa, indiferent dac a totul merge normal sau apare o except ie. Acest lucru se face prin intermediul blocului nally, care se va executa n orice situat ie. Existent a acestui bloc elimin a necesitatea existent ei blocurilor catch (cu toate c a si acestea pot s a apar 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 EXCEPT IILOR } 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

In exemplul de mai sus, mesajul Close le here se va a sa indiferent de ce parametri se transmit metodei DoDivide(). La aruncarea unei except ii se poate particulariza obiectul care se arunc a: if (b == 0) { DivideByZeroException e = new DivideByZeroException( ); e.HelpLink = http://www.greselifatale.com; throw e; } iar c and except ia este prins a, se poate prelucra informat ia: catch (System.DivideByZeroException e) { Console.WriteLine( DivideByZeroException! goto {0} and read more, e.HelpLink); } Crearea propriilor except ii In cazul n care suita de except ii predenite nu este sucient a, programatorul si poate construi propriile tipuri. Se recomand a ca acestea s a e derivate din System.ApplicationException, care este derivat a direct din System.Exception. Se indic a aceast a derivare deoarece astfel se face distinct ie ntre except iile aplicat ie si cele sistem (cele aruncate de c atre CLR). Exemplu: using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message): base(message)

156 { } }

CURS 7. TRATAREA EXCEPT IILOR. 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 EXCEPT IILOR

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 except iilor Este perfect posibil ca ntrun bloc de tratare a except iilor s a se se fac a o tratare primar a a except iei, dup a care s a se arunce mai departe o alt a except ie, de acela si tip sau de tip diferit (sau chiar except ia original a). Dac a se dore ste ca aceast a except ie s a p astreze cumva n interiorul ei except ia original a, atunci constructorul permite nglobarea unei referint e la aceasta; aceast a referint a va accesibil a prin intermediul propriet a tii InnerException : 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 EXCEPT IILOR. 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 EXCEPT IILOR 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 a dac a se dorec ste revenirea la codul care a produs except ia, dup a tratarea ei? Exist a destule situat ii n care reexecutarea acestui cod este dorit a: s a ne g andim de exemplu la cazul n care ntr-o fereastr a de dialog se specic a numele unuei sier ce trebuie procesat, numele este introdus gre sit si se dore ste ca s a se permit a corectarea numelui. Un alt exemplu clasic este cazul n care autorul unei metode stie c a o operat ie poate s a e sueze periodic de exemplu din cauza unui timeout pe ret ea dar vrea s a re ncerce operat ia de n ori nainte de a semnala eroare. In aceast a situat ie se poate deni o etichet a naintea blocului try la care s a se permit a salutl printrun goto. Urm atorul exemplu permite unui utilizator s a specice de maxim trei ori numele unui sier ce se proceseaz a, cu revenire n cazul erorii.

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

CURS 7. TRATAREA EXCEPT IILOR. 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 EXCEPT IILOR

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 n general returnarea unui cod de eroare c atre metoda apelant a. Ca atare, apelantul are sarcina de a descifra acest cod de eroare si s a react ioneze n consecint a. Ins a a sa cum se arat a mai jos, tratarea except iilor este superioar a acestei tehnici din mai multe motive.

Neconsiderarea codului de retur Apelul unei funct ii care returneaz a un cod de eroare poate f acut si f ar a a utiliza efectiv codul returnat, scriind doar numele funct iei cu parametrii de apel. Dac a de exemplu pentru o anmit a procesare se apeleaz a metoda A (de exemplu o deschidere de sier) dup a care metoda B (citirea din sier), se poate ca n A s a apar a o eroare care nu este luat a n considerare; apelul lui B este deja sortit e secului, pentru c a buna sa funct ionare depinde de efectele lui A. Dac a ns a metoda A arunc a o except ie, atunci nici m acar nu se mai ajunge la apel de B, deoarece CLR-ul va pasa execut ia unui bloc catch/finally. Altfel spus, nu se permite o propagare a erorilor.

162

CURS 7. TRATAREA EXCEPT IILOR. ATRIBUTE

Manipularea erorii n contextul adecvat In cazul n care o metod a A apeleaz a alte metode B1 , . . . Bn , este posibil ca oricare din aceste n metode s a cauzeze o eroare ( si s a returneze cod adecvat); tratarea erorii din exteriorul lui A este dicil a n acest caz, deoarece ar trebui s a se cerceteze toate codurile de eroare posibile pentru a determina motivul aparit iei erorii. Dac a se mai adauga si apelul de metod a Bn+1 n interiorul lui A, atunci orice apel al lui A trebuie s a includ a suplimentar si vericarea pentru posibilitatea ca Bn+1 s a cauzat o eroare. Ca atare, costul ment inerii codului cre ste permanent, ceea ce are un impact negativ asupra TCO-ului3 Folosind tratarea except iilor, arunc and except ii cu mesaje de eroare explicite sau except ii de un anumit tip (denit de programator) se poate trata mult mai convenabil o situat ie deosebit a. Mai mult dec at at at, introducerea apelului lui Bn+1 n interiorul lui A nu reclam a modicare suplimentar a, deoarece tipul de except ie aruncat de Bn+1 este deja tratat (desigur, se presupune c a se dene ste un tip except ie sau o ierarhie de except ii creat a convenabil). U surint a citirii codului Pentru comparat ie, se poate scrie un cod care realizeaz a procesarea cont inutului unui sier folosind coduri de eroare returnate sau except ii. In primul caz, solut ia va cont ine cod de prelucrare a cont inutului mixat cu cod de vericare si react ie pentru diferitele cazuri de except ie. Codul n al doilea caz este mult mai scurt, mai u sor de nt eles, de ment inut, de corectat si extins. Aruncarea de except ii din constructori Nimic nu opre ste ca o situat ie deosebit a s a apar a ntrun apel de constructor. Tehnica veric arii codului de retur nu mai funct ioneaz a aici, deoarece un constructor nu returneaz a valori. Folosirea except iilor este n acest caz aproape de ne nlocuit.

7.1.5

Sugestie pentru lucrul cu except iile

In Java, programatorii trebuei s a declare c a o metod a poate arunca o except ie si s a o declare explicti ntro list a astfel nc at un apelant s a stie c a se poate se poate a stepta la primirea ei. Aceast a cunoa stere n avans permite conceperea unui plan de lucru cu ecare dintre ele, preferabil dec at s a se prind a oricare dintre ele cu un catch generic. In cazul .NET se sugereaz a
3

Total Cost of Ownership.

7.2. ATRIBUTE

163

s a se ment in a o documentat ie cu except iile care poti aruncate de ecare metod a.

7.2

Atribute

O aplicat ie .NET cont ine cod, date si metadate. Metadata este o dat a despre assembly (versiune, producator, etc), tipuri de date cont inute, etc stocate mpreun a cu codul compilat. Atributele sunt folosite pentru a da o extra-informat ie compilatorului de .NET. Java folose ste o combinat ie de semne /** si @ pentru a include informat ie relativ la clase, metode, c ampuri sau parametri. Aceste comentarii ns a nu vor incluse n bytecod-ul nal, doar n eventuala documentat ie. Folosind atributele, aceast a informat ie poate stocat a n codul compilat si reutilizat a ulterior la runtime. Unde ar utile aceste atribute? Exemple de utilizare a lor ar urm arirea bugurilor care exist a ntrun sistem sau urm arirea stadiului proiectului. De asemenea, anumite atribute predenite sunt utilizate pentru a specica dac a un tip este sau nu serializabil, care sunt port iunile de cod scoase din circulat ie, informat ii despre versiunea assembly-ului, etc.

7.2.1

Generalit a ti

Atributele sunt de dou a feluri: intrinseci (predenite) si denite de utilizator. Cele intrinseci sunt integrate n platforma .NET si sunt recunoscute de CLR. Atributele denite de utilizator sunt create n funct ie de dorint ele acestuia. Atributele se pot specica pentru: assemblyuri, clase, constructori, delegat i, enumer ari, evenimente, c ampuri, interfet e, metode, module, parametri, propriet a ti, valori de retur, structuri. T inta unui atribut specic a cui anume i se va aplica un atribut anume. Tabelul 7.1 cont ine tintele posibile si o scurt a descriere a lor. Tabelul 7.1: T intele atributelor. T int 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 EXCEPT IILOR. ATRIBUTE Tabelul 7.1 (continuare) Descriere Un delegat O enumerare Un eveniment Un c amp O interfat a O metod a Un modul Un parametru O proprietate O valoare de retur O structur a

T int 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 specic aunul deasupra celuilalt, e n interiorul acelora si paranteze, desp art ite prin virgul a. Exemplu: [Serializable] [Webservice] echivalent cu: [Serializable, Webservice] In cazul atributelor ce se specic a pentru un assembly, forma lor este: [assembly:AssemblyDelaySign(false)] n cazul acestor din urm a atribute, specicarea lor se face dup a toate declarat iile using, dar naintea oric arui cod. In general, specicarea unui atribut se face prin scrierea lui imediat naintea elementului asupra c aruia se aplic a: using System; [Serializable] class ClasaSerializabila { //definitia clasei }

7.2.2

Atribute predenite

Tabelul 7.2 prezint a c ateva dintre ele:

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

165

Descriere Permite unei clase s a e serializat a pe disc sau ntro ret ea System.NonSerializedAttribute [NonSerialized] Permite unor membri s a nu e salvat i pe ret ea sau pe disc System.Web.Services.WebServiceAttribute Permite specicarea unui nume si a [WebService] unei descrieri pentru un serviciu Web System.Web.Services.WebMethodAttribute Marcheaz a o metod a ca ind expus a [WebMethod] ca parte a unui serviciu Web System.AttributeUsageAttribute Dene ste parametrii de utilizare [AttributeUsage] pentru atribute System.ObsoleteAttribute [Obsolete] Marcheaz a o secvent a ca ind scoas a din uz System.Reection.AssemblyVersionAttribute Specic a num arul de versiune al [AssemblyVersion] unui assembly System.Attribute.CLSCompliant Indic a dac a un element de program este [CLSCompliant] compatibil cu CLS System.Runtime.InteropServices. specic a locat ia DLL care cont ine DllImportAttribute [DllImport] implementarea pentruo metod a extern a Intre parantezele drepte se arat a cum se specic a acel atribut. Exemplul de mai jos folose ste atributul System.ObsoleteAttribute. 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 EXCEPT IILOR. 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 a un avertisment care semnalizeaz a faptul c a se utilizeaz a o metod a care este scoas a din uz. Mesajul specicat ca parametru al atributului este a sat ca mesaj al avertismentului. Suplimentar, pentru atributul Obsolete se poate specica dac a respectivul avertisment este sau nu interpretat ca o eroare, ad aug and un parametru de tip bool: false ca la compilare s a se genereze avertisment, true pentru ca utilizarea s a e tratat a ca o eroare. Exemplul de mai jos exemplic a serializarea si deserializarea unui obiect: 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 ata seaz a la o metod a pentru care se dore ste ca atunci c and compilatorul o nt alne ste la un apel, dac a un anumit simbol nu e denit atunci nu va chemat a. Este folosit a pentru a omite anumite apeluri de 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 EXCEPT IILOR. 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 n dou a moduri: prin folosirea unei directive de preprocesor de tipul #dene: #define DEBUG prin folosirea parametrilor din linia de comanda sau a posibilit a tilor mediului integrat de dezvoltare. De exemplu, pentru a se deni un anumit simbolul din Visual Studio se procedeaz a astfel: clic dreapta n Solution Explorer pe numele proiectului, selectare Properties apoi Conguration Properties. Pe linia Conditional Compilation Constants se adaug a simbolul dorit. Acest lucru va avea ca efect folosirea opt iunii dene a compilatorului. Dac a la compilare simbolul nu este denit atunci compilatorul va ignora apelurile de metode calicate cu atributul Conditional. Acest lucru se paote verica at at urm arind execut ia programului, c at si din inspect ia codului IL rezultat la compilare. Atributul CLSCompliant Acest atribut se aplic a pe assemblyuri. Dac a un assembly este marcat ca ind CLSCompliant, orice tip expus public n assembly care nu este compatibil CLS trebuie s a e marcat cu CLSCompliant(false). De exmplu, CLS prevede faptul c a tipurile de date ntregi ar trebui s a e cu semn. O clas a poate s a cont in a membri (c ampuri, metode) de tip unsigned, dar dac a respectiva clas a este declarat a ca ind CLSCompliant atunci acestea ar trebui

7.2. ATRIBUTE

169

declarate ca ind ne-vizibile din afara assemblyului. Dac a ele sunt expuse n afara assemblyului, unele limbaje .NET s-ar putea s a nu le poat a folosi efetiv. Pentru a ajuta dezvoltatorii .NET s a evite asemenea situat ii, platforma pune la dispozit ie atributul CLSCompliant cu care se controleaz a r aspunsul compilatorului la expunerea entit a tilor ne-compatibile cu CLS; astfel, se va 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 a se compileaz a assembly-ul de mai sus atunci apare o eroare: Type of DemoCLS.ComplianceTest.B is not CLS-compliant Pentru ca aceast a eroare s a nu mai apar a putem s a declar am B ca ind invizibil din exteriorul assembly-ului sau s a ad aug am inaintea lui B atributul: [CLSCompliant(false)] Meritul acestui atribut este c a anunt a programatorul despre eventualele neconcordant e cu Common Language Specications.

170

CURS 7. TRATAREA EXCEPT IILOR. ATRIBUTE

7.2.4

Atribute denite de utilizator

In general, atributele predenite acoper a marea majoritate a situat iilor care cer utilizarea de atribute. Eventualizatea ca utilizatorul s a doreasc a crearea propriilor sale atribute este prev azut a de c atre platforma .NET, d anduse posibilitatea denirii lor. Exist a c ateva situat ii n care e benec a denirea de noi atribute. Exemplul cel mai des nt alnit este acela n care pentru a se ment ine informat ii despre un cod la care lucreaz a mai multe echipe, se dene ste un atribut care s a serveasc a la pasarea de informat ie relativ la port iuni din cod. Un alt exemplu este utilizarea unui sistem de urm arire a stadiului de dezvoltare a codului, care ar folosi informat ia stocat a n atribute. Un atribut utilizator este o clas a denit a ca p an a acum. Se cere a derivat a din System.Attribute e direct, e indirect. Sunt c a tiva pa si care trebuie parcur si pentru realizara unui atribut: specicarea tintei, derivarea ei adecvat a, denumirea ei n conformitate cu anumite reguli (recomandat, dar nu obligatoriu), denirea clasei. Pasul 1. Declararea tintei Primul pas n crearea unui atribut utilizator este specicarea domeniului s au de aplicabilitate, adic a a elementelor c arora li se poate ata sa. T intele sunt cele din tabelul 7.1, care sunt de fapt valori din enumerarea AttributeTargets. Specicarea tintei se face prin intermediul unui (meta)atribut AttributeUsage. Pe l ang a tinta propriuzis a, se mai pot specica valorile propriet a tilor Inherited si AllowMultiple. Proprietatea Inherited este de tip boolean si precizeaz a dac a atributul poate mo stenit de c atre clasele derivate din cele c areia i se aplic a. Valoarea implicit a este true. Proprietatea AllowMultiple este de asemenea de tip boolean si specic a dac a se pot utiliza mai multe instant e ale atributului pe un acela si element. Valoarea implicit a este false. Vom deni mai jos tinta atributului pe care l vom construi ca ind 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, urm atoarele reguli trebuie s a e avute n vedere:

7.2. ATRIBUTE

171

(obligatoriu) O clas a atribut trebuie s a deriveze direct sau indirect System.Attribute (obligatoriu) O clas a atribut trebuie s a e declarat a ca ind public a (recomandat) Un atribut ar trebui s a aib a suxul Attribute. Vom declara clasa atribut CodeTrackerAttribute. Acest atribut s-ar putea folosi n cazul n care s-ar dori urm arirea dezvolt arii codului n cadrul unui proiect de dimensiuni foarte mari, la care particip a mai multe echipe. Acest atribut utilizator va inclus n codul compilat si distribuit altor echipe (care nu vor avea acces la cod). O alternativ a ar folosirea documentatt iei XML generate dup a comentariile din cod. [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] public class CodeTrackerAttribute : System.Attribute { //aici e codul dumneavoastra } Pasul 3. Declararea constructorilor si a propriet a tilor Acest pas const a n denirea efectiv a membrilor clasei. Vom considera c atributul pe care l cre am va purta trei informat ii: numele programatorului care a act ionat asupra respectivei unit a ti de cod, faza n care se a a, opt ional note. Primele dou a atribute sunt mandatorii si vor preluate prin constructor, al treilea poate setat prin intermediul unei propriet a ti. 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 EXCEPT IILOR. 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 n acela si mod ca si cele implicite. Codul de mai jso exemplic a acest lucru: [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 inspet ie a codului IL rezultat arat a c a aceste atribute s-au salvat n codul compilat. Obt inerea acestor atribute si a valorilor lor se poate face foarte u sor prin reectare.

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

ADO.NET reprezint a o parte component a a lui .NET Framework ce permite aducerea, manipularea si modicarea datelor. In mod normal, o surs a de date poate s a e o baz a de date, dar de asemenea un sier text sau Excel sau XML sau Access. Lucrul se poate face e conectat, e deconectat de la sursa de date. De si exist a variate modalit a ti de lucru cu bazele de date, ADO.NET se impune tocmai prin faptul c a permite lucrul deconectat de la baza de date, integrarea cu XML, reprezentarea comun a a datelor cu posibilitatea de a combina date din variate surse, toate pe baza unor clase .NET. Faptul c a se permite lucrul deconectat de la sursa de date rezolv a urm atoarele probleme: ment inerea conexiunilor la baza de date este o operat ie costisitoare. O bun a parte a l a timii de band a este ment inut a ocupat a pentru ni ste proces ari care nu necesit a neap arat conectare continu a probleme legate de scalabilitatea aplicat iei: se poate ca serverul de baze de date s a lucreze u sor cu 5-10 conexiuni ment inute, dar dac a num arul acestora cre ste aplicat ia poate s a react ioneze extrem de lent pentru unele servere se impun clauze asupra num arului de conexiuni ce se pot folosi simultan. Toate acestea fac ca ADO.NET s a e o tehnologie mai potrivit a pentru dezvoltarea aplicat iilor Internet dec at cele precedente (e.g. ADO, ODBC). 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 n cele ce urmeaz a preponderent pe baz a de date Microsoft SQL 2000. Pentru lucrul cu MS SQL Server se poate instala gratuit versiunea gratuit a de Microsoft SQL Server 2000 Desktop Engine1 .

8.2

Furnizori de date n ADO.NET

Din cauza existent ei mai multor tipuri de surse de date (de exemplu, a mai multor produc atori de servere de baze de date) e nevoie ca pentru ecare tip major de protocol de comunicare s a se foloseasc a o bibliotec a specializat a de clase. Toate aceste clase implementeaz a ni ste interfet e bine stabilite, ca atare trecerea de la un SGBD la altul se face cu eforturi minore (dac a codul este scris tin and cont de principiile POO). Exist a urm atorii furnizori de date2 (lista nu este complet 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 a ODBC (baze de date vechi) Surse de date care expun o interfat a OleDb, de exemplu Access si Excel sau SQL Sever versiune mai veche de 7.0 SGBD Oracle Pentru interact iune cu Microsoft SQL Server 7.0, 2000, 2005 sau MSDE Acces generic la SGBD-uri precum Interbase, SQL Server, IBM DB2, Oracle

Prexele trecute n coloana Prex sunt folosite pentru clasele de lucru 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 a n patru componente: Connection, 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 a n gura 8.1

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 cont in date specice conexiunii, cum ar locat ia sursei de date, numele si parola contului de acces, etc. In plus au metode pentru deschiderea si nchiderea conexiunilor, pornirea unei tranzact ii sau setarea perioadei de time-out. Stau la baza oric arei ncerc ari de conectare.

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) si pentru a furniza un obiect de tip DataReader sau DataSet. Pot folosite pentru apelarea de proceduri stocate aate pe server. Ele permit scrierea de interog ari SQL parametrizate sau specicarea parametrilor pentru procedurile stocate.

8.3.3

Clasele DataReader

Permit navigarea de tip forwardonly, readonly n mod conectat la sursa de date. Se obt in 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 a principal a a unui furnizor de date .NET este DataAdapter. Funct ioneaz a ca o punte ntre sursa de date si obiecte de tip DataSet deconectate, permit and efectuarea operat iilor pe baza de date. Cont in referint e c atre obiecte de tip Connection si deschid / nchid singure conexiunea la baza de date. In plus, un DataAdapter cont ine referint e c atre patru comenzi pentru selectare, stergere, modicare si ad augare la baza de date.

8.3.5

Clasa DataSet

Aceast a clas a nu este parte a unui furnizor de date .NET ci independent a de particularit a tile de conectare si lucru cu o baz a de date anume. Prezint a marele avantaj c a poate sa lucreze deconectat de la sursa de date, facilit and stocarea si modicarea datelor local, apoi reectarea acestor modic ari n baza de date. Un obiect DataSet este de fapt un container de tabele si relat ii ntre tabele. Folose ste serviciile unui obiect de tip DataAdapter pentru a si procura datele si a trimite modic arile napoi c atre baza de date. Datele sunt stocate de un DataSet n format XML; acela si format este folosit pentru transportul datelor.

8.4

Obiecte Connection

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

8.4. OBIECTE CONNECTION

177

ca o comand a s a e executat a pe o baz a de date trebuie stabilit a o conexiune la ea. Orice clas a de tip conexiune (din orice furnizor de date) implementeaz a intefat a IDbConnection. De exemplu, clasele SqlConnection (folosit a pentru conectare la server Microsoft SQL Server 2000) sau OleDbConnection (folosit a pentru conectare la siere .mdb din Access sau .xls) implementeaz 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 a instalat severul MSSQL de baze de date (serverBD), baza de date la care se face conectarea (Northwind), contul SQL cu care se face accesul (sa) si parola pentru acest cont (parola). Pentru conectarea la un sier Access Northwind.mdb aat n directorul c:\lucru se folose ste un obiect de tipul OleDbConnection sub forma: 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 sier Access) precum si locul unde se a a sursa de date (C:\Lucru\Northwind.mdb). Vom enumera principalele propriet a ti, metode si evenimente pentru un obiect de tip Connection.

8.4.1

Propriet a ti

1. ConnectionString : de tip String, cu accesori de get si set ; aceast a proprietate dene ste un string valid care permite identicarea tipului si locat iei sursei de date la care se face conectarea si eventual contul si

178

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

2. ConnectionTimeout : de tip int, cu accesor de get, valoare implicit a 15; specic a num arul de secunde pentru care un obiect de conexiune ar trebui s a a stepte pentru realizarea conect arii la server nainte de a se genera o except ie. Se poate specica o valoare diferit a de 15 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 semnicat ia a steapt a oric at, dar se sugereaz a s a nu se procedeze n acest mod. 3. Database : atribut de tip string, read-only, returneaz a numele bazei de date la care sa f acut conectarea. Folosit a pentru a ar ata unui utilizator care este baza de date pe care se face operarea. 4. Provider : atribut de tip string, read-only, returneaz a furnizorul OLE DB. 5. ServerVersion : atribut de tip string, read-only, returneaz a versiunea de server la care sa f acut conectarea. 6. State : atribut de tip enumerare ConnectionState , read-only, returneaz a starea curent a a conexiunii. Valorile posibile sunt: Broken, Closed, 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 si elibereaz a toate resursele alocate pentru ea 3. BeginTransaction() : pentru executarea unei tranzact ii pe baza de date; la sf ar sit se apeleaz a Commit() sau Rollback(). 4. ChangeDatabase() : se modic a baza de date la care se vor face conexiunile. Noua baz a de date trebuie s a existe pe acela si server ca precedenta. 5. CreateCommand() : creeaz a un obiect de tip Command valid (care implementeaz a interfat a IDbCommand ) asociat cu conexiunea curent a.

8.4.3

Evenimente

Un obiect de tip conexiune poate semnala dou a evenimente: evenimentul StateChange : apare atunci c and se schimb a starea conexiunii. Event-handlerul este de tipul delegat StateChangeEventHandler, care spune care sunt st arile ntre care sa f acut tranzit ia. evenimentul InfoMessage : apare atunci c and furnizorul trimite un avertisment sau un mesaj informat ional c atre client.

8.4.4

Stocarea stringului de conexiune n sier de congurare

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

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

CURS 8. ADO.NET

In interiorrul elementului r ad acin a conguration se va introduce elementul appSettings, care va cont ine oric ate perechi cheie-valoare n interiorrul unui 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 sierului de congurare se g asesc n spat iul 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 a reutilizarea resurselor de tip conexiune la o baz a de date. Atunci c and se creeaz a o grupare de conexiuni se genereaz a automat mai multe obiecte de tip conexiune, acest num ar ind
3

Engl: connection pooling

8.5. OBIECTE COMMAND

181

egal cu minimul setat pentru gruparea respectiv a. O nou a conexiune este creat a dac a toate conexiunile sunt ocupate si se cere o nou a conexiune. Dac a dimensiunea maxim setat a a grup arii este atins a, atunci nu se va mai crea o conexiune nou a, ci se va pune ntro coad a de a steptare. Dac a a steparea dureaz a mai mult dec at este precizat n valoarea de Timeout se va arunca o except ie. Pentru a returna o conexiune la grupare trebuie apelat a metoda Close() sau Dispose() pentru acea conexiune. Sursele de date .NET administreaz a automat acest aspect, degrev andu-l pe programator de aceast aspect ce nu tine de logica aplicat iei. La dorint a comportamentul implicit se poate modica prin intemediul cont inutului stringului de conectare.

8.5

Obiecte Command

Un clas a de tip Command dat a de un furnizor .NET trebuie s a implementeze interfat a 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 a SGBD-ul respectiv stie acest lucru). Comanda se poate executa numai dac a s-a deschis o conexiune la baza de date. Exemplu: SqlConnection con = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", con); Codul care utilizeaz a o comand a pentru lucrul cu siere mdb sau xls ar foarte asem an ator, cu diferent a c a n loc de SqlCommand se folose ste OleDbCommand, din spat iul de nume System.Data.OleDb. Nu trebuie modicat altceva, doarece locat ia sursei de date se specic a doar la conexiune. Se observ a c a obiectul de conexiune este furnizat comenzii create. Enumer am mai jos principalele propriet a ti si metode ale unui obiect de tip comand a.

8.5.1

Propriet a ti

1. CommandText : de tip String, cu ambii accesori; cont ine comanda SQL sau numele procedurii stocate care se execut a pe sursa de date. 2. CommandTimeout : de tip int, cu ambii accesori; reprezint a num arul de secunde care trebuie s a e a steptat pentru executarea interog arii. Dac a se dep a seste acest timp, atunci se arunc a o except ie.

182

CURS 8. ADO.NET

3. CommandType : de tip CommandType (enumerare), cu ambii accesori; reprezint a tipul de comand a care se execut a pe sursa de date. Valorile pot : CommandType.StoredProcedure - interpreteaz a comanda cont inut a n proprietatea CommandText ca o un apel de procedur a stocat a denit a n baza de date. CommandType.Text - interpreteaz a comanda ca ind o comand a SQL clasic a. CommandType.TableDirect - momentan disponibil numai pentru furnizorul OLE DB (deci pentru obiect de tip OleDbCommand); dac a proprietatea CommandType are aceast a valoare, atunci proprietatea CommandText este interpretat a ca numele unui tabel pentru care se aduc toate liniile si coloanele la momentul execut arii. 4. Connection - proprietate de tip System. Data. [.NET Data Provider]. PrexConnection, cu ambii accesori; cont ine 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 a o colect ie de parametri care s-au transmis comenzii; aceast a list a a fost creat a prin apelul metodei CreateParameter().Prex reprezint a acelasi lucru ca mai sus. 6. Transaction - proprietate de tip System.Data.[.NET Data Provider]. PrexTransaction, read-write; permite accesul la obiectul de tip tranzact ie care se cere a executat pe sursa de date.

8.5.2

Metode

1. Constructori - un obiect de tip comand a poate creat si prin intermediul apelului de constructor; de exemplu un obiect SqlCommand se poate obt ine 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 a s a opreasc a o comand a dac a ea se a a n execut ie. Dac a nu se a a n execut ie, sau aceast a ncercare nu are nici un efect nu se int ampl a nimic. 3. Dispose() - distruge obiectul comand a. 4. ExecuteNonQuery() - execut a o comand a care nu returneaz a un set de date din baza de date; dac a comanda a fost de tip INSERT, UPDATE, DELETE, se returneaz a num arul de nregistr ari afectate. Dac a nu este denit a conexiunea la baza de date sau aveasta nu este deschis a, se arunc a o except ie de tip InvalidOperationException. 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() In exemplul de mai sus se returneaz a num arul de nregistr ari care au fost sterse. 5. ExecuteReader() - execut a comanda cont inut a n proprietatea CommandText si se returneaz a un obiect de tip IDataReader (e.g. SqlDataReader sau OleDbDataReader ). Exemplu: se obt ine cont inutul tabelei Customers ntrun obiect de tip SqlDataReader (se presupune c a baza de date se stocheaz a pe un server 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 opt ional de tip enumerare CommandBehavior care descrie rezultatele si efectul asupra bazei de date: CommandBehavior.CloseConnection - conexiunea este nchis a atunci c and obiectul de tip IDataReader este nchis. CommandBehavior.KeyInfo - comanda returnez a informat ie despre coloane si cheia primar a. CommandBehavior.SchemaOnly - comanda returnez a doar informat ie despre coloane. CommandBehavior.SequentialAccess - d a posibilitatea unui DataReader s a manipuleze nregistr ari care cont in c ampuri cu valori binare de mare ntindere. Acest mod permite nc arcarea sub forma unui ux de date folosind GetChars() sau GetBytes(). CommandBehavior.SingleResult - se returneaz a un singur set de rezultate CommandBehavior.SingleRow - se returneaz a o singur a linie. De exemplu, dac a n codul anterior nainte de while obt inerea obiectului reader sar face cu: SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleRow); atunci sar returna doar prima nregistrare din setul de date. 6. ExecuteScalar() - execut a comanda cont inut a n proprietatea CommandText ; se returneaz a valoarea primei coloane de pe primul r and a setului de date rezultat; folosit pentru obt inerea unor rezultate de tip agregat (SELECT COUNT(*) FROM CUSTOMERS, de exemplu). 7. ExecuteXmlReader() - returneaz a un obiect de tipul XmlReader obt inut din rezultatul interog arii pe sursa de date. 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 a stocat a

Pentru a se executa pe server o procedur a stocat a denit a n baza respectiv a, este necesar ca obiectul comand a s a aibe proprietatea CommandType la valoarea CommandType.StoredProcedure iar proprietatea CommandText s a cont in a numele procedurii stocate: 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(); Observat ie: se remarc a n toate exemplele de mai sus faptul c a ecare conexiune se nchide manual, printr-un apel de tipul con.Close(). Daca conexiunea a fost folosit a pentru un obiect de tip DataRead atunci acesta din urm a trebuie s a e si el nchis, naintea nchiderii conexiunii. Dac a nu se face acest apel atunci conexiunea va tinut a ocupat a si nu va putea reutilizat a.

8.5.4

Folosirea comenzilor parametrizate

Exist a posibilitatea de a rula cod SQL (interog ari, proceduri stocate) pe server care s a e parametrizate. Orice furnizor de date .NET permite crearea obiectelor parametru care pot ad augate la o colect ie de parametri ai comenzii. Valoarea cestor parametrii se specic a e prin numele lor (cazul SqlParameter ), e prin pozi tia lor (cazul OleDbParameter ). Exemplu: vom aduce din tabela Customers toate nregistr arile care au n c ampul Country valoarea USA. 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 sir de caractere) si valoarea. De ret inut faptul c a numele parametrului se prexeaz a cu caracterul @. In cazul n care un parametru este de ie sire, acest lucru trebuie spus 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(); Remarc am urm atoarele: este posibil ca ntro comand a s a se execute mai multe interog ari

187

pentru parametrul de ie sire numit @count trebuie f acut a declarare de direct ie; implicit un parametru este de intrare parametrii de ie sire sunt accesibili doar dup 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 interfat a IDataReader 2. se lucreaz a conectat la sursa de date - pe toat a perioada c at este accesat un DataReader necesit a conexiune activ a 3. este de tip read-only; dac a se dore ste modicarea datelor se poate folosi un DataSet + DataAdapter 4. este de tip forward-only - metoda de modicare a pozit iei curente este doar n direct ia nainte; orice re ntoarcere presupune reluarea nregistr arilor (dac a programatorul nu implementeaz a el singur un mecanism de coad a) Avantajele utiliz arii acestui tip de obiecte sunt: accesul conectat, performant ele bune, consumul mic de resurse si tipizarea puternic a.

8.6.1

Propriet a ti

1. IsClosed - proprietate read-only, returneaz a true daca obiectul este deschis, false altfel 2. HasRows - proprietate boolean a read-only care spune dac a readerul cont ine cel put in o nregistrare 3. Item - indexator care d a acces la c ampurile unei nregistr ari 4. FieldCount - d a num arul de c ampuri din nregistrarea curent a

188

CURS 8. ADO.NET

8.6.2

Metode

1. Close() - nchide obiectul de citire si elibereaz a resursele client. Este obligatoriu apelul acestei metode inaintea nchiderii conexiunii. 2. GetBoolean(), GetByte(), GetChar(), GetDateTime(), GetDecimal(), GetDouble(), GetFloat(), GetInt16(), GetInt32(), GetInt64(), GetValue(), GetString() returneaz a valorile c ampurilor din nergistrarea curent a. Preiau ca parametru indicele coloanei a c arei valoare se cere. GetValue() returneaz a un obiect de tip Object, pentru celelalte tipul returnat este descris de numele metodelor. 3. GetBytes(), GetChars() - returneaz a num arul de octet i / caractere citit i dintrun c amp ce stocheaz a o structur a de dimensiuni mari; prime ste ca parametri indicele de coloan a (int), pozit ia din acea coloan a de unde se va ncepe citirea, vectorul n care se face citirea, pozit ia n buer de la care se depun datele citite, num arul de octet i/caractere ce urmeaz a a citit i. 4. GetDataTypeName() - returneaz a tipul coloanei specicat prin indice 5. GetName() - returneaz a numele coloanei 6. IsDBNull() - returneaz a true dac a n c ampul specicat prin index este o valoare de NULL (din baza de date) 7. NextResult() - determin a trecerea la urm atorul rezultat, dac a aceasta exist a; n acest caz returneaz a true, altfel false (este posibil ca ntr un DataReader s a vin a mai multe rezultate, provenind din interog ari diferite) 8. Read() - determin a trecerea la urm atoarea nregistrare, dac a aceasta exista a; n acest caz ea returneaz a true. Metoda trebuie chemat a cel put in o dat a, deoarece init ial pozit ia curent a este naintea primei nregistr ari.

8.6.3

Crearea si utilizarea unui DataReader

Nu este posibil s a se creeze un obiect de tip DataReader cu ajutorul unui constructor, ci prin intermediul unui obiect de tip Command, folosind apelul ExecuteReader() (a se vedea sect iunea 8.3.2). Pentru comanda respectiv a se specic a instruct iunea care determin a returnarea setului de date precum si obiectul de conexiune. Aceast a conexiune trebuie s a e deschis a naintea apelului ExecuteReader(). Trecerea la urm atoarea nregistrare se

8.6. OBIECTE DATAREADER

189

face folosind metoda Read(). C and se dore ste ncetarea lucrului se nchide readerul si conexiunea. Omiterea nchiderii obiectului de tip reader va duce la imposibilitatea reutiliz arii conexiunii init iale. Dup a ce se nchide acest DataReader este necesar a si n chiderea explicit a a conexiunii (acest lucru nu mai e mandatoriu doar dac a la apelul metodei ExecuteReader sa specicat CommandBehavior.CloseConnection ). Dac a se ncearc a refolosirea conexiunii f ar a ca readerul s a fost nchis se va arunca o except ie InvalidOperationException. 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 a aduc a datele prin apelul unei proceduri stocate (de fapt invocarea acestei proceduri este f acut a de c atre obiectul de tip Command ). Urm atoarele observat ii trebuie luate n considerare atunci c and se lucreaz a cu un obiect DataReader : Metoda Read() trebuie s a e ntotdeauna apelat a naintea oric arui acces la date; pozit ia curent a la deschidere este naintea primei nregistr ari. Intotdeauna apelat i metoda Close() pe un DataReader si pe conexiunea asociat a c at mai repede posibil; n caz contrar conexiunea nu poate reutilizat a Procesarea datelor citite trebuie s a se fac a dup a nchiderea conexiunii; n felul acesta conexiunea se las a liber a pentru a putea reutilizat a.

190

CURS 8. ADO.NET

8.6.4

Utilizarea de seturi de date multiple

Este posibil ca ntrun DataReader s a se aduc a mai multe seturi de date. Acest lucru ar mic sora num arul de apeluri pentru deschiderea unei conexiuni 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 a sigur a din punct de vedere a tipului

S a consider am urm atoarea secvent a de cod: 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 a cum se observ a, este posibil ca valorile c ampurilor dintro nregistrare s a e accesate prin intermediul numelui coloanei (sau a indicelui ei, pornind de la 0). Dezavantajul acestei metode este c a tipul datelor returnate este pierdut (ind returnate obiecte de tip Object ), trebuind f acut a un downcasting pentru a utiliza din plin facilit a tile tipului respectiv. Pentru ca acest lucru s a nu se nt ample se pot folosi metodele GetXY care returneaz a un tip 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 secvent ei anterioare este c a dac a se ncearc a aducerea valorii unui c amp pentru care tipul nu este dat corect se arunc a o except ie InvalidCastException ; 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 urm atoarea strategie: indicii se obt in folosind apel de metod a GetOrdinal la care se specic a numele coloanei 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 si Connection, Command, DataReader, obiectele de tip DataAdapter fac parte din furnizorul de date .NET specic ec arui tip de surs a de date. Scopul ei este s a permit a umplerea unui obiect DataSet cu date si reectarea schimb arilor efectuate asupra acestuia napoi n baza de date (DataSet permite lucrul deconectat de la baza de date). Orice clas a de tipul DataAdapter (de ex SqlDataAdapter si OleDbDataAdapter) este derivat a din clasa DbDataAdapter (clas a abstract a). Pentru orice obiect de acest tip trebuie specicat a minim comanda de tip SELECT care care s a populeze un obiect de tip DataSet ; acest lucru este stabilit prin intermediul propriet a tii SelectCommand de tip Command (SqlCommand, OleDbCommand, . . . ). In cazul n care se dore ste si modicarea informat iilor din sursa de date (inserare, modicare, stergere) trebuie specicate obiecte de tip comand a via propriet a tile: InsertCommand, UpdateCommand, DeleteCommand. Exemplu: mai jos se preiau nregistr arile din 2 tabele: Authors si TitleAuthor si se trec 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");

} } Prezent am mai jos cele mai importante componente ale unei clase de tip DataAdapter.

9.1.1

Metode

1. Constructori de la cei implicit i p an a la cei n care se specic a o comand a de tip SELECT si conexiunea la sursa de date. Pentru un obiect de tip SqlDataAdapter se poate crea o instant a n urm atoarele moduri: 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 a polimorc a, permit and umplerea unei tabele dintrun obiect de tip DataSet cu date. Permite specicarea obiectului DataSet n care se depun datele, eventual a numelui tablei din acest DataSet, num arul de nregistrare cu care s a se nceap a popularea (prima av and indicele 0) si num arul de nregistr ari care urmeaz a a aduse. Returneaz a de ecare dat a num arul de nregistr ari care au fost aduse din baz a. In clipa n care se apeleaz a Fill() se procedeaz a astfel:

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

195

(b) Se aduc datele si se populeaz a un obiect de tip DataTable din DataSet (c) Se nchide conexiunea (dac a ea nu a fost explicit deschis a!) De remarcat c a un DataAdapter si poate deschide si nchide singur conexiunea, dar dac a aceasta a fost deschis a naintea metodei Fill() atunci tot programatorul trebuie s a o nchid a. 3. Update() metod a polimorc a, permit and reectarea modic arilor efectuate ntreun DataSet. Pentru a funct iona are nevoie de obiecte de tip comand a adecvate: propriet a tile InsertCommand, DeleteCommand si UpdateCommand trebuie s a indice c atre comenzi valide. Returneaz a de ecare dat a num arul de nregistr ari afectate.

9.1.2

Propriet a ti

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

AddWithKey - ca mai sus, dar adaug a si informat ii relativ la cine este cheia primar a Ignore - se ignor a lipsa coloanei respective, ceea ce duce la pierdere de date pe DataSet Error - se genereaz a o except ie de tipul InvalidOperationException.

9.2

Clasa DataSet

Clasa DataSet nu mai face parte din biblioteca unui furnizor de date ADO.NET, ind standard. Ea poate s a cont in a reprezent ari tabelare ale datelor din baz a precum si diferite restrict ii si relat ii existente. Marele ei

196

CURS 9. ADO.NET (2)

avantaj este faptul c a permite lucrul deconectat de la sursa de date, elimin and necesitatea unei conexiuni permanente precum la DataReader. In felul acesta, un server de aplicat ii sau un client oarecare poate apela la serverul de baze de date doar c and preia datele sau c and se dore ste salvarea lor. Funct ioneaz a n str ans a leg atur a cu clasa DataAdapter care act ioneaz a ca o punte ntre un DataSet si sursa de date. Remarcabil este faptul c a un DataSet poate face abstract ie de sursa de date, procesarea datelor desf a sur anduse independent de ea. Figura 9.1 cont ine o vedere part ial a asupra clasei DataSet.

Figura 9.1: Structura unui DataSet

9.2.1

Cont inut

Prezent am succint cont inutul unui DataSet : 1. Colect ia Tables cont ine 0 sau mai multe obiecte DataTable. Fiecare DataTable este compus a dintr-o colect ie de linii si coloane. 2. Colect ia Relations cont ine 0 sau mai multe obiecte de tip DataRelation, folosite pentru marcarea leg aturilor p arintecopil. 3. Colect ia ExtendedProperties cont ine propriet a ti denite de utilizator.

9.2.2

Clasa DataTable

Datele sunt cont inute ntr-un DataSet sub forma unor tabele de tip DataTable. Aceste obiecte pot folosite at at independent, c at si n interiorul unui

9.2. CLASA DATASET

197

DataSet ca elemente ale colect iei Tables. Un DataTable cont ine o colect ie Columns de coloane, Rows de linii si Constraints de constr angeri. DataColumn Un obiect DataColumn dene ste numele si tipul unei coloane care face parte sau se adaug a unui obiect DataTable. Un obiect de acest tip se obt ine 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 ( n vederea stabilirii ei ca si cheie pe o tabel a) se face astfel: 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 a o linie dintr-un obiect DataTable. Orice obiect DataTable cont ine o proprietate Rows ce da acces la colect ia de obiecte DataRow cont inut a. Pentru crearea unei linii se poate apela metoda NewRow pentru o tabel a a c arei schem a se cunoa ste. Mai jos este dat a secvent a de cod care creeaz a o linie nou a pentru o tabel a si o adaug a acestesia: DataRow tempRow; tempRow = myTable.NewRow(); tempRow["ID"] = 1; tempRow["Name"] = "Book"; tempRow["Category"] = 1; myTable.Rows.Add(tempRow); Constr angeri Constr angerile sunt folosite pentru a descrie anumite restrict ii aplicate asupra valorilor din coloane. In ADO.NET exist a dou a tipuri de constr angeri: de unicitate si de cheie str ain a. Toate obiectele de constr angere se a a n colect ia Constraints a unei tabele.

198

CURS 9. ADO.NET (2)

UniqueConstraint - precizeaz a c a ntr-o anumit a coloan a valorile sunt unice. Incercarea de a seta valori duplicate pe o coloana pentru care s-a precizat restrict ia duce la aruncarea unei except ii. Este necesar a o asemenea coloan a n clipa n care se folose ste metoda Find pentru proprietatea Rows : n acest caz trebuie s a se specice o coloan a pe care avem unicitate. ForeignKeyConstraint - specic a act iunea care se va efectua atunci c and se sterge sau modic a valoarea dintro anumit a coloan a. De exemplu se poate decide c a dac a se sterge o nregistrare dintr-o tabel a atunci s a se stearg a si nregistr arile copil. Valorile care se pot seta pentru o asemenea constr angere se specic a n propriet a tile ForeignKeyConstraint.DeleteRule si ForeignKeyConstraint.UpdateRule : Rule.Cascade - act iunea implicit a, sterge sau modic a nregistr arile afectate Rule.SetNull - se seteaz a valoare de null pentru nregistr arile afectate Rule.SetDefault - se seteaz a valoarea implicit a denit a n baz a pentru c ampul respectiv Rule.None - nu se execut a nimic 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 relat ie de tip cheie str ain a ntre dou a tabele (CustTable si OrdersTable, care fac parte dintr-un DataSet). Restrict ia se adaug a la tabla copil. Stabilirea cheii primare O cheie primar a se dene ste ca un vector de coloane care se atribuie propriet a tii PrimaryKey a unei tabele (obiect DataTable ). DataColumn[] pk = new DataColumn[1]; pk[0] = myTable.Columns["ID"]; myTable.PrimaryKey = pk;

9.2. CLASA DATASET

199

Proprietatea Rows a clasei DataTable permite c autarea unei anumite linii din colect ia cont inut a dac a se specic a un obiect sau un array de obiecte 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

Relat ii ntre tabele

Proprietatea Relations a unui obiect de tip DataSet cont ine o colect ie de obiecte de tip DataRelation folosite pentru a gura relat iile de tip p arinte copil ntre dou a tabele. Aceste relat ii se precizeaz a n esent a ca niste perechi de array-uri de coloane sau chiar coloane simple din cele dou a tabele care se relat ioneaz a, de exemplu sub forma: 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

De si un obiect DataSet se poate popula prin crearea dinamic a a obiectelor DataTable, cazul cel mai des nt alnit este acela n care se populeaz a prin intermediul unui obiect DataAdapter. O dat a obt inut un asemenea obiect (care cont ine cel put in o comand a de tiop SELECT ) se poate apela metoda Fill() care prime ste ca parametru DataSet -ul care se umple si opt ional numele tabelei care va cont ine 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 construct ia myDataSet.Tables["Customers"] sau folosind indici ntregi (prima tabel a are indicele 0). Acela si DataSet se poate popula n continuare cu alte tabele pe baza aceluia si sau a altor obiecte DataAdapter.

9.2.5

Propagarea modic arilor c atre baza de date

Pentru a propaga modic arile efectuate asupra cont inutului tabelelor dintr-un DataSet c atre baza de date este nevoie s a se deneasc a adecvat obiecte comand a de tip INSERT, UPDATE, DELETE. Pentru cazuri simple se poate folosi clasa SqlCommandBuilder care va construi singur a aceste comenzi. Clasa CommandBuilder Un obiect de tip CommandBuilder (ce provine din furnizorul de date) va analiza comanda SELECT care a adus datele n DataSet si va construi cele 3 comenzi de update n funct ie de aceasta. E nevoie s a se satisfac a 2 condit ii atunci c and se uzeaz a de un astfel de obiect: 1. Trebuie specicat a o comand a de tip SELECT care s a aduc a datele dintr-o singur a tabel a 2. Trebuie specicat a cel put in cheia primar a sau o coloan a cu constr angere de unicitate n comanda SELECT. Pentru cea de a doua condit ie se poate proceda n felul urm ator: n comanda SELECT se specic a si aducerea cheii, iar pentru obiectul DataAdapter care face aducerea din baz a se seteaz a proprietatea MissingSchemaAction pe valoarea MissingSchemaAction.AddWithKey (implicit este doar Add ). Fiecare linie modicat a din colect ia Rows a unei tabele va avea modicat a valoarea propriet a tii RowState astfel: DataRowState.Added pentru

9.2. CLASA DATASET

201

o linie nou a ad augat a, DataRowState.Deleted dac a e stears a si DataRowState.Modied dac a a fost modicat a. Apelul de update pe un dataReader va apela comanda necesar a pentru ecare linie care a fost modicat a, n funct ie de starea ei. Ar at am mai jos modul de utilizare a clasei SqlCommandBuilder pentru ad augarea, modicarea, stergerea de nregistr ari din baza de date. 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(); } In clipa n care se creeaz a obiectul SqlCommandBuilder automat se vor completa propriet a tile InsertCommand, DeleteCommand, UpdateCommand ale dataAdapter-ului. Se determin a apoi liniile care au fost modicate (prin interogarea st arii lor) si se obt ine un nou DataSet care le va cont ine doar pe acestea. Comanda de Update se d a doar pentru acest set de modic ari, reduc and astfel tracul spre serverul de baze de date. Update folosind comenzi SQL Atunci c and interog arile de aducere a datelor sunt mai complexe (de exemplu datele sunt aduse din mai multe table, printr-un join) se pot specica propriile comenzi SQL prin intermediul propriet a tilor InsertCommand, DeleteCommand UpdateCommand ale obiectului DataAdapter. Pentru ecare linie dintr-o tabel a care este modicat a/ad augat a/ stears a se va apela comanda SQL corespunz atoare.Aceste comenzi pot fraze SQL parametrizate sau pot denumi proceduri stocate aate n baz a. S a presupunem c a s-a denit un DataAdapter legat la o baz a de date. Instruct iunea de select ie este

202

CURS 9. ADO.NET (2)

SELECT CompanyName, Address, Country, CustomerID FROM Customers unde CustomerID este cheia. Pentru inserarea unei noi nregistr ari sar putea 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 instruct iune de tip Update. Valorile pentru ace sti parametri se vor da la runtime, de exemplu prin alegerea lor dintr-un tabel. Valoarea pentru cheia CustomerID nu s-a specicat, deoarece ( n acest caz) ea este de tip AutoNumber (SGBD-ul este cel care face managementul valorilor acestor c ampuri, nu programatorul). @@Identity reprezint a id-ul noii nergistr ari ad augate n tabel a. Ultima instruct iune va duce la reactualizarea obiectului DataSet, pentru ca acesta s a cont in a modicarea efectuat a (de exemplu ar putea aduce valorile implicite puse pe anumite coloane). Pentru modicarea cont inutului unei linii se poate declara instruct iunea 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

Tranzact ii n ADO.NET

O tranzact ie este un set de operat ii care se efectueaz a e n ntregime, e deloc. S a presupunem c a se dore ste trecerea unei anumite sume de bani dintr-un cont n altul. Operat ia presupune 2 pa si: 1. scade suma din primul cont 2. adaug a suma la al doilea cont Ar inadmisibil ca primul pas s a reu seasc a iar al doilea s a e sueze. Tranzact iile satisfac ni ste propriet a ti str anse sub numele ACID:

9.3. TRANZACT II IN ADO.NET

203

atomicitate - toate operat iile din tranzact ie ar trebui s a aib a succes sau s a e sueze mpreun a consistent a - tranzact ia duce baza de date dintr-o stare stabil a n alta izolare - nici o tranzact ie nu ar trebui s a afecteze o alta care ruleaz a n acela si timp durabilitate - schimb arile care apar n tipul tranzact iei sunt permanent stocate pe un mediu. Sunt trei comenzi care se folosesc n context de tranzact ii: BEGIN - nainte de executarea unei comenzi SQL sub o tranzact ie, aceasta trebuie s a e init ializat a COMMIT - se spune c a o tranzact ie este terminat a c and toate schimb arile cerute sunt trecute n baza de date ROLLBACK - dac a o parte a tranzact iei e sueaz a, atunci toate operat iile efectuate de la nceputul tranzact iei vor neglijate Schema de lucru cu tranzact iile sub ADO.NET este: 1. deschide conexiunea la baza de date 2. ncepe tranzact ia 3. execut a comenzi pentru tranzact ie 4. dac a tranzact ia se poate efectua (nu sunt except ii sau anumite condit ii sunt ndeplinite), efectueaz a COMMIT, altfel efectueaz a ROLLBACK 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 si n alte situat ii, de exemplul comanda efectuat a dep a ste ste stocul disponibil.

Curs 10 Fire de execut ie


Firele de execut ie1 sunt responsabile cu multitaskingul n interiorul unei aplicat ii. Clasele si interfet ele responsabile pentru crearea aplicat iilor multithreading se g asesc n spat iul de nume System.Threading. Vom discuta n cele ce urmeaz a despre managementul threadurilor si despre sincronizare. De cele mai multe ori crearea de threaduri este necesar a pentru a da utilizatorului impresia c a programul execut a mai multe act iuni simultan, n cadrul unei aplicat ii. Pentru ca o interfat a utilizator s a poat a folosit a f ar a a se a stepta ncheierea unei anumite secvent e de instruct iuni, e nevoie de mai multe re de execut ie (unele pentru procesarea efectiv a a informat iei, altele care s a r aspund a act iunilor utilizatorului). Pentru probleme de calcul numeric, exprimarea procesului de calcul se poate face foarte natural sub form a de re de exect ie.

10.1
10.1.1

Managementul threadurilor
Pornirea threadurilor

Cea mai simpl a metod a de a crea un r de exect ie este de a crea o instant a a clasei Thread, al c arei constructor preia un singur argument de tip delegat. In BCL este denit tipul delegat ThreadStart, care este folosit ca prototip pentru orice metod a care se vrea a lansat a ntrun r de execut ie. Declarat ia de ThreadStart este: public delegate void ThreadStart(); Crearea unui r de execut ie se face pe baza unei metode care returneaz a void si nu preia nici un parametru:
1

Engl: threads; vom folosi alternativ termenii thread si r de execut ie

205

206

CURS 10. FIRE DE EXECUT IE

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(); } } P an a la linia t.Start() exist a un singur thread: cel dat de pornirea metodei Main. Dup a t.Start() sunt 2 threaduri: cel anterior si t. Mesajul din Main va tip arit naintea celui din threadul t; n funct ie de cum anume se planic a threadurile pentru execut ie, mesajul de dup a t.Start() poate s a apar a nainte sau dup a mesajul tip arit de metoda WorkerThreadMethod(). De remarcat c a simpla creere a rului de execut ie nu determin a si pornirea lui: acest lucru se nt ampl a dupa apelul metodei Start denit a n clasa Thread. Exemplul urm ator porne ste dou a re de execut ie. Funct iile care cont in codul ce se va executa n c ate un thread sunt Increment() si Decrement(): 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 ie sire se vor mixa mesajele tip arite de primul thread cu cele tip arite 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 EXECUT IE

Perioada de timp alocat a ec arui thread este determinatu a de c atre planicatorul de re de execut ie2 si depinde de factori precum viteza procesorului, gradul lui de ocupare, etc. O alt a modalitate de obt inere a unei referint e la un thread este prin apelul metodei statice Thread.CurrentThread pentru rul de execut ie curent. In exemplul de mai jos se obt ine obiectul Thread asociat rului de execut ie curent, i se seteaz a numele (proprietate de tip read/write) si se a seaz a pe ecran: Thread current = Thread.CurrentThread; current.Name = "My name"; Console.WriteLine("nume={0}", current.Name );

10.1.2

Metoda Join()

Exist a situat ii n care, naintea unei instruct iuni trebuie s a se asigure faptul c a un alt r de execut ie t s-a terminat; acest lucru se va face folosind apelul t.Join() naintea instruct iunii n cauz a; n acest moment rul de execut ie care a apelat t.Join() intr a n a steptare. Dac a de exemplu n metoda Main se lanseaz a o colect ie de threaduri (stocat a n myThreads ), atunci pentru a se continua execut ia numai dup a ce toate rele din colect ie sau terminat, se procedeaz a astfel: foreach( Thread myThread in myThreads ) { myThread.Join(); } Console.WriteLine(All my threads are done); Mesajul nal se va tip ari doar c and toate rele de execut ie sau terminat.
2

Engl: thread scheduler

10.1. MANAGEMENTUL THREADURILOR

209

10.1.3

Suspendarea relor de execut ie

Se poate ca n anumite cazuri s a se doreasc a suspendarea unui r de execut ie pentru o scurt a perioad a de timp. Clasa Thread ofer a o metod a static a supra nc arcat a Sleep(), care poate prelua un parametru de tip int reprezent and milisecundele de adormire, iar a doua variant a preia un argument de tip TimeSpan, care reprezint a cea mai mic a unitate de timp care poate specicat a, egal a cu 100 nanosecunde. Pentru a cere rului de execut ie curent s a se suspende pentru o secund a, se execut a n cadrul acestuia: Thread.Sleep(1000); In acest fel se semnaleaz a planicatorului de re de execut ie c a poate lansa un alt thread. Dac a n exemplul de mai sus se adaug a un apel Thread.Sleep(1) dup a ecare WriteLine(), atunci ie sirea se schimb a dramatic: 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

Omor area threadurilor

De obicei, un r de execut ie moare dup a ce se termin a de executat. Se poate totu si cere unui r de execut ie s a si nceteze execut ia folosind metoda Abort(). Acest lucru va duce la aruncarea unei except ii n interiorul rului de execut ie c aruia i se cere suspendarea: ThreadAbortedException, pe care rul respectiv o poate prinde si procesa, permit andui eliberarea de resurse alocate. Ca atare, se recomand a ca o metod a care se va lansa ca r de

210

CURS 10. FIRE DE EXECUT IE

execut ie s a se compun a dintrun bloc try care cont ine instruct iunile utile, dup a care un bloc catch si/sau nally care vor efectua eliberarea de resurse. 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 EXECUT IE

} finally { Console.WriteLine( Thread {0} Exiting. , Thread.CurrentThread.Name); } } } Ie sire: 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 priorit a tilor relor de execut ie

Un r de execut ie se lanseaz a implicit cu prioritatea ThreadPriorityLevel.Normal. Dar schedulerul poate inuent at n activitatea sa prin setarea de diferite 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 descresc atoare n lista prezentat a. Prioritatea este descresc atoare n lista prezentat a. Pe baza priorit a tii procesului care cont ine rele de execut ie si a priorit a tii relor, se calculeaz a un nivel de propritate (de ex. pe ma sini Intel valori ntre 0 si 31) care determin a prioritatea n ansamblul sistemului de operare a rului respectiv. Setarea unei anumite priorit a ti se face folosind proprietatea Priority : myThread.Priority = ThreadPriorityLevel.Highest;

10.1.6

Fire n fundal si re n prim-plan

Relativ la proprietatea boolean a IsBackground, trebuie f acut a precizarea c a un r de execut ie poate s a se execute n fundal (background) sau n prim plan (foreground). Diferent a dintre cele dou a posibilit a ti o constituie faptul c a dac a un proces are m acar un r de execut ie n foreground, CLR va ment ine aplicat ia n execut ie. O dat a ce toate rele de execut ie de tip foreground se termin a, CLR va executa Abort() pentru ecare r de execut ie de tip background (dac a mai exist a a sa ceva) si termin a procesul. Exemplu: using System; using System.Threading; class Test { static void Main()

214 {

CURS 10. FIRE DE EXECUT IE

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 ment ine procesul n execut ie p an a c and se termin a

10.2. SINCRONIZAREA

215

ciclul s au while. C and acesta se termin a, procesul este oprit, chiar dac a ciclul while din rul de execut ie din background nu si-a terminat execut ia.

10.2

Sincronizarea

Sincronizarea se ocup a cu controlarea accesului la resurse partajate de mai multe re de execut ie. De exemplu, se poate cere ca utilizarea unei resurse anume s a se fac a la un moment dat de c atre un singur r de execut ie. Vom discuta aici trei mecanisme de sincronizare: clasa Interlock, instruct iunea C# lock si clasa Monitor. Exemplele se vor baza pe acces la o resurs a partajat 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 EXECUT IE

C ampul counter se init ializeaz a cu 0. S a presupunem c a pornim dou a re de execut ie pe baza metodei Incrementer() de mai sus. Este posibil s a se nt ample urm atoarele: primul thread va citi valoarea lui counter (0) si o va atribui unei variabile temporare, pe care o va incrementa apoi. Al doilea r se activeaz a si el, va citi valoarea (nemodicat a) lui counter si va atribui aceast a valoare unei variabile temporare. Primul r de execut ie si termin a munca, apoi asigneaz a valoarea variabilei temporare (1) lui counter si o a seaz a. Al doilea thread face exact acela si lucru. Se tip are ste astfel 1, 1. La urm atoarele iterat ii se va a sa 2, 2, 3, 3, etc, n locul lui 1, 2, 3, 4. 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 } Ie sire: 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 a se realizeze o excludere reciproc a a threadurilor pentru accesul la counter.

10.2.1

Clasa Interlocked

Incrementarea si decrementarea unei valori este o situat ie at at de des nt alnit a, nc at C# pune la dispozit ie o clas a special a Interlocked pentru o rezolvare rapid a. Clasa include dou a metode statice, Increment() si Decrement(), care incrementeaz a sau decrementeaz a o valoare, ns a sub un control 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 EXECUT IE

} //blocurile catch si finally raman neschimbate } Ie sirea este cea dorit 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

Instruct iunea lock

Exist a situat ii c and vrem s a bloc am alte variabile dec at cele de tip int. Un lock marcheaz a o sect iune critic a a codului, produc and astfel sincronizare pentru un obiect. La utilizare, se specic a un obiect pentru care se stabile ste un lock, dup a care o instrut iune sau un grup de instruct iuni. Lockul este nl aturat la sf ar situl instruct iunii/blocului de instrut iuni. Sintaxa este: lock(expresie) { instructiuni } Exemplu: metoda Incrementer() se va modica dup a cum urmeaz 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 a sate exact ca la sect iunea 10.2.1.

219

10.2.3

Clasa Monitor

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

220

CURS 10. FIRE DE EXECUT IE

Metoda Reader() determin a dac a stringul message cont ine vreun mesaj valabil, iar metoda Writer() va scrie n acest string. Dac a nu sunt mesaje n timpul citirii, threadul Reader() va intra n stare de a steptare folosind Wait() p an a c and metoda Writer() scrie un mesaj si transmite un mesaj via 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(); } } Ie sirea este: ReadrThread: no messages ReaderThread: waiting... WriterThread: Done writing message... ReaderThread: Greetings Caroline and Marianne! Dup a cum se vede mai sus, threadul reader este pornit primul, el blocheaz a clasa obeictul de tip MessageBoard, ceea ce nseamn a c a are acces exclusiv la variabila message. Dar deoarece message nu cont ine nici un mesaj, thread-ul reader elibereaz a obiectul pe care la blocat mai nainte prin apelul Wait(). Threadul writer va putea acum s a blocheze obiectul pentru a scrie mesajul. Apoi el cheam a metoda Pulse() pentru a semnala rului reader c a a ap arut o schimbare de stare a obiectului indicat wde this.

222

CURS 10. FIRE DE EXECUT IE

Curs 11 ASP.NET
ASP.NET (actualmente versiunea 1.1) permite crearea de aplicat ii Web folosind platforma .NET Framework; este una din cele 3 tipuri de aplicat ii creabile sub aceast a platform a, al aturi de Windows Forms si Web Services. Este destina a nlocui vechiul ASP (Active Server Pages), ind obiectual si integrat cu toate elementele constitutive ale lui .NET Framework: CLR, BCL, CTS, FCL, etc. Se poate rula pe servere de aplicat ii IIS (versiuni 5, 6), pe serverul open-source Cassini si de asemenea poate integrat cu Apache (folosind Cassini). Rezultatele cele mai bune se obt in totu si pe IIS 6.0 (integrat n Windows 2003 Server). Enunt am mai jos o list a neexhaustiv a a tr as aturilor ASP.NET: unul din marile avantaje ale lui ASP.NET fat a de ASP este faptul c a codul este p astrat compilat (sub form a de cod .NET PE, executat de c atre CLR) n loc ca pagina s a e interpretat a la ecare rulare; modul de realizare este orientat pe obiecte, o aplicat ie ind compus a din dou a p art i: pagina a sat a n browser si pagina de code-behind, care este o clas a; acest code-behind are posibilitatea de accesare a bazelor de date sau chiar a codului unmanaged (COM-uri si DLL-uri anterior existente); n plus, se realizeaz a separarea p art ii de interfat a utilizator de ceea ce este executat pe server; pune la dispozit ie un mod de tratare a erorilor unicat (tratarea de except ii) si folose ste controale Web predenite care se pot executa pe server sau pe client; are un mecanism ncorporat care permite p astrarea st arii paginii (chiar dac a prototlul de comunicare HTTP este f ar a stare, stateless); 223

224

CURS 11. ASP.NET

ASP.NET permite includerea de mai multe funt ionalit a ti pe controalele ASP.NET iar managementul st arii lor este mult mbun at a tit permite programarea bazat a pe evenimente, cu diferent e minime fat a de programarea aplicat iilor windows; este senzitiv la browserul pentru care se face trimiterea codului HTML, aleg and automat codul optimizat pentru o colect ie de browsere erorile grave ap arute n aplicat ie ASP.NET nu vor duce la oprirea serverului Web (dar pot duce la imposibilitatea de a mai executa pagini ASP.NET p an a la repornirea serverului) permite scrierea codului care se va executa pe server n orice limbaj .NET: C#, VB.NET, etc

11.1

Anatomia unei pagini ASP.NET

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

11.1.1

Ad augarea unui control Web

Intr-o pagin a Web se poate ad auga un control pe pagin a care s a e generat de c atre serverul IIS n mod adecvat si care poate accesat programatic prin cod. Astfel, n pagina de mai sus se adaug a astfel nc at s a devin 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 a se mai ncarc a o dat a pagina, se va genera urm atorul cod HTML pentru browser (vizibil prin opt iunea 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> In sursa s-a ncorporat un control Web <asp:Label> care prezint a proprietatea Text cu textul care va af sat n pagin a. Atributul runat are ca valoare cuv antul server (singura opt iune posibil a) care spune c a respectivul control se va procesa pe server si nu pe client. Dup a cum se vede n sursa HTML generat a, acest control va avea ca rezultat un tag de tip span. De remarcat c a controlul Web a fos scris n interiorul unui formular; acest lucru este mandatoriu pentru toate controalele Web sau HTML.

11.1.2

Ad augarea scriptului inline

Vom ad auga la pagina anterioar a un buton a c arui ap asare va duce la schimbarea textului etichetei anterioare; aceast a schimbare va f acut a pe server prin apelul unei metode scrise n C#: <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 ap asarea butonului se va face submiterea formularului c atre server, spre aceea si pagin a HelloWorld.aspx (lucru care se poate verica din inspectarea codului HTML generat). Pentru a se putea accesa controlul de tip Label pe partea de server, trebuie ca acestuia s a i se asigneze un ID (lblHelloWorld n cazul nostru). Pentru controlul Button (reprezentat de tag-ul <asp:Button\>) s-au asignat textul si metoda care se va apela pe server (ClickedIt). Codul pentru ClickedIt este scris inline n aceea si pagin a ASP.NET (pentru simplitate, dar vom vedea c a aceast a metod a are o variant a mai bun a). Remarc am c a dac a s-a ap asat pe buton apoi se face refresh pe browser se va retransmite Text schimbat, deoarece serverul ment ine starea controalelor. Cea mai important a este observat ia c a la codul HTML generat pe client sa ad augat un element input de tip hidden cu numele VIEWSTATE. Acesta este mecanismul prin care ASP.NET ment ine starea unei pagini de la o trimitere la alta c atre server. Prima dat a c and se cere pagina, ASP.NET va codica valoarea tuturor controalelor si le va stoca n acest input ascuns. La ecare cerere urm atoare a paginii acest input va folosit pentru a reinit ializa controalele Web si HTML. In felul acesta este ment inut a starea unor controale (de exemplu cont inutul unui input de tip text sau de tip select, pentru care s-a f acut alegerea unei opt iuni).

11.1.3

Code-behind

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

11.1. ANATOMIA UNEI PAGINI ASP.NET

227

aspecte, deoarece de partea de interfat a se poate ocupa un web-designer, iar de cea programativ a cineva cu cuno stint e de .NET. Codul executat se va scrie ntr-un sier cu extensia aspx.cs sau cs (dac a e vorba de cod C#) care va compilat sub forma unui sier dll depus n directorul bin al aplicat iei Web. Pentru a se introduce partea de code-behind pentru pagina anterioar a, se sterge scriptul inline si se modic a astfel: <%@ 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 r and s-a scris o directiv a ASP.NET (ele pot scrise oriunde, dar se obi nuie ste trecerea lor la nceputul documentului). Directiva Page precizeaz a n acest caz care este clasa din care se deriveaz a pagina curent a. Clasa de baz a se poate declara a sa: 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 a va mo steni clasa Test1.HelloWorldCB.

228

CURS 11. ASP.NET

11.2

Clasa Page

Clasa Page este mo stenit a direct sau indirect de orice pagin a ASP.NET; ea este responsabil a de asigurarea unei funct ionalit a ti de baz a pentru procesarea de pagini pe server. Ciclul de viat a a unei pagin ASP.NET este urm atorul: 1. Pagina este init ializat a. Aceasta include crearea instant elor controalelor si setarea event-handlerelor. 2. Partea de viewstate este procesat a, popul andu-se controalele cu date. 3. Evenimentul de Load este apelat. In event-handlerul pentru acest eveniment se ncepe programarea logicii paginii. 4. Event-handlerele pentru controale sunt apelate (ap as ari de butoane, schimbarea select iei curente dintr-un dropdown list, etc) 5. Se salveaz a viewstate 6. Pagina este tradus a n HTML (randare) D am mai jos cele mai importante evenimente din clasa Page.

11.2.1

Evenimentul Init

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

11.2.2

Evenimentul Load

Se apeleaz a dup a Init. Are acces la partea de viewstate a paginii (inacceibil a pentru Init ). Aceast a metod a poate decide dac a este cerut a de c atre client pentru prima dat a sau este rezultatul unui postback, pe baza propriet a tii boolene IsPostBack). Schit a unei metode Load ar : 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 desc arcarea paginii din memoria serverului. Aici se dealoc a resurse precum conexiuni la baza de date.

11.3

Crearea unei pagini ASP.NET folosind Visual Studio.NET

De si se poate scrie ASP.NET si de m an a, este de preferat folosirea VS.NET pentru c a permite crearea simpl a a sierelor aspx si a celor de codebehind. In plus, se creeaz a ni ste siere de set ari care ar bine s a se g aseasc a n folderul aplicat iei. Fi sierele care se creeaz a automat sunt Web.cong, Global.asax, Global.asax.cs. Ultimul este util pentru cazul n care se dore ste procesarea evenimentelor de pornire a aplicat iei sau de creare a sesiunii utilizator. Fi sierul Web.cong cont ine set ari (de securitate, legate de sesiune) specice aplicat iei. De asemenea s-a creat si un web form (alt nume pentru pagin a ASP.NET) numit implicit Webform1.aspx; din partea de Solution Explorer se poate modica numele lui cu unul adecvat, de exemplu HelloWorld.aspx (se folose ste aceast a denumire n cele ce urmeaz a). Pe prima linie a sierului HelloWorld.aspx apare: <%@ Page language="c#" Codebehind="HelloWorld.aspx.cs" AutoEventWireup="false" Inherits="HelloWorld.WebForm1"%> Pentru directiva Page s-au specicat urm atoarele atribute: language cu valoarea c# ; este limbajul n care se scrie n toate blocurile de cod de tip server; Codebehind specic a numele sierului surs a; acesta se creeaz a automat de c atre VS.NET;

230

CURS 11. ASP.NET

Inherits seteaz a clasa care este mo stenit a de forma web; aceast a clas a trebuie s a e derivat a direct sau indirect din clasa System.Web.UI.Page. Se trage de pe toolbox un control Label pe forma ASP.NET, iar n partea de propriet a ti se seteaz a pentru ID valoarea lblHelloWorld, iar pentru text 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 a pentru care ID-ul implicit este 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 a n toolbox, la partea de evenimente ata sate butonului se d a dublu click pe evenimentul Click n partea de code-behind se va genera automat metoda: private void Button1_Click(object sender, System.EventArgs e)

11.4. CONTROALE SERVER { }

231

atunci c and butonul este ap asat se face o submitere a formularului c atre server si codul cont inut n metoda de mai sus va executat. Codul va : private void Button1_Click(object sender, System.EventArgs e) { lblHelloWorld.Text = "Text schimbat"; } La ap asarea butonului textul din componenta lblHelloWorld se va schimba, iar pagina nou a va trimis a c atre browser. Re mprosp at ari succesive ale paginii (refresh) vor duce la a sarea aceluia si Text schimbat, deoarece viewstate-ul cont ine valoarea modicat a de ap asarea butonului.

11.4
11.4.1

Controale server
Postback

Postback apare de c ate ori se transmite pagina de la client napoi la server. Exist a dou a modalit a ti de realizarea a acestei trimiteri napoi: prin folosirea unor elemente de tip <input type="submit"> sau <input type="image"> respectiv prin script executat pe client. De exemplu, dac a se trage pe form 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 a pentru un formular nu se specic a partea de action, aceasta implicit va accea si pagin 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 a la valoarea SubmitTheForm() : <INPUT style="Z-INDEX: 103; LEFT: 80px; POSITION: absolute; TOP: 104px" type="button" value="Button" onclick="SubmitTheForm()"> In cazul controalelor Web de tip server a c aror modicare de stare trebuie s a duc a la postback (de exemplu la selectarea unui item dintr-un combobox), se seteaz a proprietatea AutoPostBack pe true, se seteaz a o metod a pentru evenimentul de schimbare a cont inutului ( n cazul nostru: SelectedIndexChanged ) iar pagina HTML generat a pe client va cont ine o funct ie javaScript de submitere 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 si EVENTARGUMENT sunt dou a c ampuri ascunse. Combobox-ul arat a astfel n codul HTML client: <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 propriet a ti a unui control la o surs a de date, precum un c amp n baza de date. Aceast a legare se poate face scriind expresii n interiorrul unei declarat ii de control n sierul .aspx sau n code-behind. Pentru a se lega direct n sierul .aspx se folose ste tag-ul <%# %> (expresie Data Binding). Aceast a metod a se aplic a pentru orice control care nu este list a precum Label, TextBox, CheckBox. C and Data Binding se face pentru un control de tip list a (DropDownList sau CheckBoxList) se va folosi proprietatea DataSource a controlului la o surs a de date oarecare (e.g. DataSet). Aceasta paote f acut a n partea de code-behind sau ntr-un bloc de tip script din sierul .aspx. Toate controalele care au posibilit a ti de Data Binding implementeaz ao metod a numit a DataBind(). Aceasta se ocup a de evaluarea expresiilor de biding n sierul .aspx sau pentru sursele de date specicate n proprietatea DataSource. Este important de ret inut c a expresiile Data Binding si propriet a tile DataSource nu sunt evaluate p an a c and nu se apeleaz a metoda DataBind() ale controalelor cu care sunt asociate. Deoarece Page este el nsu si un control, are o implementare de metod a DataBind(), care c and este apelat a va duce la apelul tuturor metodelor DataBind() pentru controalele copil.

11.5

Controale Web

Controalele Web reprezint a o modalitate u soar a de a interat iona cu serverul. Nu exist a o relat ie univoc a ntre controale web si elemente Html, ci controalele web vor randa pe parte de client cod Html specic browserului. Clasa Page are o proprietate nestatic a numit a ClientTarget care permite denirea browserului client. Valorile pentru proprietate sunt: Auto - se va face autodetectarea browserului DownLevel - se presupune c a browserul stie doar Html 3.2 sau mai put in UpLevel - browserul stie Html 4 sau mai mult, CSS, EcmaScript, Microsoft Document Object Model Orice control Web este derivat din System.Web.UI.WebControls care este derivat din System.Web.UI.Control. Aceasta din urm a pune la dispozit ie partea de Data Binding, viewstate, evenimente precum Init, Load, Unload,

234

CURS 11. ASP.NET

etc. Clasa WebControl cont ine propriet a ti de stil precum bacgkround, height, width.

11.5.1

Label

Cont ine text de tip read-only, asignat static sau prin Data Binding. Se genereaz a pe browser ca ind un element de tip span. Pentru crearea unei etichete cu text predenit se trage un control Web de pe toolbar si se pot seta propriet a ti ale lui, ceea ce ar putea genera urm atorul cod n designer: <asp:Label id=Label1 runat="server" Width="70px" Height="50px"> This is a label </asp:Label> Dac a se dore ste folosirea DataBinding, atunci se poate scrie: <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 a e efectiv calculat a si a sat a pe parte de browser trebuie chemat a metoda DataBind() pentru obiectul etichet a; acest lucru se poate face n partea de nc arcare a paginii: protected void Page_Load(object sender, System.EventArgs e) { Label1.DataBind(); }

11.5.2

Button

Permite crearea unui buron de tip submit si care poate s a determine executarea unei metode C# pe parte de client, pe baza unui event handler denit n code-behind. In cazul n care se dore ste crearea mai multor butoane care s a e procesate pe server, e nevoie de un mecanism care s a fac a diferent ierea lor, astfel nc at s a se poat a determina care a fost butonul ap asat. Acest lucru se face prin propriet a tile CommandName si CommandArgument de tip string. Event handlerul pentru butoane poate s a e: 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 propriet a tile CommandName si CommandArgument care vor da valorile setate pentru controale.

11.5.3

LinkButton

Se comport a ca si un buton; codul Html generat va de forma <a> iar la ap asare va apela funct ia Javascript __doPostBack prezentat a mai sus.

11.5.4

TextBox

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

11.5.5

CheckBox

Determin a aparit ia unui checkbox; propriet a tile de interes sunt Checked, Text, TextAlign, AutoPostBack iar evenimentul detectat este CheckedChanged.

11.5.6

RadioButton

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

11.5.7

DropDownList

Permite crearea unei liste de opt iuni stocate sub numele de nume si valoare. Toate item-urile cont inute sunt accesibile via proprietatea Items de tip ListItemCollection. Fiecare element al unei astfel de colet ii este de tip ListItem si are propriet a tile Text, Value de tip String si Selected de tip boolean. Lista de opt iuni se poate crea e prin introducere direct a n proprietatea Items, e prin Data Binding. Evenimentul care se pate detecta este SelectedIndexChanged si se poate procesa pe server.

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 condit ii pentru cont inutul controalelor de introducere a datelor. Aceast a validare se poate face pe server sau (de preferat n majoritatea cazurilor) pe client (dac a acesta suport a DHTML). Valid ari suplimentare mai complexe pot ap area si pe server, chiar dac a au fost efectuate valid ari primare pe client. Unui control de preluare a datelor i se pot asocia oric ate controale de validare. De exemplu, se poate folosi un control de tipul RequiredFieldValidator si altul care s a valideze cont inutul folosind expresii regulate; doar primul control poate determina dac a s-a introdus ceva, celelalte return and automat valoare de adev ar dac a controlul de intrare nu cont ine nimic. Orice astfel de control de validare este derivat din clasa BaseValidator, care prezint a proprietatea ControlToValidate n care se depune ca valoare IDul controlului pentru care se face validarea. Proprietatea Display determin a dac a se rezerv a loc pe pagin a pentru a se a sa mesajul de eroare. Poate avea valorile Static si Dynamic : n primul caz se rezerv a sucient spat iu pentru mesajul de eroare, chiar dac a acesta nu este vizibil; n al doilea caz dac a mesajul de eroare este ar atat, atunci pot ap area deplas ari ale componentelor de pe pagin a pentru a face loc textului de eroare. Mesajul de eroare se dene ste n proprietatea ErrorMessage. Proprietatea EnableClientScript setat a pe true va duce la generarea de cod scripting pentru client (JavaScript sau VBScript); dac a proprietatea este setat a pe false se va face validarea doar pe server. Trebuie ret inut faptul c a validarea se face ntotdeauna si pe server. Pentru un client de tip nonDHTML, pentru un asemenea control nu se va genera nici un cod scripting client, ci atunci c and formularul este trimis c atre server se va face testare va237

238

CURS 12. ASP.NET (2)

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

12.1.1

RequiredFieldValidator

Fort eaz a utilizatorul s a introduc a ceva ntr-un control de introducere. Dac a se ncearc a submiterea formularului iar c ampul pentru care s-a setat vaidatorul nu are valoare introdus a se va semnala eroare prin aparit ia mesajului denit de utilizator n proprietatea ErrorMessage. Codul JavaScript care se efectueaz a aceste valid ari este scris n sierul surs a C:\Inetpub\wwwroot\ aspnet client\system web\1 1 4322\WebUIValidation.js.

12.1.2

RegularExpressionValidator

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

12.1.3

RangeValidator

Veric a dac a valoarea dintr-o intrare este cuprins a ntre un minim si un maxim. Pentru cont inut si extreme tipul poate String, Integer, Double, Date si Currency, specicat n proprietatea Type a controlului. Valorile minime si maxime admise se dau n propriet a tile MinimumValue si MaximumValue.

12.1.4

CompareValidator

Se folose ste pentru a compara cont inutul unui control cu cont inutul altui control sau cu o valoare x a, predenit a. Dac a se dore ste compararea cu
1

Pentru o colet ie bogat a de expresii regulate, a se vedea si www.regexlib.com

12.1. CONTROALE DE VALIDARE

239

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

12.1.5

CustomValidator

Permite crearea de validatoare de c atre programator, care se pot executa at at pe client c at si pe server. Pentru validarea pe server trebuie dat un delegat de tipul: void HandlerName (object source, ServerValidateEventArgs args) Al doilea parametru are proprietatea Value care d a acces pe server la valoarea introdus a pe client si proprietatea IsValid de tip bool care trebuie setat a pe true dac a validarea s-a f acut cu succes sau false n caz contrar. Aceast a valoare afecteaz a valoarea propriet a tii IsValid a obiectului de tip Page. Motivul pentru care s-ar folosi un asemenea validator ar c a vericarea se poate face numai pe server (de exemplu se verica dac a numele si parola unui utilizator se regasesc in baza de date). Dac a ns a se dore ste validarea si pe client folosind JavaScript, atunci se va seta n mod adecvat proprietatea ClientValidationFunction. Exemplu: pentru validarea pe server a valorii introduse ntr-un c amp, se 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 aceea si vericare si pe client, se poate scrie codul urm ator la sf ar situl paginii aspx: <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 funct iei JavaScript este setat n proprietatea ClientValidationFunction.

12.1.6

ValidationSummary

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

12.2

Comunicarea cu browserul

Pentru trimiterea de date c atre client se poate folosi pe parte de server obiectul predenit Response de tip HttpResponse. De si ASP.NET pune la

12.2. COMUNICAREA CU BROWSERUL

241

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

12.2.1

Generarea programatic a cont inutului

In unele cazuri r aspunsul care se trimite de la server la browserul client poate sucient de simplu pentru a nu necesita utilizarea de controale. Utilitatea unei asemenea metode este limitat a, dar exist a situat ii n care calea se dovede ste a bun 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 cont inutului unor siere c ate browser, via metoda WriteFile.

12.2.2

Redirectarea

Se folose ste pentru cazul n care pagina care se trimite clientului nu este cea curent a, ci o alta. De exemplu, pentru cazul n care se dore ste redirectarea utilizatorului c ater o pagin a de login. Redirectarea trebuie f acut a nainte ca vreun header s a e trimis c atre client. Exemplu: Response.Redirect("http://mysite.com/myBusiness"); Aceast a redirectare are loc de pe server, f ar a a se trimite cod c atre browser care s a l determine pe acesta s a cear a o alt a resurs a.

242

CURS 12. ASP.NET (2)

12.3

Cookies

Cookie-urile reprezint a informat ie stocat a pe client. De si nu reprezint a un mecanism garantat pentru lucrul cu clientul, n unele cazuri pot sucient de utile pentru o aplicat ie. Exist a 4 clase de lucru cu cookie-urile: HttpCookie HttpCookieCollection HttpResponse HttpRequest Obiectul Response are o colect ie numit a Cookies. C and un browser face o cerere c atre un server, va trimite prin intermediul headerelor toat a colect ia de cookie-uri care apart in acelui site. Aceast a colect ie se ncarc a n Cookies. Atunci c and se trimite un cookie de pe server pe client se poate preciza dac a acesta va valabil doar pe perioada c at este deschis browserul (a sa-numitele cookie-uri de sesiune ) sau si dup a ce browserul este nchis (cookie-uri persistente - rezist a p an a la o dat a a expir arii setat a sau pentru totdeauna). Primul tip de cookie-uri sunt utile n medii n care nu exist a intimitate a utilizatorului, un cont ind folosit de c atre mai mult i; al doilea tip se poate folosi pentru stocarea informat iilor (precum preferint ele) de la o vizit a la alta. Diferent a dintre cele dou a din punct de vedere programatic const a n absent a sau prezent a unui timp de expirare. Cele de tip sesiune nu au setat nici un timp de expirare ( si se ret in n memoria RAM a calculatorului), pe c and celelalte pot avea un timp setat n viitor (ind persistate pe disc). 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. FIS IERUL DE CONFIGURARE AL APLICAT IEI 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

Fi sierul de congurare al aplicat iei Web

Diverse set ari ale aplicat iei pot ment inute ntr-un sier de tip XML numit Web.cong. Structura si modul de accesare a cont inutului acestuia este asem an atoare cu cea prezentat a n sect iunea 8.4.4. Deosebit este ns a faptul pentru o aplicat ie Web aceast a congurare respect a o anumit a ierarhie. La r ad acina acestei ierarhii se a a sierul machine.cong aat n diorectorul %windir%\Microsoft.NET\Framework\Version\CONFIG. Set arile de aici pot suprascrise de set ari aate n directoarele Web.cong unei aplicat ii ASP.NET.

12.5

Fi sierul global.asax

Este un sier opt ional structurat XML care prezint a set ari relative la aplicat ia ASP.NET (mai exact, pentru obiectul reprezent and aplicat ia). Fi sierul se g ase ste n r ad acina aplicat iei, este compilat ca o clas a care este derivat a din HttpApplication, ind recompilat a automat la ecare modicare a sa. Metodele care se pot implementa n aceast a clas a sunt: Application Start - apelat a atunci c and un client cere pentru prima oar a o pagin a de la aceast a aplicat ie; folosit a pentru init ializarea unor resurse (grupuri de conexiuni, valori din regi stri, etc care sunt accesate de c atre p art i din aplicat ie). Session Start - ori de c ate ori se porne ste o sesiune Application BeginRequest - apelat ori de c ate ori un client cere o pagin a oarecare de la server; s-ar putea folosi la contorizarea num arului de accese la pagin a

244

CURS 12. ASP.NET (2)

Application EndRequest - apelat la sf ar situl oric arei cereri c atre server Application AuthenticateRequest - apelat a la ecare cerere de autenticare a unui utilizator Application Error - apelat a ori de c ate ori apare o eroare care nu a fost procesat a Session End - la terminarea unei sesiuni prin expirare Application End - apare la o resetare a serverului sau atunci c and se recompileaz a aplicat ia ASP.NET.

12.6

Managementul sesiunii

Sesiunea este un mecansim prin care se creeaz a impresia unei conexiuni permanente de la client la server. O sesiune este stocat a pe server si va ret ine toate obiectele necesare bunei funct ion ari pe perioada respectiv a (de exemplu poate cont ine co sul de cump ar aturi pentru un client, ID-ul acelui client, etc). Ori de c ate ori un client cere prima pagin a a unei aplicat ii Web, se creeaz a un obiect de sesiune pe server identicat printr-un num ar unic care este de asemenea trimis clientului printr-un cookie. Aceast a sesiune este ata sat a doar browserului care a cerut crearea ei; dac a acesta se nchide, atunci vechea sesiune este inaccesibil a. De ecare dat a c and se cere o pagin a de la server (dup a ce sesiunea a fost creat a), se cere id-ul stocat n cookie-ul de pe client si se a a care este sesiunea ata sat a. Stocarea unei valori n sesiune trebuie s a se fac a folosind o cheie (unic a pe acea sesiune) si o valoare ata sat a (de ori ce tip). Exemplu: Session["username"] = "jsmiley"; Dac a cheia username nu exist a, se va crea si i se va ata sa string-ul numit; dac a exist a, valoarea sa se va suprascrie. Recuperarea valorii anterioare se face cu: String uname = (string)Session["username"]; Ment ion am c a n sesiune se pot stoca orice obiecte, nu doar siruri de caractere. Dac a serverul web se reporne ste, toate valorile din acea sesiune se pierd; exist a alternativa de a stoca sesiunile pe un alt server sau chiar pe un server SQL. S tergerea unei chei si a valori ata sate din sesiune se face folosind 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 a o colect ie de valori care se stocheaz a pe client, dar nu prin intermediul cookie-urilor, ci pe baza unui c amp de tip hidden din pagina HTML trimis a clientului. Este folosit a n special pentru a ret ine 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 a cont in a doar string-uri. Exemplu: ViewState["uname"] = "Joe"; .. String suname = (string)ViewState["uname"]; Se poate sterge selectiv din obiectul ViewState prin metoda Remove(String cheie).

12.7.1

Application

Obiectul Application este foarte asem an ator cu Session, cu deosebirea c a nu este unic pentru ecare sesiune utilizator, ci pentru ntreag aplicat ie. Este utilizat pentru ment inearea unor obiecte cu caracter global, utile pentru orice sesiune (valori din regi stri sau valori imuabile care nu trebuie citite de ecare dat a). Stocarea de valori se face prin: Application["x"] = new MyObject(); iar accesarea lui prin: MyObject x = Application["x"];

12.8

Alte puncte de interes n aplicat ii ASP.NET

crearea controalelor utilizator - a paginilor care sunt convertite n controale pentru a permite reutilizarea lor mai simpl a - ex: header, footer; separarea codului de prezentare - pentru a permite separarea muncii ntre web designer si programator; separarea pe mai multe nivele u sureaz a ment inerea codului si schimbarea lui; este sugerat a folosirea a cel put in 3 nivele: interfat a utilizator, nivelul de business logic, nivelul de acces la date;

246

CURS 12. ASP.NET (2)

crearea de aplicat ii web pentru dispozitive mobile; de si la ora actual a piat a de dispozitive mobile este relativ redus a, exist a totu si un segment bine denit care cere astfel de aplicat ii. Design-ul specic si capabilit a tile unui astfel de browser pot ridica probleme noi; politici de caching - pentru optimizarea accesului la resurse; autenticarea - diverse forme de securitate: formulare, .NET Passport; folosirea protocoalelor de comunicat ie securizate (SSL) folosirea serviciilor Web - servicii care se pot accesa n Internet; bazate pe XML si protocoale de comunicat ie larg r asp andite, un serviciu Web este independent de platform a.

Curs 13 Servicii Web


13.1 Generalit a ti

Un serviciu Web (SW) expune o interfat a de invocare a unei activit a ti de c atre un client. Un client poate accesa un serviciu Web folosind standarde Internet. Un serviciu web rezolv a urm atoarele probleme: interoperabilitate - un SW trebuie s a permit a comunicarea ntre orice platforme; interfet e puternic tipizate - nu trebuie s a existe ambiguitate relativ la tipul de date trimise sau primite spre/de la un SW. Aceste tipuri de date ar trebui s a se poat a mapa rezonabil peste tipurile de date folosite de limbajele procedurale existente; folosirea standardelor Internet - implementarea unui SW trebuie s a se bazeze pe standardele si protocoalele de comunicare deja existente, pentru a evita reinventarea rot ii acces multilimbaj - un SW nu trebuie s a reduc a aria limbajelor care le pot utiliza; transparent a trebuie s a se manifeste at at la nivelul limbajului n care s-a creat SW, c at si la nivelul clientului; posibilitate de integrare n orice infrastructur a distribuit a - un SW nu trebuie s a e str ans cuplat de o anumit a infrastructur a de comunicare.

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 a se dore ste conectarea la un serviciu Web, este necesar a o modalitate de a determina locat ia serviciului - proces numit descoperire. Descoperirea poate realizat a prin intemediul unor directoare centralizate sau prin alte metode ad-hoc. Descrierea - dup a ce s-a rezolvat problema de la punctul precedent, clientul va avea nevoie de o descriere a modului de interact iune cu SW. Descrierea reprezint a metadate structurate despre interfat a ce urmeaz a a consumat a, precum si documentat ie scris a despre SW cont in and detalii sau exemple de utilizare. Formatul mesajelor - necesar a precizat pentru a se putea codica ntr-un mod convenabil toat a conversat ia ntre client si SW. Acest lucru permite abstractizarea protocoalelor de comunicare si concentrarea pe logica problemei. Codicarea - datele trimise ntre cei 2 parteneri de conversat ie trebuie codicate ntr-un mod care s a permit a procesarea lor de c atre orice limbaj. XML este alegerea potrivit a pentru a sa ceva, ind structurat si de tip text. Transportul - modul prin care datele serializate care compun un mesaj sunt trimise de la un cap at la cel alalt. S-a cerut explicit funct ionarea unui serviciu web peste protocoale prietenoase, care s a nu necesite set ari speciale de securitate.

13.2
13.2.1

Discut ii asupra blocurilor componente


Protocolul de transport

O aplicat ie bazat a pe SW poate s a e folosit a at at n LAN c at si ntr-o ret ea de mare ntindere. Protocoale precum HTTP sau SMTP sunt testate din punct de vedere al serviciilor pe care l ofer a. Faptul c a HTTP este f ar a stare permite crearea unor aplicat ii care s a permit a continuarea servirii unor client i chiar dac a serverul init ial a c azut (sarcina sa ind preluat a de alte servere). In plus, SMTP si HTTP sunt portocoale care nu cer o congurare special a a rewall-urilor care delimiteaz a o ret ea.

13.2.2

Schema de codicare

XML reprezint a alegerea natural a pentru codicarea mesajelor dintre client si server, deoarece este structurat (f ac and procesarea sa u soar a) si

13.2. DISCUT II ASUPRA BLOCURILOR COMPONENTE

249

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

13.2.3

Convent ia de formatare

Al aturi de corpul mesajului se transmit de cele mai multe ori si metadate. Simple Object Access Protocol (SOAP) este un protocol care descrie regulile si formatul pentru comunicare. Prin intermediul lui SOAP se pot transmite documente si se pot face apeluri de metode: <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> In exemplul de mai sus se transmite cont inutul unei comenzi de la Jim la Frank. Pentru apelul unei metode la distant a s-ar putea folosi: <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> corespunz ator apelului de metod a C#: GetStockPrice( "msft" ); Structura general a a unui mesaj SOAP este un plic1 care este format din header-e (metadate asociate mesajului) si cont inut propriu-zis.

13.2.4

Mecanismul de descriere

Web Services Description Language (WSDL) dene ste un document XML care permite descrierea serviciului Web tint a. Pemite descrierea cont inutului mesajului dintre client si serviciu, realizarea de tipuri de date utilizator, formatul de comunicare ntre client si server.

13.2.5

Mecanisme de descoperire

Universal Description, Discovery, and Integration (UDDI) este mecansimul prin care se aduce la cuno stint a existent a unor servicii web disponibile
1

Engl: envelope

13.3. CREAREA UNUI SERVICIU WEB SIMPLU

251

oric arui client. UDDI este un director centralizat care poate folosit pentru anunt area de SW, pentru care se poate face c autarea dup a criterii: categoria, tipul de SW, compania, etc. De asemenea se poate folosi si DISCO, un format XML proprietar Microsoft, av and ca principal consumator Visual Studio .NET.

13.3

Crearea unui serviciu Web simplu

Mai jos exemplic am crearea unui SW si a unor client i care s a interact ioneze cu acestea.

13.3.1

Adunarea a dou a numere

Vom crea un serviciu Web care preia dou a numere ntregi si returneaz a suma lor. Manual, acest lucru se face prin crearea unui director virtual (de ex. Test), se creeaz a un sier cu extensia asmx n acest director (e el Test.asmx) si se scrie codul: <%@ 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. R aspunsul este un document XML: <?xml version="1.0" encoding="utf-8" ?> <int xmlns="http://tempuri.org/">5</int> Descrierea pentru acest serviciu (documentul WSDL) este obt inut a prin adresa: http://localhost/Test/Test.asmx?WSDL.

252

CURS 13. SERVICII WEB

13.3.2

Validarea unui num ar de card de credit

S a presupunem c a avem de implementat un site de tip B2C (de exemplu v anzare de c art i). Atunci c and se efectueaz a plata prin card de credit se cere de la utilizator num arul de card si data expir arii. Se presupune c a aceast a validare nu este f acut a de c atre aplicat ie, ci pe baza unui serviciu Web specializat. Vom exemplica cele ce urmeaz a prin Visual Studio, de si se poate folosi si alt a modalitate. Pentru Web Service se poate proceda astfel: File->New->Project->se alege ASP.NET Web Service->se completeaz a numele PlataWS. Din Solution Explorer se modic a numele sierului n PlataWS.asmx. In partea de cod se scrie metoda: [WebMethod] public bool ValidateCardNo(String cardNo, String date) { return int.Parse(cardNo) %2 == 0; } (o validare simpl a, pe baza parit a tii num arului de card). Pentru partea de site se poate crea o form a windows de tipul celei din gura 13.2, n care s a se poat a introduce num arul de card si data expir arii.

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

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 ap asarea butonului 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 a. Validitatea este deci testat a prin intermediul unui serviciu Web care returneaz a true sau false, n funct ie de care se a seaz a sau nu eroare.

13.3.3

SW pentru transmitere de cont inut binar

Folosind SOAP se poate transmite orice fel de mesaj, indiferent de sursa de provenient a a acestuia. Vom ar ata mai jos modul n care se poate face transmiterea unui sier binar folosind servicii Web2 . 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 a FTP este mai potrivit pentru a sa ceva

254

CURS 13. SERVICII WEB

unde FileContent este o structur a denit a n acela si spat u de nume: namespace FileTransfer { public struct FileContent { public string Name; public byte[] Content; } } Aplicat ia client poate s a e de tip form a Windows, ce permite specicarea c aii absolute a unui sier aat pe server si care salveaz a cont inutul obiectului de tip FileTransfer pe discul local. Pentru accesarea serviciului Web se adaug a o referint a Web la serviciul creat anterior (denumit a mai jos site ), astfel put and-se referi orice tip de date declarat public n interiorul spat iului de nume. Secvent a 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 a modul de mpachetare a mesajelor. C ateva avantaje ale SOAP-ului sunt: 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 stie s a transmit a text nu este legat de nici o infrastructur a distribuit a (DCOM, Corba, etc) se folose ste de standarde existente - XML, XML schema, HTTP, SMTP, etc

13.4. SOAP

255

interoperabilitate ntre platforme: SOAP poate folosit de c atre PCuri, dispozitive mobile, mainframe-uri, etc Un mesaj SOAP const a ntr-un plic compus n principal din header-e si corp.

13.4.1

Header-e

Elementele de header sunt folosite pentru a transmite cont inut care nu are leg atur a propriu-zis a cu corpul mesajului. De exemplu, dac a se folose ste un anumit algoritm de compresie a cont inutului mesajului atunci receptorul trebuie s a stie care este acesta. De asemenea se pot include: autenticare - se poate cere receptorului s a se autentice nainte de a procesat mesajul informat ie de tip rezumat sau semn atur a digital a pentru a se asigura c a cont inutul mesajului nu afost alterat informat ie de rutare - dac a mesajul trebuie s a ajung a la mai multe destinat ii Atributul mustUnderstand Headerele pot ignorate de c atre receptor. Dar pentru o parte a acesora se poate cere ca s a se asigure nt elegerea unui anumit header s a e garantat a de c atre client sau ca ntregul mesaj s a e ignorat. Intr-un asemenea caz se specic a pentru acel element de header atributul mustUnderstand cu valoarea 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 mult i intermediari. Se folose ste un atribut actor care specic a adresa intermediarului. Acest intermediar trebuie s a elimine informat ia de intermediar care l prive ste, dar poate s a adauge la r andul lui alt a informat ie de acest tip.

13.4.2

Elementul body

Un mesaj SOAP poate avea un singur element de tip body al c arui cont inut poate s a e structurat XML sau simple siruri de caractere, orice care nu invalideaz a cont inutul documentului XML cont in ator. Pe baza acestui cont inut, un mesaj SOAP poate privit ca un mesaj orientat pe procedur a sau ca unul de tip document. Pentru primul caz, s a presupunem c a SW cere apelarea unei metode de 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> R aspunsul generat de serviciul Web este: <?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 a suport pentru transmiterea tipurilor de date denite de utilizator, a array-urilor, denirea parametrilor ca ind de tip valoare/referint a/ie sire, a 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