Visual C#

Lucian Sasu May 30, 2005

2

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 Specification 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˘s˘turi ale platformei .NET . . . . . . aa 2 Tipuri predefinite, tablouri, string-uri 2.1 Vedere general˘ asupra limbajului C# a 2.2 Tipuri de date . . . . . . . . . . . . . . 2.2.1 Tipuri predefinite . . . . . . . . 2.2.2 Tipuri valoare . . . . . . . . . . 2.2.3 Tipul enumerare . . . . . . . . 2.3 Tablouri . . . . . . . . . . . . . . . . . 2.3.1 Tablouri unidimensionale . . . . 2.3.2 Tablouri multidimensionale . . 2.4 Siruri de caractere . . . . . . . . . . . ¸ 2.4.1 Expresii regulate . . . . . . . . 3 Clase, instructiuni, spatii de ¸ ¸ 3.1 Clase – vedere general˘ . . a 3.2 Transmiterea de parametri 3.3 Conversii . . . . . . . . . . 3.3.1 Conversii implicite nume . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 11 12 12 12 13 14 15 16 17 17 17 21 21 23 24 25 29 36 36 37 41 43 45 45 49 55 55

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3.4

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

CUPRINS 5 Destructori. POO ˆ C# ın 5.1 Destructori . . . . . . . . . . . . . . . . . . . . . . . 5.2 Specializarea ¸i generalizarea . . . . . . . . . . . . . . s 5.2.1 Specificarea mo¸tenirii . . . . . . . . . . . . . s 5.2.2 Apelul constructorilor din clasa de baz˘ . . . a 5.2.3 Operatorii is ¸i as . . . . . . . . . . . . . . . s 5.3 Clase sealed . . . . . . . . . . . . . . . . . . . . . . . 5.4 Polimorfismul . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Polimorfismul parametric . . . . . . . . . . . . 5.4.2 Polimorfismul ad–hoc . . . . . . . . . . . . . . 5.4.3 Polimorfismul de mo¸tenire . . . . . . . . . . . s 5.4.4 Virtual ¸i override . . . . . . . . . . . . . . . s 5.4.5 Modificatorul new pentru metode . . . . . . . 5.4.6 Metode sealed . . . . . . . . . . . . . . . . . . 5.4.7 Exemplu folosind virtual, new, override, sealed 5.5 Clase ¸i metode abstracte . . . . . . . . . . . . . . . s 5.6 Interfete . . . . . . . . . . . . . . . . . . . . . . . . . ¸ 5.6.1 Clase abstracte sau interfete? . . . . . . . . . ¸

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

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

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

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

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

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

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

6 7.2.2 7.2.3 7.2.4

CUPRINS Atribute predefinite . . . . . . . . . . . . . . . . . . . . 164 Exemplificarea altor atribute predefinite . . . . . . . . 167 Atribute definite de utilizator . . . . . . . . . . . . . . 170

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

CUPRINS

7

9.3

9.2.5 Propagarea modific˘rilor c˘tre baza de date . . . . . . 200 a a Tranzactii ˆ ADO.NET . . . . . . . . . . . . . . . . . . . . . 202 ¸ ın . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 . 205 . 205 . 208 . 209 . 209 . 213 . 213 . 215 . 217 . 218 . 219 223 . 224 . 224 . 225 . 226 . 228 . 228 . 228 . 229 . 229 . 231 . 231 . 233 . 233 . 234 . 234 . 235 . 235 . 235 . 235 . 235

10 Fire de executie ¸ 10.1 Managementul thread–urilor . . . . . . . . . . . 10.1.1 Pornirea thread–urilor . . . . . . . . . . 10.1.2 Metoda Join() . . . . . . . . . . . . . . 10.1.3 Suspendarea firelor de executie . . . . . ¸ 10.1.4 Omorˆrea thread–urilor . . . . . . . . . a 10.1.5 Sugerarea priorit˘¸ilor firelor de executie at ¸ 10.1.6 Fire ˆ fundal ¸i fire ˆ prim-plan . . . . ın s ın 10.2 Sincronizarea . . . . . . . . . . . . . . . . . . . 10.2.1 Clasa Interlocked . . . . . . . . . . . . . 10.2.2 Instructiunea lock . . . . . . . . . . . . . ¸ 10.2.3 Clasa Monitor . . . . . . . . . . . . . . .

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

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

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

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

12.2

12.3 12.4 12.5 12.6 12.7 12.8

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

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

Curs 1 Platforma Microsoft .NET
1.1 Prezentare general˘ a

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

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

9

10

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

Motivul pentru care Microsoft a trecut la dezvoltarea acestei platforme este maturizarea industriei software, accentuˆndu–se urm˘toarele directii: a a ¸ 1. Aplicatiile distribuite - sunt din ce ˆ ce mai numeroase aplicatiile de ¸ ın ¸ tip client / server sau cele pe mai multe nivele (n−tier). Tehnologiile distribuite actuale cer de multe ori o mare afinitate fat˘ de produc˘tor ¸a a ¸i prezint˘ o carent˘ acut˘ a interoper˘rii cu Web-ul. Viziunea actual˘ s a ¸a a a a se dep˘rteaz˘ de cea de tip client/server c˘tre una ˆ care calculatoare, a a a ın dispozitive inteligente ¸i servicii conlucreaz˘ pentru atingerea scopurilor s a propuse. Toate acestea se fac deja folosind standarde Internet neproprietare (HTTP, XML, SOAP). 2. Dezvoltarea orientat˘ pe componente - este de mult timp cerut˘ sima a plificarea integr˘rii componentelor software dezvoltate de diferiti proa ¸ duc˘tori. COM (Component Object Model) a realizat acest deziderat, a dar dezvoltarea ¸i distribuirea aplicatiilor COM este prea complex˘. s ¸ a Microsoft .NET pune la dispozitie un mod mai simplu de a dezvolta ¸i ¸ s a distribui componente. 3. Modific˘ri ale paradigmei Web - de-a lungul timpului s–au adus ˆ a ımbun˘t˘¸iri tehnologiilor Web pentru a simplifica dezvoltarea aplicatiilor. a at ¸ ˆ ultimii ani, dezvoltarea aplicatiilor Web s–a mutat de la prezentare In ¸ (HTML ¸i adiacente) c˘tre capacitate sporit˘ de programare (XML ¸i s a a s SOAP). 4. Alti factori de maturizare a industriei software - reprezint˘ con¸tienti¸ a s zarea cererilor de interoperabilitate, scalabilitate, disponibilitate; unul din dezideratele .NET este de a oferi toate acestea.
Bussiness to Bussiness - Comert electronic ˆ ¸ ıntre parteneri de afaceri, diferit˘ de a comertul electronic ˆ ¸ ıntre client ¸i afacere (Bussiness to Consumer – B2C). s
3

1.2. ARHITECTURA PLATFORMEI MICROSOFT .NET

11

1.2

Arhitectura platformei Microsoft .NET

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

12

CURS 1. PLATFORMA MICROSOFT .NET

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

1.3
1.3.1

Componente ale lui .NET Framework
Microsoft Intermediate Language

Una din uneltele de care dispune ingineria software este abstractizarea. Deseori vrem s˘ ferim utilizatorul de detalii, s˘ punem la dispozitia altora a a ¸ o interfat˘ simpl˘, care s˘ permit˘ atingerea scopului, f˘r˘ a fi necesare ¸a a a a aa cunoa¸terea tuturor detaliilor. Dac˘ interfata r˘mˆne neschimbat˘, se pot s a ¸ a a a modifica toate detaliile interne, f˘r˘ a afecta actiunile celorlati beneficiari ai aa ¸ ¸ codului. ˆ cazul limbajelor de programare, s-a ajuns treptat la crearea unor nivele In de abstractizare a codului rezultat la compilare, precum p-code (cel produs de compilatorul Pascal-P) ¸i bytecode (binecunoscut celor care au lucrat ˆ Java). s ın Bytecode-ul Java, generat prin compilarea unui fi¸ier surs˘, este cod scris ˆ s a ıntrun limbaj intermediar care suport˘ POO. Bytecod-ul este ˆ acela¸i timp o a ın s abstractizare care permite executarea codului Java, indiferent de platforma ¸int˘, atˆta timp cˆt aceast˘ platform˘ are implementat˘ o ma¸in˘ virtual˘ t a a a a a a s a a Java, capabil˘ s˘ “traduc˘” mai departe fi¸ierul class ˆ cod nativ. a a a s ın Microsoft a realizat ¸i el propria sa abstractizare de limbaj, aceasta nus mindu-se Common Intermediate Language. De¸i exist˘ mai multe limbaje s a de programare de nivel ˆ ınalt (C#, Managed C++, Visual Basic .NET, etc), la compilare toate vor produce cod ˆ acela¸i limbaj intermediar: Microsoft ın s Intermediate Language (MSIL, sau IL pe scurt). Asem˘n˘tor cu bytecod-ul, a a IL are tr˘s˘turi OO, precum abstractizarea datelor, mo¸tenirea, polimorfisaa s mul, sau concepte care s-au dovedit a fi extrem de necesare, precum exceptiile ¸ sau evenimentele. De remarcat c˘ aceast˘ abstractizare de limbaj permite a a rularea aplicatiilor independent de platform˘ (cu aceea¸i conditie ca la Java: ¸ a s ¸ s˘ existe o ma¸in˘ virtual˘ pentru acea platform˘). a s a a a

1.3.2

Common Language Specification

Unul din scopurile .NET este de a sprijini integrarea limbajelor astfel ˆ at ıncˆ programele, de¸i scrise ˆ diferite limbaje, pot interopera, folosind din plin s ın mo¸tenirea, polimorfismul, ˆ s ıncapsularea, exceptiile, etc. Dar limbajele nu ¸
4

www.go-mono.com

1.3. COMPONENTE ALE LUI .NET FRAMEWORK

13

sunt identice: unele suport˘ supraˆ arcarea operatorilor (Managed C++, a ınc˘ C#), altele nu (Visual Basic .NET); unele sunt case sensitive, altele nu. Pentru a se asigua totu¸i interoperabilitatea codului scris ˆ diferite limbaje, s ın Microsoft a publicat Common Language Specification (CLS), un subset al lui CTS (Common Type System, vezi 1.3.4), continˆnd specificatii de reguli ¸ a ¸ necesare pentru integrarea limbajelor. CLS define¸te un set de reguli pentru compilatoarele .NET, asigurˆnd s a faptul c˘ fiecare compilator va genera cod care interfereaz˘ cu platforma a a (mai exact, cu CLR–ul — vezi mai jos) ˆ ıntr-un mod independent de limbajul surs˘. Obiectele ¸i tipurile create ˆ diferite limbaje pot interactiona f˘r˘ a s ın ¸ aa probleme suplimentare. Combinatia CTS/CLS realizeaz˘ de fapt interoper¸ a area limbajelor. Concret, se poate ca o clas˘ scris˘ ˆ C# s˘ fie mo¸tenit˘ a a ın a s a de o clas˘ scris˘ ˆ Visual Basic care arunc˘ exceptii ce sunt prinse de cod a a ın a ¸ scris ˆ C++ sau J#. ın

1.3.3

Common Language Runtime

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

14

CURS 1. PLATFORMA MICROSOFT .NET

ˆ esent˘, activitatea unui compilator JIT este destinat˘ a ˆ In ¸a a ımbun˘t˘¸i pera at formanta executiei, ca alternativ˘ la compilarea repetat˘ a aceleia¸i buc˘¸i ¸ ¸ a a s at de cod ˆ cazul unor apel˘ri multiple. Unul din avantajele mecanismului JIT ın a apare ˆ clipa ˆ care codul, o dat˘ ce a fost compilat, se execut˘ pe diverse ın ın a a procesoare; dac˘ ma¸ina virtual˘ este bine adaptat˘ la noua platform˘, atunci a s a a a acest cod va beneficia de toate optimiz˘rile posibile, f˘r˘ a mai fi nevoie a aa recompilarea lui (precum ˆ C++, de exemplu). ın

1.3.4

Common Type System

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

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

15

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

1.3.5

Metadata

Metadata ˆ ınseamn˘ ˆ general “date despre date”. ˆ cazul .NET, ea a ın In reprezint˘ informatii destinate a fi citite de c˘tre ma¸in˘, nu de utilizatorul a ¸ a s a uman. Este stocat˘ ˆ a ımpreun˘ cu codul pe care ˆ descrie. Pe baza metadatei, a ıl CLR ¸tie cum s˘ instantieze obiectele, cum s˘ le apeleze metodele, cum s˘ s a ¸ a a acceseze propriet˘¸ile. Printr-un mecanism numit reflection, o aplicatie (nu at ¸

16

CURS 1. PLATFORMA MICROSOFT .NET

neap˘rat CLR) poate s˘ interogheze aceast˘ metadat˘ ¸i s˘ afle ce expune a a a as a un obiect. Mai pe larg, metadata contine o declaratie a fiec˘rui tip ¸i cˆte o declaratie ¸ ¸ a s a ¸ pentru fiecare metod˘, cˆmp, proprietate, eveniment al tipului respectiv. a a Pentru fiecare metod˘ implementat˘, metadata contine informatie care pera a ¸ ¸ mite ˆ arc˘torului clasei respective s˘ localizeze corpul metodei. De asemena ınc˘ a a mai poate contine declaratii despre cultura aplicatiei respective, adic˘ despre ¸ ¸ ¸ a localizarea ei (limba folosit˘ ˆ partea de interfat˘ utilizator). Mecanismul a ın ¸a prin care aceste informatii se folosesc pe parcursul rul˘rii de c˘tre aplicatii ¸ a a ¸ se nume¸te reflection. s

1.3.6

Assemblies

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

˘ ˘ 1.4. TRASATURI ALE PLATFORMEI .NET

17

ceea ce duce la probleme. O solutie la aceast˘ dilem˘ ar fi instalarea fi¸ierelor ¸ a a s dll ˆ directorul aplicatiei, dar ˆ acest mod ar disp˘rea reutilizarea acestor ın ¸ ın a biblioteci.

1.3.7

Assembly cache

Assembly cache este un director aflat ˆ mod normal ˆ directorul \Winnt ın ın \Assemblies. Atunci cˆnd un assembly este instalat pe o ma¸in˘, el va fi a s a ad˘ugat ˆ assembly cache. Dac˘ un assembly este desc˘rcat de pe Internet, a ın a a el va fi stocat ˆ acest assembly cache, ˆ ın ıntr-o zon˘ tranzient˘. Aplicatiile a a ¸ instalate vor avea assemblies ˆ ıntr-un assembly cache global. ˆ acest assembly cache vor exista versiuni multiple ale aceluia¸i assemIn s bly. Dac˘ programul de instalare este scris corect, va evita suprascrierea a assembly-urilor deja existente (¸i care functioneaz˘ perfect cu acplicatiile s ¸ a ¸ instalate), ad˘ugˆnd doar noul assembly. Este un mod de rezolvare a proba a lemei “DLL Hell”, unde suprascrierea unei biblioteci dinamice cu o variant˘ a mai nou˘ putea duce la nefunctionarea corespunz˘toare a aplicatiilor antea ¸ a ¸ rior instalate. CLR este cel care decide, pe baza informatiilor din manifest, ¸ care este versiunea corect˘ de assembly de care o aplicatie are nevoie. Acest a ¸ mecanism pune cap˘t unei epoci de trist˘ amintire pentru programatori ¸i a a s utilizatori.

1.3.8

Garbage collection

Managementul memoriei este una din sarcinile cele mai consumatoare de resurse umane. Garbage collection este mecanismul care se declan¸eaz˘ s a atunci cˆnd alocatorul de memorie r˘spunde negativ la o cerere de alocare de a a memorie. Implementarea este de tip “mark and sweep”: se presupune initial ¸ c˘ toat˘ memoria alocat˘ se poate disponibiliza, dupa care se determin˘ a a a a care din obiecte sunt referite de variabilele aplicatiei; cele care nu mai sunt ¸ referite sunt dealocate, celelalte zone de memorie sunt compactate. Obiectele a c˘ror dimensiune de memorie este mai mare decˆt un anumit prag nu mai a a sunt mutate, pentru a nu cre¸te semnificativ penalizarea de performant˘. s ¸a ˆ general, CLR este cel care se ocup˘ de apelarea mecanismului de In a garbage collection. Totu¸i, la dorint˘, programatorul poate cere rularea lui. s ¸a

1.4

Tr˘s˘turi ale platformei .NET a a

Prin prisma celor prezentate pˆna acum putem rezuma urm˘toarele tr˘a a a s˘turi: a

18

CURS 1. PLATFORMA MICROSOFT .NET • Dezvoltarea multilimbaj: Deoarece exist˘ mai multe limbaje pentru a aceast˘ platform˘, este mai u¸or de implementat p˘rti specifice ˆ lima a s a¸ ın bajele cele mai adecvate. Numarul limbajelor curent implementate este mai mare decˆt 10. Aceast˘ dezvoltare are ˆ vedere ¸i debugging-ul a a ın s aplicatiilor dezvoltate ˆ mai multe limbaje. ¸ ın • Independenta de procesor ¸i de platform˘: IL este independent ¸ s a de procesor. O dat˘ scris˘ ¸i compilat˘, orice aplicatie .NET (al c˘rei a as a ¸ a management este f˘cut de c˘tre CLR) poate fi rulat˘ pe orice platform˘. a a a a Datorit˘ CLR-ului, aplicatia este izolat˘ de particularit˘¸ile hardware a ¸ a at sau ale sistemului de operare. • Managementul automat al memoriei: Problemele de tipul memory leakage nu mai trebuie s˘ preocupe programatorii; overhead-ul ina dus de c˘tre mecanismul de garbage collection este suportabil, fiind a implementat ˆ sisteme mult mai timpurii. ın • Suportul pentru versionare: Ca o lectie ˆ a¸at˘ din perioada de ¸ ınv˘t a “DLL Hell”, versionarea este acum un aspect de care se ¸ine cont. t Dac˘ o aplicatie a fost dezvoltat˘ ¸i testat˘ folosind anumite compoa ¸ as a nente, instalarea unei componente de versiune mai nou˘ nu va atenta la a buna functionare a aplicatiei ˆ discutie: cele dou˘ versiuni vor coexista ¸ ¸ ın ¸ a pa¸nic, alegerea lor fiind f˘cut˘ pe baza manifestelor. s a a • Sprijinirea standardelor deschise: Nu toate dispozitivele ruleaz˘ a sisteme de operare Microsoft sau folosesc procesoare Intel. Din aceast˘ a cauz˘ orice este strˆns legat de acestea este evitat. Se folose¸te XML ¸i a a s s cel mai vizibil descendent al acestuia, SOAP. Deoarece SOAP este un protocol simplu, bazat pe text, foarte asem˘n˘tor cu HTTP5 , el poate a a trece u¸or de firewall-uri, spre deosebire de DCOM sau CORBA. s • Distribuirea u¸oar˘: Actualmente instalarea unei aplicatii sub Wins a ¸ dows ˆ ınseamn˘ copierea unor fi¸iere ˆ ni¸te directoare anume, moa s ın s dificarea unor valori ˆ regi¸tri, instalare de componente COM, etc. ın s Dezinstalarea complet˘ a unei aplicatii este in majoritatea cazurilor a ¸ o utopie. Aplicatiile .NET, datorit˘ metadatelor ¸i reflect˘rii trec de ¸ a s a aceste probleme. Se dore¸te ca instalarea unei aplicatii s˘ nu ˆ s ¸ a ınsemne mai mult decˆt copierea fi¸ierelor necesare ˆ a s ıntr-un director, iar dezinstalarea aplicatiei s˘ se fac˘ prin ¸tergerea acelui director. ¸ a a s • Arhitectur˘ distribuit˘: Noua filosofie este de a asigura accesul la a a servicii Web distribuite; acestea conlucreaz˘ la obtinerea informatiei a ¸ ¸
5

Folosit la transmiterea paginilor Web pe Internet

˘ ˘ 1.4. TRASATURI ALE PLATFORMEI .NET

19

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

20

CURS 1. PLATFORMA MICROSOFT .NET

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

C#1 este un limbaj de programare modern, simplu, obiect–orientat. Este foarte asem˘n˘tor cu Java ¸i C++, motiv pentru care curba de ˆ a¸are este a a s ınv˘t foarte lin˘. a Un prim exemplu de program, care contine o serie de elemente ce se vor ¸ ˆ alni ˆ continuare, este: ıntˆ ın using System; class HelloWorld { public static void Main() { Console.WriteLine(‘‘Hello world!’’); } } Prima linie using System; este o directiv˘ care specific˘ faptul c˘ se vor a a a 2 folosi clasele care sunt incluse ˆ spatiul de nume System; un spatiu de ın ¸ ¸ nume este o colectie de tipuri sau o grupare de alte spatii de nume care pot ¸ ¸
1 2

“#” se pronunt˘ “sharp” ¸a Eng: namespace

21

22

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

fi folosite ˆ ıntr-un program (detalii vor fi date mai tˆrziu). ˆ cazul de fat˘, a In ¸a clasa care este folosit˘ din acest spatiu de nume este Console. Mai departe, a ¸ orice program este continut ˆ ¸ ıntr–o clas˘, ˆ cazul nostru HelloWorld. Punctul a ın de intrare ˆ aplicatie este metoda Main, care nu preia ˆ acest exemplu nici ın ¸ ın un argument din linia de comand˘ ¸i nu returneaz˘ explicit un indicator de as a stare a termin˘rii. a Pentru operatiile de intrare–ie¸ire cu consola, se folose¸te clasa Console ¸ s s din spatiul de nume System; pentru aceast˘ clas˘ se apeleaz˘ metoda static˘ ¸ a a a a WriteLine, care afi¸eaz˘ pe ecran mesajul, dup˘ care face trecerea la linie s a a nou˘. a O variant˘ u¸or modificat˘ a programului de mai sus, ˆ care se salut˘ a s a ın a persoanele ale c˘ror nume este transmis prin linia de comand˘: a a using System; class HelloWorld { public static void Main( String[] args) { for( int i=0; i<args.Length; i++) { Console.WriteLine( ‘‘Hello {0}’’, args[i]); } } } ˆ exemplul de mai sus metoda principal˘ preia o list˘ de parametri In a a transmi¸i din linia de comand˘ (un ¸ir de obiecte de tip String) ¸i va afi¸a s a s s s pentru fiecare nume “Hello” urmat de numele de indice i (numerotarea parametrilor ˆ ıncepe de la 0). Constructia “{0}” va fi ˆ ¸ ınlocuit˘ cu primul a argument care urmeaz˘ dup˘ “Hello {0}”. La executarea programului de a a mai sus ˆ forma: HelloWorld Ana Dan, se va afi¸a pe ecran: ın s Hello Ana Hello Dan Metoda Main poate s˘ returneze o valoare ˆ a ıntreag˘, care s˘ fie folosit˘ a a a de c˘tre sistemul de operare pentru a semnala dac˘ procesul s–a ˆ a a ıncheiat cu succes sau nu3 . Mention˘m faptul c˘ limbajul este case–sensitive4 . ¸ a a Ca metode de notare Microsoft recomand˘ folosirea urm˘toarelor dou˘ a a a conventii: ¸
3 4

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

2.2. TIPURI DE DATE

23

• Conventie Pascal, ˆ care prima liter˘ a fiec˘rui cuvˆnt se scrie ca liter˘ ın a a a a mare • conventia tip ”c˘mil˘” este la fel ca precedenta, dar primul caracter al ¸ a a primului cuvˆnt nu este scris ca liter˘ mare. a a ˆ general, conventia tip Pascal este folosit˘ pentru tot ce este vizibil din exIn ¸ a teriorul unei clase, precum clase, metode, propriet˘¸i, etc. Doar parametrii at metodelor se scriu cu conventia c˘mil˘. Se recomand˘ evitarea folosirii ¸ a a a notatiei ungare (numele unei entit˘¸i trebuie s˘ se refere la semantica ei, ¸ at a nu la tipul de reprezentare). Cuvintele cheie (cuvinte rezervate care nu pot fi folosite ca ¸i identificas tori) din C# sunt date ˆ tabelul 2.1: ın Tabelul 2.1: Cuvinte cheie ˆ C# ın abstract case const do explicit float 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 finally goto interface namespace out public sealed struct try unsafe while byte class delegate event fixed if internal new override readonly short switch typeof ushort

2.2

Tipuri de date

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

De fapt acestea sunt structuri, prezentate ˆ alt curs ın

24

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

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

2.2.1

Tipuri predefinite

C# contine un set de tipuri predefinite, pentru care nu este necesar˘ in¸ a cluderea vreunui spatiu de nume via directiva using: string, object, tipurile ¸ ˆ ıntregi cu semn ¸i f˘r˘ semn, tipuri numerice ˆ virgul˘ mobil˘, tipurile bool s aa ın a a ¸i decimal. Tipul string este folosit pentru manipularea ¸irurilor de caractere s s codificate Unicode; continutul obiectelor de tip string nu se poate modi¸ 6 fica . Clasa object este r˘d˘cina ierarhiei de clase din .NET, la care orice tip a a (inclusiv un tip valoare) poate fi convertit. Tipul bool este folosit pentru a reprezenta valorile logice true ¸i false. s Tipul char este folosit pentru a reprezenta caractere Unicode, reprezentate pe 16 biti. Tipul decimal este folosit pentru calcule ˆ care erorile determinate ¸ ın de reprezentarea ˆ virgul˘ mobil˘ sunt inacceptabile, el punˆnd la dispozitie ın a a a ¸ 28 de cifre zecimale semnificative. Tabelul de mai jos contine lista tipurilor predefinite, ar˘tˆnd totodat˘ ¸ aa a cum se scriu valorile corespunz˘toare: a Tabelul 2.2: Tipuri predefinite.

Tip object string sbyte short int long byte ushort uint

Descriere r˘dacina oric˘rui tip a a o secvent˘ de caractere Unicode ¸a tip ˆ ıntreg cu semn, pe 8 biti ¸ tip ˆ ıntreg cu semn, pe 16 biti ¸ tip ˆ ıntreg cu semn, pe 16 biti ¸ tip ˆ ıntreg cu semn, pe 64 biti ¸ tip ˆ ıntreg f˘r˘ semn, pe 8 biti aa ¸ tip ˆ ıntreg f˘r˘ semn, pe 16 biti aa ¸ tip ˆ ıntreg f˘r˘ semn, pe 32 biti aa ¸

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

6

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

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

25

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

2.2.2

Tipuri valoare

C# pune programatorului la dispozitie tipuri valoare, care sunt fie struc¸ turi, fie enumer˘ri. Exist˘ un set predefinit de structuri numite tipuri simple, a a identificate prin cuvinte rezervate. Un tip simplu este fie de tip numeric7 , fie boolean. Tipurile numerice sunt tipuri ˆ ıntregi, ˆ virgul˘ mobil˘ sau decimal. ın a a Tipurile intregi sunt sbyte, byte, short, ushort, int, uint, long, ulong, char; cele ˆ virgul˘ mobil˘ sunt float ¸i double. Tipurile enumerare se pot defini ın a a s de c˘tre utilizator. a Toate tipurile valoare deriveaz˘ din clasa System.ValueType, care la rˆndul a a ei este derivat˘ din clasa object (alias pentru System.Object). Nu este a posibil ca dintr–un tip valoare s˘ se deriveze. O variabil˘ de tip valoare a a ˆ ıntotdeauna trebuie s˘ contin˘ o valoare (nu poate fi null, spre deosebire a ¸ a de tipurile referint˘). Atribuirea pentru un astfel de tip ˆ ¸a ınseamn˘ copierea a valorii (clonarea) dintr–o parte ˆ alta. ın Structuri Un tip structur˘ este un tip valoare care poate s˘ contin˘ declaratii de a a ¸ a ¸ constante, cˆmpuri, metode, propriet˘¸i, indexatori, operatori, constructori a at sau tipuri imbricate. Vor fi descrise ˆ ˆ ın ıntr–o sectiune urm˘toare. ¸ a
7

Engl: integral type

26

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Tipuri simple C# pune are predefinit un set de tipuri structuri, numite tipuri simple. Tipurile simple sunt identificate prin cuvinte rezervate, dar acestea reprezint˘ a doar alias–uri pentru tipurile struct corespunz˘toare din spatiul de nume a ¸ System; corespondenta este dat˘ ˆ tabelul de mai jos: ¸ a ın Tabelul 2.3: Tipuri simple ¸i corespondentele lor cu tis ¸ purile din spatiul de nume System. ¸ Tabelul 2.3 Cuvˆnt rezervat Tipul alias a sbyte System.SByte byte System.Byte short System.Int16 ushort System.UInt16 int System.Int32 uint System.UInt32 long System.Int64 ulong System.UInt64 char System.Char float 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, fiind un tip alias pentru System.Int32, urm˘toarele declaratii sunt legale: a ¸ int i = int.MaxValue; //constanta System.Int32.MaxValue string s = i.ToString(); //metoda System.Int32.ToString() string t = 3.ToString(); //idem //double d = Double.ParseDouble("3.14"); Tipuri ˆ ıntregi C# suport˘ nou˘ tipuri ˆ a a ıntregi: sbyte, byte, short, ushort, int, uint, long, ulong ¸i char. Acestea au urm˘toarele dimensiuni ¸i domeniu de valori: s a s • sbyte reprezint˘ tip cu semn pe 8 biti, cu valori de la -128 la 127; a ¸

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

27

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

28

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

ˆ cazul ˆ care o operatie aritmetic˘ produce un rezultat care nu poate fi In ın ¸ a reprezentat ˆ tipul destinatie, comportamentul depinde de utilizarea operaın ¸ torilor sau a declaratiilor checked ¸i unchecked (care se pot utiliza ˆ surs˘ sau ¸ s ın a din linia de compilare): ˆ context checked, o eroare de dep˘¸ire duce la arunın as ˆ context unchecked, carea unei exceptii de tip System.OverflowException. In ¸ eroarea de dep˘¸ire este ignorat˘, iar bitii semnificativi care nu mai ˆ as a ¸ ıncap ˆ ın reprezentare sunt eliminati. ¸ Exemplu: byte i=255; unchecked { i++; }//i va avea valoarea 0, nu se semnaleaza eroare checked { i=255; i++;//se va arunca exceptie System.OverflowException } Pentru expresiile aritmetice care contin operatorii ++, - -, +, - (unar ¸i bi¸ s nar), *, / ¸i care nu sunt continute ˆ interiorul unui bloc de tip checked, s ¸ ın comportamentul este specificat prin intermediul optiunii /checked[+|−] dat ¸ din linia de comand˘ pentru compilator. a Tipuri ˆ virgul˘ mobil˘ ın a a Sunt prezente 2 tipuri numerice ˆ virgul˘ mobil˘: float ¸i double. Tipuın a a s rile sunt reprezentate folosind precizie de 32, respectivi 64 de biti, folosind ¸ formatul IEC 60559, care permit reprezentarea valorilor de “0 pozitiv” ¸i “0 s negativ” (se comport˘ la fel, dar anumite operatii duc la obtinerea acestor a ¸ ¸ dou˘ valori), +∞ ¸i −∞ (obtinute prin ˆ artirea unui num˘r strict pozia s ¸ ımp˘ ¸ a tiv, respectiv strict negativ la 0), a valorii Not–a–Number (NaN) (obtinut˘ ¸ a √ prin operatii ˆ virgul˘ mobil˘ invalide, de exemplu 0/0 sau −1), precum ¸ ın a a ¸i un set finit de numere8 . Tipul float poate reprezenta valori cuprinse ˆ s ıntre −45 38 1.5 × 10 ¸i 3.4 × 10 (¸i din domeniul negativ corespunz˘tor), cu o pres s a cizie de 7 cifre. Double poate reprezenta valori cuprinse ˆ ıntre 5.0 × 10−324 ¸i s 308 1.7 × 10 cu o precizie de 15-16 cifre. Operatiile cu floating point nu duc niciodat˘ la aparitia de exceptii, dar ¸ a ¸ ¸ ele pot duce, ˆ caz de operatii invalide, la valori 0, infinit sau NaN. ın ¸
8

Pentru detalii vezi [4], pag. 93.

2.2. TIPURI DE DATE

29

Literalii care specific˘ un num˘r reprezentat ˆ virgul˘ mobil˘ au forma: a a ın a a literal–real:: cifre-zecimale . cifre-zecimale exponentoptional sufix-de-tip-realoptional . cifre-zecimale exponentoptional sufix-de-tip-realoptional cifre-zecimale exponent sufix-de-tip-realoptional cifre-zecimale sufix-de-tip-real, unde exponent:: e semnoptional cifre-zecimale E semnoptional cifre-zecimale, semn este + sau -, sufix-de-tip-real este F, f, D, d. Dac˘ nici un sufix de tip a real nu este specificat, atunci literalul dat este de tip double. Sufixul f sau F specific˘ tip float, d sau D specific˘ double. Dac˘ literalul specificat nu a a a poate fi reprezentat ˆ tipul precizat, apare eroare de compilare. ın Tipul decimal Este un tip de date reprezentat pe 128 de biti, gˆndit a fi folosit ˆ calcule ¸ a ın financiare sau care necesit˘ precizie mai mare. Poate reprezenta valori aflate a ˆ intervalul 1.0 × 10−28 ¸i 7.9 × 1028 , cu 28 de cifre semnificative. Acest tip ın s nu poate reprezenta zero cu semn, infinit sau NaN. Dac˘ ˆ urma operatiilor, a ın ¸ un num˘r este prea mic pentru a putea fi reprezentat ca decimal, atunci el a este f˘cut 0, iar dac˘ este prea mare, rezult˘ o exceptie. Diferenta principal˘ a a a ¸ ¸ a fat˘ de tipurile ˆ virgul˘ mobil˘ este c˘ are o precizie mai mare, dar un ¸a ın a a a domeniu de reprezentare mai mic. Din cauza aceasta, nu se fac conversii implicite ˆ ıntre nici un tip ˆ virgul˘ mobil˘ ¸i decimal ¸i nu este posibil˘ ın a a s s a mixarea variabilelor de acest tip ˆ ıntr-o expresie, f˘r˘ conversii explicite. aa Literalii de acest tip se exprim˘ folosind ca sufix-de-tip-real caracterele m a sau M. Dac˘ valoarea specificat˘ nu poate fi reprezentat˘ prin tipul decimal, a a a apare o eroare la compilare. Tipul bool Este folosit pentru reprezentarea valorilor de adev˘r true ¸i false. Literalii a s care se pot folosi sunt true ¸i false. Nu exist˘ conversii standard ˆ s a ıntre bool ¸i nici un alt tip. s

2.2.3

Tipul enumerare

Tipul enumerare este un tip valoare, construit pentru a permite declararea constantelor ˆ ınrudite, ˆ ıntr–o manier˘ clar˘ ¸i sigur˘ din punct de vedere al a as a

30

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

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

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

31

Al doilea apel este legal, deoarece valorile care se pot specifica pentru un enum nu sunt limitate la valorile declarate ˆ enum. Ca atare, programatorul ın trebuie s˘ fac˘ valid˘ri suplimentare pentru a determina consistenta valorilor. a a a ¸ ˆ cazul de fat˘, la apelul de metod˘ se arunc˘ o exceptie (exceptiile vor fi In ¸a a a ¸ ¸ tratate pe larg ˆ ıntr-un curs viitor). Ca ¸i mod de scriere a enumer˘rilor, se sugereaz˘ folosirea conventiei s a a ¸ Pascal atˆt pentru numele tipului cˆt ¸i pentru numele valorilor continute, a a s ¸ precum ¸i evitarea folosirii prefixelor sau sufixelor pentru acestea. s Enumer˘rile nu pot fi declarate abstracte ¸i nu pot fi derivate. Orice a s enum este derivat automat din System.Enum, care este la rˆndul lui derivat a din System.ValueType); astfel, metodele mo¸tenite de la tipurile p˘rinte sunt s a utilizabile de c˘tre orice variabil˘ de tip enum. a a Fiecare tip enumerare care este folosit are un tip de reprezentare9 , pentru a se cunoa¸te cˆt spatiu de memorie trebuie s˘ fie alocat unei variabile de s a ¸ a acest tip. Dac˘ nu se specific˘ nici un tip de reprezentare (ca mai sus), atunci a a se presupune implicit tipul int. Specificarea unui tip de reprezentare (care poate fi orice tip integral, exceptˆnd tipul char) se face prin enuntarea tipului a ¸ dup˘ numele enumer˘rii: a a enum MyEnum : byte { small, large } Specificarea este folosit˘ atunci cˆnd dimensiunea ˆ memorie este impora a ın tant˘, sau cˆnd se dore¸te crearea unui tip de indicator (un tip flag) al c˘rui a a s a num˘r de st˘ri difer˘ de num˘rul de biti alocati tipului int (modelare de a a a a ¸ ¸ flag-uri): enum ActionAttributes : ulong { Read = 1, Write = 2, Delete = 4, Query = 8,
9

Engl: underlying type

32

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

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

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

33

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

34

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

Urm˘torul cod arat˘ cˆteva din artificiile care pot fi aplicate tipului a a a enumerare: obtinerea tipului unui element de tip enumerare precum ¸i a ¸ s tipului clasei de baz˘, a tipului de reprezentare, a valorilor continute (ca a ¸ nume simbolice ¸i ca valori), conversie de la un string la un tip enumers are pe baza numelui, etc. Exemplul este preluat din <FrameworkSDK> \Samples\Technologies\ValueAndEnumTypes. using System; namespace DemoEnum { class DemoEnum { enum Color { Red = 111, Green = 222, Blue = 333 } private static void DemoEnums() { Console.WriteLine("\n\nDemo start: Demo of enumerated types."); Color c = Color.Red; // What type is this enum & what is it derived from Console.WriteLine(" The " + c.GetType() + " type is derived from " + c.GetType().BaseType); // What is the underlying type used for the Enum’s 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 enum’s value as a string using different format specifiers Console.WriteLine("\n c’s value as a string: " + c.ToString("G"));//Red Console.WriteLine(" c’s value as a number: " + c.ToString("D"));//111 // Convert a string to an enum’s 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 enum’s value as a string Console.WriteLine("\n c’s value as a string: " + c.ToString("G"));//Blue Console.WriteLine(" c’s value as a number: " + c.ToString("D"));//333 Console.WriteLine("Demo stop: Demo of enumerated types."); }

36

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

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

2.3

Tablouri

ˆ cele mai multe cazuri nu se cunoa¸te aprioric num˘rul de obiecte care In s a se vor folosi la momentul rul˘rii. O solutie pentru stocarea elementelor avˆnd a ¸ a acela¸i tip o reprezint˘ tablourile. Sintaxa de declarare este asem˘n˘toare s a a a cu cea din Java sau C++, dar fiecare tablou este un obiect, derivat din clasa abstract˘ System.Array. Accesul la elemente se face prin intermediul indicilor a care ˆ ıncep de la 0 ¸i se termin˘ la num˘rul de elemente-1; orice dep˘¸ire a s a a as indicilor duce la aparitia unei exceptii: System.IndexOutOfRangeException. ¸ ¸ O variabil˘ de tip tablou poate avea valoare de null sau poate s˘ indice c˘tre a a a o instant˘ valid˘. ¸a a

2.3.1

Tablouri unidimensionale

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

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

37

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

2.3.2

Tablouri multidimensionale

C# cunoa¸te dou˘ tipuri de tablouri multidimensionale: rectangulare ¸i s a s 12 neregulate . Numele lor vine de la forma pe care o pot avea. Tablouri rectangulare Tablourile rectangulare au proprietatea c˘ pe fiecare dimensiune se afl˘ a a acela¸i num˘r de elemente. Se declar˘ ca ˆ exemplul de mai jos: s a a ın int[,] tab;
11 12

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

38

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

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

2.3. TABLOURI

39

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

Engl: jagged array

40

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

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

2.4. SIRURI DE CARACTERE ¸

41

2.4

Siruri de caractere ¸

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

Engl: verbatim literals

42

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

} La dispozitivul standard de ie¸ire se va afi¸a: s s a==b: true (object)a==b: false Alt operator care se poate utiliza este ”+”, reprezentˆnd concatenarea: a String s = "Salut" + " " + "lume"; s += "!!!!"; Clasa String pune la dispozitie metode pentru: comparare (Compare, Com¸ pareOrdinal, CompareTo), c˘utare (EndsWith, StartsWith, IndexOf, LastIna dexOf), modificare (a se ˆ ¸elege obtinerea altor obiecte pe baza celui curent ınt ¸ - Concat, CopyTo, Insert, Join, PadLeft, PadRight, Remove, Replace, Split, Substring, ToLower, ToUpper, Trim, TrimEnd, TrimStart)15 . Accesarea unui caracter aflat pe o pozitie i a unui ¸ir s se face prin folosirea parantezelor ¸ s drepte, cu acelea¸i restrictii asupra indicelui ca ¸i pentru tablouri: s[i]. s ¸ s ˆ clasa object se afl˘ metoda ToString() care este suprascris˘ ˆ fiecare In a a ın clas˘ ale c˘rei instante pot fi tip˘rite. Pentru obtinerea unei reprezent˘ri a a ¸ a ¸ a diferite se folose¸te metoda String.Format(). s Un exemplu de folosire a functiei Split() pentru desp˘rtirea unui ¸ir ˆ ¸ a¸ s ın functie de separatori este: ¸ using System; class Class1 { static void Main(string[] args) { String s = "Oh, I hadn’t thought of that!"; char[] x = {’ ’, ’,’ }; String[] tokens = s.Split( x ); for(int i=0; i<tokens.Length; i++) { Console.WriteLine("Token: {0}", tokens[i]);
15

A se vedea exemplele din MSDN.

2.4. SIRURI DE CARACTERE ¸ } } } va afi¸a pe ecran: s Token: Token: Token: Token: Token: Token: Token: Oh I hadn’t thought of that!

43

De remarcat c˘ pentru caracterul apostrof nu este obligatorie secventa escape a ¸ ˆ cazul ¸irurilor de caractere. Al doilea lucru care trebuie explicat este c˘ al ın s a doilea token este cuvˆntul vid, care apare ˆ a ıntre cei doi separatori al˘turati: a ¸ virgula ¸i spatiul. Metoda Split() nu face gruparea mai multor separatori, s ¸ lucru care ar fi de dorit ˆ prezenta a doi separatori al˘turati. Pentru aceasta ın ¸ a ¸ se folosesc expresii regulate. Pentru a lucra cu ¸iruri de caractere care permit modificarea lor se folose¸te s s clasa StringBuilder, din namespace–ul System.Text.

2.4.1

Expresii regulate

ˆ cazul ˆ care functiile din clasa String nu sunt suficient de puternice, In ın ¸ namespace–ul System.Text.RegularExpresions pune la dispozitie o clas˘ de ¸ a lucru cu expresii regulate numit˘ Regex. Expresiile regulate reprezint˘ o a a metod˘ extrem de facil˘ de a opera c˘ut˘ri/ˆ a a a a ınlocuiri pe text. Forma expresiilor regulate este cea din limbajul Perl. Aceast˘ clas˘ folose¸te o tehnic˘ interesant˘ pentru m˘rirea performana a s a a a ¸elor: dac˘ programatorul vrea, se scrie o secvent˘ ”din mers” pentru a t a ¸a implementa potrivirea expresiei regulate, dup˘ care codul este rulat16 . a Exemplul anterior poate fi rescris corect din puct de vedere al functionalit˘¸ii ¸ at prin folosirea unei expresii regulate, pentru a prinde ¸i cazul separatorilor s multipli adiacenti: ¸ class ExpresieRegulata { static void Main(string[] args) {
16

Codul este scris direct ˆ IL. ın

44

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI String s = "Oh, I hadn’t 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 hadn’t thought of that!

Curs 3 Clase – generalit˘¸i. at Instructiuni. Spatii de nume ¸ ¸
3.1 Clase – vedere general˘ a

Clasele reprezint˘ tipuri referint˘ definite de utilizator. O clas˘ poate s˘ a ¸a a a mo¸teneasc˘ o singur˘ clas˘ ¸i poate implementa mai multe interfete. s a a as ¸ Clasele pot contine constante, cˆmpuri, metode, propriet˘¸i, evenimente, ¸ a at indexatori, operatori, constructori de instant˘, destructori, constructori de ¸a clas˘, tipuri imbricate. Fiecare membru poate contine un nivel de accesare, a ¸ care controleaz˘ gradul de acces la el. O descriere este dat˘ ˆ tabelul 3.1: a a ın Tabelul 3.1: Modificatori de acces ai membrilor unei clase Accesor public protected Semnificatie ¸ Acces nelimitat Acces limitat la clasa contin˘toare ¸ a sau la tipuri derivate din ea internal Acces limitat la acest assembly protected internal Acces limitat la acest assembly sau la tipuri derivate din clas˘ a private Acces limitat la clas˘; a modificatorul implicit de acces

using System; 45

46

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

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

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

47

Constanta este un membru al unei clase care reprezint˘ o valoare constant˘, a a care poate fi evaluat˘ la compilare. Constantele pot depinde de alte a constante, atˆta timp cˆt nu se creeaz˘ dependente circulare. Ele sunt a a a ¸ considerate automat membri statici (dar este interzis s˘ se foloseasc˘ a a specificatorul static ˆ fata lor). Ele pot fi accesate exclusiv prin inın ¸ termediul numelui de clas˘ (MyClass.MyConst), ¸i nu prin intermediul a s vreunei instante (a.MyConst). ¸ Cˆmpul este un membru reprezentˆnd o variabil˘ asociat˘ unui obiect. a a a a

48

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

Metoda este un membru care implementeaz˘ un calcul sau o actiune care a ¸ poate fi efectuat˘ asupra unui obiect sau asupra unei clase. Metodele a statice (care au ˆ antet cuvˆntul cheie static) sunt accesate prin interın a mediul numelui de clas˘, pe cˆnd cele nestatice (metode instant˘) sunt a a ¸a apelate prin intermediul unui obiect. Proprietatea este un membru care d˘ acces la o caracteristic˘ a unui obiect a a sau unei clase. Exemplele folosite pˆn˘ acum includeau lungimea unui a a vector, num˘rul de caractere ale unui ¸ir de caractere, etc. Sintaxa a s pentru accesara cˆmpurilor ¸i a propriet˘¸ilor este aceea¸i. Reprezint˘ a s at s a o alt˘ modalitate de implementare a accesorilor pentru obiecte. a Evenimentul este un membru care permite unei clase sau unui obiect s˘ a pun˘ la dispozitia altora notific˘ri asupra evenimentelor. Tipul acestei a ¸ a declaratii trebuie s˘ fie un tip delegat. O instant˘ a unui tip delegat ¸ a ¸a ˆ ıncapsuleaz˘ una sau mai multe entit˘¸i apelabile. Exemplu: a at public delegate void EventHandler(object sender, System.EventArgs e); public class Button { public event EventHandler Click; public void Reset() { Click = null; } } using System; public class Form1 { Button Button1 = new Button1(); public Form1() { Button1.Click += new EventHandler(Button1_Click); } void Button1_Click(object sender, EventArgs e ) { Console.WriteLine("Button1 was clicked!");

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

49

Mai sus clasa Form1 adaug˘ Button1 Click ca tratare de eveniment1 a pentru evenimentul Click al lui Button1. ˆ metoda Disconnect(), acest In event handler este ˆ aturat. ınl˘ Operatorul este un membru care define¸te semnificatia (supraˆ arcarea) s ¸ ınc˘ unui operator care se aplic˘ instantelor unei clase. Se pot supraˆ arca a ¸ ınc˘ operatorii binari, unari ¸i de conversie. s Indexatorul este un membru care permite unui obiect s˘ fie indexat ˆ a ın acela¸i mod ca un tablou (pentru programatorii C++: supraˆ arcarea s ınc˘ operatorului []). Constructorii instant˘ sunt membri care implementeaz˘ actiuni cerute ¸a a ¸ pentru initializarea fiec˘rui obiect. ¸ a Destructorul este un membru special care implementeaz˘ actiunile cerute a ¸ pentru a distruge o instant˘ a unei clase. Destructorul nu are parametri, ¸a nu poate avea modificatori de acces, nu poate fi apelati explicit ¸i poate ¸ s fi apelat automat de c˘tre garbage collector. a Constructorul static este un membru care implementeaz˘ actiuni necea ¸ sare pentru a initializa o clas˘, mai exact membrii statici ai clasei. Nu ¸ a pot avea parametri, nu pot avea modificatori de acces, nu sunt apelati ¸ explicit, ci automat de c˘tre sistem. a Mo¸tenirea este de tip simplu, iar r˘d˘cina ierarhiei este clasa object (alias s a a System.Object).

3.2

Transmiterea de parametri

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

Engl: event handler

50

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

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

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

51

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

52

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

} } Ie¸irea acestui program va fi: s inainte de intrare in iesire din dupa apel: apel: 100 f: 100 f: -100 100

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

3.2. TRANSMITEREA DE PARAMETRI

53

va realiza interschimbarea valorilor a ¸i b. s Una din tr˘s˘turile specifice parametrilor referint˘ este c˘ valorile pentru aa ¸a a care se face apelul trebuie s˘ fie initializate. Neasignarea de valori pentru x a ¸ ¸i y ˆ exemplul de mai sus duce o eroare de compilare. Mai clar, exemplul s ın de mai jos genereaza eroare la compilare, mesajul fiind: ”Use of unassigned local variable ’x’”. class TestRef { static void f(ref int x) { x = 100; } static void Main() { int x; f(ref x); } } Exist˘ cazuri ˆ care dorim s˘ obtinem acela¸i efect ca la parametrii a ın a ¸ s referint˘, dar f˘r˘ a trebui s˘ initializ˘m argumentele date de c˘tre metoda ¸a aa a ¸ a a apelant˘ (de exemplu cˆnd valoarea acestui parametru se calculeaz˘ ˆ ina a a ın teriorul metodei apelate). Pentru aceasta exist˘ parametrii de ie¸ire2 , simia s lar cu parametrii referint˘, cu deosebirea c˘ nu trebuie asignat˘ o valoare ¸a a a parametrului de apel: using System; class Test { static void Main() { int l = 10; double area; ComputeSquareArea( l, out area); Console.WriteLine("Area is: {0}", area); } static void ComputeSquareArea( double l, out double area ) {
2

Engl: output parameters

54

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

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

Engl: parameter array

3.3. CONVERSII

55

3.3

Conversii

O conversie permite ca o expresie de un anumit tip s˘ fie tratat˘ ca fiind a a de alt tip. Conversiile pot fi implicite sau explicite, aceasta specificˆnd de a fapt dac˘ un operator de cast (conversie) este sau nu necesar. a

3.3.1

Conversii implicite

Sunt clasificate ca ¸i conversii implicite urm˘toarele: s a • conversiile identitate • conversiile numerice implicite • conversiile implicite de tip enumerare • conversiile implicite de referinte ¸ • boxing • conversiile implicite ale expresiilor constante • conversii implicite definite de utilizator Conversiile implicite pot ap˘rea ˆ a ıntr–o varietate de situatii, de exemplu ¸ apeluri de functii sau atribuiri. Conversiile implicite predefinite nu determin˘ ¸ a niciodat˘ aparitia de exceptii. a ¸ ¸ Conversiile indentitate O conversie identitate converte¸te de la un tip oarecare c˘tre acela¸i tip. s a s Conversiile numerice implicite Conversiile numerice implicite sunt: • de la sbyte la short, int, long, float, double, decimal; • de la byte la short, ushort, int, uint, long, ulong, float, double, decimal; • de la short la int, long, double, decimal; • de la ushort la int, uint, long, ulong, float, double, decimal; • de la int la long, float, double, decimal;

56

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

Conversiile de la int, uint, long, ulong la float, precum ¸i cele de la long s sau ulong la double pot duce la o pierdere a preciziei, dar niciodat˘ la o a reducere a ordinului de m˘rime. Alte conversii numerice implicite niciodat˘ a a nu duc la pierdere de informatie. ¸ Conversiile de tip enumerare implicite O astfel de conversie permite ca literalul 0 s˘ fie convertit la orice tip a enumerare (chiar dac˘ acesta nu contine valoarea 0) - a se vedea 2.2.3, pag. a ¸ 29. Conversii implicite de referinte ¸ Conversiile implicite de referinte implicite sunt: ¸ • de la orice tip referint˘ la object; ¸a • de la orice tip clas˘ B la orice tip clas˘ A, dac˘ B este derivat din A; a a a • de la orice tip clas˘ A la orice interfat˘ B, dac˘ A implementeaz˘ B; a ¸a a a • de al orice interfat˘ A la orice interfat˘ B, dac˘ A este derivat˘ din B; ¸a ¸a a a • de la orice tip tablou A cu tipul AE la un tip tablou B avˆnd tipul BE , a cu urm˘toarele conditii: a ¸ 1. A ¸i B au acela¸i num˘r de dimensiuni; s s a 2. atˆt AE cˆt ¸i BE sunt tipuri referint˘; a a s ¸a 3. exist˘ o conversie implicit˘ de tip referint˘ de la AE la BE a a ¸a • de la un tablou la System.Array; • de la tip delegat la System.Delegate; • de la orice tip tablou sau tip delegat la System.ICloneable; • de la tipul null la orice variabil˘ de tip referint˘; a ¸a

3.3. CONVERSII Conversie de tip boxing

57

Permite unui tip valoare s˘ fie implicit convertit c˘tre tipul object sau a a System.ValueType sau c˘tre orice tip interfat˘ pe care tipul valoare ˆ implea ¸a ıl menteaz˘. O descriere mai am˘nuntit˘ este dat˘ ˆ sectiunea 3.3.4. a a ¸ a a ın ¸

3.3.2

Conversiile implicite ale expresiilor constante

Permit urm˘toarele tipuri de conversii: a • o expresie constant˘ de tip int poate fi convertit˘ c˘tre tipurile sbyte, a a a byte, short, ushort, uint, ulong, cu conditia ca valoarea expresiei con¸ stante s˘ se afle ˆ domeniul tipului destinatie; a ın ¸ • o expresie constant˘ de tip long poate fi convertit˘ la tipul ulong, dac˘ a a a valoarea ce se converte¸te nu este negativ˘. s a Conversii implicite definite de utilizator Constau ˆ ıntr–o conversie implicit˘ standard optional˘, urmat˘ de executia a ¸ a a ¸ unui operator de conversie implicit˘ utilizator urmat˘ de alt˘ conversie ima a a plicit˘ standard optional˘. Regulile exacte sunt descrise ˆ [4]. a ¸ a ın

3.3.3

Conversii explicite

Urm˘toarele conversii sunt clasificate ca explicite: a • toate conversiile implicite • conversiile numerice explicite • conversiile explicite de enumer˘ri a • conversiile explicite de referinte ¸ • unboxing • conversii explicite definite de utilizator Din cauz˘ c˘ orice conversie implicit˘ este de asemenea ¸i una explicit˘, a a a s a aplicarea operatorului de cast este redundant˘: a int x = 0; long y = (long)x;//(long) este redundant

58

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

Conversii numerice explicite Sunt conversii de la orice tip numeric la un alt tip numeric pentru care nu exist˘ conversie numeric˘ implicit˘: a a a • de la sbyte la byte, ushort, uint, ulong, char; • de la byte la sbyte, char; • de la short la sbyte, byte, ushort, uint, ulong, char; • de la ushort la sbyte, byte, short, char; • de la int la sbyte, byte, short, ushort, int, char; • de la uint la sbyte, byte, short, ushort, int, uint, long, ulong, char; • de la long la sbyte, byte, short, ushort, int, uint, ulong, char; • de la ulong la sbyte, byte, short, ushort, int, uint, long, char; • de la char la sbyte, byte, short; • de la float la sbyte, byte, short, ushort, int, uint, long, ulong, decimal; • de la double la sbyte, byte, short, ushort, int, uint, long, ulong, char, float, decimal; • de la decimal la sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double; Pentru c˘ ˆ astfel de conversii pot ap˘rea pierderi de informatie, exist˘ a ın a ¸ a dou˘ contexte ˆ care se fac aceste conversii: checked ¸i unchecked. a ın s ˆ context checked, conversia se face cu succes dac˘ valoarea care se conIn a verte¸te este reprezentabil˘ de c˘tre tipul c˘tre care se face conversia. ˆ s a a a In cazul ˆ care conversia nu se poate face cu succes, se va arunca exceptia Sysın ¸ tem.OverflowException. ˆ context unchecked, conversia se face ˆ In ıntotdeauna, dar se poate ajunge la pierdere de informatie sau la valori ce nu sunt bine ¸ nedefinite (vezi [4], pag. 115–116). Conversii explicite de enumer˘ri a Conversiile explicite de enumer˘ri sunt: a • de la sbyte, byte, short, ushort, int, uint, long, ulong, char, float, 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, float, double, decimal; • de la orice tip enumerare la orice tip enumerare. Conversiile de tip enumerare se fac prin tratarea fiec˘rui tip enumerare a ca fiind tipul ˆ ıntreg de reprezentare, dup˘ care se efectueaz˘ o conversie a a implict˘ sau explicit˘ ˆ a a ıntre tipuri (ex: dac˘ se dore¸te conversia de la un tip a s enumerare E care are tipul de reprezentare int la un tip byte, se va face o conversie explicit˘ de la int la byte; invers, se va face o conversie implict˘ de a a la byte la int). Conversii explicite de referinte ¸ Conversiile explicite de referinte sunt: ¸ • de la object la orice tip referint˘; ¸a • de la orice tip clas˘ A la orice tip clas˘ B, cu conditia ca A s˘ fie clas˘ a a ¸ a a de baz˘ pentru B; a • de la orice tip clas˘ A la orice tip interfat˘ B, dac˘ A nu este nederiva ¸a a abil˘ ¸i A nu implementeaz˘ pe B; as a • de la orice tip interfat˘ A la orice tip clas˘ B, dac˘ B nu este nederiv¸a a a abil˘ sau cu conditia ca B s˘ implementeze A; a ¸ a • de la orice tip interfat˘ A la orice tip interfat˘ B, dac˘ A nu este derivat ¸a ¸a a din B; • de la un tip tablou A cu elemente de tip AE la un tip tablou B cu elemente BE , cu conditiile: ¸ 1. A ¸i B au acela¸i num˘r de dimensiuni; s s a 2. AE ¸i BE sunt tipuri referint˘; s ¸a 3. exist˘ o conversie de referint˘ explicit˘ de la AE al BE a ¸a a • de la System.Array ¸i interfetele pe care le implementeaz˘ la orice tip s ¸ a tablou; • de la System.Delegate ¸i interfetele pe care le implementeaz˘ la orice s ¸ a tip delegat. Acest tip de conversii cer verificare la run–time. Dac˘ o astfel de conversie a e¸ueaz˘, se va arunca o exceptie de tipul System.InvalidCastException. s a ¸

60 Unboxing

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

Unboxing-ul permite o conversie explicit˘ de la object sau System.ValueType a la orice tip valoare, sau de la orice tip interfat˘ la orice tip valoare care im¸a plementeaz˘ tipul interfat˘. Mai multe detalii se vor da ˆ sectiunea 3.3.4. a ¸a ın ¸ Conversii explicite definite de utilizator Constau ˆ ıntr–o conversie standard explicit˘ optional˘, urmat˘ de executia a ¸ a a ¸ unei conversii explicite, urmat˘ de o alt˘ conversie standard explicit˘ optional˘. a a a ¸ a

3.3.4

Boxing ¸i unboxing s

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

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

61

procesul se desf˘¸oar˘ ca ˆ figura 3.1: la linia 1, se declar˘ ¸i se initializeaz˘ as a ın as ¸ a o variabil˘ de tip valoare, care va contine valoarea 10; valoarea va fi stocat˘ a ¸ a pe stiv˘. La linia 2 se va crea o referint˘ o c˘tre un obiect alocat ˆ heap, care a ¸a a ın va contine atˆt valoarea 10, cˆt ¸i o informatie despre tipul de dat˘ continut ¸ a a s ¸ a ¸ (ˆ cazul nostru, System.Int32). Unboxing–ul se face printr–un cast explicit, ın ca ˆ linia 3. ın
i 10

o 10 j 10

System.Int32

Figura 3.1: Boxing ¸i unboxing s Determinarea tipului pentru care s–a f˘cut ˆ a ımpachetarea se face prin intermediul operatorului is: int i = 123; object o = i; if (o is int) { Console.Write("Este un int inauntru!!!!"); } Boxing–ul duce la o clonare a valorii care va fi continut˘. ¸ a secventa: ¸ int i = 10; object o = i; i++; Console.WriteLine("in o: {0}", o); va afi¸a valoarea nealterat˘ 10. s a Altfel spus,

62

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

3.3.5

Declaratii de variabile ¸i constante ¸ s

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

3.3.6

Declaratii de etichete ¸

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

3.3. CONVERSII

63

3.3.7

Instructiuni de selectie ¸ ¸

Instructiunea if ¸ Instructiunea if execut˘ o instructiune ˆ functie de valoarea de adev˘r a ¸ a ¸ ın ¸ a unei expresii logice. Are formele: if (expresie logica) instructiune; if (expresie logica) instructiune; else instructiune; Instructiunea switch ¸ Permite executarea unei instructiuni ˆ functie de valoarea unei expresii, ¸ ın ¸ care se poate reg˘si sau nu ˆ a ıntr–o list˘ de valori candidat: a switch (expresie) { case eticheta: instructiune; case eticheta: instructiune; ... default: instructiune; } O etichet˘ reprezint˘ o expresie constant˘. O instructiune poate s˘ ¸i lipseasc˘ a a a ¸ as a ¸i ˆ acest caz se va executa instructiunea de la case–ul urm˘tor, sau de la des ın ¸ a fault. Sectiunea default poate s˘ lipseasc˘. Dac˘ o instructiune este nevid˘, ¸ a a a ¸ a atunci obligatoriu va avea la sfˆr¸it o instructiune break sau goto case expreas ¸ sieConstanta sau goto default. Expresia dup˘ care se face selectia poate fi de tip sbyte, byte, short, ushort, a ¸ int, uint, long, ulong, char, string, enumerare. Dac˘ valoarea expresiei se a reg˘se¸te printre valorile specificate la clauzele case, atunci instructiunea a s ¸ corespunz˘toare va fi executat˘; dac˘ nu, atunci instructiunea de la clauza a a a ¸ default va fi executat˘ (dac˘ ea exist˘). Spre deosebire de C ¸i C++, e interzis a a a s s˘ se foloseasc˘ fenomenul de ”c˘dere” de la o etichet˘ la alta; continuarea a a a a se face folosind explicit goto. Subliniem c˘ se permite folosirea unui selector a de tip string. switch (i) { case 0: Console.WriteLine("0"); break; case 1:

64

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

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

3.3.8

Instructiuni de ciclare ¸

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

3.3. CONVERSII Instructiunea do ¸

65

Execut˘ o instructiune o dat˘ sau de mai multe ori, cˆt timp o conditie a ¸ a a ¸ logic˘ este adev˘rat˘ (ciclu cu test posterior). a a a Exemplu: do { S += i++; }while(i<=n) Poate contine instructiuni break sau continue. ¸ ¸ Instructiunea for ¸ Execut˘ o secvent˘ de initializare, dup˘ care va executa o instructiune a ¸a ¸ a ¸ atˆta timp cˆt o conditie este adev˘rat˘ (ciclu cu test anterior); poate s˘ a a ¸ a a a contin˘ un pas de reinitializare (trecerea la pasul urm˘tor). Se permite ¸ a ¸ a folosirea instructiunilor break ¸i continue. ¸ s Exemplu: for (int i=0; i<n; i++) { Console.WriteLine("i={0}", i); } Instructiunea foreach ¸ Enumer˘ elementele dintr–o coletie, executˆnd o instructiune pentru fiecare a ¸ a ¸ element. Colectia poate s˘ fie orice instant˘ a unei clase care implementeaz˘ ¸ a ¸a a interfata System.Collections.IEnumerable. ¸ Exemplu: int[] t = {1, 2, 3}; foreach( int x in t) { Console.WriteLine(x); } Elementul care se extrage este de tip read–only (deci nu poate fi transmis ca parametru ref sau out ¸i nu se poate aplica un operator care s˘ ˆ schimbe s a ıi valoarea).

66

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

3.3.9

Instructiuni de salt ¸

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

3.3.10

Instructiunile try, throw, catch, finally ¸

Permit tratarea exceptiilor. Vor fi studiate ˆ detaliu la capitolul de ¸ ın exceptii. ¸

3.3. CONVERSII

67

3.3.11

Instructiunile checked ¸i unchecked ¸ s

Controleaz˘ contextul de verificare de dep˘¸ire a domeniului pentru arita as metica pe ˆ ıntregi ¸i conversii. Au forma: s checked { //instructiuni } unchecked { //instructiuni } Verificare se va face la run–time.

3.3.12

Instructiunea lock ¸

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

3.3.13

Instructiunea using ¸

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

68

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

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

3.4

Spatii de nume ¸

ˆ cazul cre˘rii de tipuri este posibil s˘ se foloseasc˘ un acela¸i nume In a a a s pentru tipurile noi create de c˘tre dezvoltatorii de soft. Pentru a putea a folosi astfel de clase care au numele comun, dar responsabilit˘¸i diferite, treat buie prev˘zut˘ o modalitate de a le adresa ˆ mod unic. Solutia la aceast˘ a a ın ¸ a 4 problem˘ este crearea spatiilor de nume care rezolv˘, printr–o adresare coma ¸ a plet˘ astfel de ambiguit˘¸i. Astfel, putem folosi de exemplu clasa Buffer din a at spatiul System (calificare complet˘: System.Buffer), al˘turi de clasa Buffer ¸ a a din spatiul de nume Curs3: Curs3.Buffer. ¸ Crearea unui spatiu de nume se face prin folosirea cuvˆntului namespace: ¸ a using System; namespace Curs3 { public class Buffer { public Buffer() { Console.WriteLine("Bufferul meu!"); } } } Se pot de asemenea crea spatii de nume imbricate. Altfel spus, un spatiu ¸ ¸ de nume este o colectie de tipuri sau de alte spatii de nume. ¸ ¸
4

engl: namespaces

3.4. SPATII DE NUME ¸

69

3.4.1

Declaratii de spatii de nume ¸ ¸

O declaratie de spatiu de nume const˘ ˆ cuvˆntul cheie namespace, urmat ¸ ¸ a ın a de identificatorul spatiului de nume ¸i de blocul spatiului de nume, delimitat ¸ s ¸ de acolade. Spatiile de nume sunt implicit publice ¸i acest tip de acces nu se ¸ s poate modifica. ˆ interiorul unui spatiu de nume se pot utiliza alte spatii In ¸ ¸ de nume, pentru a se evita calificarea complet˘ a claselor. a Identificatorul unui spatiu de nume poate fi simplu sau o secventa de ¸ ¸˘ identificatori separati prin ”.”. Cea de a doua form˘ permite definirea de ¸ a spatii de nume imbricate, f˘r˘ a se imbrica efectiv: ¸ aa namespace N1.N2 { class A{} class B{} } este echivalent˘ cu: a namespace N1 { namespace N2 { class A{} class B{} } } Dou˘ declaratii de spatii de nume cu aceea¸i denumire contribuie la a ¸ ¸ s declararea unui acela¸i spatiu de nume: s ¸ namespace N1.N2 { class A{} } namespace N1.N2 { class B{} } este echivalent˘ cu cele dou˘ declaratii anterioare. a a ¸

70

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

3.4.2

Directiva using

Directiva using faciliteaz˘ ˆ primul rˆnd utilizarea spatiilor de nume ¸i a a ın a ¸ s tipurilor definite ˆ acestea; ele nu creeaz˘ membri noi ˆ cadrul unit˘¸ii de ın a ın at program ˆ care sunt folosite, ci au rol de a u¸ura referirea tipurilor. Nu se ın s pot utiliza ˆ interiorul claselor, structurilor, enumer˘ririlor. ın a Exemplu: e mai u¸or de ˆ s ınteles un cod de forma: using System; class A { static void Main() { Console.WriteLine("Mesaj"); } } decˆt: a class A { static void Main() { System.Console.WriteLine("Mesaj"); } } Directiva using poate fi de fapt folosit˘ atˆt pentru importuri simbolice, a a cˆt ¸i pentru crearea de aliasuri. a s Directiva using pentru import simbolic O directiv˘ using permite importarea simbolic˘ a tuturor tipurilor continute a a ¸ direct ˆ ıntr–un spatiu de nume, i.e. folosirea lor f˘r˘ a fi necesar˘ o calificare ¸ aa a complet˘. Acest import nu se refer˘ ¸i la spatiile de nume continute: a as ¸ ¸ namespace N1.N2 { class A{} } namespace N3.N4 { class B{};

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

71

ˆ situatia de mai sus, conflictul (care poate foarte u¸or ˆ cazul ˆ care se In ¸ s ın ın folosesc tipuri produse de dezvoltatori diferiti) poate fi rezolvat de o calificare ¸ complet˘: a namespace N3 { using N1; using N2; class B {

72

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

} Tipurile declarate ˆ interiorul unui spatiu de nume pot avea modificatori de ın ¸ acces public sau internal (modificatorul implicit). Un tip internal nu poate fi folosit prin import ˆ afara assembly-ului, pe cˆnd unul public, da. ın a Directiva using ca alias Introduce un identificator care serve¸te drept alias pentru un spatiu de s ¸ nume sau pentru un tip. Exemplu: namespace N1.N2 { class A{} } namespace N3 { using A = N1.N2.A; class B { A a = null; } } Acela¸i efect se obtine creˆ un alias la spatiul de nume N1.N2: s ¸ ınd ¸ namespace N3 { using N = N1.N2; class B { N.A a = null; } } Identificatorul dat unui alias trebuie s˘ fie unic, adic˘ ˆ interiorul unui a a ın namespace nu trebuie s˘ existe tipuri ¸i aliasuri cu acela¸i nume: a s s

3.4. SPATII DE NUME ¸

73

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

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

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

= null;

N3

= null;

Numele create prin directive de alias sunt ascunse de c˘tre alte declaratii a ¸ care folosesc acela¸i identificator ˆ interiorul unui bloc: s ın

74

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME ¸ ¸

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

Curs 4 Clase
4.1 Declararea unei clase

Declararea unei clase se face ˆ felul urm˘tor: ın a atributeopt modificatori-de-clasaopt class identificator clasa-de-bazaopt corpclasa ;opt Modificatorii de clas˘ sunt: a public - clasele publice sunt accesibile de oriunde; poate fi folosit atˆt pentru a clase interioare, cˆt ¸i pentru clase care sunt continute ˆ spatii de a s ¸ ın ¸ nume; internal - se poate folosi atˆt pentru clase interioare, cˆt ¸i pentru clase care a a s sunt continute ˆ spatii de nume (este modificatorul implicit pentru ¸ ın ¸ clase care sunt continute ˆ spatii de nume). Semnific˘ acces permis ¸ ın ¸ a doar ˆ clasa sau spatiul de nume care o cuprinde; ın ¸ protected - se poate specifica doar pentru clase interioare; tipurile astfel calificate sunt accesibile ˆ clasa curent˘ sau ˆ cele derivate (chiar ın a ın dac˘ clasa derivat˘ face parte din alt spatiu de nume); a a ¸ private - doar pentru clase interioare; semnific˘ acces limitat la clasa cona ¸in˘toare; este modificatorul implicit; t a protected internal - folosibil doar pentru clase interioare; tipul definit este accesibil ˆ spatiul de nume curent, ˆ clasa contin˘toare sau ˆ tipurile ın ¸ ın ¸ a ın derivate din clasa contin˘toare; ¸ a new - permis pentru clasele interioare; clasa astfel calificat˘ ascunde un a membru cu acela¸i nume care este mo¸tenit; s s 75

76

CURS 4. CLASE

sealed - o clas˘ sealed nu poate fi mo¸tenit˘; poate fi clas˘ interioar˘ sau a s a a a nu; abstract - clasa care este incomplet definit˘ ¸i care nu poate fi instantiat˘; as ¸ a folosibil˘ pentru clase interioare sau continute ˆ spatii de nume; a ¸ ın ¸

4.2

Membrii unei clase

Corpul unei clase se specific˘ ˆ felul urm˘tor: a ın a { declaratii-de-membri };opt Membrii unei clase sunt ˆ artiti ˆ urm˘toarele categorii: ımp˘ ¸ ın a • constante • cˆmpuri a • metode • propriet˘¸i at • evenimente • indexatori • operatori • constructori (de instant˘) ¸a • destructor • constructor static • tipuri Acestor membri le pot fi ata¸ati modificatorii de acces: s ¸ public - membrul este accesibil de oriunde; protected - membrul este accesabil de c˘tre orice membru al clasei contin˘toare a ¸ a ¸i de c˘tre clasele derivate; s a internal - membrul este accesabil doar ˆ assembly-ul curent; ın protected internal - reuniunea precedentelor dou˘; a private accesabil doar ˆ clasa contin˘toare; este specificatorul implicit. ın ¸ a

ˆ 4.3. CAMPURI

77

4.3

Cˆmpuri a

Un cˆmp reprezint˘ un membru asociat cu un obiect sau cu o clas˘. a a a Modificatorii de cˆmp care se pot specifica optional ˆ a ¸ ınaintea unui cˆmp sunt a cei de mai sus, la care se adaug˘ modificatorii new, readonly, volatile, static, a ce vor fi prezentati mai jos. Pentru orice cˆmp este necesar˘ precizarea unui ¸ a a tip de date, ce trebuie s˘ aibe gradul de accesibilitate cel putin cu al cˆmpului a ¸ a ce se declar˘. Optional, cˆmpurile pot fi initializate cu valori compatibile. a ¸ a ¸ Un astfel de cˆmp se poate folosi fie prin specificarea numelui s˘u, fie printr-o a a calificare bazat˘ pe numele clasei sau al unui obiect. a Exemplu: class A { int a;//acces implicit de tip privat static void Main() { A objA = new A(); objA.a = 1;//se poate accesa in interiorul clasei } }

4.3.1

Cˆmpuri instante a ¸

Dac˘ o declaratie de cˆmp nu include modificatorul static, atunci acel a ¸ a cˆmp se va reg˘si ˆ orice obiect de tipul clasei curente care va fi instantiat. a a ın ¸ Modific˘ri ale acestor cˆmpuri se vor face independent pentru fiecare obiect. a a Deoarece un astfel de cˆmp are o valoare specific˘ fiec˘rui obiect, accesarea a a a lui se va face prin calificarea cu numele obiectului: objA.a = 1; (dac˘ modificatorii de acces permit a¸a ceva). a s Un cˆmp special este this (cuvˆnt cheie) care reprezint˘ o referint˘ la a a a ¸a obiectul curent.

4.3.2

Cˆmpuri statice a

Cˆnd o declaratie de cˆmp include un specificator static, cˆmpul respectiv a ¸ a a nu apatine fiec˘rei instante ˆ particular, ci clasei ˆ a¸i. Accesarea unui ¸ a ¸ ın ıns˘s cˆmp static din exteriorul clasei se face exclusiv prin intermediul numelui de a clas˘: a

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

CURS 4. CLASE

Dac˘ se face calificarea unui astfel de cˆmp folosind un nume de obiect se a a semnaleaz˘ o eroare de compilare. a

4.3.3

Cˆmpuri readonly a

Declararea unui cˆmp de tip readonly (static sau nu) se face prin specifia carea cuvˆntului readonly ˆ declaratia sa: a ın ¸ class A { public readonly string salut = ‘‘Salut’’; public readonly string nume; public class A(string nume) { this.nume = nume; } } Atribuirea asupra unui cˆmp de tip readonly se poate face doar la declararea a sa (ca ˆ exemplu de mai sus) sau prin intermediul unui constructor. Valoarea ın unor astfel de cˆmpuri nu se presupune a fi cunoscut˘ la compilare. a a

4.3.4

Cˆmpuri volatile a

Modificatorul ”volatile” se poate specifica doar pentru tipurile: • byte, sbyte, short, ushort, int, uint, char, float, bool; • un tip enumerare avˆnd tipul de reprezentare byte, sbyte, short, ushort, a int, uint; • un tip referint˘ ¸a

4.4. CONSTANTE

79

Pentru cˆmpuri nevolatile, tehnicile de optimizare care reordoneaz˘ instructiunile a a ¸ pot duce la rezultate nea¸teptate sau nepredictibile ˆ programe multithreads ın ing care acceseaz˘ cˆmpurile f˘r˘ sincronizare (efectuabil˘ cu instructiunea a a aa a ¸ lock). Aceste optimiz˘ri pot fi f˘cute de c˘tre compilator, de c˘tre sistemul a a a a 1 de rulare sau de c˘tre hardware. Urm˘toarele tipuri de optimiz˘ri sunt a a a afectate ˆ prezenta unui modificator volatile: ın ¸ • citirea unui cˆmp volatile este garantat˘ c˘ se va ˆ ampla ˆ a a a ıntˆ ınainte de orice referire la cˆmp care apare dup˘ citire; a a • orice scriere a unui cˆmp volatile este garantat˘ c˘ se va petrece dup˘ a a a a orice instructiune anterioar˘ care se refer˘ la cˆmpul respectiv. ¸ a a a

4.3.5

Initializarea cˆmpurilor ¸ a

Pentru fiecare cˆmp declarat se va asigna o valoare implicit˘ astfel: a a • numeric: 0 • bool: false • char: ‘\0’ • enum: 0 • referint˘: null ¸a

4.4

Constante

O constant˘ este un cˆmp a c˘rui valoare poate fi calculat˘ la compia a a a lare. O constant˘ poate fi prefixat˘ de urm˘torii modificatori: new, public, a a a protected, internal, protected internal,private. Cuvantul new poate s˘ se coma bine cu unul din ceilalti 4 modificatori de acces. Pentru un cˆmp constant e ¸ a obligatoriu s˘ se asigneze o valoare calculabil˘ la compilare: a a class A { public const int n=2; }
1

Engl: runtime system

80

CURS 4. CLASE

Tipul unei constante poate fi sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double decimal, bool, string, enum, referint˘. Valoarea care se ¸a asignez˘ unei constante trebuie s˘ admit˘ o conversie implicit˘ c˘tre tipul a a a a a constantei. Tipul unei constante trebuie s˘ fie cel putin la fel de accesibil ca a ¸ ¸i constanta ˆ a¸i. s ıns˘s Orice cˆmp constant este automat un cˆmp static. Un cˆmp constant a a a difer˘ de un cˆmp static readonly: const-ul are o valoare cunoscut˘ la coma a a pilare, pe cˆnd valoarea unui readonly poate fi initializat˘ la runtime ˆ a ¸ a ın interiorul constructorului (cel mai tˆrziu, de altfel). a

4.5

Metode

O metod˘ este un membru care implementeaz˘ un calcul sau o actiune a a ¸ care poate fi efectuat˘ de c˘tre un obiect sau o clas˘. Antetul unei metode a a a se declar˘ ˆ felul urm˘tor: a ın a atributeopt modificator-de-metodaopt tip-de-retur nume (lista-parametrilorformaliopt ) corp-metoda unde modificator-de-metoda poate fi: • orice modificator de acces • new • static • virtual • sealed • override • abstract • extern Tipul de retur poate fi orice tip de dat˘ care este cel putin la fel de accesibil a ¸ ca ¸i metoda ˆ a¸i sau void (absenta informatiei returnate); nume poate fi un s ıns˘s ¸ ¸ identificator de metod˘ din clasa curent˘ sau un identificator calificat cu nua a mele unei interfete pe care o implementeaz˘ (NumeInterfata.numeMetoda); ¸ a parametrii pot fi de tip ref, out, params, sau f˘r˘ nici un calificator; corpaa metoda este un bloc cuprins ˆ ıntre acolade sau doar caracterul “;” (dac˘ este a vorba de o metod˘ ce nu se implementeaz˘ ˆ tipul curent). a a ın Despre calificatorii virtual, override, sealed, new, abstract se va discuta mai pe larg ˆ ıntr–o sectiune viitoare. ¸

˘¸ 4.6. PROPRIETATI

81

4.5.1

Metode statice ¸i nestatice s

O metod˘ se declar˘ a fi static˘ dac˘ numele ei este prefixat cu moda a a a ificatorul de metod˘ static. O astfel de metod˘ nu opereaz˘ asupra unei a a a instante anume, ci doar asupra clasei. Este o eroare ca o metod˘ static˘ ¸ a a s˘ fac˘ referire la un membru nestatic al unei clase. Apelul unei astfel de a a metode se face prin NumeClasa.NumeMetoda sau direct NumeMetoda dac˘ a este apelat˘ din context static al aceleia¸i clase. a s O metod˘ nestatic˘ nu are cuvˆntul “static” specificat; ea este apelabil˘ a a a a ˆ conjunctie cu un obiect anume. ın ¸

4.5.2

Metode externe

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

4.6

Propriet˘¸i at

O proprietate este un membru care permite acces la un atribut al unui obiect sau al unei clase. Exemple de propriet˘¸i sunt: lungimea unui ¸ir, nuat s mele unui client, textul continut ˆ ıntr–un control de tip TextBox. Propriet˘¸ile at sunt extensii naturale ale cˆmpurilor, cu deosebirea c˘ ele nu presupun aloa a carea de memorie. Ele sunt de fapt ni¸te metode (accesori) care permit citirea s sau setarea unor atribute ale unui obiect sau clase; reprezint˘ modalitatea a de scriere a unor metode de tip get/set pentru clase sau obiecte.

82

CURS 4. CLASE

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

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

83

Un accesor de tip get corespunde unei metode f˘r˘ parametri, care returneaz˘ aa a o valoare de tipul propriet˘¸ii. Cˆnd o proprietate este folosit˘ ˆ at a a ıntr–o 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˘¸ii ¸i tip de retur void. Acest parametru implicit al lui set at s este numit ˆ ıntotdeauna value. Cˆnd o proprietate este folosit˘ ca destinatar a a ˆ ıntr–o atribuire, sau cˆnd se folosesc operatorii ++ ¸i −−, accesorului set i a s se transmite un parametru care reprezint˘ noua valoare. a ˆ functie de prezenta sau absenta accesorilor, o proprietate este clasifiIn ¸ ¸ ¸ cat˘ dup˘ cum urmeaz˘: a a a proprietate read–write, dac˘ are ambele tipuri de accesori; a proprietate read–only, dac˘ are doar accesor de tip get; este o eroare de a compilare s˘ se fac˘ referire ˆ program la o proprietate ˆ sensul ˆ a a ın ın ın care s–ar cere operarea cu un accesor de tip set; proprietate write–only, dac˘ este prezent doar accesorul de tip set; este a o eroare de compilare utilizarea unei propriet˘¸i ˆ at ıntr–un context ˆ care ın ar fi necesar˘ prezenta accesorului get. a ¸ Demn de mentionat este c˘ ei pot fi folositi nu numai pentru a asigura ¸ a ¸ o sintax˘ simplu de folosit pentru metodele traditionale de tip get/set, ci ¸i a ¸ s pentru scrierea controalelor .NET utilizator, care se face extrem de u¸or. s ˆ figura 4.1 este dat˘ reprezentarea unui control utilizator: In a Codul corespunz˘tor este dat mai jos: a using using using using using using System; System.Collections; System.ComponentModel; System.Drawing; System.Data; System.Windows.Forms;

84

CURS 4. CLASE

Figura 4.1: Control definit de utilizator

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

˘¸ 4.6. PROPRIETATI

85

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

86

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

}

Interesante sunt aici propriet˘¸ile publice Street, Number ¸i Phone care vor at s fi vizibile ˆ fereastra Properties atunci cˆnd acest control va fi ad˘ugat la o ın a a form˘. Atributele cuprinse ˆ a ıntre paranteze drepte sunt optionale, dar vor face ¸ ca aceste propriet˘¸i s˘ fie grupate ˆ sectiunea de date a ferestrei Properties, at a ın ¸ ¸i nu ˆ cea “Misc”. Pentru mai multe detalii despre componente utilizator, s ın a se vedea [1], cap. 4.

4.7. INDEXATORI

87

4.7

Indexatori

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

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

CURS 4. CLASE

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

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

89

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

CURS 4. CLASE

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

4.7. INDEXATORI

91

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

92

CURS 4. CLASE

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

4.8

Operatori

Un operator este un membru care define¸te semnificatia unei expresii ops ¸ erator care poate fi aplicat˘ unei instante a unei clase. Corespunde supraˆ arc˘rii a ¸ ınc˘ a operatorilor din C++. O declaratie de operator are forma: ¸ atributeopt modificatori-de-operator declaratie-de-operator corp-operator Se pot declara operatori unari, binari ¸i de conversie. s

4.8. OPERATORI Urm˘toarele reguli trebuie s˘ fie respectate pentru orice operator: a a 1. Orice operator trebuie s˘ fie declarat public ¸i static. a s

93

2. Parametrii unui operator trebuie s˘ fie de tip valoare; nu se admite s˘ a a fie de tip ref sau out. 3. Acela¸i modificator nu poate ap˘rea de mai multe ori ˆ antetul unui s a ın operator

4.8.1

Operatori unari

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

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

CURS 4. CLASE

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

4.8. OPERATORI

95

4.8.2

Operatori binari

Declararea unui operator binar se face astfel: tip operator operator-binar-supraincarcabil ( tip identificator, tip identificator) corp Operatorii binari supraˆ arcabili sunt: + - * / % & | ^ << >> == != > < >= <=. ınc˘ Cel putin unul dintre cei doi parametri preluati trebuie s˘ fie de tipul contin˘tor. ¸ ¸ a ¸ a Operatorii de shiftare trebuie s˘ aib˘ primul parametru de tipul clasei ˆ care a a ın se declar˘, iar al doilea parametru de tip int. Unii operatori trebuie s˘ se a a declare ˆ pereche: ın 1. operatorii == ¸i != s 2. operatorii > ¸i < s 3. operatorii >= ¸i <= s Pentru operaratorul ==, este indicat˘ ¸i definirea metodei Equals(), deoarece as tipul respectiv va putea fi astfel folosit ¸i de c˘tre limbaje care nu suport˘ s a a supraˆ arcarea operatorilor, dar pot apela metoda polimorfic˘ Equals() definit˘ ınc˘ a a ˆ clasa object. ın Nu se pot supaˆ arca operatorii + =, − =, / =, ∗ =; dar pentru ca ınc˘ ace¸tia s˘ functioneze, este suficient s˘ se supraˆ s a ¸ a ıncarce operatorii corespunz˘tori: a +, −, /, ∗.

4.8.3

Operatori de conversie

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

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

CURS 4. CLASE

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

4.8.4

Exemplu: clasa Fraction

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

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

97

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

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

CURS 4. CLASE

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

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

99

4.9

Constructori de instant˘ ¸a

Un constructor de instant˘ este un membru care implementeaz˘ actiuni ¸a a ¸ care sunt cerute pentru a initializa o instant˘ a unei clase. Declararea unui ¸ ¸a astfel de constructor se face ˆ felul urm˘tor: ın a atributeopt modificatori-de-constructor declarator-de-constructor corp-constructor Un modificator de constructor poate fi: 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 fi: un bloc de declaratii ¸i instructiuni delimitat de ¸ s ¸ acolade sau caracterul punct ¸i virgul˘. s a Un constructor are acela¸i nume ca ¸i clasa din care face parte ¸i nu s s s returneaz˘ un tip. Constructorii de instant˘ nu se mo¸tenesc. Dac˘ o clas˘ a ¸a s a a nu contine nici o declaratie de constructor de instant˘, atunci compilatorul va ¸ ¸ ¸a crea automat unul implicit. O clas˘ care este mo¸tenit˘ dintr-o alt˘ clas˘ ce a s a a a nu are constructori f˘r˘ parametri va trebui s˘ utilizeze un apel de constructor aa a de clas˘ de baz˘ pentru care s˘ furnizeze parametrii potriviti; acest apel se a a a ¸ face prin intermediul initializatorului de constructor. Un constructor poate ¸ apela la un alt constructor al clasei din care face parte pentru a efectua initializ˘i. Cˆnd exist˘ cˆmpuri instant˘ care au o expresie de initializare ¸ r a a a ¸a ¸ ˆ afara constructorilor clasei respective, atunci aceste initializ˘ri se vor face ın ¸ a ˆ ınainte de apelul de constructor al clasei de baz˘. Pentru a nu se permite a crearea sau mo¸tenirea unei clase trebuie ca aceast˘ clas˘ s˘ contin˘ cel putin s a a a ¸ a ¸ un constructor definit de utilizator ¸i toti constructorii s˘ fie declarati privati. s ¸ a ¸ ¸

4.10

Constructori statici

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

100

CURS 4. CLASE

atributeopt modificator-de-constructor-static identificator( ) corp Modificatorii de constructori statici se pot da sub forma: externopt static sau static externopt Constructorii statici nu se mo¸tenesc, nu se pot apela direct ¸i nu se pot supras s ˆ arca. Un constructor static se va executa cel mult o dat˘ ˆ ınc˘ a ıntr-o aplicatie. ¸ Se garanteaz˘ faptul c˘ acest constructor se va apela ˆ a a ınaintea cre˘rii vreunei a instante a clasei respective sau ˆ ¸ ınaintea accesului la un membru static. Acest apel este nedeterminist, necunoscˆndu–se exact cˆnd sau dac˘ se va apela. a a a Un astfel de constuctor nu are specificator de acces ¸i poate s˘ acceseze doar s a membri statici. Exemplu: class Color { public Color(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } int red; int green; int blue; public static readonly Color Red; public static readonly Color Green; public static readonly Color Blue; // constructor static static Color() { Red = new Color(255, 0, 0); Green = new Color(0, 255, 0); Blue = new Color(0, 0, 255); } } class Test { static void Main() { Color background = Color.Red;

4.11. CLASE INTERIOARE } }

101

4.11

Clase interioare

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

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

CURS 4. CLASE

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

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

103

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

104

CURS 4. CLASE

Clasele interioare se folosesc intens ˆ cadrul containerilor pentru care treın buie s˘ se construiasc˘ un enumerator. Clasa interioar˘ va fi ˆ acest caz a a a ın strˆns legat˘ de container ¸i va duce la o implementare u¸or de urm˘rit ¸i de a a s s a s ˆ ıntretinut. ¸

Curs 5 Destructori. POO ˆ C# ın
5.1 Destructori

Managementul memoriei este f˘cut sub platforma .NET ˆ mod automat, a ın de c˘tre garbage collector, parte component˘ a CLR–ului. Pentru o explicatie a a ¸ asupra modului cum actioneaz˘ garbage collector-ul, a se vedea [5], pag. 225 ¸ a ¸i urm. s ˆ general, acest garbage collector scute¸te programatorul de grija dealoc˘rii In s a memoriei. Dar ˆ anumite situatii, se dore¸te s˘ se fac˘ management manın ¸ s a a ual al dealoc˘rii resurselor (de exemplu al resurselor care ¸in de sistemul de a t operare: fi¸iere, conexiuni la retea sau la baza de date, ferestre, etc, sau al s ¸ altor resurse al c˘ror management nu se face de c˘tre CLR). ˆ C# exist˘ a a In a posibilitatea de a lucra cu destructori, sau cu metode de tipul Dispose(), Close(). Un destructor se declar˘ ˆ felul urm˘tor: a ın a atributeopt externopt ~identificator() corp-destructor unde identificator este numele clasei. Un destructor nu are modificator de acces, nu poate fi apelat manual, nu poate fi supraˆ arcat, nu este mo¸tenit. ınc˘ s Un destructor este o scurt˘tur˘ sintactic˘ pentru metoda Finalize(), care a a a este definit˘ ˆ clasa System.Object. Programatorul nu poate s˘ suprascrie a ın a sau s˘ apeleze aceast˘ metod˘. a a a Exemplu: ~MyClass() { // Perform some cleanup operations here. } Metoda de mai sus este automat translatat˘ ˆ a ın: 105

106

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

5.2. SPECIALIZAREA SI GENERALIZAREA ¸ } }

107

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

5.2

Specializarea ¸i generalizarea s

Specializarea reprezint˘ o tehnic˘ de a obtine noi clase pornind de la cele a a ¸ existente. Deseori ˆ ıntre clasele pe care le model˘m putem observa relatii de a ¸ genul “este un/o”: un om este un mamifer, un salariat este un angajat, etc. Toate acestea duc la crearea unei ierarhii de clase, ˆ care din clase de baz˘ ın a (mamifer sau angajat) descind alte clase, care pe lˆng˘ cˆmpuri din clasa de a a a baz˘ mai au ¸i caracteristici proprii. Obtinerea unei clase derivate plecˆnd a s ¸ a de la alt˘ clas˘ se nume¸te specializare, operatia invers˘ purtˆnd numele de a a s ¸ a a generalizare. O clas˘ de baz˘ define¸te un tip comun, compatibil cu oricare a a s din clasele derivate (direct sau indirect). ˆ C# o clas˘ nu trebuie s˘ mo¸teneasc˘ explicit din alt˘ clas˘; ˆ acest In a a s a a a ın caz se va considera c˘ ea este implicit derivat˘ din clasa predefinit˘ object a a a (tot una cu System.Object). C# nu permite mo¸tenire multipl˘, eliminˆnd s a a astfel complicatiile ˆ alnite ˆ C++. Ca alternativ˘, se permite totu¸i im¸ ıntˆ ın a s plementarea de mai multe interfete (la fel ca ˆ Java). ¸ ın

5.2.1

Specificarea mo¸tenirii s

ˆ C# se pentru o clas˘ D se define¸te clasa de baz˘ B folosind urm˘toarea In a s a a formul˘: a

108 class D: B { //declaratii }

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

5.2.2

Apelul constructorilor din clasa de baz˘ a

ˆ exemplul anterior nu s–a definit nici un constructor ˆ clasa de baz˘ In ın a Employee; constructorul clasei derivate trebuie s˘ fac˘ initializ˘rile cˆmpurilor a a ¸ a a ˆ conformitate cu parametrii transmi¸i, chiar dac˘ o parte din aceste cˆmpuri ın s a a provin din clasa de baz˘. Mai logic ar fi ca ˆ clasa de baz˘ s˘ se g˘seasc˘ un a ın a a a a ˆ constructor care s˘ initializeze cˆmpurile proprii: name ¸i ssn. Intrucˆt cona ¸ a s a structorii nu se mo¸tenesc, e nevoie ca ˆ clasa derivat˘ s˘ se fac˘ un apel exs ın a a a plicit al constructorului clasei de baz˘. Acest apel se face prin initializator de a ¸ constructor care are forma: dou˘ puncte urmate de base(parametrii-efectivi). a

5.2. SPECIALIZAREA SI GENERALIZAREA ¸

109

public class Employee { protected string name; protected string ssn; public Employee( string name, string ssn) { this.name = name; this.ssn = ssn; System.Console.WriteLine(‘‘Employee constructor: {0}, {1}’’, name, ssn); } } public class Salaried : Employee { protected double salary; public Salaried(string name, string ssn, double salary): base(name, ssn) { this.salary = salary; System.Console.WriteLine(‘‘Salaried constructor: {0}’’, salary); } } class Test { Salaried s = new Salaried(‘‘Jesse’’, ‘‘1234567890’’, 100000.00); } La rulare se va obtine: ¸ Employee constructor: Jesse, 1234567890 Salaried constructor: 100000.00 de unde se deduce c˘ apelul de constructor de clas˘ de baz˘ se face ˆ a a a ınaintea execut˘rii oric˘ror alte instructiuni continute ˆ constructorul clasei derivate. a a ¸ ¸ ın Dac˘ o clas˘ de baz˘ nu are definit nici un constructor, atunci se va a a a crea unul implicit (f˘r˘ parametri). Dac˘ dup˘ un constructor al unei clase aa a a derivate nu se specific˘ un initializator de constructor, atunci va fi apelat a ¸ constructorul implicit (fie creat automat de compilator, fie scris de c˘tre a programator); dac˘ nu exist˘ nici un constructor implicit ˆ clasa de baz˘, a a ın a atunci programatorul trebuie s˘ specifice un constructor din clasa de baz˘ a a care va fi apelat, ˆ ımpreun˘ cu parametrii adecvati. a ¸

110

CURS 5. DESTRUCTORI. POO ˆ C# IN

5.2.3

Operatorii is ¸i as s

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

5.3

Clase sealed

Specificatorul sealed care se poate folosi ˆ ınaintea cuvˆntului cheie class a specific˘ faptul c˘ clasa curent˘ nu se poate deriva. Este o eroare de compilare a a a ca o clas˘ sealed s˘ fie declarat˘ drept clas˘ de baz˘. a a a a a

5.4. POLIMORFISMUL

111

5.4

Polimorfismul

Polimorfismul este capacitatea unei entit˘¸i de a lua mai multe forme. at ˆ limbajul C# polimorfismul este de 3 feluri: parametric, ad–hoc ¸i de In s mo¸tenire. s

5.4.1

Polimorfismul parametric

Este cea mai slab˘ form˘ de polimorfism, fiind reg˘sit˘ ˆ majoritatea a a a a ın altor limbaje, ce nu sunt orientate pe obiecte: Pascal, C. Prin polimorfismul parametric se permite ca o implementare de functie s˘ poat˘ prelucra ¸ a a orice num˘r de parametri. Acest lucru se poate obtine prin folosirea unui a ¸ parametru de tip params (vezi 3.2).

5.4.2

Polimorfismul ad–hoc

Se mai nume¸te ¸i supraˆ arcarea metodelor, mecanism prin care ˆ s s ınc˘ ın cadrul unei clase se pot scrie mai multe metode, avˆnd acela¸i nume, dar a s tipuri ¸i numere diferite de parametri de apel. Alegerea functiei care va fi s ¸ apelat˘ se va face la compilare, pe baza corespondentei ˆ a ¸ ıntre tipurile de apel ¸i cele formale. s

5.4.3

Polimorfismul de mo¸tenire s

Este forma cea mai evoluat˘ de polimorfism. Dac˘ precedentele forme de a a polimorfism sunt aplicabile f˘r˘ a se pune problema de mo¸tenire, ˆ acest aa s ın caz este necesar s˘ existe o ierarhie de clase. Mecanismul se bazeaz˘ pe faptul a a c˘ o clas˘ de baz˘ define¸te un tip care este compatibil din punct de vedere a a a s al atribuirii cu orice tip derivat, ca mai jos: class B{...} class D: B{...} class Test { static void Main() { B b = new D();//upcasting=conversie implicita catre baza } } ˆ Intr–un astfel de caz se pune problema: ce se ˆ ampl˘ cu metodele avˆnd ıntˆ a a aceea¸i list˘ de parametri formali ¸i care se reg˘sesc ˆ cele dou˘ clase? s a s a ın a

112

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

5.4. POLIMORFISMUL

113

Dac˘ cea de–a doua linie afi¸at˘ este conform˘ cu intuitia, primul rezultat a s a a ¸ este discutabil, dar justificabil: apelul de metod˘ Draw() este rezolvat ˆ a ın fiecare caz la compilare pe baza tipului declarat al obiectelor; ca atare apelul precedent este legat de corpul metodei Draw din clasa Shape, chiar dac˘ s a a fost instantiat de fapt pe baza unui obiect de tip Polygon. ¸ Este posibil ca s˘ se doreasc˘ schimbarea acestui comportament: apelul de a a metod˘ Draw s˘ fie rezolvat ˆ functie de tipul efectiv al obiectului care face a a ın ¸ acest apel, ¸i nu de tipul formal declarat. ˆ cazul precedent, apelul s.Draw() s In trebuie s˘ se rezolve de fapt ca fiind c˘tre metoda Draw() din Polygon, pentru a a c˘ acesta este tipul la rulare al obiectului s. Cu alte cuvinte, apelul ar trebui a s˘ fie rezolvat la rulare ¸i nu la compilare, ˆ functie de natura obiectelor. a s ın ¸ Acest comportament polimorfic este referit sub denumirea polimorfism de mo¸tenire. s

5.4.4

Virtual ¸i override s

Pentru a asigura faptul c˘ legarea apelului de metode se face la rulare a ¸i nu la compilare, e necesar ca ˆ clasa de baz˘ s˘ se specifice c˘ metoda s ın a a a Draw() este virtual˘, iar ˆ clasa derivat˘ pentru aceea¸i metod˘ trebuie s˘ a ın a s a a se spun˘ c˘ este o suprascriere a celei din baz˘: a a a class Shape{ public virtual void Draw(){...} } class Polygon{ public override void Draw(){...} } ˆ urma execut˘rii metodei Main din clasa de mai sus, se va afi¸a: In a s Polygon.Draw() Polygon.Draw() adic˘ s–a apelat metoda corespunz˘toare tipului efectiv de la rulare, ˆ fiecare a a ın caz. ˆ cazul ˆ care clasa Polygon este la rˆndul ei mo¸tenit˘ ¸i se dore¸te In ın a s a s s ca polimorfismul s˘ functioneze ˆ continuare va trebui ca ˆ aceast˘ a treia a ¸ ın ın a clas˘ s˘ suprascrie (override) metoda Draw(). a a Un astfel de comportament polimorfic este benefic atunci cˆnd se folose¸te a s o colectie de obiecte de tipul unei clase de baz˘: ¸ a Shape[] painting = new Shape[10]; painting[0] = new Shape();

114

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

5.4.5

Modificatorul new pentru metode

Modificatorul new se folo¸este pentru a indica faptul c˘ o metod˘ dintr-o s a a clas˘ derivat˘ care are aceea¸i semn˘tur˘ cu una dintr–o clas˘ de baz˘ nu a a s a a a a este o suprascriere a ei, ci o nou˘ metod˘. Este ca ¸i cum metoda declarat˘ a a s a new ar avea o semn˘tur˘ diferit˘. a a a S˘ presupunem urm˘torul scenariu: compania A creaz˘ o clas˘ A care a a a a are forma: public class A{ public void M(){ Console.WriteLine(‘‘A.M()’’); } } O alt˘ companie B va crea o clas˘ B care mo¸tene¸te clasa A. Compania a a s s B nu are nici o influent˘ asupra companiei A (sau asupra modului ˆ care ¸a ın aceasta va face modific˘ri asupra clasei A). Ea va defini ˆ interiorul clasei B a ın o metod˘ M() ¸i una N(): a s class B: A{ public void M(){ Console.WriteLine(‘‘B.M()’’); N(); base.M(); } protected virtual void N(){ Console.WriteLine(‘‘B.N()’’); } } Atunci cˆnd compania B compileaz˘ codul, compilatorul C# va produce a a urm˘torul avertisment: a warning CS0108: The keyword new is required on ‘B.M()’ because it hides inherited member ‘A.M()’

5.4. POLIMORFISMUL

115

Acest avertisment va notifica programatorul c˘ clasa B define¸te o metod˘ a s a M(), care va ascunde metoda M() din clasa de baz˘ A. Aceast˘ nou˘ metod˘ a a a a ar putea schimba ˆ ¸elesul (semantica) lui M(), a¸a cum a fost creat initial ınt s ¸ de compania A. Este de dorit ˆ astfel de cazuri compilatorul s˘ avertizeze ın a despre posibile nepotriviri semantice. Programatorii din B vor trebui s˘ pun˘ a a ˆ orice caz specificatorul new ˆ ın ınaintea metodei B.M(). S˘ presupunem c˘ o aplicatie folose¸te clasa B() ˆ felul urm˘tor: a a ¸ s ın a class App{ static void Main(){ B b = new B(); b.M(); } } La rulare se va afi¸a: s B.M() B.N() A.M() S˘ presupunem c˘ A decide ad˘ugarea unei metode virtuale N() ˆ clasa sa, a a a ın metod˘ ce va fi apelat˘ din M(): a a public class A { public void M() { Console.WriteLine(‘‘A.M()’’); N(); } protected virtual void N() { Console.WriteLine(‘‘A.N()’’); } } La o recompilare f˘cut˘ de B, este dat urm˘torul avertisment: a a a warning CS0114: ‘B.N()’ hides inherited member ‘A.N()’. To make the current member override that implementation, add the override keyword. Otherwise, add the new keyword.

116

CURS 5. DESTRUCTORI. POO ˆ C# IN

ˆ acest mod compilatorul avertizeaz˘ c˘ ambele clase ofer˘ o metod˘ N() In a a a a a c˘ror semantic˘ poate s˘ difere. Dac˘ B decide c˘ metodele N() nu sunt a a a a a semantic legate ˆ cele dou˘ clase, atunci va specifica new, informˆnd compiın a a latorul de faptul c˘ versiunea sa este una nou˘, care nu suprascrie polimorfic a a metoda din clasa de baz˘. a Atunci cˆnd codul din clasa App este rulat, se va afi¸a la ie¸ire: a s s B.M() B.N() A.M() A.N() Ultima linie afi¸at˘ se explic˘ tocmai prin faptul c˘ metoda N() din B este s a a a declarat˘ new ¸i nu override (dac˘ ar fi fost override, ultima linie ar fi fost a s a B.N(), din cauza polimorfismului). Se poate ca B s˘ decid˘ c˘ metodele M() ¸i N() din cele dou˘ clase sunt a a a s a legate semantic. ˆ acest caz, ea poate ¸terge definitia metodei B.M, iar In s ¸ pentru a semnala faptul c˘ metoda B.N() suprascrie metoda omonim˘ din a a clasa p˘rinte, va ˆ a ınlocui cuvˆntul new cu override. ˆ acest caz, metoda a In App.Main va produce: A.M() B.N() ultima linie fiind explicat˘ de faptul c˘ B.N() suprascrie o metod˘ virtual˘. a a a a

5.4.6

Metode sealed

O metod˘ de tip override poate fi declarat˘ ca fiind de tip sealed, astfel a a ˆ ımpiedicˆndu–se suprascrierea ei ˆ a ıntr–o clas˘ derivat˘ din cea curent˘: a a a using System; class A { public virtual void F() { Console.WriteLine(‘‘A.F()’’); } public virtual void G() { Console.WriteLine(‘‘A.G()’’); } }

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

117

Modificatorul sealed pentru B.F va ˆ ımpiedica tipul C s˘ suprascrie metoda a F.

5.4.7

Exemplu folosind virtual, new, override, sealed

S˘ presupunem urm˘toare ierarhie de clase, reprezentat˘ ˆ Fig. 5.1; o a a a ın clas˘ X mo¸teneste o clas˘ Y dac˘ sensul s˘getii este de la X la Y. Fiecare a s a a a ¸ clas˘ are o metod˘ void f() care determin˘ afi¸area clasei ˆ care este definit˘ a a a s ın a ¸i pentru care se vor specifica new, virtual, override, sealed. S˘ presupunem s a c˘ clasa de test arat˘ astfel: a a public class Test { static void Main() { A[] x = new A[4]; x[0] = new A(); x[1] = new B(); x[2] = new C(); x[3] = new D(); A a = new A(); B b = new B();

118

CURS 5. DESTRUCTORI. POO ˆ C# IN
A

+foo(): void

D

B

+foo(): void

+foo(): void

C

+foo(): void

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

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

119

Metoda Specificator Ie¸ire secv. 1 s Ie¸ire secv. 2 s Specificator Ie¸ire secv. 1 s Ie¸ire secv. 2 s Specificator Ie¸ire secv. 1 s Ie¸ire secv. 2 s Specificator Eroare de compilare deoarece C.f nu poate suprascrie metoda nevirtual˘ B.f() a Specificator Ie¸ire secv. 1 s Ie¸ire secv. 2 s Avertisment la compilare deoarece B.f ˆ ınlocuie¸te A.f s Specificator Eroare de compilare deoarece deoarece B.f nu poate fi suprascris˘ de C.f a

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

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

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

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

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

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

virtual sealed override override override

5.5

Clase ¸i metode abstracte s

Deseori pentru o anumit˘ clas˘ nu are sens crearea de instante, din cauza a a ¸ unei generalit˘¸i prea mari a tipului respectiv. Spunem c˘ aceast˘ clas˘ este at a a a abstract˘, iar pentru a ˆ a ımpiedica efectiv crearea de instante de acest tip, se ¸ va specifica cuvˆntul abstract ˆ a ınaintea metodei. ˆ exemplele de mai sus, In clasele Employee ¸i Shape ar putea fi gˆndite ca fiind abstracte: ele contin s a ¸ prea putin˘ informatie pentru a putea crea instante utile. ¸ a ¸ ¸ Analog, pentru o anumit˘ metod˘ din interiorul unei clase uneori nu se a a poate specifica o implementare. De exemplu, pentru clasa Shape de mai sus,

120

CURS 5. DESTRUCTORI. POO ˆ C# IN

este imposibil s˘ se dea o implementare la metoda Draw(), tocmai din cauza a generalit˘¸ii acestei clase. Ar fi util dac˘ pentru aceast˘ metod˘ programat a a a atorul ar fi obligat s˘ dea implement˘ri specifice ale acestei metode pentru a a diversele clase derivate. Pentru a se asigura tratarea polimorfic˘ a acestui a tip abstract, orice metod˘ abstract˘ este automat ¸i virtual˘. Orice metod˘ a a s a a care este declarat˘ abstract˘ implic˘ declararea clasei ca fiind abstract˘. a a a a Exemplu: abstract class Shape { public abstract void Draw(); //de remarcat lipsa implementarii si semnul punct si virgula } Orice clas˘ care este derivat˘ dintr–o clas˘ abstract˘ va trebui fie s˘ nu aib˘ a a a a a a nici o metod˘ abstract˘ mo¸tenit˘ f˘r˘ implementare, fie s˘ se declare ca fiind a a s a aa a abstract˘. Existenta de metode neimplementate nu va permite instantierea a ¸ ¸ clasei respective.

5.6

Interfete ¸

O interfat˘ define¸te un contract. O clas˘ sau o structur˘ care imple¸a s a a menteaz˘ o interfat˘ ader˘ la acest contract. Relatia dintre o interfat˘ ¸i a ¸a a ¸ ¸a s un tip care o implementeaz˘ este deosebit˘ de cea existent˘ ˆ a a a ıntre clase (este un/o): este o relatie de implementare. ¸ O interfat˘ poate contine metode, propriet˘¸i, evenimente, indexatori. Ea ¸a ¸ at ˆ a nu va contine implement˘ri pentru acestea, doar declaratii. Declararea ıns˘ ¸ a ¸ unei interfete se face astfel: ¸ atributeopt modificatori-de-interfat˘opt interface identificator baza-interfeteiopt ¸a ¸ corp-interfat˘ ;opt ¸a Modificatorii de interfat˘ sunt: new, public, protected, internal, private. O ¸a interfat˘ poate s˘ mo¸teneasc˘ de la zero sau mai multe interfete. Corpul ¸a a s a ¸ interfetei contine declaratii de metode, f˘r˘ implement˘ri. Orice metod˘ are ¸ ¸ ¸ aa a a gradul de acces public. Nu se poate specifica pentru o metod˘ din interiorul a unei interfete: abstract, public, protected, internal, private, virtual, override, ¸ ori static. Exemplu: interface IStorable { void Read( );

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

121

O clas˘ care implementeaz˘ o interfat˘ trebuie s˘ defineasc˘ toate metodele a a ¸a a a care se reg˘sesc ˆ interfata respectiv˘. Urm˘toarele reguli trebuie respectate a ın ¸ a a la implementarea de interfete: ¸ 1. Tipul de retur al metodei din clas˘ trebuie s˘ coincid˘ cu tipul de retur a a a al metodei din interfat˘ ¸a 2. Tipul parametrilor formali din metod˘ trebuie s˘ fie acela¸i cu tipul a a s parametrilor formali din interfat˘ ¸a 3. Metoda din clas˘ trebuie s˘ fie declarat˘ public˘ ¸i nestatic˘. a a a as a Aceste implement˘ri pot fi declarate folosind specificatorul virtual (deci suba clasele clasei curente pot folosi new ¸i override). s Exemplu: using System; interface ISavable { void Read(); void Write(); } public class TextFile : ISavable { public virtual void Read() { Console.WriteLine("TextFile.Read()"); } public void Write() { Console.WriteLine("TextFile.Write()");

122 }

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

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

123

ˆ exemplul de mai sus se folo¸este operatorul as pentru a obtine o referint˘ la In s ¸ ¸a ˆ general, se prefer˘ ca apelul metodelor interfete, pe baza obiectelor create. In ¸ a care sunt implementate din interfat˘ s˘ se fac˘ via o referint˘ la interfata ¸a a a ¸a ¸ respectiv˘, obtinut˘ prin intermediul operatorului as (ca mai sus) sau dup˘ a ¸ a a o testare prealabil˘ prin is urmat˘ de conversie explicit˘, ca mai jos: a a a if (textRef is ISavable) { ISavable is = (ISavable)textRef; is.Read();//etc } ˆ general, dac˘ se dore¸te doar r˘spunsul la ˆ In a s a ıntrebarea ”este obiectul curent un implementator al interfetei I ?”, atunci se recomand˘ folosirea operatorului ¸ a is. Dac˘ se ¸tie c˘ va trebui f˘cut˘ ¸i o conversie la tipul interfat˘, atunci a s a a as ¸a este mai eficient˘ folosirea lui as. Afirmatia se bazeaz˘ pe studiul codului IL a ¸ a rezultat ˆ fiecare caz. ın S˘ presupunem c˘ exista o interfat˘ I avˆnd metoda M() care este ima a ¸a a plementat˘ de o clas˘ C, care define¸te metoda M(). Este posibil ca aceast˘ a a s a metod˘ s˘ nui aib˘ o semnificatie ˆ afara clasei C, ca atare a nu e util ca a a a ¸ ın metoda M() s˘ fie declarat˘ public˘. Mecanismul care permite acest lucru se a a a nume¸te ascunderea numelor. Aceast˘ tehnic˘ permite ascunderea metodelor s a a mo¸tenite dintr-o interfat˘, acestea devenind private (calificarea lor ca fiind s ¸a publice este semnalat˘ ca o eroare). a Metodele din interfete care s–au implementat explicit (prin calificarea lor ¸ cu numele interfetei de origine) nu pot fi declarate abstract, virtual, override, ¸ new. Mai mult, asemenea metode nu pot fi accesate direct prin intermediul unui obiect (obiect.NumeMetoda), ci doar prin intermediul unei conversii c˘tre interfat˘ respectiv˘, deoarece prin implementare explicit˘ a metodelor a ¸a a a aceste devin private ¸i singura modalitate de acces a lor este upcasting-ul s c˘tre interfat˘. a ¸a Exemplu:

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

CURS 5. DESTRUCTORI. POO ˆ C# IN

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

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

125

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

126 {

CURS 5. DESTRUCTORI. POO ˆ C# IN

static void Main() { IFriendly mansBestFriend = new Dog() ; mansBestFriend.GreetOwner() ; (mansBestFriend as IAffectionate).GreetOwner() ; } } La ie¸ire se va afi¸a: s s Jump up! Woof! Dac˘ ˆ a ˆ clasa Dog se adaug˘ metoda a ıns˘ ın a public void GreetOwner() { Console.WriteLine( "Woof!" ) ; } (care a fost initial definit˘), atunci se poate face apel de tipul dog.GreetOwner() ¸ a (dog este instant˘ de Dog); apelurile de metode din interfat˘ r˘mˆn de aseme¸a ¸a a a nea valide. Rezultatul este ca ¸i la exemplul precedent: se afi¸eaz˘ mesajul s s a Woof.

5.6.1

Clase abstracte sau interfete? ¸

Atˆt interfetele cˆt ¸i clasele abstracte au comportamente similare ¸i pot a ¸ a s s fi folosite ˆ situatii similare. Dar totu¸i ele nu se pot substitui reciproc. ın ¸ s Cˆteva principii generale de utilizare a lor sunt date mai jos. a Dac˘ o relatie se poate exprima mai degrab˘ ca “este un/o” decˆt altfel, a ¸ a a atunci entitatea de baz˘ ar trebui gˆndit˘ ca o clas˘ abstract˘. a a a a a Un alt aspect este bazat pe obiectele care ar folosi capabilit˘¸ile din tipul at de baz˘. Dac˘ aceste capabilit˘¸i ar fi folosite de c˘tre obiecte care nu sunt a a at a legate ˆ ıntre ele, atunci ar fi indicat˘ o interfat˘. a ¸a Dezavantajul claselor abstracte este c˘ nu poate fi decˆt baz˘ unic˘ pentru a a a a orice alt˘ clas˘. a a

Curs 6 Delegati. Evenimente. ¸ Structuri
6.1 Tipul delegat

ˆ programare deseori apare urm˘toarea situatie: trebuie s˘ se execute o In a ¸ a anumit˘ actiune, dar nu se ¸tie de dinainte care anume, sau chiar ce obiect va a ¸ s trebui efectiv utilizat. De exemplu, un buton poate ¸ti c˘ trebuie s˘ anunte s a a ¸ un obiect despre faptul c˘ fost ap˘sat, dar nu va ¸ti aprioric pe cine. Mai a a s degrab˘ decˆt s˘ se lege butonul de un obiect particular, butonul va declara a a a un delegat, pentru care clasa interesat˘ de evenimentul de ap˘sare va da o a a implementare. Fiecare actiune pe care utilizatorul o execut˘ pe o interfat˘ grafic˘ de¸ a ¸a a clan¸eaz˘ un eveniment. Alte evenimente se pot declan¸a independent de s a s actiunile utilizatorului: sosirea unui email, terminarea copierii unor fi¸iere, ¸ s sfˆr¸itul unei interog˘ri pe o baz˘ de date, etc. Un eveniment este o ˆ as a a ıncapsulare a ideii c˘ “se ˆ ampl˘ ceva” la care programul trebuie s˘ r˘spund˘. a ıntˆ a a a a Evenimentele ¸i delegatii sunt strˆns legate deoarece r˘spunsul la acest evenis ¸ a a ment se va face de c˘tre un event handler, care ˆ C# se implementeaz˘ ca a ın a un delegat. Un delegat este un tip referint˘ folosit pentru a ˆ ¸a ıncapsula o metod˘ cu un a anumit antet (tipul parametrilor formali ¸i tipul de retur). Orice metod˘ care s a are acela¸i antet poate fi legat˘ la un anumit delegat. ˆ s a Intr–un limbaj precum C++, acest lucru se rezolv˘ prin intermediul pointerilor la funtii. Delegatii a ¸ ¸ rezolv˘ aceea¸i problem˘, dar ˆ a s a ıntr–o manier˘ orientat˘ obiect ¸i cu garantii a a s ¸ asupra sigurantei codului rezultat, precum ¸i cu o u¸oar˘ generalizare (vezi ¸ s s a delegatii multicast). ¸ Un delegat este creat dup˘ urm˘toarea sintax˘: a a a 127

128

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

atributeopt modificatori-de-delegatopt delegate tip-retur identificator( listaparam-formaliopt ); Modificatorul de delegat poate fi: new, public, protected, internal, private. Un delegat se poate specifica atˆt ˆ interiorul unei clase, cˆt ¸i ˆ exteriorul a ın a s ın ei, fiind de fapt o declaratie de clas˘ derivat˘ din System.Delegate. Dac˘ ¸ a a a este declarat ˆ interiorul unei clase, atunci este ¸i static (a se vedea statutul ın s claselor interioare). Exemplu: public delegate int WhichIsFirst(object obj1, object obj2);

6.1.1

Utilizarea delegatilor pentru a specifica metode ¸ la runtime

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

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

129

130

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

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

6.1. TIPUL DELEGAT

131

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

6.1.2

Delegati statici ¸

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

132

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

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

6.1.3

Multicasting

Uneori este nevoie ca un delegat s˘ poat˘ apela mai mult de o singur˘ a a a metod˘. De exemplu, atunci cˆnd un buton este ap˘sat, se poate s˘ vrei a a a a s˘ efectuezi mai mult de o sigur˘ actiune: s˘ scrii ˆ a a ¸ a ıntr–un textbox un ¸ir s de caractere ¸i s˘ ˆ s a ınregistrezi ˆ ıntr–un fi¸ier faptul c˘ s–a ap˘sat acel buton s a a (logging). Acest lucru s–ar putea rezolva prin construirea unui vector de delegati care s˘ contin˘ toate metodele dorite, ˆ a acest lucru ar duce la un ¸ a ¸ a ıns˘ cod greu de urm˘rit ¸i inflexibil; pentru un astfel de exemplu, a se vedea [2], a s pag. 261–265. Mult mai simplu ar fi dac˘ un delegat ar putea s˘ contin˘ a a ¸ a mai mult de o singur˘ metod˘. Acest lucru se nume¸te multicasting ¸i este a a s s folosit intens la tratarea evenimentelor (vezi sectiunea urm˘toare). ¸ a Orice delegat care returnez˘ void este un delegat multicast, care poate fi a tratat ¸i ca un delegat single-cast. Doi delegati multicast pot fi combinati s ¸ ¸ folosind semnul +. Rezultatul unei astfel de “adun˘ri” este un nou delegat a multicast care la apelare va invoca metodele continute, ˆ ordinea ˆ care ¸ ın ın s–a f˘cut adunarea. De exemplu, dac˘ Writer ¸i Logger sunt delegati care a a s ¸ returneaz˘ void, atunci urm˘toarea linie va produce combinarea lor ˆ a a ıntr–un singur delegat: myMulticastDelegate = Writer + Logger; Se pot ad˘uga delegati multicast folosind operatorul +=, care va ad˘uga a ¸ a delegatul de la dreapta operatorului la delegatul multicast aflat ˆ stˆnga sa: ın a myMulticastDelegate += Transmitter; presupunˆnd c˘ Transmitter este compatibil cu myMulticastDelegate (are a a aceea¸i semn˘tur˘). Operatorul − = functioneaz˘ invers fat˘ de + =. s a a ¸ a ¸a Exemplu:
1

Tehnic˘ numit˘ initializaer tˆrzie (lazy initialization) a a ¸ a

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

133

134

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

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

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

135

6.2

Evenimente

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

6.2.1

Publicarea ¸i subscrierea s

ˆ C#, orice obiect poate s˘ publice un set de evenimente la care alte clase In a pot s˘ subscrie. Cˆnd obiectul care a publicat evenimentul ˆ ¸i semnaleaza˘, a a ıl s a ˆ acest toate obiectele care au subscris la acest eveniment sunt notificate. In mod se define¸te o dependent˘ de tip one–to–many ˆ s ¸a ıntre obiecte astfel ˆ at ıncˆ un obiect ˆsi schimb˘ starea, toate celelate obiecte dependente sunt notificate ı¸ a ¸i modificate automat. s De exemplu, un buton poate s˘ notifice un num˘r oarecare de observatori a a atunci cˆnd a fost ap˘sat. Butonul va fi numit publicator 2 deoarece publia a c˘ evenimentul Click iar celelalte clase sunt numite abonati 3 deoarece ele a ¸ subscriu la evenimentul Click.

6.2.2

Evenimente ¸i delegati s ¸

Tratarea evenimentelor ˆ C# se face folosind delegati. Clasa ce public˘ ın ¸ a define¸te un delegat pe care clasele abonate trebuie s˘ ˆ implementeze. Cˆnd s a ıl a evenimentul este declan¸at, metodele claselor abonate vor fi apelate prin s
2 3

Engl: publisher Engl: subscribers

136

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

intermediul delegatului (pentru care se prevede posibilitatea de a fi multicast, astfel ˆ at s˘ se permit˘ mai multi abonati). ıncˆ a a ¸ ¸ Metodele care r˘spund la un eveniment se numesc event handlers. Prin a conventie, un event handler ˆ .NET Framework returneaz˘ void ¸i preia doi ¸ ın a s parametri: primul parametru este sursa evenimentului (obiectul publicator); al doilea parametru are tip EventArgs sau derivat din acesta. Declararea unui eveniment se face astfel: atributeopt modificatori-de-evenimentopt event tip nume–eveniment Modificator-de-eveniment poate fi 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˘toarele: o clas˘ Clock a a care folose¸te un eveniment (OnSecondChange) pentru a notifica potentialii s ¸ abonati atunci cˆnd timpul local se schimb˘ cu o secund˘. Tipul acestui ¸ a a a eveniment este un delegat SecondChangeHandler care se declar˘ astfel: a public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation ); ˆ conformitate cu metodologia de declarare a unui event handler, pomenit˘ ın a mai sus. Tipul TimeInfoEventArgs este definit de noi ca o clas˘ derivat˘ din a a EventArgs: public class TimeInfoEventArgs : EventArgs { public TimeInfoEventArgs( int hour, int minute, int second ) { this.hour = hour; this.minute = minute; this.second = second; } public readonly int hour; public readonly int minute; public readonly int second; } Aceast˘ clas˘ va contine informatie despre timpul curent. Informatia este a a ¸ ¸ ¸ accesibil˘ readonly. a Clasa Clock va contine o metod˘ Run(): ¸ a

6.2. EVENIMENTE

137

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

138

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

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

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

139

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

140 {

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

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

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

141

142 Current Logging Current Logging

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

6.2.3

Comentarii

S–ar putea pune urm˘toarea ˆ a ıntrebare: de ce este nevoie de o astfel de redirectare de eveniment, cˆnd ˆ metoda Run() se poate afi¸a direct pe ecran a ın s sau ˆ ıntr–un fi¸ier informatia cerut˘? Avantajul abord˘rii anterioare este c˘ s ¸ a a a se pot crea oricˆte clase care s˘ fie notificate atunci cˆnd acest eveniment se a a a declan¸eaz˘. Clasele abonate nu trebuie s˘ ¸tie despre modul ˆ care lucreaz˘ s a as ın a clasa Clock, iar clasa Clock nu trebuie s˘ ¸tie despre clasele care vor subscrie as la evenimentul s˘u. Similar, un buton poate s˘ publice un eveniment OnClick a a ¸i orice num˘r de obiecte pot subscrie la acest eveniment, primind o notificare s a atunci cˆnd butonul este ap˘sat. a a Publicatorul ¸i abonatii sunt decuplati. Clasa Clock poate s˘ modifice s ¸ ¸ a modalitatea de detectare a schimb˘rii de timp f˘r˘ ca acest lucru s˘ impun˘ a aa a a o schimbare ˆ clasele abonate. De asemenea, clasele abonate pot s˘ ˆsi ın a ı¸ modifice modul de tratare a evenimentului, ˆ mod transparent fat˘ de clasa ın ¸a Clock. Toate aceste caracteristici fac ˆ ıntretinerea codului extrem de facil˘. ¸ a

6.3

Structuri

Structurile reprezint˘ tipuri de date asem˘n˘toare claselor, cu principala a a a diferent˘ c˘ sunt tipuri valoare (o astfel de variabil˘ va contine direct val¸a a a ¸ oarea, ¸i nu o adres˘ de memorie). Sunt considerate versiuni “u¸oare” ale s a s claselor, sunt folosite predilect pentru tipuri pentru care aspectul comportamental este mai putin pronuntat. ¸ ¸ Declaratia unei structuri se face astfel: ¸ atributeopt modificatori-de-structopt struct identificator :interfeteopt corp ;opt ¸ Modificatorii de structur˘ sunt: new, public, protected, internal, private. O a structur˘ este automat derivat˘ din System.ValueType, care la rˆndul ei este a a a derivat˘ din System.Object; de asemenea, este automat considerat˘ sealed a a (sect. 5.3). Poate ˆ a s˘ implementeze una sau mai multe interfete. ıns˘ a ¸ O structur˘ poate s˘ contin˘ declaratii de constante, cˆmpuri, metode, a a ¸ a ¸ a propriet˘¸i, evenimente, indexatori, operatori, constructori, constructori statici, at tipuri interioare. Nu poate contine destructor. ¸ La atribuire, se face o copiere a valorilor continute de c˘tre surs˘ ˆ ¸ a a ın destinatie (indiferent de tipul cˆmpurilor: valoare sau referint˘). ¸ a ¸a

6.3. STRUCTURI Exemplu: using System; public struct Point { public Point(int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } public int X { get { return xVal; } set { xVal = value; } } public int Y { get { return yVal; } set { yVal = value; } } public override string ToString( ) { return (String.Format(‘‘{0}, {1}’’, xVal,yVal)); } public int xVal; public int yVal; }

143

144

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸

public class Tester { public static void myFunc(Point loc) { loc.X = 50; loc.Y = 100; Console.WriteLine(‘‘In MyFunc loc: {0}’’, loc); } static void Main( ) { Point loc1 = new Point(200,300); Console.WriteLine(‘‘Loc1 location: {0}’’, loc1); myFunc(loc1); Console.WriteLine(‘‘Loc1 location: {0}’’, loc1); } } Dup˘ cum este dat ˆ exemplul de mai sus, crearea unei instante se face a ın ¸ folosind operatorul new ; dar ˆ acest caz, nu se va crea o instant˘ ˆ memoria ın ¸a ın heap, ci pe stiv˘. Transmiterea lui loc1 ca parametru se face prin valoare, a adic˘ metoda myFunc nu face decˆt s˘ modifice o copie de pe stiv˘ a lui a a a a loc1. La revenire, se va afi¸a tot valoarea original˘, deoarece loc1 a r˘mas s a a nemodificat: Loc1 location: 200, 300 In MyFunc loc: 50, 100 Loc1 location: 200, 300 Deseori pentru o structur˘ se declar˘ cˆmpurile ca fiind publice, pentru a a a a nu mai fi necesare definirea accesorilor (simplificare implement˘rii). Alti a ¸ programatori consider˘ ˆ a c˘ accesarea membrilor trebuie s˘ se fac˘ precum a ıns˘ a a a la clase, folosind propriet˘¸i. Oricare ar fi alegerea, limbajul o sprijin˘. at a Alte aspecte demne de retinut: ¸ • Cˆmpurile nu pot fi initializate la declarare; altfel spus, dac˘ ˆ exema ¸ a ın plul de mai sus se scria: public int xVal = 10; public int yVal = 20; s-ar fi semnalat o eroare la compilare.

6.3. STRUCTURI

145

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

146

CURS 6. DELEGATI. EVENIMENTE. STRUCTURI ¸ Use of unassigned local variable ‘p’ Dar dup˘ ni¸te asign˘ri de tipul: a s a p.xVal=p.yVal=0; afi¸area este posibil˘ (practic, orice apel de metod˘ pe instant˘ este s a a ¸a acum acceptat).

• Dac˘ se ˆ cearc˘ definirea unei structuri care contine un cˆmp de tipul a ın a ¸ a structurii, atunci va ap˘rea o eroare de compilare: a struct MyStruct { MyStruct s; } va genera un mesaj din partea compilatorului:

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

6.3.1

Structuri sau clase?

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

Curs 7 Tratarea exceptiilor. Atribute ¸
7.1 Tratarea exceptiilor ¸

C#, la fel ca alte limbaje, permite tratarea erorilor ¸i a situatiilor des ¸ osebite prin exceptii. O exceptie este un obiect care ˆ ¸ ¸ ıncapsuleaz˘ informatie a ¸ despre o situatie anormal˘. Ea este folosit˘ pentru a semnala contextul ˆ ¸ a a ın care apare situatia deosebit˘ ¸ a Un programator nu trebuie s˘ confunde tratarea exceptiilor cu erorile sau a ¸ bug–urile. Un bug este o eroare de programare care ar trebui s˘ fie fixat˘ a a ˆ ınainte de livrarea codului. Exceptiile nu sunt gˆndite pentru a preveni bug– ¸ a urile (cu toate c˘ un bug poate s˘ duc˘ la aparitia unei exceptii), pentru c˘ a a a ¸ ¸ a acestea din urm˘ ar trebui s˘ fie eliminate. a a Chiar dac˘ se scot toate bug–urile, vor exista erori predictibile dar neprea venibile, precum deschiderea unui fi¸ier al c˘rui nume este gre¸it sau ˆ artiri s a s ımp˘ ¸ la 0. Nu se pot preveni astfel de situatii, dar se pot manipula astfel ˆ at ¸ ıncˆ nu vor duce la pr˘bu¸irea programului. Cˆnd o metod˘ ˆ alne¸te o situatie a s a a ıntˆ s ¸ exceptional˘, atunci se va arunca o exceptie; cineva va trebui s˘ sesizeze (s˘ ¸ a ¸ a a “prind˘”) aceast˘ exceptie, sau eventual s˘ lase o functie de nivel superior s˘ a a ¸ a ¸ a o trateze. Dac˘ nimeni nu trateaz˘ aceast˘ exceptie, atunci CLR o va face, a a a ¸ dar aceasta duce la oprirea thread–ului 1 .

7.1.1

Tipul Exception

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

Si nu neap˘rat a ˆ ¸ a ıntregului proces!

147

148

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

7.1.2

Aruncarea ¸i prinderea exceptiilor s ¸

Aruncarea cu throw Aruncarea unei exceptii se face folosind instructiunea throw. Exemplu: ¸ ¸ throw new System.Exception(); Aruncarea unei exceptii opre¸te executia metodei curente, dup˘ care CLR ¸ s ¸ a ˆ ıncepe s˘ caute un manipulator de exceptie. Dac˘ un handler de exceptie a ¸ a ¸ nu este g˘sit ˆ metoda curent˘, atunci CLR va cur˘¸a stiva, ajungˆndu–se a ın a at a la metoda apelant˘. Fie undeva ˆ lantul de metode care au fost apelate a ın ¸ se g˘se¸te un exception handler, fie thread–ul curent este terminat de c˘tre a s a CLR. Exemplu:
MethodBase este o clas˘ care pune la dispozitie informatii despre metodele ¸i cona ¸ ¸ s structorii unei clase
2

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

149

Se exemplific˘ apelul de metode: Main() apeleaz˘ Func1(), care apeleaz˘ a a a Func2(); aceasta va arunca o exceptie. Deoarece lipse¸te un event handler ¸ s care s˘ trateze aceast˘ excepctie, se va ˆ a a ¸ ıntrerupe thread–ul curent (¸i fiind s singurul, ¸i ˆ s ıntregul proces) de c˘tre CLR, iar la ie¸ire vom avea: a s Enter Main... Enter Func1... Enter Func2... Exception occurred: System.Exception: An exception of type System.Exception was thrown at Test.Func2( ) in ...Test.cs:line 24 at Test.Func1( ) in ...Test.cs:line 18 at Test.Main( ) in ...Test.cs:line 12 Deoarece este aruncat˘ o exceptie, ˆ metoda Func2() nu se va mai executa a ¸ ın ultima linie, ci CLR–ul va ˆ ıncepe imediat c˘utarea event handler–ului care a

150

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

7.1. TRATAREA EXCEPTIILOR ¸

151

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

152

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

} public void Func2( ) { Console.WriteLine(‘‘Enter Func2...’’); throw new System.Exception( ); Console.WriteLine(‘‘Exit Func2...’’); } } La ie¸ire se va afi¸a: s s Enter Main... Enter Func1... Entering try block... Enter Func2... Exception caught and handled. Exit Func1... Exit Main... Este posibil ca ˆ ıntr–o secvent˘ de instructiuni s˘ se arunce mai multe tipuri de ¸a ¸ a ˆ acest caz, prinderea exceptiei exceptii, ˆ functie de natura st˘rii ap˘rute. In ¸ ın ¸ a a ¸ printr–un bloc catch generic, ca mai sus, nu este util˘; am vrea ca ˆ functie de a ın ¸ natura exceptiei aruncate, s˘ facem o tratare anume. Se sugereaza chiar s˘ nu ¸ a a se foloseasc˘ aceast˘ constructie de prindere generic˘, deoarece ˆ majoritatea a a ¸ a ın cazurilor este necesar s˘ se cunoasc˘ natura erorii (de exemplu pentru a fi a a scris˘ ˆ a ıntr-un fi¸ier de logging, pentru a fi consultat˘ mai tˆrziu). s a a Acest lucru se face specificˆnd tipul exceptiei care ar trebui tratate ˆ a ¸ ın blocul catch: using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); }

7.1. TRATAREA EXCEPTIILOR ¸

153

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

154

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

fie exact tipul obiectului aruncat, fie un tip de baz˘ al acestuia - din cauz˘ a a de upcasting) este cel care va face tratarea exceptiei ap˘rute. Ca atare, este ¸ a important ca ordinea exceptiilor tratate s˘ fie de la cel mai derivat la cel mai ¸ a general. ˆ exemplul anterior, System.DivideByZeroException este derivat In din clasa System.ArithmeticException. Blocul finally Uneori, aruncarea unei exceptii ¸i golirea stivei pˆn˘ la blocul de tratare ¸ s a a a exceptiei poate s˘ nu fie o idee bun˘. De exemplu, dac˘ exceptia apare ¸ a a a ¸ atunci cˆnd un fi¸ier este deschis (¸i ˆ a s s ınchiderea lui se poate face doar ˆ ın metoda curent˘), atunci ar fi util ca s˘ se ˆ a a ınchid˘ fi¸ierul ˆ a s ınainte ca s˘ fie a preluat controlul de c˘tre metoda apelant˘. Altfel spus, ar trebui s˘ existe o a a a garantie c˘ un anumit cod se va executa, indiferent dac˘ totul merge normal ¸ a a sau apare o exceptie. Acest lucru se face prin intermediul blocului finally, ¸ care se va executa ˆ orice situatie. Existenta acestui bloc elimin˘ necesitatea ın ¸ ¸ a existentei blocurilor catch (cu toate c˘ ¸i acestea pot s˘ apar˘). ¸ as a a Exemplu: using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } public void TestFunc( ) { try { Console.WriteLine(‘‘Open file here’’); double a = 5; double b = 0; Console.WriteLine (‘‘{0} / {1} = {2}’’, a, b, DoDivide(a,b)); Console.WriteLine (‘‘This line may or may not print’’); } finally { Console.WriteLine (‘‘Close file here.’’); }

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

155

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

156 { } }

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

7.1. TRATAREA EXCEPTIILOR ¸

157

public double DoDivide(double a, double b) { if (b == 0) { DivideByZeroException e = new DivideByZeroException( ); e.HelpLink= ‘‘http://www.greselifatale.com’’; throw e; } if (a == 0) { MyCustomException e = new MyCustomException( ‘‘Can’t have zero divisor’’); e.HelpLink = ‘‘http://www.greselifatale.com/NoZeroDivisor.htm’’; throw e; } return a/b; } } Rearuncarea exceptiilor ¸ Este perfect posibil ca ˆ ıntr–un bloc de tratare a exceptiilor s˘ se se fac˘ ¸ a a o tratare primar˘ a exceptiei, dup˘ care s˘ se arunce mai departe o alt˘ a ¸ a a a exceptie, de acela¸i tip sau de tip diferit (sau chiar exceptia original˘). Dac˘ ¸ s ¸ a a se dore¸te ca aceast˘ exceptie s˘ p˘streze cumva ˆ interiorul ei exceptia s a ¸ a a ın ¸ original˘, atunci constructorul permite ˆ a ınglobarea unei referinte la aceasta; ¸ aceast˘ referint˘ va fi accesibil˘ prin intermediul propriet˘¸ii InnerException: a ¸a a at using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message,Exception inner): base(message,inner) { } } public class Test { public static void Main( ) { Test t = new Test( );

158 t.TestFunc( );

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

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

159

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

7.1.3

Reˆ ıncercarea codului

Se poate pune ˆ ıntrebarea: cum se procedeaz˘ dac˘ se dorec¸te revenirea a a s la codul care a produs exceptia, dup˘ tratarea ei? Exist˘ destule situatii ˆ ¸ a a ¸ ın care reexecutarea acestui cod este dorit˘: s˘ ne gˆndim de exemplu la cazul a a a ˆ care ˆ ın ıntr-o fereastr˘ de dialog se specific˘ numele unuei fi¸ier ce trebuie a a s procesat, numele este introdus gre¸it si se dore¸te ca s˘ se permit˘ corectarea s s a a numelui. Un alt exemplu clasic este cazul ˆ care autorul unei metode ¸tie c˘ ın s a o operatie poate s˘ e¸ueze periodic – de exemplu din cauza unui timeout pe ¸ a s retea – dar vrea s˘ reˆ ¸ a ıncerce operatia de n ori ˆ ¸ ınainte de a semnala eroare. ˆ aceast˘ situatie se poate defini o etichet˘ ˆ In a ¸ a ınaintea blocului try la care s˘ se permit˘ salutl printr–un goto. Urm˘torul exemplu permite unui utia a a lizator s˘ specifice de maxim trei ori numele unui fi¸ier ce se proceseaz˘, cu a s a revenire ˆ cazul erorii. ın

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

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

7.1. TRATAREA EXCEPTIILOR ¸

161

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

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

7.1.4

Compararea tehnicilor de manipulare a erorilor

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

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

162

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

Manipularea erorii ˆ contextul adecvat ın ˆ cazul ˆ care o metod˘ A apeleaz˘ alte metode B1 , . . . Bn , este posibil ca In ın a a oricare din aceste n metode s˘ cauzeze o eroare (¸i s˘ returneze cod adecvat); a s a tratarea erorii din exteriorul lui A este dificil˘ ˆ acest caz, deoarece ar trebui a ın s˘ se cerceteze toate codurile de eroare posibile pentru a determina motivul a aparitiei erorii. Dac˘ se mai adauga ¸i apelul de metod˘ Bn+1 ˆ interiorul ¸ a s a ın lui A, atunci orice apel al lui A trebuie s˘ includ˘ suplimentar ¸i verificarea a a s pentru posibilitatea ca Bn+1 s˘ fi cauzat o eroare. Ca atare, costul mentinerii a ¸ codului cre¸te permanent, ceea ce are un impact negativ asupra TCO-ului3 s Folosind tratarea exceptiilor, aruncˆnd exceptii cu mesaje de eroare ex¸ a ¸ plicite sau exceptii de un anumit tip (definit de programator) se poate trata ¸ mult mai convenabil o situatie deosebit˘. Mai mult decˆt atˆt, introduc¸ a a a erea apelului lui Bn+1 ˆ interiorul lui A nu reclam˘ modificare suplimenın a tar˘, deoarece tipul de exceptie aruncat de Bn+1 este deja tratat (desigur, a ¸ se presupune c˘ se define¸te un tip exceptie sau o ierarhie de exceptii creat˘ a s ¸ ¸ a convenabil). U¸urinta citirii codului s ¸ Pentru comparatie, se poate scrie un cod care realizeaz˘ procesarea continutului ¸ a ¸ ˆ primul caz, unui fi¸ier folosind coduri de eroare returnate sau exceptii. In s ¸ solutia va contine cod de prelucrare a continutului mixat cu cod de verificare ¸ ¸ ¸ ¸i reactie pentru diferitele cazuri de exceptie. Codul ˆ al doilea caz este mult s ¸ ¸ ın mai scurt, mai u¸or de ˆ ¸eles, de mentinut, de corectat ¸i extins. s ınt ¸ s Aruncarea de exceptii din constructori ¸ Nimic nu opre¸te ca o situatie deosebit˘ s˘ apar˘ ˆ s ¸ a a a ıntr–un apel de constructor. Tehnica verific˘rii codului de retur nu mai functioneaz˘ aici, deoarece a ¸ a un constructor nu returneaz˘ valori. Folosirea exceptiilor este ˆ acest caz a ¸ ın aproape de neˆ ınlocuit.

7.1.5

Sugestie pentru lucrul cu exceptiile ¸

ˆ Java, programatorii trebuei s˘ declare c˘ o metod˘ poate arunca o In a a a exceptie ¸i s˘ o declare explicti ˆ ¸ s a ıntr–o list˘ astfel ˆ at un apelant s˘ ¸tie a ıncˆ a s c˘ se poate se poate a¸tepta la primirea ei. Aceast˘ cunoa¸tere ˆ avans a s a s ın permite conceperea unui plan de lucru cu fiecare dintre ele, preferabil decˆt a ˆ cazul .NET se sugereaz˘ s˘ se prind˘ oricare dintre ele cu un catch generic. In a a a
3

Total Cost of Ownership.

7.2. ATRIBUTE

163

s˘ se mentin˘ o documentatie cu exceptiile care poti fi aruncate de fiecare a ¸ a ¸ ¸ metod˘. a

7.2

Atribute

O aplicatie .NET contine cod, date ¸i metadate. Metadata este o dat˘ ¸ ¸ s a despre assembly (versiune, producator, etc), tipuri de date continute, etc ¸ stocate ˆ ımpreun˘ cu codul compilat. a Atributele sunt folosite pentru a da o extra-informatie compilatorului ¸ de .NET. Java folose¸te o combinatie de semne /** ¸i @ pentru a include s ¸ s informatie relativ la clase, metode, cˆmpuri sau parametri. Aceste comentarii ¸ a ˆ a nu vor fi incluse ˆ bytecod-ul final, doar ˆ eventuala documentatie. ıns˘ ın ın ¸ Folosind atributele, aceast˘ informatie poate fi stocat˘ ˆ codul compilat ¸i a ¸ a ın s reutilizat˘ ulterior la runtime. a Unde ar fi utile aceste atribute? Exemple de utilizare a lor ar fi urm˘rirea a bug–urilor care exist˘ ˆ a ıntr–un sistem sau urm˘rirea stadiului proiectului. De a asemenea, anumite atribute predefinite sunt utilizate pentru a specifica dac˘ a un tip este sau nu serializabil, care sunt portiunile de cod scoase din circulatie, ¸ ¸ informatii despre versiunea assembly-ului, etc. ¸

7.2.1

Generalit˘¸i at

Atributele sunt de dou˘ feluri: intrinseci (predefinite) ¸i definite de utia s lizator. Cele intrinseci sunt integrate ˆ platforma .NET ¸i sunt recunoscute ın s de CLR. Atributele definite de utilizator sunt create ˆ functie de dorintele ın ¸ ¸ acestuia. Atributele se pot specifica pentru: assembly–uri, clase, constructori, delegati, ¸ enumer˘ri, evenimente, cˆmpuri, interfete, metode, module, parametri, proa a ¸ priet˘¸i, valori de retur, structuri. at Tinta unui atribut specific˘ cui anume i se va aplica un atribut anume. ¸ a Tabelul 7.1 contine ¸intele posibile ¸i o scurt˘ descriere a lor. ¸ t s a Tabelul 7.1: Tintele atributelor. ¸ Tint˘ ¸ a All Assembly ClassMembers Class Constructor Descriere Orice element Un assembly Orice membru al unei clase O clas˘ a Un constructor

164

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

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

Aplicarea atributelor se face prin specificarea numelui lor ˆ ıntre paranteze drepte; mai multe atribute fie se specific˘unul deasupra celuilalt, fie ˆ a ın interiorul acelora¸i paranteze, desp˘rtite prin virgul˘. Exemplu: s a¸ a [Serializable] [Webservice] echivalent cu: [Serializable, Webservice] ˆ cazul atributelor ce se specific˘ pentru un assembly, forma lor este: In a [assembly:AssemblyDelaySign(false)] ˆ cazul acestor din urm˘ atribute, specificarea lor se face dup˘ toate declaratiile ın a a ¸ using, dar ˆ ınaintea oric˘rui cod. ˆ general, specificarea unui atribut se face a In prin scrierea lui imediat ˆ ınaintea elementului asupra c˘ruia se aplic˘: a a using System; [Serializable] class ClasaSerializabila { //definitia clasei }

7.2.2

Atribute predefinite

Tabelul 7.2 prezint˘ cˆteva dintre ele: a a

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

165

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

166

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

7.2. ATRIBUTE

167

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

7.2.3

Exemplificarea altor atribute predefinite

Atributul Conditional Acest atribut se ata¸eaz˘ la o metod˘ pentru care se dore¸te ca atunci s a a s cˆnd compilatorul o ˆ alne¸te la un apel, dac˘ un anumit simbol nu e definit a ıntˆ s a atunci nu va fi chemat˘. Este folosit˘ pentru a omite anumite apeluri de a a metode ( de exemplu cele folosite la etapa de debugging). Exemplu: using System; using System.Diagnostics; namespace CondAttrib { class Thing { private string name; public Thing(string name) { this.name = name; SomeDebugFunc(); SomeFunc(); } public void SomeFunc() { Console.WriteLine("SomeFunc"); } [Conditional("DEBUG")]

168

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

} public class Class1 { [STAThread] static void Main(string[] args) { Thing t = new Thing("T1"); } } } Definirea unui anumit simbol (de exemplu DEBUG) se poate face ˆ dou˘ ın a moduri: • prin folosirea unei directive de preprocesor de tipul #define: #define DEBUG • prin folosirea parametrilor din linia de comanda sau a posibilit˘¸ilor at mediului integrat de dezvoltare. De exemplu, pentru a se defini un anumit simbolul din Visual Studio se procedeaz˘ astfel: clic dreapta a ˆ Solution Explorer pe numele proiectului, selectare Properties apoi ın Configuration Properties. Pe linia Conditional Compilation Constants se adaug˘ simbolul dorit. Acest lucru va avea ca efect folosirea optiunii a ¸ define a compilatorului. Dac˘ la compilare simbolul nu este definit atunci compilatorul va ignora a apelurile de metode calificate cu atributul Conditional. Acest lucru se paote verifica atˆt urm˘rind executia programului, cˆt ¸i din inspectia codului IL a a ¸ a s ¸ rezultat la compilare. Atributul CLSCompliant Acest atribut se aplic˘ pe assembly–uri. Dac˘ un assembly este marcat a a ca fiind CLSCompliant, orice tip expus public ˆ assembly care nu este comın patibil CLS trebuie s˘ fie marcat cu CLSCompliant(false). De exmplu, CLS a prevede faptul c˘ tipurile de date ˆ a ıntregi ar trebui s˘ fie cu semn. O clas˘ a a poate s˘ contin˘ membri (cˆmpuri, metode) de tip unsigned, dar dac˘ rea ¸ a a a spectiva clas˘ este declarat˘ ca fiind CLSCompliant atunci acestea ar trebui a a

7.2. ATRIBUTE

169

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

170

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

7.2.4

Atribute definite de utilizator

ˆ general, atributele predefinite acoper˘ marea majoritate a situatiilor In a ¸ care cer utilizarea de atribute. Eventualizatea ca utilizatorul s˘ doreasc˘ a a crearea propriilor sale atribute este prev˘zut˘ de c˘tre platforma .NET, a a a dˆndu–se posibilitatea definirii lor. a Exist˘ cˆteva situatii ˆ care e benefic˘ definirea de noi atribute. Exema a ¸ ın a plul cel mai des ˆ alnit este acela ˆ care pentru a se mentine informatii ıntˆ ın ¸ ¸ despre un cod la care lucreaz˘ mai multe echipe, se define¸te un atribut care a s s˘ serveasc˘ la pasarea de informatie relativ la portiuni din cod. Un alt a a ¸ ¸ exemplu este utilizarea unui sistem de urm˘rire a stadiului de dezvoltare a a codului, care ar folosi informatia stocat˘ ˆ atribute. ¸ a ın Un atribut utilizator este o clas˘ definit˘ ca pˆn˘ acum. Se cere a fi a a a a derivat˘ din System.Attribute fie direct, fie indirect. Sunt cˆ¸iva pa¸i care a at s trebuie parcur¸i pentru realizara unui atribut: specificarea ¸intei, derivarea ei s t adecvat˘, denumirea ei ˆ conformitate cu anumite reguli (recomandat, dar a ın nu obligatoriu), definirea clasei. Pasul 1. Declararea ¸intei t Primul pas ˆ crearea unui atribut utilizator este specificarea domeniului ın s˘u de aplicabilitate, adic˘ a elementelor c˘rora li se poate ata¸a. Tintele sunt a a a s ¸ cele din tabelul 7.1, care sunt de fapt valori din enumerarea AttributeTargets. Specificarea ¸intei se face prin intermediul unui (meta)atribut AttributeUsage. t Pe lˆng˘ ¸inta propriu–zis˘, se mai pot specifica valorile propriet˘¸ilor Inhera at a at ited ¸i AllowMultiple. s Proprietatea Inherited este de tip boolean ¸i precizeaz˘ dac˘ atributul s a a poate fi mo¸tenit de c˘tre clasele derivate din cele c˘reia i se aplic˘. Valoarea s a a a implicit˘ este true. a Proprietatea AllowMultiple este de asemenea de tip boolean ¸i specific˘ s a dac˘ se pot utiliza mai multe instante ale atributului pe un acela¸i element. a ¸ s Valoarea implicit˘ este false. a Vom defini mai jos ¸inta atributului pe care ˆ vom construi ca fiind t ıl assembly–ul, 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˘toarele reguli trebuie s˘ fie a a avute ˆ vedere: ın

7.2. ATRIBUTE

171

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

172

CURS 7. TRATAREA EXCEPTIILOR. ATRIBUTE ¸

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

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

ADO.NET reprezint˘ o parte component˘ a lui .NET Framework ce pera a mite aducerea, manipularea ¸i modificarea datelor. ˆ mod normal, o surs˘ s In a de date poate s˘ fie o baz˘ de date, dar de asemenea un fi¸ier text sau Exa a s cel sau XML sau Access. Lucrul se poate face fie conectat, fie deconectat de la sursa de date. De¸i exist˘ variate modalit˘¸i de lucru cu bazele de s a at date, ADO.NET se impune tocmai prin faptul c˘ permite lucrul deconectat a de la baza de date, integrarea cu XML, reprezentarea comun˘ a datelor cu a posibilitatea de a combina date din variate surse, toate pe baza unor clase .NET. Faptul c˘ se permite lucrul deconectat de la sursa de date rezolv˘ urm˘toarele a a a probleme: • mentinerea conexiunilor la baza de date este o operatie costisitoare. ¸ ¸ O bun˘ parte a l˘¸imii de band˘ este mentinut˘ ocupat˘ pentru ni¸te a at a ¸ a a s proces˘ri care nu necesit˘ neap˘rat conectare continu˘ a a a a • probleme legate de scalabilitatea aplicatiei: se poate ca serverul de baze ¸ de date s˘ lucreze u¸or cu 5-10 conexiuni mentinute, dar dac˘ num˘rul a s ¸ a a acestora cre¸te aplicatia poate s˘ reactioneze extrem de lent s ¸ a ¸ • pentru unele servere se impun clauze asupra num˘rului de conexiuni ce a se pot folosi simultan. Toate acestea fac ca ADO.NET s˘ fie o tehnologie mai potrivit˘ pentru deza a voltarea aplicatiilor Internet decˆt cele precedente (e.g. ADO, ODBC). ¸ a Pentru o prezentare a metodelor de lucru cu surse de date sub platform˘ a Windows se poate consulta [7]. 173

174

CURS 8. ADO.NET

Vom exemplifica ˆ cele ce urmeaz˘ preponderent pe baz˘ de date Miın a a crosoft SQL 2000. Pentru lucrul cu MS SQL Server se poate instala gratuit versiunea gratuit˘ de Microsoft SQL Server 2000 Desktop Engine1 . a

8.2

Furnizori de date ˆ ADO.NET ın

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

Prefixele trecute ˆ coloana ”Prefix” sunt folosite pentru clasele de lucru ın specifice unui anumit furnizor ADO.NET: de exemplu, pentru o connexiune SQL se va folosi clasa SqlConnection.

8.3

Componentele unui furnizor de date

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

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

8.3. COMPONENTELE UNUI FURNIZOR DE DATE prezentat˘ ˆ figura 8.1 a ın

175

Figura 8.1: Principalele clase ADO.NET

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

8.3.1

Clasele Connection

Sunt folosite pentru a reprezenta o conexiune la surse de date specifice. Ele contin date specifice conexiunii, cum ar fi locatia sursei de date, nu¸ ¸ mele ¸i parola contului de acces, etc. ˆ plus au metode pentru deschiderea s In ¸i ˆ s ınchiderea conexiunilor, pornirea unei tranzactii sau setarea perioadei de ¸ time-out. Stau la baza oric˘rei ˆ a ıncerc˘ri de conectare. a

176

CURS 8. ADO.NET

8.3.2

Clasele Command

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

8.3.3

Clasele DataReader

Permit navigarea de tip forward–only, read–only ˆ mod conectat la sursa ın de date. Se obtin pe baza unui obiect de tip Command prin apelul metodei ¸ ExecuteReader(). Accesul rezultat este extrem de rapid cu minim de resurse consumate.

8.3.4

Clasele DataAdapter

Ultima component˘ principal˘ a unui furnizor de date .NET este DataAdapter. a a Functioneaz˘ ca o punte ˆ ¸ a ıntre sursa de date ¸i obiecte de tip DataSet deconecs tate, permitˆnd efectuarea operatiilor pe baza de date. Contin referinte c˘tre ¸a ¸ ¸ ¸ a obiecte de tip Connection ¸i deschid / ˆ s ınchid singure conexiunea la baza de date. ˆ plus, un DataAdapter contine referinte c˘tre patru comenzi pentru In ¸ ¸ a selectare, ¸tergere, modificare ¸i ad˘ugare la baza de date. s s a

8.3.5

Clasa DataSet

Aceast˘ clas˘ nu este parte a unui furnizor de date .NET ci independent˘ a a a de particularit˘¸ile de conectare ¸i lucru cu o baz˘ de date anume. Prezint˘ at s a a marele avantaj c˘ poate sa lucreze deconectat de la sursa de date, facilitˆnd a a stocarea ¸i modificarea datelor local, apoi reflectarea acestor modific˘ri ˆ s a ın baza de date. Un obiect DataSet este de fapt un container de tabele ¸i relatii s ¸ ˆ ıntre tabele. Folose¸te serviciile unui obiect de tip DataAdapter pentru as ¸i procura datele ¸i a trimite modific˘rile ˆ s s a ınapoi c˘tre baza de date. Datele a sunt stocate de un DataSet ˆ format XML; acela¸i format este folosit pentru ın s transportul datelor.

8.4

Obiecte Connection

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

8.4. OBIECTE CONNECTION

177

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

8.4.1

Propriet˘¸i at

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

178

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

2. ConnectionTimeout: de tip int, cu accesor de get, valoare implicit˘ a 15; specific˘ num˘rul de secunde pentru care un obiect de conexiune a a ar trebui s˘ a¸tepte pentru realizarea conect˘rii la server ˆ a s a ınainte de a se genera o exceptie. Se poate specifica o valoare diferit˘ de 15 ˆ ¸ a ın ConnectionString folosind parametrul Connect Timeout: SqlConnection cn = new SqlConnection("Data Source=serverBD; Database=Northwind;User ID=sa;Password=parola; Connect Timeout=30"); Se poate specifica pentru Connect Timeout valoarea 0 cu semnificatia ¸ ”a¸teapt˘ oricˆt”, dar se sugereaz˘ s˘ nu se procedeze ˆ acest mod. s a a a a ın 3. Database: atribut de tip string, read-only, returneaz˘ numele bazei de a date la care s–a f˘cut conectarea. Folosit˘ pentru a ar˘ta unui utilizator a a a care este baza de date pe care se face operarea. 4. Provider : atribut de tip string, read-only, returneaz˘ furnizorul OLE a DB. 5. ServerVersion: atribut de tip string, read-only, returneaz˘ versiunea a de server la care s–a f˘cut conectarea. a 6. State: atribut de tip enumerare ConnectionState , read-only, returneaz˘ a starea curent˘ a conexiunii. Valorile posibile sunt: Broken, Closed, a Connecting, Executing, Fetching, Open.

8.4. OBIECTE CONNECTION

179

8.4.2

Metode

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

8.4.3

Evenimente

Un obiect de tip conexiune poate semnala dou˘ evenimente: a • evenimentul StateChange: apare atunci cˆnd se schimb˘ starea conexa a iunii. Event-handlerul este de tipul delegat StateChangeEventHandler, care spune care sunt st˘rile ˆ a ıntre care s–a f˘cut tranzitia. a ¸ • evenimentul InfoMessage: apare atunci cˆnd furnizorul trimite un avera tisment sau un mesaj informational c˘tre client. ¸ a

8.4.4

Stocarea stringului de conexiune ˆ fi¸ier de conın s figurare

ˆ general este contraindicat ca stringul de conexiune s˘ fie scris direct ˆ In a ın cod; modificarea datelor de conectare (de exemplu parola pe cont sau sursa de date) ar ˆ ınsemna recompilarea codului. .NET Framework permite mentinerea unor perechi de tipul chei-valoare, ¸ specifice aplicatiei; fi¸ierul este de tip XML. Pentru aplicatiile Web fi¸ierul se ¸ s ¸ s nume¸te web.config, pentru aplicatiile de tip consol˘ fi¸ierul de configurare are s ¸ a s extensia config ¸i numele aplicatiei, iar pentru aplicatiile Windows acest fi¸ier s ¸ ¸ s nu exist˘ implicit, dar se adaug˘ prin click dreapta pe proiect ˆ Solution a a ın Explorer->Add->Add New Item->Application Configuration File, implicit acesta avˆnd numele App.config. ˆ fie care caz, structura fi¸ierului XML de a In s configurare este similar˘. Implicit, acesta arat˘ astfel: a a

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

CURS 8. ADO.NET

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

8.4.5

Gruparea conexiunilor

Gruparea conexiunilor3 reprezint˘ reutilizarea resurselor de tip conexia une la o baz˘ de date. Atunci cˆnd se creeaz˘ o grupare de conexiuni se a a a genereaz˘ automat mai multe obiecte de tip conexiune, acest num˘r fiind a a
3

Engl: connection pooling

8.5. OBIECTE COMMAND

181

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

8.5

Obiecte Command

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

8.5.1

Propriet˘¸i at

1. CommandText: de tip String, cu ambii accesori; contine comanda SQL ¸ sau numele procedurii stocate care se execut˘ pe sursa de date. a 2. CommandTimeout: de tip int, cu ambii accesori; reprezint˘ num˘rul a a de secunde care trebuie s˘ fie a¸teptat pentru executarea interog˘rii. a s a Dac˘ se dep˘¸este acest timp, atunci se arunc˘ o exceptie. a as a ¸

182

CURS 8. ADO.NET

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

8.5.2

Metode

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

8.5. OBIECTE COMMAND

183

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

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

CURS 8. ADO.NET

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

8.5. OBIECTE COMMAND

185

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

8.5.3

Utilizarea unei comenzi cu o procedur˘ stocat˘ a a

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

8.5.4

Folosirea comenzilor parametrizate

Exist˘ posibilitatea de a rula cod SQL (interog˘ri, proceduri stocate) a a pe server care s˘ fie parametrizate. Orice furnizor de date .NET permite a crearea obiectelor parametru care pot fi ad˘ugate la o colectie de parametri a ¸ ai comenzii. Valoarea cestor parametrii se specific˘ fie prin numele lor (cazul a SqlParameter ), fie prin poziˇia lor (cazul OleDbParameter ). t Exemplu: vom aduce din tabela Customers toate ˆ ınregistr˘rile care au ˆ a ın cˆmpul Country valoarea “USA”. a SqlConnection con = new SqlConnection(

186

CURS 8. ADO.NET

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

8.6. OBIECTE DATAREADER con.Close(); Remarc˘m urm˘toarele: a a • este posibil ca ˆ ıntr–o comand˘ s˘ se execute mai multe interog˘ri a a a

187

• pentru parametrul de ie¸ire numit “@count” trebuie f˘cut˘ declarare s a a de directie; implicit un parametru este de intrare ¸ • parametrii de ie¸ire sunt accesibili doar dup˘ ˆ s a ınchiderea obiectului de tip DataReader

8.6

Obiecte DataReader

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

8.6.1

Propriet˘¸i at

1. IsClosed - proprietate read-only, returneaz˘ true daca obiectul este a deschis, false altfel 2. HasRows - proprietate boolean˘ read-only care spune dac˘ readerul a a contine cel putin o ˆ ¸ ¸ ınregistrare 3. Item - indexator care d˘ acces la cˆmpurile unei ˆ a a ınregistr˘ri a 4. FieldCount - d˘ num˘rul de cˆmpuri din ˆ a a a ınregistrarea curent˘ a

188

CURS 8. ADO.NET

8.6.2

Metode

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

8.6.3

Crearea ¸i utilizarea unui DataReader s

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

8.6. OBIECTE DATAREADER

189

face folosind metoda Read(). Cˆnd se dore¸te ˆ a s ıncetarea lucrului se ˆ ınchide reader–ul ¸i conexiunea. Omiterea ˆ s ınchiderii obiectului de tip reader va duce la imposibilitatea reutiliz˘rii conexiunii initiale. Dup˘ ce se ˆ a ¸ a ınchide acest DataReader este necesar˘ ¸i ˆ chiderea explicit˘ a conexiunii (acest lucru nu a s ın a mai e mandatoriu doar dac˘ la apelul metodei ExecuteReader s–a specificat a CommandBehavior.CloseConnection). Dac˘ se ˆ a ıncearc˘ refolosirea conexia unii f˘r˘ ca readerul s˘ fi fost ˆ aa a ınchis se va arunca o exceptie InvalidOpera¸ tionException. Exemplu: SqlConnection conn = new SqlConnection ( ConfigurationSettings.AppSettings["constring"]); SqlCommand selectCommand = new SqlCommand("select * from ORDERS", conn); conn.Open (); OleDbDataReader reader = selectCommand.ExecuteReader ( ); while ( reader.Read () ) { object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } reader.Close (); conn.Close (); Este perfect posibil ca un obiect de tip DataReader s˘ aduc˘ datele prin a a apelul unei proceduri stocate (de fapt invocarea acestei proceduri este f˘cut˘ a a de c˘tre obiectul de tip Command ). a Urm˘toarele observatii trebuie luate ˆ considerare atunci cˆnd se lua ¸ ın a creaz˘ cu un obiect DataReader : a • Metoda Read() trebuie s˘ fie ˆ a ıntotdeauna apelat˘ ˆ a ınaintea oric˘rui acces a la date; pozitia curent˘ la deschidere este ˆ ¸ a ınaintea primei ˆ ınregistr˘ri. a • ˆ Intotdeauna apelati metoda Close() pe un DataReader ¸i pe conexiunea ¸ s asociat˘ cˆt mai repede posibil; ˆ caz contrar conexiunea nu poate fi a a ın reutilizat˘ a • Procesarea datelor citite trebuie s˘ se fac˘ dup˘ ˆ a a a ınchiderea conexiunii; ˆ felul acesta conexiunea se las˘ liber˘ pentru a putea fi reutilizat˘. ın a a a

190

CURS 8. ADO.NET

8.6.4

Utilizarea de seturi de date multiple

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

8.6.5

Accesarea datelor ˆ ıntr–o manier˘ sigur˘ din punct a a de vedere a tipului

S˘ consider˘m urm˘toarea secvent˘ de cod: a a a ¸a while ( reader.Read () ) { object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } Dup˘ cum se observ˘, este posibil ca valorile cˆmpurilor dintr–o ˆ a a a ınregistrare s˘ fie accesate prin intermediul numelui coloanei (sau a indicelui ei, pornind a de la 0). Dezavantajul acestei metode este c˘ tipul datelor returnate este a pierdut (fiind returnate obiecte de tip Object), trebuind f˘cut˘ un downcasta a ing pentru a utiliza din plin facilit˘¸ile tipului respectiv. Pentru ca acest at lucru s˘ nu se ˆ ample se pot folosi metodele GetXY care returneaz˘ un tip a ıntˆ a specific de date:

8.6. OBIECTE DATAREADER

191

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

192

CURS 8. ADO.NET

Curs 9 ADO.NET (2)
9.1 Obiecte DataAdapter

La fel ca ¸i Connection, Command, DataReader, obiectele de tip DataAdapter s fac parte din furnizorul de date .NET specific fiec˘rui tip de surs˘ de date. a a Scopul ei este s˘ permit˘ umplerea unui obiect DataSet cu date ¸i reflectarea a a s schimb˘rilor efectuate asupra acestuia ˆ a ınapoi ˆ baza de date (DataSet perın mite lucrul deconectat de la baza de date). Orice clas˘ de tipul DataAdapter (de ex SqlDataAdapter ¸i OleDbDataAdapter) a s este derivat˘ din clasa DbDataAdapter (clas˘ abstract˘). Pentru orice obiect a a a de acest tip trebuie specificat˘ minim comanda de tip SELECT care care a s˘ populeze un obiect de tip DataSet; acest lucru este stabilit prin intera mediul propriet˘¸ii SelectCommand de tip Command (SqlCommand, OleDat bCommand, . . . ). ˆ cazul ˆ care se dore¸te ¸i modificarea informatiilor din In ın s s ¸ sursa de date (inserare, modificare, ¸tergere) trebuie specificate obiecte de tip s comand˘ via propriet˘¸ile: InsertCommand, UpdateCommand, DeleteComa at mand. Exemplu: mai jos se preiau ˆ ınregistr˘rile din 2 tabele: Authors ¸i TitleAua s thor ¸i se trec ˆ s ıntr–un obiect de tip DataSet pentru a fi 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˘m mai jos cele mai importante componente ale unei clase de tip a DataAdapter.

9.1.1

Metode

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

2. Fill() – metod˘ polimorfic˘, permitˆnd umplerea unei tabele dintr–un a a ¸a obiect de tip DataSet cu date. Permite specificarea obiectului DataSet ˆ care se depun datele, eventual a numelui tablei din acest DataSet, ın num˘rul de ˆ a ınregistrare cu care s˘ se ˆ a ınceap˘ popularea (prima avˆnd a a indicele 0) ¸i num˘rul de ˆ s a ınregistr˘ri care urmeaz˘ a fi aduse. Rea a turneaz˘ de fiecare dat˘ num˘rul de ˆ a a a ınregistr˘ri care au fost aduse din a ˆ clipa ˆ care se apeleaz˘ Fill() se procedeaz˘ astfel: baz˘. In a ın a a

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

195

(b) Se aduc datele ¸i se populeaz˘ un obiect de tip DataTable din s a DataSet (c) Se ˆ ınchide conexiunea (dac˘ ea nu a fost explicit deschis˘!) a a De remarcat c˘ un DataAdapter ˆsi poate deschide ¸i ˆ a ı¸ s ınchide singur conexiunea, dar dac˘ aceasta a fost deschis˘ ˆ a a ınaintea metodei Fill() atunci tot programatorul trebuie s˘ o ˆ a ınchid˘. a 3. Update() – metod˘ polimorfic˘, permitˆnd reflectarea modific˘rilor efeca a ¸a a tuate ˆ ıntre–un DataSet. Pentru a functiona are nevoie de obiecte de ¸ tip comand˘ adecvate: propriet˘¸ile InsertCommand, DeleteCommand a at ¸i UpdateCommand trebuie s˘ indice c˘tre comenzi valide. Returneaz˘ s a a a de fiecare dat˘ num˘rul de ˆ a a ınregistr˘ri afectate. a

9.1.2

Propriet˘¸i at

1. DeleteCommand, InsertCommand, SelectCommand, UpdateCommand – de tip Command, contin comenzile ce se execut˘ pentru selectarea ¸ a sau modificarea datelor ˆ sursa de date. M˘car proprietatea Selectın a Command trebuie s˘ indice c˘tre un obiect valid, pentru a se putea a a face popularea setului de date. 2. MissingSchemaAction – de tip enumerare MissingSchemaAction, determin˘ ce se face atunci cˆnd datele care sunt aduse nu se potrivesc a a peste schema tablei ˆ care sunt depuse. Poate avea urm˘toarele valori: ın a • Add - implicit, DataAdapter adaug˘ coloana la schema tablei a

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

9.2

Clasa DataSet

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

196

CURS 9. ADO.NET (2)

avantaj este faptul c˘ permite lucrul deconectat de la sursa de date, eliminˆnd a a ˆ felul acesta, necesitatea unei conexiuni permanente precum la DataReader. In un server de aplicatii sau un client oarecare poate apela la serverul de baze ¸ de date doar cˆnd preia datele sau cˆnd se dore¸te salvarea lor. Functioneaz˘ a a s ¸ a ˆ strˆns˘ leg˘tur˘ cu clasa DataAdapter care actioneaz˘ ca o punte ˆ ın a a a a ¸ a ıntre un DataSet ¸i sursa de date. Remarcabil este faptul c˘ un DataSet poate face s a abstractie de sursa de date, procesarea datelor desf˘¸urˆndu–se independent ¸ as a de ea. Figura 9.1 contine o vedere partial˘ asupra clasei DataSet. ¸ ¸ a

Figura 9.1: Structura unui DataSet

9.2.1

Continut ¸

Prezent˘m succint continutul unui DataSet: a ¸ 1. Colectia Tables contine 0 sau mai multe obiecte DataTable. Fiecare ¸ ¸ DataTable este compus˘ dintr-o colectie de linii ¸i coloane. a ¸ s 2. Colectia Relations contine 0 sau mai multe obiecte de tip DataRelation, ¸ ¸ folosite pentru marcarea leg˘turilor p˘rinte–copil. a a 3. Colectia ExtendedProperties contine propriet˘¸i definite de utilizator. ¸ ¸ at

9.2.2

Clasa DataTable

Datele sunt continute ˆ ¸ ıntr-un DataSet sub forma unor tabele de tip DataTable. Aceste obiecte pot fi folosite atˆt independent, cˆt ¸i ˆ interiorul unui a a s ın

9.2. CLASA DATASET

197

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

198

CURS 9. ADO.NET (2)

• UniqueConstraint - precizeaz˘ c˘ ˆ a a ıntr-o anumit˘ coloan˘ valorile sunt a a ˆ unice. Incercarea de a seta valori duplicate pe o coloana pentru care s-a precizat restrictia duce la aruncarea unei exceptii. Este necesar˘ ¸ ¸ a o asemenea coloan˘ ˆ clipa ˆ care se folose¸te metoda Find pentru a ın ın s proprietatea Rows: ˆ acest caz trebuie s˘ se specifice o coloan˘ pe care ın a a avem unicitate. • ForeignKeyConstraint - specific˘ actiunea care se va efectua atunci a ¸ cˆnd se ¸terge sau modific˘ valoarea dintr–o anumit˘ coloan˘. De exa s a a a emplu se poate decide c˘ dac˘ se sterge o ˆ a a ınregistrare dintr-o tabel˘ a atunci s˘ se ¸tearg˘ ¸i ˆ a s a s ınregistr˘rile copil. Valorile care se pot seta a pentru o asemenea constrˆngere se specific˘ ˆ propriet˘¸ile ForeignKa a ın at eyConstraint.DeleteRule ¸i ForeignKeyConstraint.UpdateRule: s – Rule.Cascade - actiunea implicit˘, ¸terge sau modific˘ ˆ ¸ a s a ınregistr˘rile a afectate – Rule.SetNull - se seteaz˘ valoare de null pentru ˆ a ınregistr˘rile afeca tate – Rule.SetDefault - se seteaz˘ valoarea implicit˘ definit˘ ˆ baz˘ a a a ın a pentru cˆmpul respectiv a – Rule.None - nu se execut˘ nimic a Exemplu: ForeignKeyConstraint custOrderFK=new ForeignKeyConstraint ("CustOrderFK",custDS.Tables["CustTable"].Columns["CustomerID"], custDS.Tables["OrdersTable"].Columns["CustomerID"]); custOrderFK.DeleteRule = Rule.None; //Nu se poate sterge un client care are comenzi facute custDS.Tables["OrdersTable"].Constraints.Add(custOrderFK); Mai sus s-a declarat o relatie de tip cheie str˘in˘ ˆ ¸ a a ıntre dou˘ tabele a (“CustTable” ¸i “OrdersTable”, care fac parte dintr-un DataSet). Restrictia s ¸ se adaug˘ la tabla copil. a Stabilirea cheii primare O cheie primar˘ se define¸te ca un vector de coloane care se atribuie a s propriet˘¸ii PrimaryKey a unei tabele (obiect DataTable). at DataColumn[] pk = new DataColumn[1]; pk[0] = myTable.Columns["ID"]; myTable.PrimaryKey = pk;

9.2. CLASA DATASET

199

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

9.2.3

Relatii ˆ ¸ ıntre tabele

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

9.2.4

Popularea unui DataSet

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

200

CURS 9. ADO.NET (2)

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

9.2.5

Propagarea modific˘rilor c˘tre baza de date a a

Pentru a propaga modific˘rile efectuate asupra continutului tabelelor a ¸ dintr-un DataSet c˘tre baza de date este nevoie s˘ se defineasc˘ adecvat a a a obiecte comand˘ de tip INSERT, UPDATE, DELETE. Pentru cazuri sima ple se poate folosi clasa SqlCommandBuilder care va construi singur˘ aceste a comenzi. Clasa CommandBuilder Un obiect de tip CommandBuilder (ce provine din furnizorul de date) va analiza comanda SELECT care a adus datele ˆ DataSet ¸i va construi cele ın s 3 comenzi de update ˆ functie de aceasta. E nevoie s˘ se satisfac˘ 2 conditii ın ¸ a a ¸ atunci cˆnd se uzeaz˘ de un astfel de obiect: a a 1. Trebuie specificat˘ o comand˘ de tip SELECT care s˘ aduc˘ datele a a a a dintr-o singur˘ tabel˘ a a 2. Trebuie specificat˘ cel putin cheia primar˘ sau o coloan˘ cu constrˆngere a ¸ a a a de unicitate ˆ comanda SELECT. ın Pentru cea de a doua conditie se poate proceda ˆ felul urm˘tor: ˆ comanda ¸ ın a ın SELECT se specific˘ ¸i aducerea cheii, iar pentru obiectul DataAdapter care as face aducerea din baz˘ se seteaz˘ proprietatea MissingSchemaAction pe vala a oarea MissingSchemaAction.AddWithKey (implicit este doar Add ). Fiecare linie modificat˘ din colectia Rows a unei tabele va avea moda ¸ ificat˘ valoarea propriet˘¸ii RowState astfel: DataRowState.Added pentru a at

9.2. CLASA DATASET

201

o linie nou˘ ad˘ugat˘, DataRowState.Deleted dac˘ e ¸tears˘ ¸i DataRowa a a a s a s State.Modified dac˘ a fost modificat˘. Apelul de update pe un dataReader a a va apela comanda necesar˘ pentru fiecare linie care a fost modificat˘, ˆ a a ın functie de starea ei. ¸ Ar˘t˘m mai jos modul de utilizare a clasei SqlCommandBuilder pentru aa ad˘ugarea, modificarea, ¸tergerea de ˆ a s ınregistr˘ri din baza de date. a SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["constring"]); da = new SqlDataAdapter("SELECT id, name, address FROM customers",conn); da.Fill(ds); SqlCommandBuilder cb = new SqlCommandBuilder(da); //determina liniile care au fost schimbate DataSet dsChanges = ds.GetChanges(); if (dsChanges != null) { // modifica baza de date da.Update(dsChanges); //accepta schimbarile din dataset ds.AcceptChanges(); } ˆ clipa ˆ care se creeaz˘ obiectul SqlCommandBuilder automat se vor comIn ın a pleta propriet˘¸ile InsertCommand, DeleteCommand, UpdateCommand ale at dataAdapter-ului. Se determin˘ apoi liniile care au fost modificate (prin a interogarea st˘rii lor) ¸i se obtine un nou DataSet care le va contine doar a s ¸ ¸ pe acestea. Comanda de Update se d˘ doar pentru acest set de modific˘ri, a a reducˆnd astfel traficul spre serverul de baze de date. a Update folosind comenzi SQL Atunci cˆnd interog˘rile de aducere a datelor sunt mai complexe (de a a exemplu datele sunt aduse din mai multe table, printr-un join) se pot specifica propriile comenzi SQL prin intermediul propriet˘¸ilor InsertCommand, at DeleteCommand UpdateCommand ale obiectului DataAdapter. Pentru fiecare linie dintr-o tabel˘ care este modificat˘/ad˘ugat˘/¸tears˘ se va apela coa a a a s a manda SQL corespunz˘toare.Aceste comenzi pot fi fraze SQL parametrizate a sau pot denumi proceduri stocate aflate ˆ baz˘. ın a S˘ presupunem c˘ s-a definit un DataAdapter legat la o baz˘ de date. a a a Instructiunea de selectie este ¸ ¸

202

CURS 9. ADO.NET (2)

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

9.3

Tranzactii ˆ ADO.NET ¸ ın

O tranzactie este un set de operatii care se efectueaz˘ fie ˆ ˆ ¸ ¸ a ın ıntregime, fie deloc. S˘ presupunem c˘ se dore¸te trecerea unei anumite sume de bani a a s dintr-un cont ˆ altul. Operatia presupune 2 pa¸i: ın ¸ s 1. scade suma din primul cont 2. adaug˘ suma la al doilea cont a Ar fi inadmisibil ca primul pas s˘ reu¸easc˘ iar al doilea s˘ e¸ueze. Tranzactiile a s a a s ¸ satisfac ni¸te propriet˘¸i strˆnse sub numele ACID: s at a

9.3. TRANZACTII ˆ ADO.NET ¸ IN

203

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

204

CURS 9. ADO.NET (2)

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

Curs 10 Fire de executie ¸
Firele de executie1 sunt responsabile cu multitasking–ul ˆ interiorul unei ¸ ın aplicatii. Clasele ¸i interfetele responsabile pentru crearea aplicatiilor mul¸ s ¸ ¸ tithreading se g˘sesc ˆ spatiul de nume System.Threading. Vom discuta ˆ a ın ¸ ın cele ce urmeaz˘ despre managementul thread–urilor ¸i despre sincronizare. a s De cele mai multe ori crearea de thread–uri este necesar˘ pentru a da a utilizatorului impresia c˘ programul execut˘ mai multe actiuni simultan, ˆ a a ¸ ın cadrul unei aplicatii. Pentru ca o interfat˘ utilizator s˘ poat˘ fi folosit˘ f˘r˘ ¸ ¸a a a a aa a se a¸tepta ˆ s ıncheierea unei anumite secvente de instructiuni, e nevoie de ¸ ¸ mai multe fire de executie (unele pentru procesarea efectiv˘ a informatiei, ¸ a ¸ altele care s˘ r˘spund˘ actiunilor utilizatorului). Pentru probleme de calcul a a a ¸ numeric, exprimarea procesului de calcul se poate face foarte natural sub form˘ de fire de exectie. a ¸

10.1
10.1.1

Managementul thread–urilor
Pornirea thread–urilor

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

Engl: threads; vom folosi alternativ termenii “thread” ¸i “fir de executie” s ¸

205

206

CURS 10. FIRE DE EXECUTIE ¸

ThreadStart ts = new ThreadStart(MyFunc); Thread myThread = new Thread( ts ); Un exemplu simplu este: using System; using System.Threading; class SimpleThreadApp { public static void WorkerThreadMethod() { Console.WriteLine("[WorkerThreadMethod] Worker " + "thread started"); } public static void Main() { ThreadStart worker = new ThreadStart(WorkerThreadMethod); Console.WriteLine("[Main] Creating worker thread"); Thread t = new Thread(worker); t.Start(); Console.WriteLine( "[Main] Have requested the start of worker thread"); Console.ReadLine(); } } Pˆn˘ la linia t.Start() exist˘ un singur thread: cel dat de pornirea metodei a a a Main. Dup˘ t.Start() sunt 2 thread–uri: cel anterior ¸i t. Mesajul din a s Main va fi tip˘rit ˆ a ınaintea celui din thread–ul t; ˆ functie de cum anume ın ¸ se planific˘ thread–urile pentru executie, mesajul de dup˘ t.Start() poate s˘ a ¸ a a apar˘ ˆ a ınainte sau dup˘ mesajul tip˘rit de metoda WorkerThreadMethod(). a a De remarcat c˘ simpla creere a firului de executie nu determin˘ ¸i pornirea a ¸ as lui: acest lucru se ˆ ampl˘ dupa apelul metodei Start definit˘ ˆ clasa ıntˆ a a ın Thread. Exemplul urm˘tor porne¸te dou˘ fire de executie. Functiile care contin a s a ¸ ¸ ¸ codul ce se va executa ˆ cˆte un thread sunt Increment() ¸i Decrement(): ın a s using System; using System.Threading; class Tester

10.1. MANAGEMENTUL THREAD–URILOR { 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¸ire se vor mixa mesajele tip˘rite de primul thread cu cele tip˘rite s a a de cel de–al doilea thread: ... Incrementer: Incrementer: Incrementer: Incrementer: 102 103 104 105

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

CURS 10. FIRE DE EXECUTIE ¸

Perioada de timp alocat˘ fiec˘rui thread este determinatu˘ de c˘tre plania a a a ficatorul de fire de executie2 ¸i depinde de factori precum viteza procesorului, ¸ s gradul lui de ocupare, etc. O alt˘ modalitate de obtinere a unei referinte la un thread este prin apelul a ¸ ¸ metodei statice Thread.CurrentThread pentru firul de executie curent. ˆ ¸ In exemplul de mai jos se obtine obiectul Thread asociat firului de executie ¸ ¸ curent, i se seteaz˘ numele (proprietate de tip read/write) ¸i se afi¸eaz˘ pe a s s a ecran: Thread current = Thread.CurrentThread; current.Name = "My name"; Console.WriteLine("nume={0}", current.Name );

10.1.2

Metoda Join()

Exist˘ situatii ˆ care, ˆ a ¸ ın ınaintea unei instructiuni trebuie s˘ se asigure fap¸ a tul c˘ un alt fir de executie t s-a terminat; acest lucru se va face folosind apelul a ¸ t.Join() ˆ ınaintea instructiunii ˆ cauz˘; ˆ acest moment firul de executie care ¸ ın a ın ¸ a apelat t.Join() intr˘ ˆ a¸teptare. a ın s Dac˘ de exemplu ˆ metoda Main se lanseaz˘ o colectie de thread–uri a ın a ¸ (stocat˘ ˆ myThreads), atunci pentru a se continua executia numai dup˘ ce a ın ¸ a toate firele din colectie s–au terminat, se procedeaz˘ astfel: ¸ a foreach( Thread myThread in myThreads ) { myThread.Join(); } Console.WriteLine(‘‘All my threads are done’’); Mesajul final se va tip˘ri doar cˆnd toate firele de executie s–au terminat. a a ¸
2

Engl: thread scheduler

10.1. MANAGEMENTUL THREAD–URILOR

209

10.1.3

Suspendarea firelor de executie ¸

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

10.1.4

Omorˆrea thread–urilor a

De obicei, un fir de executie moare dup˘ ce se termin˘ de executat. Se ¸ a a poate totu¸i cere unui fir de executie s˘ ˆsi ˆ s ¸ a ı¸ ınceteze executia folosind metoda ¸ Abort(). Acest lucru va duce la aruncarea unei exceptii ˆ interiorul firului ¸ ın de executie c˘ruia i se cere suspendarea: ThreadAbortedException, pe care ¸ a firul respectiv o poate prinde ¸i procesa, permitˆndu–i eliberarea de resurse s ¸a alocate. Ca atare, se recomand˘ ca o metod˘ care se va lansa ca fir de a a

210

CURS 10. FIRE DE EXECUTIE ¸

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

10.1. MANAGEMENTUL THREAD–URILOR Console.WriteLine("All my threads are done.");

211

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

212 Thread.CurrentThread.Name);

CURS 10. FIRE DE EXECUTIE ¸

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

10.1. MANAGEMENTUL THREAD–URILOR 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˘¸ilor firelor de executie at ¸

Un fir de executie se lanseaz˘ implicit cu prioritatea ThreadPriorityLevel.Normal. ¸ a Dar scheduler–ul poate fi influentat ˆ activitatea sa prin setarea de diferite ¸ ın nivele de prioritate pentru fire; aceste nivele fac parte din enumerarea ThreadPriorityLevel : ThreadPriorityLevel.TimeCritical, ThreadPriorityLevel.Highest, ThreadPriorityLevel.AboveNormal, ThreadPriorityLevel.Normal, ThreadPriorityLevel.BelowNormal, ThreadPriorityLevel.Lowest, ThreadPriorityLevel.Idle. Prioritatea este descresc˘toare ˆ lista prezentat˘. Prioritatea este descresc˘toare a ın a a ˆ lista prezentat˘. Pe baza priorit˘¸ii procesului care contine firele de ın a at ¸ executie ¸i a priorit˘¸ii firelor, se calculeaz˘ un nivel de propritate (de ex. ¸ s at a pe ma¸ini Intel valori ˆ s ıntre 0 ¸i 31) care determin˘ prioritatea ˆ ansamblul s a ın sistemului de operare a firului respectiv. Setarea unei anumite priorit˘¸i se face folosind proprietatea Priority: at myThread.Priority = ThreadPriorityLevel.Highest;

10.1.6

Fire ˆ fundal ¸i fire ˆ prim-plan ın s ın

Relativ la proprietatea boolean˘ IsBackground, trebuie f˘cut˘ precizarea a a a c˘ un fir de executie poate s˘ se execute ˆ fundal (background) sau ˆ prim a ¸ a ın ın plan (foreground). Diferenta dintre cele dou˘ posibilit˘¸i o constituie faptul ¸ a at c˘ dac˘ un proces are m˘car un fir de executie ˆ foreground, CLR va mentine a a a ¸ ın ¸ aplicatia ˆ executie. O dat˘ ce toate firele de executie de tip foreground ¸ ın ¸ a ¸ se termin˘, CLR va executa Abort() pentru fiecare fir de executie de tip a ¸ background (dac˘ mai exist˘ a¸a ceva) ¸i termin˘ procesul. a a s s a Exemplu: using System; using System.Threading; class Test { static void Main()

214 {

CURS 10. FIRE DE EXECUTIE ¸

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

10.2. SINCRONIZAREA

215

ciclul s˘u while. Cˆnd acesta se termin˘, procesul este oprit, chiar dac˘ a a a a ciclul while din firul de executie din background nu ¸i-a terminat executia. ¸ s ¸

10.2

Sincronizarea

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

216

CURS 10. FIRE DE EXECUTIE ¸

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

10.2. SINCRONIZAREA } Ie¸ire: s Started thread ThreadOne Started thread ThreadTwo Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer:

217

1 2 3 3 4 4 5 5 6 6

Trebuie deci s˘ se realizeze o excludere reciproc˘ a thread–urilor pentru aca a cesul la counter.

10.2.1

Clasa Interlocked

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

218 Thread.CurrentThread.Name, counter); }

CURS 10. FIRE DE EXECUTIE ¸

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

1 2 3 4 5 6 7 8

10.2.2

Instructiunea lock ¸

Exist˘ situatii cˆnd vrem s˘ bloc˘m alte variabile decˆt cele de tip int. a ¸ a a a a Un lock marcheaz˘ o sectiune critic˘ a codului, producˆnd astfel sincronizare a ¸ a a pentru un obiect. La utilizare, se specific˘ un obiect pentru care se stabile¸te a s un lock, dup˘ care o instrutiune sau un grup de instructiuni. Lock–ul este a ¸ ¸ ˆ aturat la sfˆr¸itul instructiunii/blocului de instrutiuni. Sintaxa este: ınl˘ as ¸ ¸ lock(expresie) { instructiuni } Exemplu: metoda Incrementer() se va modifica dup˘ cum urmeaz˘: a a public void Incrementer( ) { try { while (counter < 1000) { lock (this) {

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

219

10.2.3

Clasa Monitor

Clasa Monitor contine metode pentru a controla sincronizarea firelor de ¸ executie, permitˆnd declararea unei zone critice ˆ care la un moment dat ¸ ¸a ın doar un thread trebuie s˘ opereze. a Atunci cˆnd se dore¸te s˘ se ˆ a s a ınceap˘ sincronizarea, se va apela metoda a Enter, dˆnd obiectul pentru care se va face blocarea: a Monitor.Enter( obiect ); Dac˘ monitorul este nedisponibil, atunci ˆ a ınseamn˘ c˘ un alt thread este a a ˆ ıntr–o regiune critic˘ a obiectului respectiv. Se mai poate folosi de asemenea a metoda Wait(), care elibereaz˘ monitorul, dar blocheaz˘ threadul, informˆnd a a a CLR c˘ atunci cˆnd monitorul devine din nou liber, thread–ul curent ar vrea a a s˘ ˆsi continue executia (este ad˘ugat ˆ a ı¸ ¸ a ıntr–o coad˘ de a¸teptare format˘ din a s a fire de executie blocate pe obiect). Terminarea zonei critice se face folosind ¸ metoda Exit() a clasei Monitor. Metoda Pulse() semnaleaz˘ c˘ a avut loc a a o schimbare de stare, ˆ urma c˘reia este posibil ca un alt fir de executie ın a ¸ care a¸teapt˘ va putea s˘ fie continuat (ordinea de selectare a firelor ce vor s a a fi executate fiind ordinea introducerii ˆ coada de a¸teptare). ˆ ın s Inrudit˘ este a metoda PulseAll care anunt˘ toate obiectele blocate pe un anumit obiect de ¸a schimbarea de stare. S˘ presupunem c˘ avem o clas˘ MessageBoard unde fire individuale pot a a a citi ¸i scrie mesaje. Vom sincroniza accesul la aceast˘ clas˘ astfel ˆ at doar s a a ıncˆ un thread s˘ poat˘ actiona la un moment dat. Clasa MessageBoard va avea a a ¸ o metod˘ Reader() ¸i una Writer(). a s

220

CURS 10. FIRE DE EXECUTIE ¸

Metoda Reader() determin˘ dac˘ string–ul message contine vreun mesaj a a ¸ valabil, iar metoda Writer() va scrie ˆ acest string. Dac˘ nu sunt mesaje ın a ˆ timpul citirii, thread–ul Reader() va intra ˆ stare de a¸teptare folosind ın ın s Wait() pˆn˘ cˆnd metoda Writer() scrie un mesaj ¸i transmite un mesaj via a a a s Pulse() pentru a trezi alte thread–uri. 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¸irea este: s ReadrThread: no messages ReaderThread: waiting... WriterThread: Done writing message... ReaderThread: Greetings Caroline and Marianne! Dup˘ cum se vede mai sus, thread–ul reader este pornit primul, el blocheaz˘ a a clasa obeictul de tip MessageBoard, ceea ce ˆ ınseamn˘ c˘ are acces exclusiv la a a variabila message. Dar deoarece message nu contine nici un mesaj, thread-ul ¸ reader elibereaz˘ obiectul pe care l–a blocat mai ˆ a ınainte prin apelul Wait(). Thread–ul writer va putea acum s˘ blocheze obiectul pentru a scrie mesajul. a Apoi el cheam˘ metoda Pulse() pentru a semnala firului reader c˘ a ap˘rut a a a o schimbare de stare a obiectului indicat wde this.

222

CURS 10. FIRE DE EXECUTIE ¸

Curs 11 ASP.NET
ASP.NET (actualmente versiunea 1.1) permite crearea de aplicatii Web ¸ folosind platforma .NET Framework; este una din cele 3 tipuri de aplicatii ¸ creabile sub aceast˘ platform˘, al˘turi de Windows Forms ¸i Web Services. a a a s Este destina a ˆ ınlocui vechiul ASP (Active Server Pages), fiind obiectual ¸i integrat cu toate elementele constitutive ale lui .NET Framework: CLR, s BCL, CTS, FCL, etc. Se poate rula pe servere de aplicatii IIS (versiuni ¸ 5, 6), pe serverul open-source Cassini ¸i de asemenea poate fi integrat cu s Apache (folosind Cassini). Rezultatele cele mai bune se obtin totu¸i pe IIS ¸ s 6.0 (integrat ˆ Windows 2003 Server). ın Enunt˘m mai jos o list˘ neexhaustiv˘ a tr˘s˘turilor ASP.NET: ¸a a a aa • unul din marile avantaje ale lui ASP.NET fat˘ de ASP este faptul c˘ ¸a a codul este p˘strat compilat (sub form˘ de cod .NET PE, executat de a a c˘tre CLR) ˆ loc ca pagina s˘ fie interpretat˘ la fiecare rulare; a ın a a • modul de realizare este orientat pe obiecte, o aplicatie fiind compus˘ din ¸ a dou˘ p˘rti: pagina afi¸at˘ ˆ browser ¸i pagina de “code-behind”, care a a¸ s a ın s este o clas˘; acest code-behind are posibilitatea de accesare a bazelor a de date sau chiar a codului unmanaged (COM-uri ¸i DLL-uri anterior s existente); ˆ plus, se realizeaz˘ separarea p˘rtii de interfat˘ utilizator ın a a¸ ¸a de ceea ce este executat pe server; • pune la dispozitie un mod de tratare a erorilor unificat (tratarea de ¸ exceptii) ¸i folose¸te controale Web predefinite care se pot executa pe ¸ s s server sau pe client; • are un mecanism ˆ ıncorporat care permite p˘strarea st˘rii paginii (chiar a a dac˘ prototlul de comunicare HTTP este f˘r˘ stare, stateless); a aa 223

224

CURS 11. ASP.NET

• ASP.NET permite includerea de mai multe funtionalit˘¸i pe controalele ¸ at ASP.NET iar managementul st˘rii lor este mult ˆ a ımbun˘t˘¸it a at • permite programarea bazat˘ pe evenimente, cu diferente minime fat˘ a ¸ ¸a de programarea aplicatiilor windows; ¸ • este senzitiv la browserul pentru care se face trimiterea codului HTML, alegˆnd automat codul optimizat pentru o colectie de browsere a ¸ • erorile grave ap˘rute ˆ aplicatie ASP.NET nu vor duce la oprirea a ın ¸ serverului Web (dar pot duce la imposibilitatea de a mai executa pagini ASP.NET pˆn˘ la repornirea serverului) a a • permite scrierea codului care se va executa pe server ˆ orice limbaj ın .NET: C#, VB.NET, etc

11.1

Anatomia unei pagini ASP.NET

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

11.1.1

Ad˘ugarea unui control Web a

ˆ Intr-o pagin˘ Web se poate ad˘uga un control pe pagin˘ care s˘ fie generat a a a a de c˘tre serverul IIS ˆ mod adecvat ¸i care poate fi accesat programatic prin a ın s cod. Astfel, ˆ pagina de mai sus se adaug˘ astfel ˆ at s˘ devin˘: ın a ıncˆ a a <html> <head> </head>

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

225

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

11.1.2

Ad˘ugarea scriptului inline a

Vom ad˘uga la pagina anterioar˘ un buton a c˘rui ap˘sare va duce la a a a a schimbarea textului etichetei anterioare; aceast˘ schimbare va fi f˘cut˘ pe a a a server prin apelul unei metode scrise ˆ C#: ın <html> <head> </head> <body> <form method="post" runat="server"> <asp:Label id=lblHelloWorld text="Hello World" runat="Server"/> <br>

226

CURS 11. ASP.NET

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

11.1.3

Code-behind

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

11.1. ANATOMIA UNEI PAGINI ASP.NET

227

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

228

CURS 11. ASP.NET

11.2

Clasa Page

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

11.2.1

Evenimentul Init

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

11.2.2

Evenimentul Load

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

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

11.2.3

Evenimentul Unload

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

11.3

Crearea unei pagini ASP.NET folosind Visual Studio.NET

De¸i se poate scrie ASP.NET ¸i “de mˆn˘”, este de preferat folosirea s s a a VS.NET pentru c˘ permite crearea simpl˘ a fi¸ierelor aspx ¸i a celor de codea a s s behind. ˆ plus, se creeaz˘ ni¸te fi¸iere de set˘ri care ar fi bine s˘ se g˘seasc˘ In a s s a a a a ˆ folderul aplicatiei. ın ¸ Fi¸ierele care se creeaz˘ automat sunt Web.config, Global.asax, Global.asax.cs. s a Ultimul este util pentru cazul ˆ care se dore¸te procesarea evenimentelor de ın s pornire a aplicatiei sau de creare a sesiunii utilizator. ¸ Fi¸ierul Web.config contine set˘ri (de securitate, legate de sesiune) specis ¸ a fice aplicatiei. ¸ De asemenea s-a creat ¸i un web form (alt nume pentru pagin˘ ASP.NET) s a numit implicit Webform1.aspx; din partea de Solution Explorer se poate modifica numele lui cu unul adecvat, de exemplu HelloWorld.aspx (se folose¸te s aceast˘ denumire ˆ cele ce urmeaz˘). a ın a Pe prima linie a fi¸ierului HelloWorld.aspx apare: s <%@ Page language="c#" Codebehind="HelloWorld.aspx.cs" AutoEventWireup="false" Inherits="HelloWorld.WebForm1"%> Pentru directiva Page s-au specificat urm˘toarele atribute: a • language cu valoarea c#; este limbajul ˆ care se scrie ˆ toate blocurile ın ın de cod de tip server; • Codebehind specific˘ numele fi¸ierului surs˘; acesta se creeaz˘ automat a s a a de c˘tre VS.NET; a

230

CURS 11. ASP.NET

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

11.4. CONTROALE SERVER { }

231

atunci cˆnd butonul este ap˘sat se face o submitere a formularului c˘tre a a a server ¸i codul continut ˆ metoda de mai sus va fi executat. Codul va fi: s ¸ ın private void Button1_Click(object sender, System.EventArgs e) { lblHelloWorld.Text = "Text schimbat"; } La ap˘sarea butonului textul din componenta lblHelloWorld se va schimba, a iar pagina nou˘ va fi trimis˘ c˘tre browser. Reˆ a a a ımprosp˘t˘ri succesive ale aa paginii (refresh) vor duce la afi¸area aceluia¸i “Text schimbat”, deoarece s s viewstate-ul contine valoarea modificat˘ de ap˘sarea butonului. ¸ a a

11.4
11.4.1

Controale server
Postback

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

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

CURS 11. ASP.NET

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

11.5. CONTROALE WEB

233

11.4.2

Data Binding

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

11.5

Controale Web

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

234

CURS 11. ASP.NET

etc. Clasa WebControl contine propriet˘¸i de stil precum bacgkround, height, ¸ at width.

11.5.1

Label

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

11.5.2

Button

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

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

235

unde obiectul e are propriet˘¸ile CommandName ¸i CommandArgument care at s vor da valorile setate pentru controale.

11.5.3

LinkButton

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

11.5.4

TextBox

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

11.5.5

CheckBox

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

11.5.6

RadioButton

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

11.5.7

DropDownList

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

236

CURS 11. ASP.NET

Curs 12 ASP.NET (2)
12.1 Controale de validare

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

238

CURS 12. ASP.NET (2)

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

12.1.1

RequiredFieldValidator

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

12.1.2

RegularExpressionValidator

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

12.1.3

RangeValidator

Verific˘ dac˘ valoarea dintr-o intrare este cuprins˘ ˆ a a a ıntre un minim ¸i un s maxim. Pentru continut ¸i extreme tipul poate fi String, Integer, Double, ¸ s Date ¸i Currency, specificat ˆ proprietatea Type a controlului. Valorile s ın minime ¸i maxime admise se dau ˆ propriet˘¸ile MinimumValue ¸i Maxis ın at s mumValue.

12.1.4

CompareValidator

Se folose¸te pentru a compara continutul unui control cu continutul als ¸ ¸ tui control sau cu o valoare fix˘, predefinit˘. Dac˘ se dore¸te compararea cu a a a s
1

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

12.1. CONTROALE DE VALIDARE

239

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

12.1.5

CustomValidator

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

240

CURS 12. ASP.NET (2)

Pentru a se face aceea¸i verificare ¸i pe client, se poate scrie codul urm˘tor s s a la sfˆr¸itul paginii aspx: as <script language="JavaScript"> function vldUserIDCstm_ClientValidate(source, args) { if (args.Value = "JoeHealy" || args.Value = "SpikePierson" || args.Value = "AndyJohnston") { args.IsValid=false; } else { args.IsValid=true; } } </script> Numele functiei JavaScript este setat ˆ proprietatea ClientValidationFunc¸ ın tion.

12.1.6

ValidationSummary

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

12.2

Comunicarea cu browserul

Pentru trimiterea de date c˘tre client se poate folosi pe parte de server a obiectul predefinit Response de tip HttpResponse. De¸i ASP.NET pune la s

12.2. COMUNICAREA CU BROWSERUL

241

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

12.2.1

Generarea programatic˘ continutului a ¸

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

12.2.2

Redirectarea

Se folose¸te pentru cazul ˆ care pagina care se trimite clientului nu este s ın cea curent˘, ci o alta. De exemplu, pentru cazul ˆ care se dore¸te redirectarea a ın s utilizatorului c˘ter o pagin˘ de login. Redirectarea trebuie f˘cut˘ ˆ a a a a ınainte ca vreun header s˘ fie trimis c˘tre client. a a Exemplu: Response.Redirect("http://mysite.com/myBusiness"); Aceast˘ redirectare are loc de pe server, f˘r˘ a se trimite cod c˘tre browser a aa a care s˘ ˆ determine pe acesta s˘ cear˘ o alt˘ resurs˘. a ıl a a a a

242

CURS 12. ASP.NET (2)

12.3

Cookies

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

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

243

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

12.4

Fi¸ierul de configurare al aplicatiei Web s ¸

Diverse set˘ri ale aplicatiei pot fi mentinute ˆ a ¸ ¸ ıntr-un fi¸ier de tip XML nus mit Web.config. Structura ¸i modul de accesare a continutului acestuia este s ¸ asem˘n˘toare cu cea prezentat˘ ˆ sectiunea 8.4.4. Deosebit este ˆ a fapa a a ın ¸ ıns˘ tul pentru o aplicatie Web aceast˘ configurare respect˘ o anumit˘ ierarhie. ¸ a a a La r˘d˘cina acestei ierarhii se afl˘ fi¸ierul machine.config aflat ˆ dioreca a a s ın torul %windir%\Microsoft.NET\Framework\Version\CONFIG. Set˘rile de a aici pot fi suprascrise de set˘ri aflate ˆ directoarele Web.config unei aplicatii a ın ¸ ASP.NET.

12.5

Fi¸ierul global.asax s

Este un fi¸ier optional structurat XML care prezint˘ set˘ri relative la s ¸ a a aplicatia ASP.NET (mai exact, pentru obiectul reprezentˆnd aplicatia). Fi¸ierul ¸ a ¸ s se g˘se¸te ˆ r˘d˘cina aplicatiei, este compilat ca o clas˘ care este derivat˘ a s ın a a ¸ a a din HttpApplication, fiind recompilat˘ automat la fiecare modificare a sa. a Metodele care se pot implementa ˆ aceast˘ clas˘ sunt: ın a a • Application Start - apelat˘ atunci cˆnd un client cere pentru prima a a oar˘ o pagin˘ de la aceast˘ aplicatie; folosit˘ pentru initializarea unor a a a ¸ a ¸ resurse (grupuri de conexiuni, valori din regi¸tri, etc care sunt accesate s de c˘tre p˘rti din aplicatie). a a¸ ¸ • Session Start - ori de cˆte ori se porne¸te o sesiune a s • Application BeginRequest - apelat ori de cˆte ori un client cere o pagin˘ a a oarecare de la server; s-ar putea folosi la contorizarea num˘rului de a accese la pagin˘ a

244

CURS 12. ASP.NET (2)

• Application EndRequest - apelat la sfˆr¸itul oric˘rei cereri c˘tre server as a a • Application AuthenticateRequest - apelat˘ la fiecare cerere de autentifia care a unui utilizator • Application Error - apelat˘ ori de cˆte ori apare o eroare care nu a fost a a procesat˘ a • Session End - la terminarea unei sesiuni prin expirare • Application End - apare la o resetare a serverului sau atunci cˆnd se a recompileaz˘ aplicatia ASP.NET. a ¸

12.6

Managementul sesiunii

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

12.7. VIEWSTATE

245

12.7

ViewState

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

12.7.1

Application

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

12.8

Alte puncte de interes ˆ aplicatii ASP.NET ın ¸

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

246

CURS 12. ASP.NET (2)

• crearea de aplicatii web pentru dispozitive mobile; de¸i la ora actual˘ ¸ s a piata de dispozitive mobile este relativ redus˘, exist˘ totu¸i un seg¸ a a s ment bine definit care cere astfel de aplicatii. Design-ul specific ¸i ¸ s capabilit˘¸ile unui astfel de browser pot ridica probleme noi; at • politici de caching - pentru optimizarea accesului la resurse; • autentificarea - diverse forme de securitate: formulare, .NET Passport; • folosirea protocoalelor de comunicatie securizate (SSL) ¸ • folosirea serviciilor Web - servicii care se pot accesa ˆ Internet; bazate ın pe XML ¸i protocoale de comunicatie larg r˘spˆndite, un serviciu Web s ¸ a a este independent de platform˘. a

Curs 13 Servicii Web
13.1 Generalit˘¸i at

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

13.1.1

Componentele unui WS

Principalele blocuri componente ale unui serviciu Web sunt redate ˆ ın figura 13.1. 247

248

CURS 13. SERVICII WEB

• Descoperirea - dac˘ se dore¸te conectarea la un serviciu Web, este necea s sar˘ o modalitate de a determina locatia serviciului - proces numit a ¸ descoperire. Descoperirea poate fi realizat˘ prin intemediul unor direca toare centralizate sau prin alte metode ad-hoc. • Descrierea - dup˘ ce s-a rezolvat problema de la punctul precedent, a clientul va avea nevoie de o descriere a modului de interactiune cu SW. ¸ Descrierea reprezint˘ metadate structurate despre interfata ce urmeaz˘ a ¸ a a fi “consumat˘”, precum ¸i documentatie scris˘ despre SW continˆnd a s ¸ a ¸ a detalii sau exemple de utilizare. • Formatul mesajelor - necesar a fi precizat pentru a se putea codifica ˆ ıntr-un mod convenabil toat˘ conversatia ˆ a ¸ ıntre client ¸i SW. Acest lucru s permite abstractizarea protocoalelor de comunicare ¸i concentrarea pe s logica problemei. • Codificarea - datele trimise ˆ ıntre cei 2 parteneri de conversatie trebuie ¸ codificate ˆ ıntr-un mod care s˘ permit˘ procesarea lor de c˘tre orice a a a limbaj. XML este alegerea potrivit˘ pentru a¸a ceva, fiind structurat a s ¸i de tip text. s • Transportul - modul prin care datele serializate care compun un mesaj sunt trimise de la un cap˘t la cel˘lalt. S-a cerut explicit functionarea a a ¸ unui serviciu web peste protocoale “prietenoase”, care s˘ nu necesite a set˘ri speciale de securitate. a

13.2
13.2.1

Discutii asupra blocurilor componente ¸
Protocolul de transport

O aplicatie bazat˘ pe SW poate s˘ fie folosit˘ atˆt ˆ LAN cˆt ¸i ˆ ¸ a a a a ın a s ıntr-o retea de mare ˆ ¸ ıntindere. Protocoale precum HTTP sau SMTP sunt testate din punct de vedere al serviciilor pe care ˆ ofer˘. Faptul c˘ HTTP este f˘r˘ ıl a a aa stare permite crearea unor aplicatii care s˘ permit˘ continuarea servirii unor ¸ a a clienti chiar dac˘ serverul initial a c˘zut (sarcina sa fiind preluat˘ de alte ¸ a ¸ a a servere). ˆ plus, SMTP ¸i HTTP sunt portocoale care nu cer o configurare In s special˘ a firewall-urilor care delimiteaz˘ o retea. a a ¸

13.2.2

Schema de codificare

XML reprezint˘ alegerea natural˘ pentru codificarea mesajelor dintre a a client ¸i server, deoarece este structurat (f˘cˆnd procesarea sa u¸oar˘) ¸i s a a s a s

13.2. DISCUTII ASUPRA BLOCURILOR COMPONENTE ¸

249

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

13.2.3

Conventia de formatare ¸

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

250

CURS 13. SERVICII WEB

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

13.2.4

Mecanismul de descriere

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

13.2.5

Mecanisme de descoperire

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

Engl: envelope

13.3. CREAREA UNUI SERVICIU WEB SIMPLU

251

oric˘rui client. UDDI este un director centralizat care poate fi folosit pentru a anuntarea de SW, pentru care se poate face c˘utarea dup˘ criterii: cate¸ a a goria, tipul de SW, compania, etc. De asemenea se poate folosi ¸i DISCO, s un format XML proprietar Microsoft, avˆnd ca principal consumator Visual a Studio .NET.

13.3

Crearea unui serviciu Web simplu

Mai jos exemplific˘m crearea unui SW ¸i a unor clienti care s˘ interactioneze a s ¸ a ¸ cu acestea.

13.3.1

Adunarea a dou˘ numere a

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

252

CURS 13. SERVICII WEB

13.3.2

Validarea unui num˘r de card de credit a

S˘ presupunem c˘ avem de implementat un site de tip B2C (de exemplu a a vˆnzare de c˘rti). Atunci cˆnd se efectueaz˘ plata prin card de credit se a a¸ a a cere de la utilizator num˘rul de card ¸i data expir˘rii. Se presupune c˘ a s a a aceast˘ validare nu este f˘cut˘ de c˘tre aplicatie, ci pe baza unui serviciu a a a a ¸ Web specializat. Vom exemplifica cele ce urmeaz˘ prin Visual Studio, de¸i a s se poate folosi ¸i alt˘ modalitate. s a Pentru Web Service se poate proceda astfel: File->New->Project->se alege ASP.NET Web Service->se completeaz˘ numele PlataWS. Din Solution a Explorer se modific˘ numele fi¸ierului ˆ PlataWS.asmx. ˆ partea de cod se a s ın In scrie metoda: [WebMethod] public bool ValidateCardNo(String cardNo, String date) { return int.Parse(cardNo) %2 == 0; } (o validare simpl˘, pe baza parit˘¸ii num˘rului de card). a at a Pentru partea de site se poate crea o form˘ windows de tipul celei din a figura 13.2, ˆ care s˘ se poat˘ introduce num˘rul de card ¸i data expir˘rii. ın a a a s a

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

13.3. CREAREA UNUI SERVICIU WEB SIMPLU

253

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

13.3.3

SW pentru transmitere de continut binar ¸

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

Cu toate c˘ FTP este mai potrivit pentru a¸a ceva a s

254

CURS 13. SERVICII WEB

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

13.4

SOAP

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

13.4. SOAP

255

• interoperabilitate ˆ ıntre platforme: SOAP poate fi folosit de c˘tre PCa uri, dispozitive mobile, mainframe-uri, etc Un mesaj SOAP const˘ ˆ a ıntr-un plic compus ˆ principal din header-e ¸i ın s corp.

13.4.1

Header-e

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

256 Atributul actor

CURS 13. SERVICII WEB

Un mesaj SOAP paote fi rutat prin mai multi intermediari. Se folose¸te ¸ s un atribut actor care specific˘ adresa intermediarului. Acest intermediar a trebuie s˘ elimine informatia de intermediar care ˆ prive¸te, dar poate s˘ a ¸ ıl s a adauge la rˆndul lui alt˘ informatie de acest tip. a a ¸

13.4.2

Elementul body

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

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

257

Exist˘ suport pentru transmiterea tipurilor de date definite de utilizator, a a array-urilor, definirea parametrilor ca fiind de tip valoare/referint˘/ie¸ire, a ¸a s grafurilor de obiecte, etc.

258

CURS 13. SERVICII WEB

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

259

Sign up to vote on this title
UsefulNot useful