Sunteți pe pagina 1din 329

Limbajul C#

PhD. Lucian Sasu

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 Common Intermediate Language 1.3.2 Common Language Specication 1.3.3 Common Language Runtime . . . 1.3.4 Common Type System . . . . . . 1.3.5 Metadate . . . . . . . . . . . . . 1.3.6 Assemblies . . . . . . . . . . . . . 1.3.7 Assembly cache . . . . . . . . . . 1.3.8 Garbage collection . . . . . . . . 1.4 Tr as aturi ale platformei .NET . . . . . . 2 Tipuri predenite, tablouri, string-uri 2.1 Vedere general a asupra limbajului C# 2.2 Tipuri de date . . . . . . . . . . . . . . 2.2.1 Tipuri predenite . . . . . . . . 2.2.2 Tipuri valoare . . . . . . . . . . 2.2.3 Tipul enumerare . . . . . . . . 2.3 Tablouri . . . . . . . . . . . . . . . . . 2.3.1 Tablouri unidimensionale . . . . 2.3.2 Tablouri multidimensionale . . 2.4 S iruri de caractere . . . . . . . . . . . 2.4.1 Expresii regulate . . . . . . . . 3 Clase, instruc tiuni, spa tii de 3.1 Clase vedere general a . . 3.2 Transmiterea de parametri 3.3 Conversii . . . . . . . . . . 3.3.1 Conversii implicite nume . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 13 14 14 14 15 16 17 18 18 19 19 23 23 25 25 27 32 38 38 39 43 45 47 47 51 57 57

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

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

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

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

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

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

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

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

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

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

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

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

4 3.3.2 Conversiile implicite ale expresiilor constante 3.3.3 Conversii explicite . . . . . . . . . . . . . . 3.3.4 Boxing si unboxing . . . . . . . . . . . . . . 3.4 Declara tii de variabile si constante . . . . . . . . . . 3.5 Instruc tiuni C# . . . . . . . . . . . . . . . . . . . . 3.5.1 Declara tii de etichete . . . . . . . . . . . . . 3.5.2 Instruc tiuni de selec tie . . . . . . . . . . . . 3.5.3 Instruc tiuni de ciclare . . . . . . . . . . . . 3.5.4 Instruc tiuni de salt . . . . . . . . . . . . . . 3.5.5 Instruc tiunile try, throw, catch, nally . . . 3.5.6 Instruc tiunile checked si unchecked . . . . . 3.5.7 Instruc tiunea lock . . . . . . . . . . . . . . . 3.5.8 Instruc tiunea using . . . . . . . . . . . . . . 3.6 Spa tii de nume . . . . . . . . . . . . . . . . . . . . 3.6.1 Declara tii de spa tii de nume . . . . . . . . . 3.6.2 Directiva using . . . . . . . . . . . . . . . . 3.7 Declararea unei clase . . . . . . . . . . . . . . . . . 3.8 Membrii unei clase . . . . . . . . . . . . . . . . . . 3.9 Constructori de instan ta . . . . . . . . . . . . . . . 3.10 C ampuri . . . . . . . . . . . . . . . . . . . . . . . . 3.10.1 C ampuri instan te . . . . . . . . . . . . . . . 3.10.2 C ampuri statice . . . . . . . . . . . . . . . . 3.10.3 C ampuri readonly . . . . . . . . . . . . . . . 3.10.4 C ampuri volatile . . . . . . . . . . . . . . . 3.10.5 Ini tializarea c ampurilor . . . . . . . . . . . . 3.11 Constante . . . . . . . . . . . . . . . . . . . . . . . 3.12 Metode . . . . . . . . . . . . . . . . . . . . . . . . . 3.12.1 Metode statice si nestatice . . . . . . . . . . 3.12.2 Metode externe . . . . . . . . . . . . . . . . 4 Clase (continuare) 4.1 Propriet a ti . . . . . . . . . . . . 4.2 Indexatori . . . . . . . . . . . . 4.3 Operatori . . . . . . . . . . . . 4.3.1 Operatori unari . . . . . 4.3.2 Operatori binari . . . . . 4.3.3 Operatori de conversie . 4.3.4 Exemplu: clasa Fraction 4.4 Constructor static . . . . . . . . 4.5 Clase imbricate . . . . . . . . . 4.6 Destructori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 59 62 64 64 64 65 66 68 68 69 69 69 70 71 72 76 77 78 78 79 79 80 80 81 81 82 83 83 85 85 91 97 97 99 99 101 103 104 107

CUPRINS 4.7 4.8 Clase statice . . . . . . . . . . . . . . . . . . . . Specializarea si generalizarea . . . . . . . . . . . 4.8.1 Specicarea mo stenirii . . . . . . . . . . 4.8.2 Apelul constructorilor din clasa de baz a 4.8.3 Operatorii is si as . . . . . . . . . . . . Clase sealed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 109 111 111 112 113 114

4.9

5 Clase, structuri, interfe te, delega ti 115 5.1 Polimorsmul . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 5.1.1 Polimorsmul parametric . . . . . . . . . . . . . . . . . 115 5.1.2 Polimorsmul adhoc . . . . . . . . . . . . . . . . . . . 115 5.1.3 Polimorsmul de mo stenire . . . . . . . . . . . . . . . . 116 5.1.4 Virtual si override . . . . . . . . . . . . . . . . . . . . 117 5.1.5 Modicatorul new pentru metode . . . . . . . . . . . . 118 5.1.6 Metode sealed . . . . . . . . . . . . . . . . . . . . . . . 121 5.1.7 Exemplu folosind virtual, new, override, sealed . . . . . 122 5.2 Clase si metode abstracte . . . . . . . . . . . . . . . . . . . . 124 5.3 Tipuri par tiale . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 5.4 Structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 5.4.1 Structuri sau clase? . . . . . . . . . . . . . . . . . . . . 131 5.5 Interfe te . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 5.5.1 Clase abstracte sau interfe te? . . . . . . . . . . . . . . 137 5.6 Tipul delegat . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 5.6.1 Utilizarea delega tilor pentru a specica metode la runtime139 5.6.2 Delega ti statici . . . . . . . . . . . . . . . . . . . . . . 142 5.6.3 Multicasting . . . . . . . . . . . . . . . . . . . . . . . . 143 6 Metode anonime. Evenimente. Excep tii. 6.1 Metode anonime . . . . . . . . . . . . . . . . 6.2 Evenimente . . . . . . . . . . . . . . . . . . . 6.2.1 Publicarea si subscrierea . . . . . . . . 6.2.2 Evenimente si delega ti . . . . . . . . . 6.2.3 Comentarii . . . . . . . . . . . . . . . 6.3 Tratarea excep tiilor . . . . . . . . . . . . . . . 6.3.1 Tipul Exception . . . . . . . . . . . . . 6.3.2 Aruncarea si prinderea excep tiilor . . . 6.3.3 Re ncercarea codului . . . . . . . . . . 6.3.4 Compararea tehnicilor de manipulare a 6.3.5 Sugestie pentru lucrul cu excep tiile . . 147 . 147 . 149 . 150 . 150 . 156 . 157 . 157 . 158 . 169 . 171 . 172

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . erorilor . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

CUPRINS 173 . 173 . 176 . 177 . 178 . 179 . 179 . 182 . 187 . 188 . 189 . 190 . 191 . 192 . 192 . 192 . 193 . 193 . 194 . 196

7 Colec tii. Clase generice. 7.1 Colec tii . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.1.1 Iteratori pentru colec tii . . . . . . . . . . . . . . . 7.1.2 Colec tii de tip list a . . . . . . . . . . . . . . . . . 7.1.3 Colec tii de tip dic tionar . . . . . . . . . . . . . . 7.2 Crearea unei colec tii . . . . . . . . . . . . . . . . . . . . 7.2.1 Colec tie iterabil a (stil vechi) . . . . . . . . . . . . 7.2.2 Colec tie iterabil a (stil nou) . . . . . . . . . . . . . 7.3 Clase generice . . . . . . . . . . . . . . . . . . . . . . . . 7.3.1 Metode generice . . . . . . . . . . . . . . . . . . . 7.3.2 Tipuri generice . . . . . . . . . . . . . . . . . . . 7.3.3 Constr angeri asupra parametrilor de genericitate . 7.3.4 Interfe te si delega ti generici . . . . . . . . . . . . 7.4 Colec tii generice . . . . . . . . . . . . . . . . . . . . . . . 7.4.1 Probleme cu colec tiile de obiecte . . . . . . . . . 7.4.2 Colec tii generice . . . . . . . . . . . . . . . . . . . 7.5 Elemente specice C# 3.0 . . . . . . . . . . . . . . . . . 7.5.1 Propriet a ti implementate automat . . . . . . . . . 7.5.2 Ini tializatori de obiecte . . . . . . . . . . . . . . . 7.5.3 Ini tializatori de colec tii . . . . . . . . . . . . . . .

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

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

8 ADO.NET 199 8.1 Ce reprezint a ADO.NET? . . . . . . . . . . . . . . . . . . . . 199 8.2 Furnizori de date n ADO.NET . . . . . . . . . . . . . . . . . 200 8.3 Componentele unui furnizor de date . . . . . . . . . . . . . . . 200 8.3.1 Clasele Connection . . . . . . . . . . . . . . . . . . . . 201 8.3.2 Clasele Command . . . . . . . . . . . . . . . . . . . . . 201 8.3.3 Clasele DataReader . . . . . . . . . . . . . . . . . . . . 202 8.3.4 Clasele DataAdapter . . . . . . . . . . . . . . . . . . . 202 8.3.5 Clasa DataSet . . . . . . . . . . . . . . . . . . . . . . . 202 8.4 Obiecte Connection . . . . . . . . . . . . . . . . . . . . . . . . 202 8.4.1 Propriet a ti . . . . . . . . . . . . . . . . . . . . . . . . . 203 8.4.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 205 8.4.3 Evenimente . . . . . . . . . . . . . . . . . . . . . . . . 205 8.4.4 Stocarea stringului de conexiune n sier de congurare 205 8.4.5 Gruparea conexiunilor . . . . . . . . . . . . . . . . . . 207 8.4.6 Mod de lucru cu conexiunile . . . . . . . . . . . . . . . 207 8.5 Obiecte Command . . . . . . . . . . . . . . . . . . . . . . . . 208 8.5.1 Propriet a ti . . . . . . . . . . . . . . . . . . . . . . . . . 208 8.5.2 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . 209 8.5.3 Utilizarea unei comenzi cu o procedur a stocat a . . . . . 212

CUPRINS 8.5.4 Folosirea comenzilor parametrizate . Obiecte DataReader . . . . . . . . . . . . . 8.6.1 Propriet a ti . . . . . . . . . . . . . . . 8.6.2 Metode . . . . . . . . . . . . . . . . 8.6.3 Crearea si utilizarea unui DataReader 8.6.4 Utilizarea de seturi de date multiple . 8.6.5 Seturi de date cu tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . baza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 212 214 215 215 216 217 217 219 219 220 221 221 222 222 225 225 226 227 229 231 233 235 235 237 237 238 239 240 241 242 242 243 245 245 248 250 251 252 253

8.6

9 ADO.NET (2) 9.1 Obiecte DataAdapter . . . . . . . . . 9.1.1 Metode . . . . . . . . . . . . 9.1.2 Propriet a ti . . . . . . . . . . . 9.2 Clasa DataSet . . . . . . . . . . . . . 9.2.1 Con tinut . . . . . . . . . . . . 9.2.2 Clasa DataTable . . . . . . . 9.2.3 Rela tii ntre tabele . . . . . . 9.2.4 Popularea unui DataSet . . . 9.2.5 Clasa DataTableReader . . . . 9.2.6 Propagarea modic arilor c atre 9.3 Tranzac tii n ADO.NET . . . . . . . 9.4 Lucrul generic cu furnizori de date . 9.5 Tipuri nulabile . . . . . . . . . . . . 10 LINQ (I) 10.1 Generalit a ti . . . . . . . . . . . . 10.2 Motiva tie . . . . . . . . . . . . . 10.2.1 Codul clasic ADO.NET . 10.2.2 Nepotrivirea de paradigme 10.3 LINQ to Objects: exemplicare . 10.4 Mecanisme utilizate de LINQ . . 10.4.1 Inferen ta tipului . . . . . . 10.4.2 Tipuri anonime . . . . . . 10.4.3 Metode par tiale . . . . . . 10.4.4 Metode de extensie . . . . 10.4.5 Expresii lambda . . . . . . 10.5 Operatori LINQ . . . . . . . . . . 10.6 LINQ to Objects . . . . . . . . . 10.6.1 Filtarea cu Where . . . . . 10.6.2 Operatorul de proiec tie . . 10.6.3 Operatorul SelectMany . 10.6.4 Jonc tiuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8 10.6.5 Grupare . . . . . . . 10.6.6 Ordonare . . . . . . 10.6.7 Agregare . . . . . . . 10.6.8 Parti tionare . . . . . 10.6.9 Concatenarea . . . . 10.6.10 Referirea de elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . din secven te . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CUPRINS . . . . . . . . . . . . . . . . . . . . . . . . 254 254 256 257 258 258

11 LINQ (II): Linq to SQL 11.1 Ob tinerea unui context de date . . . . . . . . . . . . . . . . 11.2 Ad augarea, modicarea si stergerea de nregistr ari n tabel a 11.2.1 Ad augarea . . . . . . . . . . . . . . . . . . . . . . . . 11.2.2 Modicarea unei nregistr ari . . . . . . . . . . . . . . 11.2.3 S tergerea unei nregistr ari . . . . . . . . . . . . . . . 11.3 Op tiuni de nc arcare a datelor . . . . . . . . . . . . . . . . . 12 Atribute. Fire de execu tie 12.1 Atribute . . . . . . . . . . . . . . . . . . . . . . 12.1.1 Generalit a ti . . . . . . . . . . . . . . . . 12.1.2 Atribute predenite . . . . . . . . . . . . 12.1.3 Exemplicarea altor atribute predenite 12.1.4 Atribute denite de utilizator . . . . . . 12.2 Fire de execu tie . . . . . . . . . . . . . . . . . . 12.3 Managementul threadurilor . . . . . . . . . . . 12.3.1 Pornirea threadurilor . . . . . . . . . . 12.3.2 Metoda Join() . . . . . . . . . . . . . . 12.3.3 Suspendarea relor de execu tie . . . . . 12.3.4 Omor area threadurilor . . . . . . . . . 12.3.5 Sugerarea priorit a tilor relor de execu tie 12.3.6 Fire n fundal si re n prim-plan . . . . 12.4 Sincronizarea . . . . . . . . . . . . . . . . . . . 12.4.1 Clasa Interlocked . . . . . . . . . . . . . 12.4.2 Instruc tiunea lock . . . . . . . . . . . . . 12.4.3 Clasa Monitor . . . . . . . . . . . . . . . 13 Nout a ti n C# 4.0 13.1 Parallel Linq . . . . . . . . . . . . . . . . 13.2 Parametri cu nume si parametri op tionali 13.3 Tipuri de date dinamice . . . . . . . . . 13.4 COM Interop . . . . . . . . . . . . . . . 13.5 Covarian ta si contravarian ta . . . . . . .

261 . 261 . 264 . 264 . 265 . 265 . 266 269 . 269 . 269 . 271 . 273 . 276 . 279 . 279 . 279 . 282 . 282 . 283 . 286 . 287 . 288 . 291 . 292 . 293 297 . 297 . 299 . 301 . 302 . 304

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

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

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

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

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

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

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

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

CUPRINS

14 Fluxuri 307 14.1 Sistemul de siere . . . . . . . . . . . . . . . . . . . . . . . . . 307 14.1.1 Lucrul cu directoarele: clasele Directory si DirectoryInfo 308 14.1.2 Lucrul cu sierele: clasele FileInfo si File . . . . . . . . 310 14.2 Citirea si scrierea datelor . . . . . . . . . . . . . . . . . . . . . 315 14.2.1 Clasa Stream . . . . . . . . . . . . . . . . . . . . . . . 315 14.2.2 Clasa FileStream . . . . . . . . . . . . . . . . . . . . . 317 14.2.3 Clasa MemoryStream . . . . . . . . . . . . . . . . . . . 317 14.2.4 Clasa BueredStream . . . . . . . . . . . . . . . . . . . 318 14.2.5 Clasele BinaryReader si BinaryWriter . . . . . . . . . 319 14.2.6 Clasele TextReader, TextWriter si descendentele lor . . 320 14.3 Operare sincron a si asincron a . . . . . . . . . . . . . . . . . . 322 14.4 Streamuri Web . . . . . . . . . . . . . . . . . . . . . . . . . . 325 14.5 Serializarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 14.5.1 Crearea unui obiect serializabil . . . . . . . . . . . . . 325 14.5.2 Serializarea . . . . . . . . . . . . . . . . . . . . . . . . 326 14.5.3 Deserializarea unui obiect . . . . . . . . . . . . . . . . 326 14.5.4 Date tranziente . . . . . . . . . . . . . . . . . . . . . . 327 14.5.5 Opera tii la deserializare . . . . . . . . . . . . . . . . . 327 Bibliograe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

10

CUPRINS

Curs 1 Platforma Microsoft .NET


1.1 Prezentare general a

Platforma .NET 3.5 este un cadru de dezvoltare a softului, sub care se vor realiza, distribui si rula aplica tiile de tip forme Windows, aplica tii WEB si servicii WEB. Ea const a n trei p ar ti principale: Common Language Runtime, clasele specice platformei si ASP.NET. O infrastructur a ajut atoare, Microsoft .NET Compact Framework este un set de interfe te de programare care permite dezvoltatorilor realizarea de aplica tii pentru dispozitive mobile precum telefoane inteligente si PDA-uri1 . .NET Framework constituie un nivel de abstractizare ntre aplica tie si nucleulul sistemului de operare (sau alte programe), pentru a asigura portabilitatea codului; de asemenea integreaz a tehnologii care au fost lansate de catre Microsoft incep and cu mijlocul anilor 90 (COM, DCOM, ActiveX, etc) sau tehnologii actuale (servicii Web, XML). Platforma const a n c ateva grupe de produse: 1. Unelte de dezvoltare - un set de limbaje (C#, Visual Basic .NET, J#, C++/CLI, JScript.NET, Objective-C, Python, Smalltalk, Eiel, Perl, Fortran, Cobol, Lisp, Haskell, Pascal, RPG, etc), un set de medii de dezvoltare (Visual Studio .NET, Visio), infrastructura .NET Framework, o bibliotec a cuprinz atoare de clase pentru crearea serviciilor Web (Web 2 Services) , aplica tiilor Web (Web Forms, ASP.NET MVC) si a aplica tiilor Windows (Windows Forms). 2. Servere specializate - un set de servere Enterprise .NET: SQL Server 2008, Exchange 2007, BizTalk Server 2006, SharePoint, etc, care pun la
1 2

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

11

12

CURS 1. PLATFORMA MICROSOFT .NET dispozi tie func tionalit a ti diverse pentru stocarea bazelor de date, email, 3 aplica tii B2B . 3. Servicii Web - cel mai notabil exemplu este .NET Passport - un mod prin care utilizatorii se pot autentica pe site-urile Web vizitate, folosind un singur nume si o parol a pentru toate. Alt exemplu este MapPoint care permite vizualizarea, editarea si integrarea hartilor. 4. Dispozitive - noi dispozitive nonPC, programabile prin .NET Compact Framework, o versiune redus a a lui .NET Framework: Pocket PC Phone Edition, Smartphone, Tablet PC, Smart Display, XBox, set-top boxes, etc.

Motivul pentru care Microsoft a trecut la dezvoltarea acestei platforme este maturizarea industriei software, accentu anduse urm atoarele direc tii: 1. Aplica tiile distribuite - sunt din ce n ce mai numeroase aplica tiile de tip client / server sau cele pe mai multe nivele (ntier). Tehnologiile distribuite actuale cer de multe ori o mare anitate fa ta de produc ator si prezint a o caren ta acut a a interoper arii cu Web-ul. Viziunea actual a se dep arteaz a de cea de tip client/server c atre una n care calculatoare, dispozitive inteligente si servicii conlucreaz a pentru atingerea scopurilor propuse. Toate acestea se fac deja folosind standarde Internet neproprietare (HTTP, XML, SOAP). 2. Dezvoltarea orientat a pe componente - este de mult timp cerut a simplicarea integr arii componentelor software dezvoltate de diferi ti produc atori. COM (Component Object Model) a realizat acest deziderat, dar dezvoltarea si distribuirea aplica tiilor COM este prea complex a. Microsoft .NET pune la dispozi tie un mod mai simplu de a dezvolta si a distribui componente. 3. Modic ari ale paradigmei Web - de-a lungul timpului sau adus mbun at a tiri tehnologiilor Web pentru a simplica dezvoltarea aplica tiilor. In ultimii ani, dezvoltarea aplica tiilor Web sa mutat de la prezentare (HTML si adiacente) c atre capacitate sporit a de programare (XML si SOAP). 4. Al ti factori de maturizare a industriei software - reprezint a con stientizarea cererilor de interoperabilitate, scalabilitate, disponibilitate; unul din dezideratele .NET este de a oferi toate acestea.
Bussiness to Bussiness - Comer t electronic ntre parteneri de afaceri, diferit a de comer tul electronic ntre client si afacere (Bussiness to Consumer B2C).
3

1.2. ARHITECTURA PLATFORMEI MICROSOFT .NET

13

1.2

Arhitectura platformei Microsoft .NET

Figura 1.1: Arhitectura .NET Figura 1.1 schematizeaz a arhitectura platformei Microsoft .NET. Orice program scris ntr-unul din limbajele .NET este compilat n Common Intermediate Language4 , n concordan ta cu Common Language Specication (CLS). Aceste limbaje sunt sprijinite de o bogat a colec tie de biblioteci de clase, ce pun la dispozi tie facilit a ti pentru dezvoltarea de Web Forms, Windows Forms si Web Services. Comunicarea dintre aplica tii si servicii se face pe baza unor clase de manipulare XML si a datelor, ceea ce sprijin a dezvoltarea aplica tiilor cu arhitectur a n-tier. Base Class Library exist a pentru a asigura func tionalitate de nivel sc azut, precum opera tii de I/O, re de execu tie, lucrul cu siruri de caractere, comunica tie prin re tea, etc. Aceste clase sunt reunite sub numele de .NET Framework Class Library, ce permite dezvoltarea rapid a a aplica tiilor. La baza tuturor se a a cea mai important a component a a lui .NET Framework - Common Language Runtime, care r aspunde de execu tia ec arui program. Evident, nivelul inferior este rezervat sistemului de operare. Trebuie spus c a platforma .NET nu este exclusiv dezvoltat a pentru sistemul
Anterior numit si Microsoft Intermediate Language (MSIL) sau Intermediate Language (IL).
4

14

CURS 1. PLATFORMA MICROSOFT .NET

de operare Microsoft Windows, ci si pentru arome de Unix (FreeBSD sau 5 Linux - a se vedea proiectul Mono ).

1.3
1.3.1

Componente ale lui .NET Framework


Common Intermediate Language

Una din uneltele de care dispune ingineria software este abstractizarea. Deseori vrem s a ferim utilizatorul de detalii, s a punem la dispozi tia altora mecansime sau cuno stin te generale, care s a permit a atingerea scopului, f ar aa necesare cunoa sterea tuturor detaliilor. Dac a interfa ta r am ane neschimbat a, se pot modica toate detaliile interne, f ar a a afecta ac tiunile celorla ti beneciari ai codului. In cazul limbajelor de programare, s-a ajuns treptat la crearea unor nivele de abstractizare a codului rezultat la compilare, precum p-code (cel produs de compilatorul Pascal-P) si bytecode (binecunoscut celor care au lucrat n Java). Bytecode-ul Java, generat prin compilarea unui sier surs a, este cod scris ntrun limbaj intermediar care suport a POO. Bytecod-ul este n acela si timp o abstractizare care permite executarea codului Java, indiferent de platforma tint a, at ata timp c at aceast a platform a are implementat a o ma sin a virtual a Java, capabil a s a traduc a mai departe sierul class n cod nativ. Microsoft a realizat si el propria sa abstractizare de limbaj, aceasta numindu-se Common Intermediate Language. De si exist a mai multe limbaje de programare de nivel nalt (C#, Managed C++, Visual Basic .NET, etc), la compilare toate vor produce cod n acela si limbaj intermediar: Common Intermediate Language. Asem an ator cu bytecod-ul, CIL are tr as aturi OO, precum abstractizarea datelor, mo stenirea, polimorsmul, sau concepte care s-au dovedit a extrem de necesare, precum excep tiile sau evenimentele. De remarcat c a aceast a abstractizare de limbaj permite rularea aplica tiilor independent de platform a (cu aceea si condi tie ca la Java: s a existe o ma sin a virtual a pentru acea platform a).

1.3.2

Common Language Specication

Unul din scopurile .NET este de a sprijini integrarea limbajelor astfel nc at programele, de si scrise n diferite limbaje, pot interopera, folosind din plin mo stenirea, polimorsmul, ncapsularea, excep tiile, etc. Dar limbajele nu sunt identice: de exemplu, unele sunt case sensitive, altele nu. Pentru a se asigura interoperabilitatea codului scris n diferite limbaje, Microsoft
5

www.go-mono.com

1.3. COMPONENTE ALE LUI .NET FRAMEWORK

15

a publicat Common Language Specication (CLS), un subset al lui CTS (Common Type System, vezi 1.3.4), con tin and specica tii de reguli necesare pentru integrarea limbajelor. CLS dene ste un set de reguli pentru compilatoarele .NET, asigur and faptul c a ecare compilator va genera cod care interfereaz a cu platforma (mai exact, cu CLRul vezi mai jos) ntr-un mod independent de limbajul surs a. Obiectele si tipurile create n diferite limbaje pot interac tiona f ar a probleme suplimentare. Combina tia CTS/CLS realizeaz a de fapt interoperarea limbajelor. Concret, se poate ca o clas a scris a n C# s a e mo stenit a de o clas a scris a n Visual Basic care arunc a excep tii ce sunt prinse de cod scris n C++ sau J#.

1.3.3

Common Language Runtime

CLR este de departe cea mai important a component a a lui .NET Framework. Este responsabil a cu managementul si execu tia codului scris n limbaje .NET, aat n format CIL; este foarte similar cu Java Virtual Machine. CLR instan tiaz a obiectele, face veric ari de securitate, depune obiectele n memorie, disponibilizeaz a memoria prin garbage collection. In urma compil arii unei aplica tii poate rezulta un sier cu extensia exe, dar care nu este un executabil portabil Windows, ci un executabil portabil .NET (.NET PE). Acest cod nu este deci un executabil nativ, ci se va rula de c atre CLR, ntocmai cum un sier class este rulat n cadrul JVM. CLR folose ste tehnologia compil arii JIT - o implementare de ma sin a virtual a, n care o metod a sau o func tie, n momentul n care este apelat a pentru prima oar a, este tradus a n cod ma sin a. Codul translatat este depus ntr-un cache, evit and-se astfel recompilarea ulterioar a. Exist a 3 tipuri de compilatoare JIT: 1. Normal JIT - a se vedea descrierea de mai sus. 2. Pre-JIT - compileaz a ntregul cod n cod nativ singur a dat a. In mod normal este folosit la instal ari. 3. Econo-JIT - se folo se ste pe dispozitive cu resurse limitate. Compileaz a codul CIL bit cu bit, eliber and resursele folosite de codul nativ ce este stocat n cache. In esen ta , activitatea unui compilator JIT este destinat a a mbun at a ti performan ta execu tiei, ca alternativ a la compilarea repetat a a aceleia si buc a ti de cod n cazul unor apel ari multiple. Unul din avantajele mecanismului JIT apare n clipa n care codul, o dat a ce a fost compilat, se execut a pe diverse

16

CURS 1. PLATFORMA MICROSOFT .NET

procesoare; dac a ma sina virtual a este bine adaptat a la noua platform a, atunci acest cod va benecia de toate optimiz arile posibile, f ar a a mai nevoie recompilarea lui (precum n C++, de exemplu).

1.3.4

Common Type System

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

1.3. COMPONENTE ALE LUI .NET FRAMEWORK

17

Inversa boxing-ului este unboxing-ul, prin care se poate converti un obiect n tipul valoare echivalent, ca mai jos: int j = (int)box; unde operatorul de conversie este sucient pentru a converti de la un obiect la o variabil a de tip valoare. 4. Clase, propriet a ti, indexatori - platforma .NET suport a pe deplin programarea orientat a pe obiecte, concepte legate de obiecte ( ncapsularea, mo stenirea, polimorsmul) sau tr as aturi legate de clase (metode, c ampuri, membri statici, vizibilitate, accesibilitate, tipuri imbricate, etc). De asemenea se includ tr as aturi precum propriet a ti, indexatori, evenimente. 5. Interfe te - reprezint a acela si concept precum clasele abstracte din C++ (dar con tin and doar func tii virtuale pure), sau interfe tele Java. O clas a care se deriveaz a dintr-o interfa ta trebuie s a implementeze toate metodele acelei interfe te. Se permite implementarea simultan a a mai multor interfe te ( n rest mo stenirea claselor este simpl a). 6. Delega ti - inspira ti de pointerii la func tii din C, ce permit programarea generic a. Reprezint a versiunea sigur a a pointerilor c atre func tii din C/C++ si sunt mecanismul prin care se trateaz a evenimentele. Exist a numeroase componente ale lui CLR care l fac cea mai important a parte a lui .NET Framework: metadata, assemblies, assembly cache, reection, garbage collection.

1.3.5

Metadate

Metadatele nseamn a date despre date. In cazul .NET, ele reprezint a detalii destinate a citite si folosite de c atre platform a. Sunt stocate mpreun a cu codul pe care l descrie. Pe baza metadatelor, CLR stie cum s a instan tieze obiectele, cum s a le apeleze metodele, cum s a acceseze propriet a tile. Printrun mecanism numit reectare, o aplica tie (nu neap arat CLR) poate s a interogheze aceast a metadat a si s a ae ce expune un tip de date (clas a, structur a, etc). Mai pe larg, metadata con tine o declara tie a ec arui tip si c ate o declara tie pentru ecare metod a, c amp, proprietate, eveniment al tipului respectiv. Pentru ecare metod a implementat a, metadata con tine informa tie care permite nc arc atorului clasei respective s a localizeze corpul metodei. De asemena mai poate con tine declara tii despre cultura aplica tiei respective, adic a despre localizarea ei (limba folosit a n partea de interfa ta utilizator).

18

CURS 1. PLATFORMA MICROSOFT .NET

1.3.6

Assemblies

Un assembly reprezint a un bloc func tional al unei aplica tii .NET. El formeaz a unitatea fundamental a de distribuire, versionare, reutilizare si permisiuni de securitate. La runtime, un tip de date exist a n interiorul unui assembly ( si nu poate exista n exteriorul acestuia). Un assembly con tine metadate care sunt folosite de c atre CLR. Scopul acestor assemblies este s a se asigure dezvoltarea softului n mod plug-and-play. Dar metadatele nu sunt suciente pentru acest lucru; mai sunt necesare si manifestele. Un manifest reprezint a metadate despre assembly-ul care g azduie ste tipurile de date. Con tine numele assembly-ului, num arul de versiune, referiri la alte assemblies, o list a a tipurilor n assembly, permisiuni de securitate si altele. Un assembly care este mp ar tit ntre mai multe aplica tii are de asemenea un shared name. Aceast a informa tie care este unic a este op tional a, neap ar and n manifestul unui assembly daca acesta nu a fost g andit ca o aplica tie partajat a. Deoarece un assembly con tine date care l descriu, instalarea lui poate f acut a copiind assemblyul n directorul destina tie dorit. C and se dore ste rularea unei aplica tii con tinute n assembly, manifestul va instrui mediul .NET despre modulele care sunt con tinute n assembly. Sunt folosite de asemenea si referin tele c atre orice assembly extern de care are nevoie aplica tia. Versionarea este un aspect deosebit de important pentru a se evita a sa numitul DLL Hell. Scenariile precedente erau de tipul: se instaleaza o aplica tie care aduce ni ste siere .dll necesare pentru functionare. Ulterior, o alt a aplica tie care se instaleaz a suprascrie aceste siere (sau m acar unul din ele) cu o versiune mai nou a, dar cu care vechea aplica tie nu mai func tioneaz a corespunz ator. Reinstalarea vechii aplica tii nu rezolv a problema, deoarece a doua aplica tie nu va func tiona. De si sierele dll con tin informa tie relativ la versiune n interiorul lor, ea nu este folosit a de c atre sistemul de operare, ceea ce duce la probleme. O solu tie la aceast a dilem a ar instalarea sierelor dll n directorul aplica tiei, dar n acest mod ar disp area reutilizarea acestor biblioteci.

1.3.7

Assembly cache

Assembly cache este un director aat n mod normal n directorul %windir% \Assembly. Atunci c and un assembly este instalat pe o ma sin a, el va ad augat n assembly cache. Dac a un assembly este desc arcat de pe Internet, el va stocat n assembly cache, ntr-o zon a tranzient a. Aplica tiile instalate vor avea assemblies ntr-un assembly cache global.

ATURI 1.4. TRAS ALE PLATFORMEI .NET

19

In acest assembly cache vor exista versiuni multiple ale aceluia si assembly. Dac a programul de instalare este scris corect, va evita suprascrierea assemblyurilor deja existente ( si care func tioneaz a perfect cu acplica tiile instalate), ad aug and doar noul assembly. Este un mod de rezolvare a problemei DLL Hell, unde suprascrierea unei biblioteci dinamice cu o variant a mai nou a putea duce la nefunc tionarea corespunz atoare a aplica tiilor anterior instalate. CLR este cel care decide, pe baza informa tiilor din manifest, care este versiunea corect a de assembly de care o aplica tie are nevoie. Acest mecanism pune cap at unei epoci de trist a amintire pentru programatori si utilizatori.

1.3.8

Garbage collection

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

1.4

Tr as aturi ale platformei .NET

Prin prisma celor prezentate p ana acum putem rezuma urm atoarele tr as aturi: Dezvoltarea multilimbaj: Deoarece exist a mai multe limbaje pentru aceast a platform a, este mai u sor de implementat p ar ti specice n limbajele cele mai adecvate. Numarul limbajelor curent implementate este mai mare dec at 10. Aceast a dezvoltare are n vedere si debuggingul aplica tiilor dezvoltate n mai multe limbaje. Independen ta de procesor si de platform a: CIL este independent de procesor. O dat a scris a si compilat a, orice aplica tie .NET (al c arei management este f acut de c atre CLR) poate rulat a pe orice platform a. Datorit a CLR-ului, aplica tia este izolat a de particularit a tile hardware sau ale sistemului de operare.

20

CURS 1. PLATFORMA MICROSOFT .NET Managementul automat al memoriei: Problemele de dealocare de memorie sunt n mare parte rezolvate; overhead-ul indus de c atre mecanismul de garbage collection este suportabil, ind implementat n sisteme mult mai timpurii. Men tion am totu si c a la ora actual a exist a nc a posibilitatea de a avea memory leak intr-o aplica tie gestionat a de c atre platforma .NET6 . Suportul pentru versionare: Ca o lec tie nv a tat a din perioada de DLL Hell, versionarea este acum un aspect de care se tine cont. Dac a o aplica tie a fost dezvoltat a si testat a folosind anumite componente, instalarea unei componente de versiune mai nou a nu va atenta la buna func tionare a aplica tiei n discu tie: cele dou a versiuni vor coexista pa snic, alegerea lor ind f acut a pe baza manifestelor. Sprijinirea standardelor deschise: Nu toate dispozitivele ruleaz a sisteme de operare Microsoft sau folosesc procesoare Intel. Din aceast a cauz a orice este str ans legat de acestea este evitat. Se folose ste XML si cel mai vizibil descendent al acestuia, SOAP. Deoarece SOAP este un protocol simplu, bazat pe XML, ce folose ste ca protocol de transmisie 7 HTTP , el poate trece u sor de rewall-uri, spre deosebire de DCOM sau CORBA. Distribuirea u soar a: Actualmente instalarea unei aplica tii sub Windows nseamn a copierea unor siere n ni ste directoare anume, modicarea unor valori n regi stri, instalare de componente COM, etc. Dezinstalarea complet a a unei aplica tii este in majoritatea cazurilor o utopie. Aplica tiile .NET, datorit a metadatelor si reect arii trec de aceste probleme. Se dore ste ca instalarea unei aplica tii s a nu nsemne mai mult dec at copierea sierelor necesare ntr-un director, iar dezinstalarea aplica tiei s a se fac a prin stergerea acelui director. Arhitectur a distribuit a: Noua losoe este de a asigura accesul la servicii Web distribuite; acestea conlucreaz a la ob tinerea informa tiei dorite. Platforma .NET asigur a suport si unelte pentru realizarea acestui tip de aplica tii. Interoperabilitate cu codul unmanaged: Codul unmanaged se refer a la cod care nu se a a n totalitate sub controlul CLR. El este rulat de CLR, dar nu beneciaz a de CTS sau garbage collection. Este vorba de apelurile func tiilor din DLL-uri, folosirea componentelor COM, sau
6 7

How to Detect and Avoid Memory and Resource Leaks in .NET Applications Folosit la transmiterea paginilor Web pe Internet

ATURI 1.4. TRAS ALE PLATFORMEI .NET

21

folosirea de c atre o component a COM a unei componente .NET. Codul existent se poate folosi n continuare. Securitate: Aplica tiile bazate pe componente distribuite cer automat securitate. Modalitatea actual a de securizare, bazat a pe drepturile contului utilizatorului, sau cel din Java, n care codul suspectat este rulat ntr-un sandbox, f ar a acces la resursele critice este nlocuit n .NET de un control mai n, pe baza metadatelor din assembly (zona din care provine - ex. Internet, intranet, ma sina local a, etc) precum si a politicilor de securitate ce se pot seta.

22

CURS 1. PLATFORMA MICROSOFT .NET

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

C#1 este un limbaj de programare imperativ, obiectorientat. Este foarte asem an ator cu Java si C++, motiv pentru care curba de nv a tare este foarte lin a. Un prim exemplu de program, care con tine o serie de elemente ce se vor nt alni n continuare, este: using System; class HelloWorld { public static void Main() { Console.WriteLine(Hello world!); } } Prima linie using System; este o directiv a care specic a faptul c a se vor folosi clasele care sunt incluse n spa tiul de nume2 System ; un spa tiu de nume este o colec tie de tipuri sau o grupare de alte spa tii de nume care pot folosite ntr-un program (detalii vor date mai t arziu). In cazul de fa ta , clasa care este folosit a din acest spa tiu de nume este Console. Mai departe,
1 2

# se pronun t a sharp Eng: namespace

23

24

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

orice program este con tinut ntro clas a, n cazul nostru HelloWorld. Punctul de intrare n aplica tie este metoda Main, care nu preia n acest exemplu nici un argument din linia de comand a si nu returneaz a explicit un indicator de stare a termin arii. Pentru opera tiile de intrareie sire cu consola se folose ste clasa Console ; pentru ea se apeleaz a metoda static a WriteLine, care a seaz a pe ecran mesajul, dup a care face trecerea la linie nou a. O variant a u sor modicat a a programului de mai sus, n care se salut a persoanele ale c aror nume este transmis prin linia de comand a: using System; class HelloWorld { public static void Main( String[] args) { for( int i=0; i<args.Length; i++) { Console.WriteLine( Hello {0}, args[i]); } } } In exemplul precedent metoda principal a preia o list a de parametri transmi si din linia de comand a (un sir de obiecte de tip String ) si va a sa pentru ecare nume Hello urmat de numele de indice i (numerotarea parametrilor ncepe de la 0). Construc tia {0} va nlocuit a cu primul argument care urmeaz a dup a Hello {0}. La executarea programului de mai sus n forma: HelloWorld Ana Dan, se va a sa pe ecran: Hello Ana Hello Dan Metoda Main poate s a returneze o valoare ntreag a, care s a e folosit a de c atre sistemul de operare pentru a semnala dac a procesul sa ncheiat cu succes sau nu3 . Men tion am faptul c a limbajul este casesensitive4 . Ca metode de notare Microsoft recomand a folosirea urm atoarelor dou a conven tii: conventie Pascal, n care prima liter a a ec arui cuv ant se scrie ca liter a mare; exemplu: LoadData, SaveLogFile
3 4

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

2.2. TIPURI DE DATE

25

conven tia tip "c amil a" este la fel ca precedenta, dar primul caracter al primului cuv ant nu este scris ca liter a mare; exemplu: userIdentier, rstName. In general, conven tia tip Pascal este folosit a pentru tot ce este vizibil (public), precum nume de clase, metode, propriet a ti, etc. Parametrii metodelor si numele c ampurilor se scriu cu conven tia c amil a. Se recomand a evitarea folosirii nota tiei ungare (numele unei entit a ti trebuie s a se refere la semantica ei, nu la tipul de reprezentare).

2.2

Tipuri de date

C# prezint a dou a grupuri de tipuri de date: tipuri valoare si tipuri referin t a. Tipurile valoare includ tipurile simple (ex. char, int, oat )5 , tipurile enumerare si structur a si au ca principale caracteristici faptul c a ele con tin direct datele referite si sunt alocate pe stiv a sau inline ntro structur a. Tipurile referin ta includ tipurile clas a, interfa ta , delegat si tablou, toate av and proprietatea c a variabilele de acest tip stocheaz a referin te c atre obiectele con tinute. Demn de remarcat este c a toate tipurile de date sunt derivate (direct sau nu) din tipul System.Object, pun and astfel la dispozi tie un mod unitar de tratare a lor.

2.2.1

Tipuri predenite

C# con tine un set de tipuri predenite, pentru care nu este necesar a referirea vreunui spa tiu de nume via directiva using sau calicare complet a: string, object, tipurile ntregi cu semn si f ar a semn, tipuri numerice n virgul a mobil a, tipurile bool si decimal. Tipul string este folosit pentru manipularea sirurilor de caractere codicate Unicode; con tinutul obiectelor de tip string nu se poate modica6 . Clasa object este r ad acina ierarhiei de clase din .NET, la care orice tip (inclusiv un tip valoare) poate convertit. Tipul bool este folosit pentru a reprezenta valorile logice true si false. Tipul char este folosit pentru a reprezenta caractere Unicode, reprezentate pe 16 bi ti. Tipul decimal este folosit pentru calcule n care erorile determinate de reprezentarea n virgul a mobil a sunt inacceptabile, el pun and la dispozi tie 28 de cifre zecimale semnicative.
5 6

De fapt acestea sunt structuri, prezentate n alt curs Spunem despre un string c a este invariabil - engl. immutable

26

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Tabelul de mai jos con tine lista tipurilor predenite, ar at and totodat a cum se scriu valorile corespunz atoare:

2.2. TIPURI DE DATE Tabelul 2.1: Tipuri predenite.

27

Tip object string sbyte short int long byte ushort uint ulong

Descriere r adacina oric arui tip o secven ta de caractere Unicode tip ntreg cu semn, pe 8 bi ti tip ntreg cu semn, pe 16 bi ti tip ntreg cu semn, pe 16 bi ti tip ntreg cu semn, pe 64 bi ti tip ntreg tip ntreg tip ntreg tip ntreg f ar a f ar a f ar a f ar a semn, semn, semn, semn, pe pe pe pe 8 bi ti 16 bi ti 32 bi ti 64 de bi ti

oat double bool char decimal

tip cu virgul a mobil a, simpl a precizie tip n virgul a mobil a, dubl a precizie tip boolean tip caracter din setul Unicode tip zecimal cu 28 de cifre semnicative

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; ulong val1=12; ulong val2=34U; ulong val3=56L; ulong val4=76UL; oat val=1.23F; double val1=1.23; double val2=4.56D; bool val1=false; bool val2=true; char val=h; decimal val=1.23M;

Fiecare din tipurile predenite este un alias pentru un tip pus la dispozi tie 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 dispozi tie tipuri valoare, care sunt e structuri, e enumer ari. Exist a un set predenit de structuri numite tipuri simple, identicate prin cuvinte rezervate. Un tip simplu este e de tip numeric7 , e boolean. Tipurile numerice sunt tipuri ntregi, n virgul a mobil a sau decimal. Tipurile intregi sunt sbyte, byte, short, ushort, int, uint, long, ulong, char; cele n virgul a mobil a sunt oat si double. Tipurile enumerare se pot deni de c atre utilizator.
7

Engl: integral type

28

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Toate tipurile valoare deriveaz a din clasa System.ValueType, care la r andul ei este derivat a din clasa object (alias pentru System.Object). Nu este posibil ca dintrun tip valoare s a se deriveze. Atribuirea pentru un astfel de tip nseamn a copierea valorii dintro parte n alta. Structuri Un tip structur a este un tip valoare care poate s a con tin a declara tii de constante, c ampuri, metode, propriet a ti, indexatori, operatori, constructori sau tipuri imbricate. Vor descrise ntrun capitol urm ator. Tipuri simple C# pune are predenit un set de tipuri structuri numite tipuri simple. Tipurile simple sunt identicate prin cuvinte rezervate, dar acestea reprezint a doar aliasuri pentru tipurile struct corespunz atoare din spa tiul de nume System; coresponden ta este dat a n tabelul de mai jos: Tabelul 2.2: Tipuri simple si coresponden tele lor cu tipurile din spa tiul de nume System. Tabelul 2.2 Cuv ant rezervat Tipul alias sbyte System.SByte byte System.Byte short System.Int16 ushort System.UInt16 int System.Int32 uint System.UInt32 long System.Int64 ulong System.UInt64 char System.Char oat System.Single double System.Double bool System.Boolean decimal System.Decimal

Deoarece un tip simplu este un alias pentru un tip struct, orice tip simplu are membri. De exemplu, tipul int, ind un tip alias pentru System.Int32, urm atoarele declara tii sunt legale:

2.2. TIPURI DE DATE int i = int.MaxValue; //constanta System.Int32.MaxValue string s = i.ToString(); //metoda System.Int32.ToString() string t = 3.ToString(); //idem double d = Double.Parse("3.14"); Tipuri ntregi

29

C# suport a nou a tipuri ntregi: sbyte, byte, short, ushort, int, uint, long, ulong si char. Acestea au urm atoarele dimensiuni si domeniu de valori: sbyte reprezint a tip cu semn pe 8 bi ti, cu valori de la -128 la 127; byte reprezint a tip f ar a semn pe 8 bi ti, ntre 0 si 255; short reprezint a tip cu semn pe 16 bi ti, ntre -32768 si 32767; ushort reprezint a tip f ar a semn pe 16 bi ti, ntre 0 si 65535; int reprezint a tip cu semn pe 32 de bi ti, ntre 231 si 231 1; uint reprezint a tip f ar a semn pe 32 de bi ti, ntre 0 si 232 1; long reprezint a tip cu semn pe 64 de bi ti, ntre 263 si 263 1; ulong reprezint a tip f ar a semn pe 64 de bi ti, ntre 0 si 264 1; char reprezint a tip f ar a semn pe 16 bi ti, cu valori ntre 0 si 65535. Mul timea valorilor posibile pentru char corespunde setului de caractere Unicode. Reprezentarea unei variable de tip ntreg se poate face sub form a de sir de cifre zecimale sau hexazecimale, urmate eventual de un prex. Numerele exprimate n hexazecimal sunt prexate cu 0x sau 0X. Regulile dup a care se asigneaz a un tip pentru o valoare sunt: 1. dac a sirul de cifre nu are un sux, atunci el este considerat ca ind primul tip care poate s a con tin a valoarea dat a: int, uint, long, ulong; 2. dac a sirul de cifre are suxul u sau U, el este considerat ca ind din primul tip care poate s a con tin a valoarea dat a: uint, ulong; 3. dac a sirul de cifre are suxul l sau L, el este considerat ca ind din primul tip care poate s a con tin a valoarea dat a: long, ulong; 4. dac a sirul de cifre are suxul ul, uL, Ul, UL, lu, lU, Lu, LU, el este considerat ca ind din tipul ulong.

30

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Dac a o valoare este n afara domeniului lui ulong, apare o eroare la compilare. Literalii de tip caracter au forma: caracter unde caracter poate exprimat printrun caracter, printro secven ta escape simpl a, secven ta escape hexazecimal a sau secven ta escape Unicode. In prima form a poate folosit orice caracter except and apostrof, backslash si new line. Secven ta escape simpl a poate : \, \", \\, \0, \a, \b, \f, \n, \r, \t, \v, cu semnica tiile cunoscute din C++. O secven ta escape hexazecimal a ncepe cu \x urmat de 14 cifre hexa. De si ca reprezentare, char este identic cu ushort, nu toate opera tiile ce de pot efectua cu ushort sunt valabile si pentru char. In cazul n care o opera tie aritmetic a produce un rezultat care nu poate reprezentat n tipul destina tie, comportamentul depinde de utilizarea operatorilor sau a declara tiilor checked si unchecked (care se pot utiliza n surs a sau din linia de compilare): n context checked, o eroare de dep a sire duce la aruncarea unei excep tii de tip System.OverowException. In context unchecked, eroarea de dep a sire este ignorat a, iar bi tii semnicativi care nu mai ncap n reprezentare sunt elimina ti. 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 con tin operatorii ++, - -, +, - (unar si binar), *, / si care nu sunt con tinute n interiorul unui bloc de tip checked, comportamentul este specicat prin intermediul op tiunii /checked[+|] dat din linia de comand a pentru compilator. Dac a nu se specic a nimic, atunci se va considera implicit unchecked. Tipuri n virgul a mobil a Sunt prezente 2 tipuri numerice n virgul a mobil a: oat si double. Tipurile sunt reprezentate folosind precizie de 32, respectivi 64 de bi ti, folosind formatul IEC 60559, care permit reprezentarea valorilor de 0 pozitiv si 0 negativ (se comport a la fel, dar anumite opera tii duc la ob tinerea acestor

2.2. TIPURI DE DATE

31

dou a valori), + si (ob tinute prin mp ar tirea unui num ar strict pozitiv, respectiv strict negativ la 0), a valorii NotaNumber (NaN) tinut a prin (ob si opera tii n virgul a mobil a invalide, de exemplu 0/0 sau 1), precum un set nit de numere. Tipul oat poate reprezenta valori cuprinse ntre 45 38 1.5 10 si 3.4 10 ( si din domeniul negativ corespunz ator), cu o precizie de 7 cifre. Double poate reprezenta valori cuprinse ntre 5.0 10324 si 308 1.7 10 cu o precizie de 15-16 cifre. Opera tiile cu oating point nu duc niciodat a la apari tia de excep tii, dar ele pot duce, n caz de opera tii invalide, la valori 0, innit sau NaN. Literalii care specic a un num ar reprezentat n virgul a mobil a au forma: literalreal:: cifre-zecimale . cifre-zecimale exponentoptional sux-de-tip-realoptional . cifre-zecimale exponentoptional sux-de-tip-realoptional cifre-zecimale exponent sux-de-tip-realoptional cifre-zecimale sux-de-tip-real, unde exponent:: e semnoptional cifre-zecimale E semnoptional cifre-zecimale, semn este + sau -, sux-de-tip-real este F, f, D, d. Dac a nici un sux de tip real nu este specicat, atunci literalul dat este de tip double. Suxul f sau F specic a tip oat, d sau D specic a double. Dac a literalul specicat nu poate reprezentat n tipul precizat, apare eroare de compilare.

Tipul decimal Este un tip de date reprezentat pe 128 de bi ti, g andit a folosit n calcule nanciare sau care necesit a precizie mai mare. Poate reprezenta valori aate n intervalul 1.0 1028 si 7.9 1028 , cu 28 de cifre semnicative. Acest tip nu poate reprezenta zero cu semn, innit sau NaN. Dac a n urma opera tiilor, un num ar este prea mic pentru a putea reprezentat ca decimal, atunci el este f acut 0, iar dac a este prea mare, rezult a o excep tie. Diferen ta principal a fa ta de tipurile n virgul a mobil a este c a are o precizie mai mare, dar un domeniu de reprezentare mai mic. Din cauza aceasta, nu se fac conversii implicite ntre nici un tip n virgul a mobil a si decimal si nu este posibil a mixarea variabilelor de acest tip ntr-o expresie, f ar a conversii explicite. Literalii de acest tip se exprim a folosind ca sux-de-tip-real caracterele m sau M. Dac a valoarea specicat a nu poate reprezentat a prin tipul decimal, apare o eroare la compilare.

32

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Tipul bool Este folosit pentru reprezentarea valorilor de adev ar true si false. Literalii care se pot folosi sunt true si false. Nu exist a conversii standard ntre bool si nici un alt tip.

2.2.3

Tipul enumerare

Tipul enumerare este un tip valoare, construit pentru a permite declararea constantelor nrudite, ntro manier a clar a si sigur a din punct de vedere al 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);

2.2. TIPURI DE DATE } } } class Test { public static void Main() { Draw draw = new Draw(); draw.DrawLine(0, 0, 10, 10, Draw.LineStyle.Solid); draw.DrawLine(0, 0, 10, 10, (Draw.LineStyle)100); } }

33

Al doilea apel este legal, deoarece valorile care se pot specica pentru un enum nu sunt limitate la valorile declarate n enum. Ca atare, programatorul trebuie s a fac a valid ari suplimentare pentru a determina consisten ta valorilor. In cazul de fa ta , la apelul de metod a se arunc a o excep tie (excep tiile vor tratate pe larg ntr-un curs viitor). Ca si mod de scriere a enumer arilor, se sugereaz a folosirea conven tiei Pascal at at pentru numele tipului c at si pentru numele valorilor con tinute. Enumer arile nu pot declarate abstracte si nu pot derivate. Orice enum este derivat automat din System.Enum, care este la r andul lui derivat din System.ValueType; astfel, metodele mo stenite de la tipurile p arinte sunt utilizabile de c atre orice variabil a de tip enum. Fiecare tip enumerare care este folosit are un tip de reprezentare8 , pentru a se cunoa ste c at spa tiu de memorie trebuie s a e alocat unei variabile de acest tip. Dac a nu se specic a nici un tip de reprezentare (ca mai sus), atunci se presupune implicit tipul int. Specicarea unui tip de reprezentare (care poate orice tip integral, except and tipul char) se face prin enun tarea tipului dup a numele enumer arii: enum MyEnum : byte { small, large } Specicarea este folosit a atunci c and dimensiunea n memorie este important a, sau c and se dore ste crearea unui tip de indicator (un tip ag) al c arui num ar de st ari difer a de num arul de bi ti aloca ti tipului int (modelare de ag-uri):
8

Engl: underlying type

34

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

enum ActionAttributes : ulong { Read = 1, Write = 2, Delete = 4, Query = 8, Sync = 16 //etc } ... ActionAttributes aa=ActionAttributes.Read|ActionAttributes.Write | ActionAttributes.Query; ... In mod implicit, valoarea primului membru al unei structuri este 0, si ecare variabl a care urmeaz a are valoarea mai mare cu o unitate dec at precedenta. La dorin ta , valoarea ec arui c amp poate specicat explicit: enum Values { a = 1, b = 2, c = a + b } Urm atoarele observa tii se impun relativ la lucrul cu tipul enumerare: 1. valorile specicate ca ini tializatori trebuie s a e reprezentabile prin tipul de reprezentare a enumer arii, altfel apare o eroare la compilare: enum Out : byte { A = -1 }//eroare semnalata la compilare 2. mai mul ti membri pot avea aceea si valoare (manevr a dictat a de semantica tipului construit): enum ExamState { passed = 10, failed = 1, rejected = failed }

2.2. TIPURI DE DATE

35

3. dac a pentru un membru nu este dat a o valoare, acesta va lua valoarea membrului precedent + 1 (cu excep tia primului membru vezi mai sus) 4. nu se permit referin te circulare: enum CircularEnum { A = B, B }//A depinde explicit de B, B depinde implicit de A //eroare semnalata la compilare 5. este recomandat ca orice tip enum s a con tin a un membru cu valoarea 0, pentru c a n anumite contexte valoarea implicit a pentru o variabil a enum este 0, ceea ce poate duce la inconsisten te si bug-uri greu de depanat enum Months { InvalidMonth,//are valoarea implicita 0, fiind primul element January, February, //etc } Tipurile enumerare pot convertite c atre tipul lor de baz a si napoi, folosind o conversie explicit a (cast): enum Values { a = 1, b = 5, c= 3 } class Test { public static void Main() { Values v = (Values)3; int ival = (int)v;

36 } }

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Valoarea 0 poate convertit a c atre un enum f ar a cast: ... MyEnum me; ... if (me == 0) { //cod }

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

2.2. TIPURI DE DATE typeof(Color)));

37

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

38

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Console.WriteLine("Demo stop: Demo of enumerated types."); } static void Main() { DemoEnums(); } } }

2.3

Tablouri

De multe ori se dore ste a se lucra cu o colec tie de elemente de un anumit tip. O solu tie pentru aceast a problem a o reprezint a tablourile. Sintaxa de declarare este asem an atoare cu cea din Java sau C++, dar ecare tablou este un obiect, derivat din clasa abstract a System.Array. Accesul la elemente se face prin intermediul indicilor care ncep de la 0 si se termin a la num arul de elemente-1 (pentru un tablou unidimensional; n cadrul unui tablou multidimensional valoarea indicelui maxim este num arul de elemente de pe dimensiunea respectiv a minus 1); orice dep a sire a indicilor duce la apari tia unei excep tii: System.IndexOutOfRangeEx O variabil a de tip tablou poate avea valoare de null sau poate s a indice c atre o instan ta valid a.

2.3.1

Tablouri unidimensionale

Declararea unui tablou unidimensional se face prin plasarea de paranteze drepte ntre numele tipului tabloului si numele s au, ca mai jos9 : int[] sir; Declararea de mai sus nu duce la alocare de spa tiu pentru memorarea sirului; instan tierea se poate face ca mai jos: sir = new int[10]; Exemplu: using System; class Unidimensional {
Spre deosebire de Java, nu se poate modica locul parantezelor, adic a nu se poate scrie: int sir[].
9

2.3. TABLOURI public static int Main() { int[] sir; int n; 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; } }

39

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

2.3.2

Tablouri multidimensionale

C# cunoa ste dou a tipuri de tablouri multidimensionale: rectangulare si 11 neregulate . Numele lor vine de la forma pe care o pot avea.
10 11

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

40

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Tablouri rectangulare Tablourile rectangulare au proprietatea c a num arul de elemente pentru o anumit a dimensiune este p astrat constant; altfel spus, acest tip de tablouri au chiar form a dreptunghiular a. int[,] tab; unde tab este un tablou rectangular bidimensional. Instan tierea se face: tab = new int[2,3]; rezult and un tablou cu 2 linii si 3 coloane; ecare linie are exact 3 eleemnte si acest lucru nu se poate schimba pentru tabloul declarat. Referirea la elementul aat pe linia i si coloana j se face cu tab[i, j ]. La declararea tabloului se poate face si ini tializare: 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]); }

2.3. TABLOURI } Console.WriteLine(tabInm.Length={0}, tabInm.Length); } }

41

Dup a ce se a seaz a tabla nmul tirii p an a la 10, se va a sa: tabInm.Length=100, deoarece proprietatea Length d a num arul total de elemente aat n tablou (pe toate dimensiunile). Am folosit ns a metoda GetLength(d) care returneaz a num arul de elemente aate pe dimensiunea num arul d (num ararea dimensiunilor ncepe cu 0). Determinarea num arului de dimensiuni pentru un tablou rectangular la runtime se face folosind proprietatea Rank a clasei de baz a System.Array. Exemplu: using System; class Dimensiuni { public static void Main() { int[] t1 = new int[2]; int[,] t2 = new int[3,4]; int[,,] t3 = new int[5,6,7]; Console.WriteLine(t1.Rank={0}\nt2.Rank={1}\nt3.Rank={2}, t1.Rank, t2.Rank, t3.Rank); } } Pe ecran va ap area: t1.Rank=1 t2.Rank=2 t3.Rank=3 Tablouri neregulate Un tablou neregulat12 reprezint a un tablou de tabouri. Declararea unui tablou neregulat cu dou a dimensiuni se face ca mai jos: int[][] tab; Referirea la elementul de indici i si j se face prin tab[i][j]. Exemplu:
12

Engl: jagged array

42

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

using System; class JaggedArray { public static void Main() { int[][] a = new int[2][]; a[0] = new int[2]; a[1] = new int[3]; for( int i=0; i<a[0].Length; i++) { a[0][i] = i; } for( int i=0; i<a[1].Length; i++) { a[1][i] = i * i; } for(int i=0; i<a.Length; i++) { for( int j=0; j<a[i].Length; j++ ) { Console.Write({0} , a[i][j]); } Console.WriteLine(); } Console.WriteLine(a.Rank={0}, a.Rank); } } va scrie pe ecran: 0 1 0 1 4 a.Rank=1 Ultima linie a sat a se explic a prin faptul c a un tablou neregulat este un vector care con tine referin te, deci este unidimensional. Ini tializarea 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},

2.4. S IRURI DE CARACTERE 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} };

43

2.4

S iruri de caractere

Tipul de date folosit pentru reprezentarea sirurilor de caractere este clasa System.String (pentru care se poate folosi aliasul "string"; reamintim c a este un tip predenit). Obiectele de acest tip sunt imutabile (caracterele con tinute nu se pot schimba, dar pe baza unui sir se poate ob tine un alt sir). S irurile pot con tine secven te escape si pot de dou a tipuri: regulate si de tip "verbatim"13 . S irurile regulate sunt demarcate prin ghilimele si necesit a secven te escape pentru reprezentarea caracterelor escape. Exemplu: String a = "string literal"; String versuri = "vers1\nvers2"; String caleCompleta = "\\\\minimax\\protect\\csharp"; Pentru situa tia n care se utilizeaz a masiv secven te escape, se pot folosi sirurile verbatim. Un literal de acest tip are simbolul "@" naintea ghilimelelor de nceput. Pentru cazul n care ghilimelele sunt nt alnite n interiorul sirului, ele se vor dubla. Un sir de caractere poate reprezentat pe mai multe r anduri f ar a a folosi caracterul \n. S irurile verbatim sunt folosite pentru a face referiri la siere sau chei n regi stri, sau pentru expresii regulate. 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.";
13

Engl: verbatim literals

44

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI

Operatorii "==" si "!=" pentru dou a siruri de caractere se comport a n felul urm ator: dou a siruri de caractere se consider a egale dac a sunt e am andou a null, e au aceea si lungime si caracterele de pe acelea si pozi tii coincid; "!=" d a negarea rela tiei de egalitate. Clasa String pune la dispozi tie metode pentru: comparare (Compare, CompareOrdinal, CompareTo), c autare (EndsWith, StartsWith, IndexOf, LastIndexOf), modicare (a se n telege ob tinerea altor obiecte pe baza celui curent - Concat, CopyTo, Insert, Join, PadLeft, PadRight, Remove, Replace, Split, Substring, ToLower, ToUpper, Trim, TrimEnd, TrimStart)14 . Accesarea unui caracter aat pe o pozi tie i a unui sir s se face prin folosirea parantezelor drepte, cu acelea si restric tii asupra indicelui ca si pentru tablouri: s[i]. In clasa object se a a metoda ToString() care este suprascris a n ecare clas a ale c arei instan te pot tip arite. Pentru ob tinerea unei reprezent ari diferite se folose ste metoda String.Format(). Un exemplu de folosire a func tiei Split() pentru desp ar tirea unui sir n func tie de separatori este: using System; class Class1 { static void Main(string[] args) { String s = "Oh, I hadnt thought of that!"; char[] x = { , , }; String[] tokens = s.Split( x ); for(int i=0; i<tokens.Length; i++) { Console.WriteLine("Token: {0}", tokens[i]); } } } va a sa pe ecran: Token: Token: Token: Token: Token: Token: Token:
14

Oh I hadnt thought of that!

A se vedea exemplele din MSDN.

2.4. S IRURI DE CARACTERE

45

De remarcat c a pentru caracterul apostrof nu este obligatorie secven ta escape n cazul sirurilor de caractere. Al doilea lucru care trebuie explicat este c a al doilea token este cuv antul vid, care apare ntre cei doi separatori al atura ti: virgula si spa tiul. Metoda Split() nu face gruparea mai multor separatori, lucru care ar de dorit n prezen ta a doi separatori al atura ti. Pentru aceasta putem apela la dou a metode. Prima presupune folosirea unei variante supra nc arcate a metodei Split, n care se precizeaz a ca al doilea parametru optiunea de ignorare a rezultatelor goale: String[] tokens = s.Split( new char[]{ , ,}, StringSplitOptions.RemoveEmptyEntries ); A doua modalitate se bazeaz a pe folosirea expresiilor regulate. Pentru a lucra cu siruri de caractere care permit modicarea lor (concaten ari repetate, substituiri de sub siruri) se folose ste clasa StringBuilder, din spa tiul de nume System.Text.

2.4.1

Expresii regulate

In cazul n care func tiile din clasa String nu sunt sucient de puternice, namespaceul System.Text.RegularExpresions pune la dispozi tie o clas a de lucru cu expresii regulate numit a Regex. Expresiile regulate reprezint a o metod a extrem de facil a de a opera c aut ari/ nlocuiri pe text. Forma expresiilor regulate este cea din limbajul Perl. Aceast a clas a folose ste o tehnic a interesant a pentru m arirea performan telor: dac a programatorul vrea, se scrie o secven ta "din mers" pentru a implementa potrivirea expresiei regulate, dup a care codul este rulat15 . Exemplul anterior poate rescris corect din puct de vedere al func tionalit a tii prin folosirea unei expresii regulate, pentru a prinde si cazul separatorilor multipli adiacen ti: class ExpresieRegulata { static void Main(string[] args) { String s = "Oh, I hadnt thought of that!"; //separator: virgula, spatiu sau punct si virgula //unul sau mai multe, orice combinatie Regex regex = new Regex("[, ;]+"); String[] strs = regex.Split(s);
15

Codul este scris direct n IL.

46

CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI for( int i=0; i<strs.Length; i++) { Console.WriteLine("Word: {0}", strs[i]); }

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

Curs 3 Clase generalit a ti. Instruc tiuni. Spa tii de nume


3.1 Clase vedere general a

Clasele reprezint a tipuri referin ta . O clas a poate s a mo steneasc a o singur a clas a si poate implementa mai multe interfe te. Clasele pot con tine constante, c ampuri, metode, propriet a ti, evenimente, indexatori, operatori, constructori de instan ta , destructori, constructori de clas a, tipuri imbricate. Fiecare membru poate con tine un nivel de protec tie, care controleaz a gradul de acces la el. O descriere este dat a n tabelul 3.1: Tabelul 3.1: Modicatori de acces ai membrilor unei clase Accesor public protected Semnica tie Acces nelimitat Acces limitat la clasa con tin atoare sau la tipuri derivate din ea internal Acces limitat la acest assembly protected internal Acces limitat la acest assembly sau la tipuri derivate din clas a private Acces limitat la clas a; modicatorul implicit de acces

using System; 47

48

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; private int myField = 42; public void MyMethod() { Console.WriteLine("this.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); } }

49

class Test { static void Main() { MyClass a = new MyClass(); MyClass b = new MyClass(1); Console.WriteLine("MyConst={0}", MyClass.MyConst); //a.myField++;//gradul de acces nu permite lucrul direct cu campul 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 {} } Constanta este un membru al unei clase care reprezint a o valoare nemodicabil a, care poate evaluat a la compilare. Constantele pot depinde de alte constante, at ata timp c at nu se creeaz a dependen te circulare. Ele sunt considerate automat membri statici (dar este interzis s a se foloseasc a specicatorul static n fa ta lor). Ele pot accesate exclusiv prin intermediul numelui de clas a (MyClass.MyConst), si nu prin intermediul vreunei instan te (a.MyConst). C ampul este un membru asociat ec arui obiect; c ampul stocheaza o valoare care contribuie la starea obiectului.

50

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Metoda este un membru care implementeaz a un calcul sau o ac tiune care poate efectuat a asupra unui obiect sau asupra unei clase. Metodele statice (care au n antet cuv antul cheie static) sunt accesate prin intermediul numelui de clas a, pe c and cele nestatice (metode instan ta ) sunt apelate prin intermediul unui obiect. Proprietatea este un membru care d a acces la o caracteristic a a unui obiect sau unei clase. Exemplele folosite p an a acum includeau lungimea unui vector, num arul de caractere ale unui sir de caractere, etc. Sintaxa pentru accesara c ampurilor si a propriet a tilor este aceea si. Reprezint a o alt a modalitate de implementare a accesorilor pentru obiecte. Evenimentul este un membru care permite unei clase sau unui obiect s a pun a la dispozi tia altora notic ari asupra evenimentelor. Tipul acestei declara tii trebuie s a e un tip delegat. O instan ta a unui tip delegat ncapsuleaz a una sau mai multe entit a ti apelabile. Exemplu: public delegate void EventHandler(object sender, System.EventArgs e); public class Button { public event EventHandler Click; public void Reset() { Click = null; } } using System; public class Form1 { Button Button1 = new Button1(); public Form1() { Button1.Click += new EventHandler(Button1_Click); } void Button1_Click(object sender, EventArgs e ) { Console.WriteLine("Button1 was clicked!");

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

51

Mai sus clasa Form1 adaug a Button1_Click ca tratare de eveniment1 pentru evenimentul Click al lui Button1. In metoda Disconnect(), acest event handler este nl aturat. Operatorul este un membru care dene ste semnica tia (supra nc arcarea) unui operator care se aplic a instan telor unei clase. Se pot supra nc arca operatorii binari, unari si de conversie. Indexatorul este un membru care permite unui obiect s a e indexat n acela si mod ca un tablou (pentru programatorii C++: supra nc arcarea operatorului []). Constructorii instan t a sunt membri care implementeaz a ac tiuni cerute pentru ini tializarea ec arui obiect. Destructorul este un membru special care implementeaz a ac tiunile cerute pentru a distruge o instan ta a unei clase. Destructorul nu are parametri, nu poate avea modicatori de acces, nu poate apelat explicit si este apelat automat de c atre garbage collector. Constructorul static este un membru care implementeaz a ac tiuni necesare pentru a ini tializa o clas a, mai exact membrii statici ai clasei. Nu poate avea parametri, nu poate avea modicatori de acces, nu este apelat explicit, ci automat de c atre sistem. Mo stenirea este de tip simplu, iar r ad acina ierarhiei este clasa object (alias System.Object).

3.2

Transmiterea de parametri

In general, transmiterea parametrilor se face prin valoare. Acest lucru nseamn a c a la apelul unei metode n stiva gestionat a de compilator se copiaz a valoarea parametrului actual transmis, iar la revenire din metod a aceast a
1

Engl: event handler

52

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

valoare va stears a. Exemplic am mai jos acest lucru pentru tipurile valoare si referin ta . 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 referin ta , pe stiv a se depune tot o copie a valorii obiectului. Ins a pentru un asemenea tip de variabil a acest lucru nseamn a c a pe stiv a se va depune ca valoare adresa de memorie la care este stocat obiectul respectiv. Ca atare, metoda apelat a poate s a modice starea obiectului care se transmite, dar nu obiectul in sine (adic a referin ta sa): 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

53

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

54 {

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

MyClass myClass = new MyClass(); myClass.x = 100; Console.WriteLine("inainte de apel: {0}", myClass.x); f( myClass ); Console.WriteLine("dupa apel: {0}", myClass.x); } } Ie sirea acestui program va : inainte de intrare in iesire din dupa apel: apel: 100 f: 100 f: -100 100

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

3.2. TRANSMITEREA DE PARAMETRI

55

va realiza interschimbarea valorilor a si b . Una din tr as aturile specice parametrilor referin ta este c a valorile pentru care se face apelul trebuie s a e ini tializate. Neasignarea de valori pentru x si y n exemplul de mai sus duce o eroare de compilare. Mai clar, exemplul de mai jos genereaza eroare la compilare, mesajul ind: "Use of unassigned local variable x": class TestRef { static void f(ref int x) { x = 100; } static void Main() { int x; f(ref x); } } Exist a cazuri n care dorim s a ob tinem acela si efect ca la parametrii referin ta , dar f ar a a trebui s a ini tializ am argumentele date de c atre metoda apelant a (de exemplu c and valoarea acestui parametru se calculeaz a n interiorul 2 metodei apelate). Pentru aceasta exist a parametrii de ie sire , similar cu parametrii referin ta , cu deosebirea c a nu trebuie asignat a o valoare parametrului de apel: using System; class Test { static void Main() { int l = 10; double area; ComputeSquareArea( l, out area); Console.WriteLine("Area is: {0}", area); } static void ComputeSquareArea( double l, out double area ) {
2

Engl: output parameters

56

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

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

Engl: parameter array

3.3. CONVERSII

57

3.3

Conversii

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

3.3.1

Conversii implicite

Sunt clasicate ca si conversii implicite urm atoarele: conversiile identitate conversiile numerice implicite conversiile implicite de tip enumerare conversiile implicite de referin te boxing conversiile implicite ale expresiilor constante conversii implicite denite de utilizator Conversiile implicite pot ap area ntro varietate de situa tii, de exemplu apeluri de func tii sau atribuiri. Conversiile implicite predenite nu determin a niciodat a apari tia de excep tii. Conversiile indentitate O conversie identitate converte ste de la un tip oarecare c atre acela si tip. Conversiile numerice implicite Conversiile numerice implicite sunt: de la sbyte la short, int, long, oat, double, decimal; de la byte la short, ushort, int, uint, long, ulong, oat, double, decimal; de la short la int, long, double, decimal; de la ushort la int, uint, long, ulong, oat, double, decimal; de la int la long, oat, double, decimal;

58

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

Conversiile de la int, uint, long, ulong la oat, precum si cele de la long sau ulong la double pot duce la o pierdere a preciziei, dar niciodat a la o reducere a ordinului de m arime. Alte conversii numerice implicite niciodat a nu duc la pierdere de informa tie. Conversiile de tip enumerare implicite O astfel de conversie permite ca literalul 0 s a e convertit la orice tip enumerare (chiar dac a acesta nu con tine valoarea 0) - a se vedea 2.2.3, pag. 32. Conversii implicite de referin te Conversiile implicite de referin te implicite sunt: de la orice tip referin ta la object; de la orice tip clas a B la orice tip clas a A, dac a B este derivat din A; de la orice tip clas a A la orice interfa ta B, dac a A implementeaz a B; de al orice interfa ta A la orice interfa ta B, dac a A este derivat a din B; de la orice tip tablou A cu tipul AE la un tip tablou B av and tipul BE , cu urm atoarele condi tii: 1. A si B au acela si num ar de dimensiuni; 2. at at AE c at si BE sunt tipuri referin ta ; 3. exist a o conversie implicit a de tip referin ta de la AE la BE de la un tablou la System.Array; de la tip delegat la System.Delegate; de la orice tip tablou sau tip delegat la System.ICloneable; de la tipul null la orice variabil a de tip referin ta ;

3.3. CONVERSII Conversie de tip boxing

59

Permite unui tip valoare s a e implicit convertit c atre tipul object sau System.ValueType sau c atre orice tip interfa ta pe care tipul valoare l implementeaz a. O descriere mai am anun tit a este dat a n sec tiunea 3.3.4.

3.3.2

Conversiile implicite ale expresiilor constante

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

3.3.3

Conversii explicite

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

60

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 a conversie numeric a implicit a: de la sbyte la byte, ushort, uint, ulong, char; de la byte la sbyte, char; de la short la sbyte, byte, ushort, uint, ulong, char; de la ushort la sbyte, byte, short, char; de la int la sbyte, byte, short, ushort, int, char; de la uint la sbyte, byte, short, ushort, int, uint, long, ulong, char; de la long la sbyte, byte, short, ushort, int, uint, ulong, char; de la ulong la sbyte, byte, short, ushort, int, uint, long, char; de la char la sbyte, byte, short; de la oat la sbyte, byte, short, ushort, int, uint, long, ulong, decimal; de la double la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, decimal; de la decimal la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double; Pentru c a n astfel de conversii pot ap area pierderi de informa tie, exist a dou a contexte n care se fac aceste conversii: checked si unchecked. In context checked, conversia se face cu succes dac a valoarea care se converte ste este reprezentabil a de c atre tipul c atre care se face conversia. In cazul n care conversia nu se poate face cu succes, se va arunca excep tia System.OverowException. In context unchecked, conversia se face ntotdeauna, dar se poate ajunge la pierdere de informa tie sau la valori ce nu sunt bine nedenite (vezi [6], pag. 115116). Conversii explicite de enumer ari Conversiile explicite de enumer ari sunt: de la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double, decimal la orice tip enumerare;

3.3. CONVERSII

61

de la orice tip enumerare la sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double, decimal; de la orice tip enumerare la orice tip enumerare. Conversiile de tip enumerare se fac prin tratarea ec arui tip enumerare ca ind tipul ntreg de reprezentare, dup a care se efectueaz a o conversie implict a sau explicit a ntre tipuri (ex: dac a se dore ste conversia de la un tip enumerare E care are tipul de reprezentare int la un tip byte, se va face o conversie explicit a de la int la byte; invers, se va face o conversie implict a de la byte la int). Conversii explicite de referin te Conversiile explicite de referin te sunt: de la object la orice tip referin ta ; de la orice tip clas a A la orice tip clas a B, cu condi tia ca A s a e clas a de baz a pentru B; de la orice tip clas a A la orice tip interfa ta B, dac a A nu este nederivabil a si A nu implementeaz a pe B; de la orice tip interfa ta A la orice tip clas a B, dac a B nu este nederivabil a sau cu condi tia ca B s a implementeze A; de la orice tip interfa ta A la orice tip interfa ta B, dac a A nu este derivat din B; de la un tip tablou A cu elemente de tip AE la un tip tablou B cu elemente BE , cu condi tiile: 1. A si B au acela si num ar de dimensiuni; 2. AE si BE sunt tipuri referin ta ; 3. exist a o conversie de referin ta explicit a de la AE al BE de la System.Array si interfe tele pe care le implementeaz a la orice tip tablou; de la System.Delegate si interfe tele pe care le implementeaz a la orice tip delegat. Acest tip de conversii cer vericare la runtime. Dac a o astfel de conversie e sueaz a, se va arunca o excep tie de tipul System.InvalidCastException.

62 Unboxing

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Unboxing-ul permite o conversie explicit a de la object sau System.ValueType la orice tip valoare, sau de la orice tip interfa ta la orice tip valoare care implementeaz a tipul interfa ta . Mai multe detalii se vor da n sec tiunea 3.3.4. Conversii explicite denite de utilizator Constau ntro conversie standard explicit a op tional a, urmat a de execu tia unei conversii explicite, urmat a de o alt a conversie standard explicit a op tional a.

3.3.4

Boxing si unboxing

Boxingul si unboxingul reprezint a modalitatea prin care C# permite utilizarea simpl a a sistemului unicat de tipuri. Spre deosebire de Java, unde exist a tipuri primitive (care nu pot con tine metode) si tipuri referin ta , n C# toate tipurile sunt derivate din clasa object (alias System.Object). De exemplu, tipul int (alias System.Int32) este derivat din clasa System.ValueType care la r andul ei este derivat a din clasa object (alias System.Object). Ca atare, un ntreg este compatibil cu object. Boxing Conversia de tip boxing permite oric arui tip valoare s a e implicit convertit c atre tipul object sau c atre un tip interfa ta implementat de tipul valoare. Boxingul unei valori const a n alocarea unei variabile de tip obiect si copierea valorii ini tiale n acea instan ta . Procesul de boxing al unei valori sau variabile de tip valoare se poate n telege ca o simulare de creare de clas a pentru acel tip: sealed class T_Box { T value; public T_Box(T t) { value = t; } } Astfel, declara tiile: int i = 123; object box = i;

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

63

procesul se desf a soar a ca n gura 3.1: la linia 1, se declar a si se ini tializeaz a o variabil a de tip valoare, care va con tine valoarea 10.La urm atoarea linie se va crea o referin ta o c atre un obiect alocat n heap, care va con tine at at valoarea 10, c at si o informa tie despre tipul de dat a con tinut ( n cazul nostru, System.Int32). Unboxingul se face printro conven tie explicit a, ca n linia a treia.
i 10

o 10 j 10

System.Int32

Figura 3.1: Boxing si unboxing Determinarea tipului pentru care sa f acut mpachetarea se face prin intermediul operatorului is : int i = 123; object o = i; if (o is int) { Console.Write("Este un int inauntru!"); } Boxingul duce la o clonare a valorii care va con tinut a. secven ta: int i = 10; object o = i; i++; Console.WriteLine("in o: {0}", o); va a sa valoarea nglobat a n obiect, 10. Altfel spus,

64

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.4

Declara tii de variabile si constante

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

3.5
3.5.1

Instruc tiuni C#
Declara tii de etichete

O etichet a poate prexa o instruc tiune. Ea este vizibil a n ntregul bloc si toate sub-blocurile con tinute. O etichet a poate referit a de c atre o instruc tiune 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.5. INSTRUCTIUNI C# } }

65

3.5.2

Instruc tiuni de selec tie

Instruc tiunea if Instruc tiunea if execut a o instruc tiune n func tie de valoarea de adev ar a unei expresii logice. Are formele: if (expresie logica) instructiune; if (expresie logica) instructiune; else instructiune; Instruc tiunea switch Permite executarea unei instruc tiuni n func tie de valoarea unei expresii, care se poate reg asi sau nu ntro list a de valori candidat: switch (expresie) { case eticheta: instructiune; case eticheta: instructiune; ... default: instructiune; } O etichet a reprezint a o expresie constant a. O instruc tiune poate s a si lipseasc a si n acest caz se va executa instruc tiunea de la case ul urm ator, sau de la default. Sec tiunea default poate s a lipseasc a. Dac a o instruc tiune este nevid a, atunci va trebui s a e terminat a cu o instruc tiune break sau goto case expresieConstanta sau goto default. Expresia dup a care se face selec tia poate de tip sbyte, byte, short, ushort, int, uint, long, ulong, char, string, enumerare. Dac a valoarea expresiei se reg ase ste printre valorile specicate la clauzele case, atunci instruc tiunea corespunz atoare va executat a; dac a nu, atunci instruc tiunea de la clauza default va executat a (dac a ea exist a). Spre deosebire de C si C++, e interzis s a se foloseasc a fenomenul de "c adere" de la o etichet a la alta; continuarea se face folosind explicit goto. switch (i) { case 0: Console.WriteLine("0");

66 break;

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

case 1: Console.Write("Valoarea "); goto case 2; case 2: case 3: Console.WriteLine(i); break; case 4: goto default; default: Console.WriteLine("Numar in afara domeniului admis"); break;//neaparat, altfel eroare de compilare } Remarc am n exemplul de mai sus c a chiar si n cazul lui default e necesar s a se foloseasc a instruc tiune de salt ( n cazul nostru break ); o motiva tie ar c a aceast a clauz a default nu e necesar s a e trecut a ultima n switch, ci chiar si pe prima pozi tie desigur caz mai rar nt alnit. Exist a un caz n care break, goto case valoare sau goto default pot s a lipseasc a: c and este evident c a o asemenea instruc tiune break/goto nu ar putea atins a (i.e. sunt prezente instruc tiunile return, throw sau o ciclare despre care se poate arma la compilare c a este innit a).

3.5.3

Instruc tiuni de ciclare

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

3.5. INSTRUCTIUNI C# b = r; } Instruc tiunea do

67

Execut a o instruc tiune o dat a sau de mai multe ori, c at timp o condi tie logic a este adev arat a (ciclu cu test posterior). Exemplu: do { S += i++; }while(i<=n) Poate con tine instruc tiuni break sau continue. Instruc tiunea for Execut a o secven ta de ini tializare, dup a care va executa o instruc tiune at ata timp c at o condi tie este adev arat a (ciclu cu test anterior); poate s a con tin a un pas de reini tializare (trecerea la pasul urm ator). Se permite folosirea instruc tiunilor break si continue. Exemplu: for (int i=0; i<n; i++) { Console.WriteLine("i={0}", i); } Instruc tiunea foreach Enumer a elementele dintro cole tie, execut and o instruc tiune pentru ecare element. Colec tia poate s a e orice instan ta a unei clase care implementeaz a interfa ta System.Collections.IEnumerable. Exemplu: int[] t = {1, 2, 3}; foreach( int x in t) { Console.WriteLine(x); } Elementul care se extrage este de tip readonly (deci nu poate transmis ca parametru ref sau out si nu se poate aplica un operator care s a i schimbe valoarea).

68

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.5.4

Instruc tiuni de salt

Permit schimbarea ordinii de execu tie a instruc tiunilor. Ele sunt: break, continue, goto, return, throw. Instruc tiunea break Produce ie sirea for tat a dintrun ciclu de tip while, do, for, foreach. Instruc tiunea continue Porne ste o nou a itera tie n interiorul celui mai apropiat ciclu con tin ator de tip while, do, for, foreach. Instruc tiunea goto Goto permite saltul al o anumit a instruc tiune. Are 3 forme: goto eticheta; goto case expresieconstanta; goto default; Cerin ta este ca eticheta la care se face saltul s a e denit a n cadrul func tiei curente si saltul s a nu se fac a n interiorul unor blocuri de instruc tiuni, deoarece nu se poate reface ntotdeauna contextul acelui bloc. Se recomand a evitarea utiliz arii intense a acestui cuv ant cheie, n caz contrar se poate ajunge la fenomenul de "spagetti code". Pentru o argumentare consistent a a acestei indica tii, a se vedea articolul clasic al lui Edsger W. Dijkstra, "Go To Statement Considered Harmful": http://www.acm.org/classics/oct95/ Instruc tiunea return Determin a cedarea controlului fun tiei apelante de c atre func tia apelat a. Dac a func tia apelat a are tip de retur, atunci instruc tiunea return trebuie s a e urmat a de o expresie care suport a o conversie implicit a c atre tipul de retur.

3.5.5

Instruc tiunile try, throw, catch, nally

Permit tratarea excep tiilor. Vor studiate n detaliu la capitolul de excep tii.

3.5. INSTRUCTIUNI C#

69

3.5.6

Instruc tiunile checked si unchecked

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

3.5.7

Instruc tiunea lock

Ob tine excluderea mutual a asupra unui obiect pentru executarea unui bloc de instruc tiuni. Are forma: lock (x) instructiune X trebuie s a e de tip referin ta (dac a este de tip valoare, nu se face boxing).

3.5.8

Instruc tiunea using

Determin a ob tinerea a unei sau mai multor resurse, execut a o instruc tiune si apoi disponibilizeaz a resursa: using ( achizitie de resurse ) instructiune O resurs a este o clas a sau o structur a care implementeaz a interfa ta System.IDisposable, care include o sigur a metod a f ar a parametri Dispose(). Achizi tia de resurse se poate face sub form a de variabile locale sau a unor expresii; toate acestea trebuie s a e implicit convertibile la IDisposable. Variabilele locale alocate ca resurse sunt readonly. Resursele sunt automat dealocate (prin apelul de Dispose ) la sf ar situl instruc tiunii (care poate bloc de instruc tiuni). Motivul pentru care exist a aceast a instruc tiune este unul simplu: uneori se dore ste ca pentru anumite obiecte care de tin resurse importante s a se apeleze automat metod a Dispose() de dezalocare a lor, c at mai repede cu putin ta . Exemplu:

70

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

Spa tii de nume

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

engl: namespaces

3.6. SPATII DE NUME

71

3.6.1

Declara tii de spa tii de nume

O declara tie de spa tiu de nume const a n cuv antul cheie namespace, urmat de identicatorul spa tiului de nume si de blocul spa tiului de nume, delimitat de acolade. Spa tiile de nume sunt implicit publice si acest tip de acces nu se poate modica. In interiorul unui spa tiu de nume se pot utiliza alte spa tii de nume, pentru a se evita calicarea complet a a claselor. Identicatorul unui spa tiu de nume poate simplu sau o secven ta de identicatori separa ti prin ".". Cea de a doua form a permite denirea de spa tii de nume imbricate, f ar a a se imbrica efectiv: namespace N1.N2 { class A{} class B{} } este echivalent a cu: namespace N1 { namespace N2 { class A{} class B{} } } Dou a declara tii de spa tii de nume cu aceea si denumire contribuie la declararea unui acela si spa tiu de nume: namespace N1.N2 { class A{} } namespace N1.N2 { class B{} } este echivalent a cu cele dou a declara tii anterioare.

72

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

3.6.2

Directiva using

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

3.6. 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 spa tii de nume nu trebuie s a duc a la ambiguit a ti: namespace N1 { class A{} } namespace N2 { class A{} } namespace N3 { using N1; using N2; class B { A a = null;//ambiguitate: N1.A sau N2.A? } }

73

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

74

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

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

3.6. SPATII DE NUME

75

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

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

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

= null;

N3

= null;

Numele create prin directive de alias sunt ascunse de c atre alte declara tii care folosesc acela si identicator n interiorul unui bloc:

76

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 inuen teaz a reciproc: namespace N1.N2{} namespace N3 { using R1 = N1; using R2 = N1.N2; using R3 = R1.N2;//eroare, R1 necunoscut }

3.7

Declararea unei clase

Declararea unei clase se face n felul urm ator: atributeopt modicatori-de-clasaopt class identicator clasa-de-bazaopt corpclasa ;opt Modicatorii de clas a sunt: public - clasele publice sunt accesibile de oriunde; poate folosit at at pentru clase imbricate, c at si pentru clase care sunt con tinute n spa tii de nume; internal - se poate folosi at at pentru clase imbricate, c at si pentru clase care sunt con tinute n spa tii de nume (este modicatorul implicit pentru clase care sunt con tinute n spa tii de nume). Semnic a acces permis doar n clasa sau spa tiul de nume care o cuprinde; protected - se poate specica doar pentru clase imbricate; tipurile astfel calicate sunt accesibile n clasa curent a sau n cele derivate (chiar dac a clasa derivat a face parte din alt spa tiu de nume); private - doar pentru clase imbricate; semnic a acces limitat la clasa con tin atoare; este modicatorul implicit;

3.8. MEMBRII UNEI CLASE

77

protected internal - folosibil doar pentru clase imbricate; tipul denit este accesibil n spa tiul de nume curent, n clasa con tin atoare sau n tipurile derivate din clasa con tin atoare; new - permis pentru clasele imbricate; clasa astfel calicat a ascunde un membru cu acela si nume care este mo stenit; sealed - o clas a sealed nu poate mo stenit a; poate clas a imbricat a sau nu; abstract - clasa care este incomplet denit a si care nu poate instan tiat a; folosibil a pentru clase imbricat sau con tinute n spa tii de nume; partial - clasa este denit a n mai multe siere

3.8

Membrii unei clase

Corpul unei clase se specic a n felul urm ator: { declaratii-de-membri };opt Membrii unei clase sunt mp ar titi n urm atoarele categorii: constante c ampuri metode propriet a ti evenimente indexatori operatori constructori (de instan ta ) destructor constructor static tipuri Acestor membri le pot ata sa ti modicatorii de acces: public - membrul este accesibil de oriunde;

78

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

protected - membrul este accesabil de c atre orice membru al clasei con tin atoare si de c atre clasele derivate; internal - membrul este accesabil doar n assembly-ul curent; protected internal - reuniunea precedentelor dou a; private - accesabil doar n clasa con tin atoare; este specicatorul implicit.

3.9

Constructori de instan ta

Un constructor de instan ta este un membru care implementeaz a ac tiuni care sunt cerute pentru a ini tializa o instan ta a unei clase. Declararea unui astfel de constructor se face n felul urm ator: atributeopt modicatori-de-constructor declarator-de-constructor corp-constructor Un modicator de constructor poate : public, protected, internal, private, extern. Un declarator de constructor are forma: nume-clasa (lista-parametrilor-formaliopt ) initializator-de-constructoropt unde initializatorul-de-constructor are forma: : base( lista-argumenteopt ) sau : this( lista-argumenteopt ). Corp-constructor poate : un bloc de declara tii si instruc tiuni delimitat de acolade sau caracterul punct si virgul a. Un constructor are acela si nume ca si clasa din care face parte si nu returneaz a un tip. Constructorii de instan ta nu se mo stenesc. Dac a o clas a nu con tine nici o declara tie de constructor de instan ta , atunci compilatorul va crea automat unul implicit. O clas a care este mo stenit a dintr-o alt a clas a ce nu are constructori f ar a parametri va trebui s a utilizeze un apel de constructor de clas a de baz a pentru care s a furnizeze parametrii potrivi ti; acest apel se face prin intermediul ini tializatorului de constructor. Un constructor poate apela la un alt constructor al clasei din care face parte pentru a efectua ini tializ ri. C and exist a c ampuri instan ta care au o expresie de ini tializare n afara constructorilor clasei respective, atunci aceste ini tializ ari se vor face nainte de apelul de constructor al clasei de baz a.

3.10

C ampuri

Un c amp reprezint a un membru asociat cu un obiect sau cu o clas a. Modicatorii de c amp care se pot specica op tional naintea unui c amp sunt cei de mai sus, la care se adaug a modicatorii new, readonly, volatile, static,

3.10. CAMPURI

79

ce vor prezenta ti mai jos. Pentru orice c amp este necesar a precizarea unui tip de date, ce trebuie s a aibe gradul de accesibilitate cel pu tin cu al c ampului ce se declar a. Op tional, c ampurile pot ini tializate cu valori compatibile. Un astfel de c amp se poate folosi e prin specicarea numelui s au, e printr-o calicare bazat a pe numele clasei sau al unui obiect. Exemplu: class A { int a;//acces implicit de tip privat static void Main() { A objA = new A(); objA.a = 1;//se poate accesa in interiorul clasei } }

3.10.1

C ampuri instan te

Dac a o declara tie de c amp nu include modicatorul static, atunci acel c amp se va reg asi n orice obiect de tipul clasei curente care va instan tiat. Modic ari ale acestor c ampuri se vor face independent pentru ecare obiect. Deoarece un astfel de c amp are o valoare specic a ec arui obiect, accesarea lui se va face prin calicarea cu numele obiectului: objA.a = 1; (dac a modicatorii de acces permit a sa ceva). In interiorul unui instan te de clase se poate folosi cuv antul this, reprezent and referin ta la obiectul curent.

3.10.2

C ampuri statice

C and o declara tie de c amp include un specicator static, c ampul respectiv nu apa tine ec arei instan te n particular, ci clasei ns a si. Accesarea unui c amp static din exteriorul clasei nu se face prin intermediul unui obiect, ci prin numele clasei: class B { public static int V = 3; static void Main() {

80

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME B.V++;//corect V++;//corect B b = new B(); b.V++//incorect }

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

3.10.3

C ampuri readonly

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

3.10.4

C ampuri volatile

Modicatorul "volatile" se poate specica doar pentru tipurile: byte, sbyte, short, ushort, int, uint, char, oat, bool; un tip enumerare av and tipul de reprezentare byte, sbyte, short, ushort, int, uint; un tip referin ta Pentru c ampuri nevolatile, tehnicile de optimizare care reordoneaz a instruc tiunile pot duce la rezultate nea steptate sau nepredictibile n programe multithreading care acceseaz a c ampurile f ar a sincronizare (efectuabil a cu instruc tiunea lock).

3.11. CONSTANTE

81

Aceste optimiz ari pot f acute de c atre compilator, de c atre sistemul de 5 rulare sau de c atre hardware. Urm atoarele tipuri de optimiz ari sunt afectate n prezen ta unui modicator volatile: citirea unui c amp volatile este garantat a c a se va nt ampla nainte de orice referire la c amp care apare dup a citire; orice scriere a unui c amp volatile este garantat a c a se va petrece dup a orice instruc tiune anterioar a care se refer a la c ampul respectiv.

3.10.5

Ini tializarea c ampurilor

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

3.11

Constante

O constant a este un c amp a c arui valoare poate calculat a la compilare. O constant a poate prexat a de urm atorii modicatori: new, public, protected, internal, protected internal,private. Cuvantul new poate s a se combine cu unul din ceilal ti 4 modicatori de acces. Pentru un c amp constant e obligatoriu s a se asigneze o valoare calculabil a la compilare: class A { public const int n=2; } Tipul unei constante poate sbyte, byte, short, ushort, int, uint, long, ulong, char, oat, double decimal, bool, string, enum, referin ta . Valoarea care se asignez a unei constante trebuie s a admit a o conversie implicit a c atre tipul constantei. Tipul unei constante trebuie s a e cel pu tin la fel de accesibil ca si constanta ns a si.
5

Engl: runtime system

82

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Orice c amp constant este automat un c amp static. Un c amp constant difer a de un c amp static readonly : const -ul are o valoare cunoscut a la compilare, pe c and valoarea unui readonly poate ini tializat a la runtime n interiorul constructorului (cel mai t arziu, de altfel).

3.12

Metode

O metod a este un membru care implementeaz a o ac tiune care poate efectuat a de c atre un obiect sau o clas a. Antetul unei metode se declar a n felul urm ator: atributeopt modicator-de-metodaopt tip-de-retur nume (lista-parametrilorformaliopt ) corp-metoda unde modicator-de-metoda poate : orice modicator de acces new static virtual sealed override abstract extern Tipul de retur poate orice tip de dat a care este cel pu tin la fel de accesibil ca si metoda ns a si sau void (absen ta informa tiei returnate); nume poate un identicator de metod a din clasa curent a sau un identicator calicat cu numele unei interfe te pe care o implementeaz a (NumeInterfata.numeMetoda ); parametrii pot de tip ref, out, params, sau f ar a nici un calicator; corpmetoda este un bloc cuprins ntre acolade sau doar caracterul ; (dac a este vorba de o metod a ce nu se implementeaz a n tipul curent). Despre calicatorii virtual, override, sealed, new, abstract se va discuta mai pe larg ntro sec tiune viitoare.

3.12. METODE

83

3.12.1

Metode statice si nestatice

O metod a se declar a a static a dac a numele ei este prexat cu modicatorul de metod a static. O astfel de metod a nu opereaz a asupra unei instan te anume, ci doar asupra clasei. Este o eroare ca o metod a static a s a fac a referire la un membru nestatic al unei clase. Apelul unei astfel de metode se face prin NumeClasa.NumeMetoda sau direct NumeMetoda dac a este apelat a din context static al aceleia si clase (de exemplu de c atre o metod a static a, dar se poate si dintro clas a imbricat a a se vedea sec tiunea dedicat a 4.5). O metod a nestatic a nu are cuv antul static specicat; ea este apelabil a pentru un obiect anume.

3.12.2

Metode externe

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

84

CURS 3. CLASE, INSTRUCTIUNI, SPATII DE NUME

Curs 4 Clase (continuare)


4.1 Propriet a ti

O proprietate este un membru care permite acces la partea de stare a unei clase. Exemple de propriet a ti sunt: lungimea unui sir, numele unui client, textul continut ntrun control de tip TextBox. Propriet a tile sunt extensii naturale ale c ampurilor, cu deosebirea c a ele nu presupun alocarea de memorie. Ele sunt de fapt ni ste metode (accesori) care permit citirea sau setarea unor atribute ale unui obiect sau clase; reprezint a modalitatea de scriere a unor metode de tip get/set pentru clase sau obiecte. Declararea unei propriet a ti se face astfel: modicator-de-proprietateopt tip numeproprietate denitie-getopt denitie-setopt unde modicator-de-proprietate este: atributeopt modicator-de-accesopt get corp-get atributeopt modicator-de-accesopt set corp-set Modicatorii de acces sunt: protected, internal, private, protected internal, public. Tipul unei propriet a ti specic a tipul de dat a ce poate accesat, i.e. ce valori vor putea atribuite propriet a tii respective (dac a accesorul de tip set a fost denit), respectiv care este tipul valorii returnate de aceast a proprietate (corespunz ator accesorului de tip get ). Exemplu: using System; class Circle { private double radius; public double Radius { 85

86 get { return radius; } set { radius = value; }

CURS 4. CLASE (CONTINUARE)

} public double Area { get { return Math.PI * radius * radius; } set { radius = Math.Sqrt(value/Math.PI); } } } class Test { static void Main() { Circle c = new Circle(); c.Radius = 10; Console.WriteLine(Area: {0}, c.Area); c.Area = 15; Console.WriteLine(Radius: {0}. c.Radius) } } Un accesor de tip get corespunde unei metode f ar a parametri, care returneaz a o valoare de tipul propriet a tii. C and o proprietate este folosit a ntro expresie, accesorul get este o apelat pentru a returna valoarea cerut a. Un accesor de tip set corespunde unei metode cu un singur parametru de tipul propriet a tii si tip de retur void. Acest parametru implicit al lui set este numit ntotdeauna value. C and o proprietate este folosit a ca destinatar ntro atribuire, sau c and se folosesc operatorii ++ si , accesorului set i

TI 4.1. PROPRIETA

87

se transmite un parametru care reprezint a noua valoare. In func tie de prezen ta sau absen ta accesorilor, o proprietate este clasicat a dup a cum urmeaz a: proprietate readwrite, dac a are ambele tipuri de accesori; proprietate readonly, dac a are doar accesor de tip get ; este o eroare de compilare s a se fac a referire n program la o proprietate n sensul n care sar cere operarea cu un accesor de tip set ; proprietate writeonly, dac a este prezent doar accesorul de tip set ; este o eroare de compilare utilizarea unei propriet a ti ntrun context n care ar necesar a prezen ta accesorului get. Exist a cazuri n care se dore ste ca un accesor sa aib a un anumit grad de acces (public, de exemplu), iar celalat alt tip de acces (e.g. protected). Incep and cu .NET Framework 2.0, acest lucru este posibil: public class Employee { private string name; public Employee(string name) { this.name = name; } public string Name { get { return name; } protected set { name = value; } } } Intrun asemenea caz, trebuie respectat a urm atoarea regul a: ntreaga proprietate trebuie s a e declarat a cu grad de acces mai larg dec at accesorul pentru care se restic tioneaz a gradul de acces. Demn de men tionat este c a propriet a tile pot folosite nu doar pentru a asigura o sintax a simplu de folosit pentru metodele tradi tionale de tip get/set, ci si pentru scrierea controalelor .NET utilizator. a reprezentarea unui control utilizator: In gura 4.1 este dat Codul corespunz ator este dat mai jos: using System; using System.Collections;

88

CURS 4. CLASE (CONTINUARE)

Figura 4.1: Control denit de utilizator

using using using using

System.ComponentModel; System.Drawing; System.Data; System.Windows.Forms;

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

TI 4.1. PROPRIETA #region Component Designer generated code

89

private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); 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 = "";

90

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

} Interesante sunt aici propriet a tile publice Street, Number si Phone care vor vizibile n fereastra Properties atunci c and acest control va ad augat la o form a. Atributele cuprinse ntre paranteze drepte sunt op tionale, dar vor face ca aceste propriet a ti s a e grupate n sec tiunea de date a ferestrei Properties, si nu n cea Misc.

4.2. INDEXATORI

91

4.2

Indexatori

Uneori are sens tratarea unui obiect ca ind un vector de elemente. Un indexator este o generalizare a supra nc arc arii operatorului [] din C++. Declararea unui indexator se face n felul urm ator: atributeopt modicatori-de-indexatoropt declarator-de-indexator {declaratiide-accesori} Modicatorii de indexator pot : new, public, protected, internal, private, protected internal, virtual, sealed, override, abstract, extern. Declaratorul de indexator are forma: tip-de-retur this[lista-parametrilor-formali]. Lista parametrilor formali trebuie s a con tin a cel pu tin un parametru si nu poate s a aibe vreun parametru de tip ref sau out. Declara tiile de accesor vor con tine accesor get sau accesor set, asem an ator cu cei de la propriet a ti. Exemple: 1. Exemplul 1: un indexator simplu: using System; class MyVector { private double[] v; public MyVector( int length ) { v = new double[ length ]; } public int Length { get { return length; } } public double this[int index] { get { return v[ index]; } set {

92 v[index] = value; } } }

CURS 4. CLASE (CONTINUARE)

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

4.2. INDEXATORI { 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]); }

93

94

CURS 4. CLASE (CONTINUARE) set { row[column - 1] = value; } } private 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"]; Console.WriteLine("Id: {0}", val.Data);

4.2. INDEXATORI

95

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 mul ti 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); } }

96

CURS 4. CLASE (CONTINUARE) 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(); } } } }

Remarc am ca accesarea elementelor se face prin perechi de tipul get/set, precum la propriet a ti. Ca si n cazul propriet a tilor, este posibil ca un accesor s a aibe un alt grad de acces dec at cel alalt, folosind acelasi mecanism: se declar a indexatorul ca av and un anumit grad de accesibilitate, iar pentru un accesor se va declara un grad de acces mai restrictiv.

4.3. OPERATORI

97

4.3

Operatori

Un operator este un membru care dene ste semnica tia unei expresii operator care poate aplicat a unei instan te a unei clase. Corespunde supra nc arc arii operatorilor din C++. O declara tie de operator are forma: atributeopt modicatori-de-operator declaratie-de-operator corp-operator Se pot declara operatori unari, binari si de conversie. Urm atoarele reguli trebuie s a e respectate pentru orice operator: 1. Orice operator trebuie s a e declarat public si static. 2. Parametrii unui operator trebuie s a e transmi si prin valoare; 3. Acela si modicator nu poate ap area de mai multe ori n antetul unui operator

4.3.1

Operatori unari

Supra nc arcarea operatorilor unari are forma: tip operator operator-unar-supraincarcabil (tip identicator) corp Operatorii unari supra nc arcabili sunt: + - ! ++ true false. Urm atoarele reguli trebuie s a e respectate la supra nc arcarea unui operator unar (T reprezint a clasa care con tine deni tia operatorului): 1. Un operator +, -, !, trebuie s a preia un singur parametru de tip T si poate returna orice tip. 2. Un operator ++ sau trebuie s a preia un singur parametru de tip T si trebuie s a returneze un rezultat de tip T. 3. Un operator unar true sau false trebuie s a preia un singur parametru de tip T si s a returneze bool. Operatorii true si false trebuie s a e ori ambii deni ti, ori nici unul (altfel apare o eroare de compilare). Ei sunt necesari pentru cazuri de genul: if( a==true ) sau if( a==false )

98

CURS 4. CLASE (CONTINUARE)

Mai general, ei pot folosi ti ca expresii de control n if, do, while si for, precum si n operatorul ternar "? :". De si pare paradoxal, nu este obligatoriu ca if (a==true) s a e echivalent a cu if (!(a==false)), de exemplu pentru tipuri SQL care pot avea valoare de nul, ceea ce nu nseam a nici true, nici false, ci lips a de informa tie. Exemplu: 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; } ... } Exemplul de mai jos arat a modul n care se face supra nc arcarea operatorului ++, care poate folosit at at ca operator de preincrementare c at si ca operator de postincrementare: public class IntVector { public int Length { ... } // read-only property public int this[int index] { ... } // read-write indexer public IntVector(int vectorLength) { ... } public static IntVector operator++(IntVector iv) { IntVector temp = new IntVector(iv.Length); for (int i = 0; i < iv.Length; ++i) temp[i] = iv[i] + 1; return temp; } } class Test { static void Main()

4.3. OPERATORI { 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 } }

99

4.3.2

Operatori binari

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

4.3.3

Operatori de conversie

O declara tie de operator de conversie trebuie introduce o conversie denit a de utilizator, care se va ad auga (dar nu va suprascrie) la conversiile predenite. Declararea unui operator de conversie se face astfel: implicit operator tip (tip parametru) corp explicit operator tip (tip parametru) corp Dup a cum se poate deduce, conversiile pot implicite sau explicite. Un astfel de operator va face conversia de la un tip surs a, indicat de tipul parametrului

100

CURS 4. CLASE (CONTINUARE)

din antet la un tip destina tie, indicat de tipul de retur. O clas a poate s a declare un operator de conversie de la un tip surs a S la un tip destina tie T cu urm atoarele condi tii: 1. S si T sunt tipuri diferite 2. Unul din cele dou a tipuri este clasa n care se face denirea. 3. T si S nu sunt object sau tip interfa ta . 4. T si S nu sunt baze una pentru cealalt a. Un bun design asupra operatorilor de conversie are n vedere urm atoarele: Conversiile implicite nu ar trebui s a duc a la pierdere de informa tie sau la apari tia de excep tii; Dac a prima condi tie nu este ndeplinit a, atunci neap arat trebuie declarat a ca o conversie explicit a. Exemplu: using System; public class Digit { byte value; public Digit(byte value) { if (value < 0 || value > 9) throw new ArgumentException(); this.value = value; } public static implicit operator byte(Digit d) { return d.value; } public static explicit operator Digit(byte b) { return new Digit(b); } } Prima conversie este implicit a pentru c a nu va duce la pierderea de informa tie. Cea de doua poate s a arunce o excep tie (via constructor) si de aceea este declarat a ca si conversie explicit a.

4.3. OPERATORI

101

4.3.4

Exemplu: clasa Fraction

using System; public class Fraction { public Fraction(int numerator, int denominator) { Console.WriteLine("In constructor Fraction(int, int)"); this.numerator=numerator; this.denominator=denominator; } public Fraction(int wholeNumber) { Console.WriteLine("In Constructor Fraction(int)"); numerator = wholeNumber; denominator = 1; } 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) {

102

CURS 4. CLASE (CONTINUARE) Console.WriteLine("In operator !="); return !(lhs==rhs);

} public override bool Equals(object o) { Console.WriteLine("In metoda Equals"); if (! (o is Fraction) ) { return false; } return this == (Fraction) o; } 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( ));

4.4. CONSTRUCTOR STATIC 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}", f5.ToString( ), f2.ToString( )); } } }

103

4.4

Constructor static

Un constructor static este un membru care implementeaz a ac tiunile cerute pentru ini tializara unei clase. Declararea unui constructor static se face ca mai jos: atributeopt modicator-de-constructor-static identicator( ) corp Modicatorii de constructori statici se pot da sub forma: externopt static sau static externopt Constructorii statici nu se mo stenesc, nu se pot apela direct si nu se pot supra nc arca. Un constructor static se va executa cel mult o dat a ntr-o aplica tie. Se garanteaz a faptul c a acest constructor se va apela naintea primei cre ari a unei instan te a clasei respective sau naintea primului acces la un membru static. Acest apel este nedeterminist, necunosc anduse exact c and sau dac a se va apela. Un astfel de constuctor nu are specicator de acces si poate s a acceseze doar membri statici. Exemplu: class Color { public Color(byte red, byte green, byte blue) { this.red = red; this.green = green; this.blue = blue; } byte red;

104 byte green; byte blue;

CURS 4. CLASE (CONTINUARE)

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

Clase imbricate

O clas a con tine membri, iar n particular ace stia pot si clase. Exemplul 1: using System; class A { class B { public static void F() { Console.WriteLine(A.B.F); } } static void Main() { A.B.F(); }

4.5. CLASE IMBRICATE } 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; //Interfata publica public void AddToFront(object o) {...} public void AddToBack(object o) {...} public object RemoveFromFront() {...} public object RemoveFromBack() {...} public int Count { get {...} } }

105

Accesarea unei clase imbricate se face prin NumeClasaExterioara.NumeClasaInterioara (a sa cum este ar atat n Exemplul 1), de unde se deduce c a o clas a imbricat a se comport a ca un membru static al tipului con tin ator. O clas a declarat a n interiorul unei alte clase poate avea unul din gradele de accesibilitate public, protected internal, protected, internal, private (implicit este private ). O clas a declarat a n interiorul unei structuri poate declarat a public, internal sau private (implicit private ). Crearea unei instan te a unei clase imbricate nu trebuie s a e precedat a de crearea unei instan te a clasei exterioare con tin atoare, a sa cum se vede din Exemplul 1. O clas a imbricat a nu are vreo rela tie special a cu membrul predenit this al clasei con tin atoare. Altfel spus, nu se poate folosi this n interiorul unei clase imbricate pentru a accesa membri instan ta din tipul con tin ator. Dac a o clas a imbricat a are nevoie s a acceseze membri instan ta ai clasei con tin atoare, va trebui s a primeasc a prin constructor parametrul this care s a se refer a la o astfel de instan ta :

106 using System; class C { int i = 123; public void F() { Nested n = new Nested(this); n.G(); }

CURS 4. CLASE (CONTINUARE)

public class Nested { C this_c; public Nested(C c) { this_c = c; } public void G() { Console.WriteLine(this_c.i); } } } class Test { static void Main() { C c = new C(); c.F(); } } Se observ a de mai sus c a o clas a imbricat a poate manipula to ti membrii din interiorul clasei con tin atoare, indiferent de gradul lor de accesibilitate. In cazul n care clasa exterioar a (con tin atoare) are membri statici, ace stia pot utiliza ti f ar a a se folosi numele clasei con tin atoare: using System; class C { private static void F() {

4.6. DESTRUCTORI Console.WriteLine("C.F"); } public class Nested { public static void G() { F(); } } } class Test { static void Main() { C.Nested.G(); } }

107

Clasele imbricate se folosesc intens n cadrul containerilor pentru care trebuie s a se construiasc a un enumerator. Clasa imbricat a va n acest caz str ans legat a de container si va duce la o implementare u sor de urm arit si de ntre tinut.

4.6

Destructori

Managementul memoriei este f acut sub platforma .NET n mod automat, de c atre garbage collector, parte component a a CLRului. Acest mecanism de garbage collection scute ste programatorul de grija dealoc arii memoriei. Dar exist a situa tii n care se dore ste s a se fac a management manual al dealoc arii resurselor (de exemplu al resurselor care tin de sistemul de operare sau de servere: siere, conexiuni la re tea sau la serverul de baze de date, ferestre, etc, sau al altor resurse al c aror management nu se face de c atre CLR). In C# exist a posibilitatea de a lucra cu destructori sau cu metode de tipul Dispose(), Close(). Un destructor se declar a n felul urm ator: atributeopt externopt ~identicator() corp-destructor unde identicator este numele clasei. Un destructor nu are modicator de acces, nu poate apelat manual, nu poate supra nc arcat, nu este mo stenit. Un destructor este o scurt atur a sintactic a pentru metoda Finalize(), care este denit a n clasa System.Object. Programatorul nu poate s a suprascrie sau s a apeleze aceast a metod a.

108 Exemplu:

CURS 4. CLASE (CONTINUARE)

~MyClass() { // Perform some cleanup operations here. } Metoda de mai sus este automat translatat a n: protected override void Finalize() { try { // Perform some cleanup operations here. } finally { base.Finalize(); } } Problema cu destructorul este c a el e chemat doar de c atre garbage collector, dar acest lucru se face nedeterminist (cu toate c a apelarea de destructor se face n cele din urm a, dac a programatorul nu mpiedic a explicit acest lucru). Exist a cazuri n care programatorul dore ste s a fac a dealocarea manual, astfel nc at s a nu a stepte ca garbage collectorul s a apeleze destructorul. Programatorul poate scrie o metoda care s a fac a acest lucru. Se sugereaz a denirea unei metode Dispose() care ar trebui s a e explicit apelat a atunci c and resurse de sistem de operare trebuie s a e eliberate. In plus, clasa respectiv a ar trebui s a implementeze interfa ta System.IDisposable, care con tine aceast a metod a. In acest caz, Dispose() ar trebui s a inhibe executarea ulterioar a a destructorului (care am vazut ca e de fapt un nalizator, metoda Finalize) pentru instan ta curent a. Aceast a manevr a permite evitarea eliber arii unei resurse de dou a ori. Dac a clientul nu apeleaz a explicit Dispose(), atunci garbage collectorul va apela el destructorul la un moment dat. Intruc at utilizatorul poate s a nu apeleze Dispose(), este necesar ca tipurile care implemeteaz a aceast a metod a s a deneasc a de asemenea destructor. Exemplu: public class ResourceUser: IDisposable {

4.7. CLASE STATICE

109

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

4.7

Clase statice

Incep and cu versiunea 2.0 a platformei .NET s-a introdus posibilitatea de a deni clase statice. Acest tip de clas a se folose ste atunci c and se dore ste accesarea membrilor f ar a a nevoie s a se lucreze cu obiecte; se pot folosi acolo unde buna func tionare nu este dependent a de starea unor instan te. Pentru a crea o clas a static a se folose ste cuv antul static n declara tia de clas a: static class MyStaticClass { //membri statici }

110

CURS 4. CLASE (CONTINUARE)

Orice clas a static a are urm atoarele propriet a ti: 1. nu poate instan tiat a 2. nu poate mo stenit a (este automat sealed, vezi sec tiunea 4.9) 3. con tine doar membri statici Exemplu: public static class TemperatureConverter { public static double CelsiusToFahrenheit(string temperatureCelsius) { double celsius = Double.Parse(temperatureCelsius); double fahrenheit = (celsius * 9 / 5) + 32; return fahrenheit; } public static double FahrenheitToCelsius(string temperatureFahrenheit) { double fahrenheit = Double.Parse(temperatureFahrenheit); //Convert Fahrenheit to Celsius. double celsius = (fahrenheit - 32) * 5 / 9; return celsius; } } class TestTemperatureConverter { static void Main() { Console.WriteLine("Optiuni"); Console.WriteLine("1. Celsius->Fahrenheit."); Console.WriteLine("2. Fahrenheit->Celsius."); Console.Write(":"); string selection = Console.ReadLine(); double f, c = 0; switch (selection) { case "1": Console.Write("Temperatura Celsius: "); f = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine()); Console.WriteLine("Temperatura in Fahrenheit: {0:F2}", f); break;

4.8. SPECIALIZAREA S I GENERALIZAREA

111

case "2": Console.Write("Temperatura Fahrenheit: "); c = TemperatureConverter.FahrenheitToCelsius(Console.ReadLine()); Console.WriteLine("Temperature in Celsius: {0:F2}", c); break; } } }

4.8

Specializarea si generalizarea

Specializarea reprezint a o tehnic a de a ob tine noi clase pornind de la cele existente. Deseori ntre clasele pe care le model am putem observa rela tii de genul este un/o: un om este un mamifer, un salariat este un angajat, etc. Toate acestea duc la crearea unei ierarhii de clase, n care din clase de baz a (mamifer sau angajat) descind alte clase, care pe l ang a comportament din clasa de baz a mai au si caracteristici proprii. Ob tinerea unei clase derivate plec and de la alt a clas a se nume ste specializare iar opera tia invers a se nume ste generalizare. O clas a de baz a dene ste un tip comun, compatibil cu oricare din clasele derivate (direct sau indirect). In C# o clas a nu trebuie s a mo steneasc a explicit din alt a clas a; n acest caz se va considera c a ea este implicit derivat a din clasa predenit a object (tot una cu Object ). C# nu permite mo stenire multipl a, elimin and astfel complica tiile nt alnite n C++. Ca alternativ a, se permite totu si implementarea de mai multe interfe te.

4.8.1

Specicarea mo stenirii

In C# se pentru o clas a D se dene ste clasa de baz a B folosind urm atoarea formul a: class D: B { //declaratii si instructiuni } Dac a pentru o anumit a clas a nu se specic a dou a puncte urmate de numele unei clase de baz a atunci object va deveni baz a pentru clasa n cauz a. Exemplu: //clasa de baza in C#

112 public class Employee { protected string name; protected string ssn; }

CURS 4. CLASE (CONTINUARE)

//clasa derivata in C# public class Salaried : Employee { protected double salary; public Salaried( string name, string ssn, double salary ) { this.name = name; this.ssn = ssn; this.salary = salary; } } Se observ a c a c ampurile name si ssn din clasa de baz a sunt accesibile n clasa derivat a, datorit a specicatorului de acces protected.

4.8.2

Apelul constructorilor din clasa de baz a

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

4.8. SPECIALIZAREA S I GENERALIZAREA 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 ob tine: Employee constructor: Jesse, 1234567890 Salaried constructor: 100000.00

113

de unde se deduce c a apelul de constructor de clas a de baz a se face naintea execut arii oric aror alte instruc tiuni con tinute n constructorul clasei derivate. Dac a o clas a de baz a nu are denit nici un constructor, atunci se va crea unul implicit (f ar a parametri). Dac a dup a un constructor al unei clase derivate nu se specic a un ini tializator de constructor, atunci va apelat constructorul implicit (e creat automat de compilator, e scris de c atre programator); dac a nu exist a nici un constructor implicit n clasa de baz a, atunci programatorul trebuie s a specice un constructor din clasa de baz a care va apelat, mpreun a cu parametrii adecva ti.

4.8.3

Operatorii is si as

Operatorul is Operatorl is este folosit pentru a verica dac a un anumit obiect este de un anumit tip. Este folosit de obicei nainte opera tiilor de downcasting (conversie explicit a de la un tip de baz a la unul derivat). Operatorul se folose ste astfel:

114 instanta is NumeClasa

CURS 4. CLASE (CONTINUARE)

rezultatul acestei opera tii ind true sau false. Exemplu: Employee e = ...; if (e is Salaried) { Salaried s = (Salaried)e; } In cazul n care sar face conversia explicit a iar obiectul nu este de tipul la care se face conversia ar rezulta o excep tie: System.InvalidCastException. Operatorul as Acest operator este folosit pentru conversii explicite, return and un obiect de tipul la care se face conversia sau null dac a conversia nu se poate face (nu se arunc a excep tii). Determinarea validit a tii conversiei se face test and valoarea rezultat a fa ta de null: dac a rezultatul e null atunci conversia nu sa putut face. Ca si precedentul operator se folose ste n special la downcasting. Exemplu: Employee e = ...; Salaried s = e as Salaried; if (s != null) { //se lucreaza cu instanta valida de tip Salaried }

4.9

Clase sealed

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

Curs 5 Clase - polimorsm, clase abstracte. Structuri, interfe te, delega ti


5.1 Polimorsmul

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

5.1.1

Polimorsmul parametric

Este cea mai slab a form a de polimorsm, ind reg asit a n majoritatea limbajelor. Prin polimorsmul parametric se permite ca o implementare de func tie s a poat a prelucra orice num ar de parametri. Acest lucru se poate ob tine prin folosirea n C# a unui parametru de tip params (vezi 3.2).

5.1.2

Polimorsmul adhoc

Se mai nume ste si supra nc arcarea metodelor, mecanism prin care n cadrul unei clase se pot scrie mai multe metode, av and acela si nume, dar tipuri si numere diferite de parametri de apel. Alegerea func tiei care va apelat a se va face la compilare, pe baza coresponden tei ntre tipurile parametrilor de apel si tipurile parametrilor formali. 115

116

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

5.1.3

Polimorsmul de mo stenire

Este forma cea mai evoluat a de polimorsm. Dac a precedentele forme de polimorsm sunt aplicabile f ar a a se pune problema de mo stenire, n acest caz este necesar s a existe o ierarhie de clase. Mecanismul se bazeaz a pe faptul c a o clas a de baz a dene ste un tip care este compatibil din punct de vedere al atribuirii cu orice tip derivat, ca mai jos: class B{...} class D: B{...} class Test { static void Main() { B b = new D();//upcasting=conversie implicita catre baza B } } Intrun astfel de caz se pune problema: ce se nt ampl a cu metodele av and aceea si list a de parametri formali si care se reg asesc n cele dou a clase? S a consider am exemplul urm ator: avem o clas a Shape care con tine o metod a public void Draw() ; din Shape se deriveaz a clasa Polygon care implementeaz a aceea si metod a n mod specic. Problema care se pune este cum se rezolv a un apel al metodei Draw n context de upcasting: class Shape { public void Draw() { System.Console.WriteLine(Shape.Draw()); } } class Polygon: Shape { public void Draw() { System.Console.WriteLine(Polygon.Draw()); //desenarea s-ar face prin GDI+ } } class Test {

5.1. POLIMORFISMUL static void Main() { Polygon p = new Polygon(); Shape s = p;//upcasting p.Draw(); s.Draw(); } } La compilarea acestui cod se va ob tine un avertisment:

117

warning CS0108: The keyword new is required on Polygon.Draw() because it hides inherited member Shape.Draw() dar despre specicatorul new vom vorbi mai jos (oricum, ad augarea lui nu va schimba cu nimic comportamentul de mai jos, doar va duce la dispari tia de avertisment). Codul de mai sus va a sa: Polygon.Draw() Shape.Draw() Dac a prima linie a sat a este conform a cu intui tia, cea de-a doua pare la prima vedere ciudat a, dar de fapt este perfect justicabil a: apelul de metod a Draw() este rezolvat n ecare caz la compilare pe baza tipului declarat al obiectelor; ca atare apelul precedent este legat de corpul metodei Draw din clasa Shape, chiar dac a s a fost instan tiat de fapt pe baza unui obiect de tip Polygon. Este posibil ca s a se doreasc a schimbarea acestui comportament: apelul de metod a Draw s a e rezolvat n func tie de tipul efectiv al obiectului care face acest apel, si nu de tipul formal declarat. In cazul precedent, apelul s.Draw() trebuie s a se rezolve de fapt ca ind c atre metoda Draw() din Polygon, pentru c a acesta este tipul la rulare al obiectului s. Cu alte cuvinte, apelul ar trebui s a e rezolvat la rulare si nu la compilare, n func tie de natura efectiv a a obiectelor. Acest comportament polimorc este referit sub denumirea polimorsm de mo stenire.

5.1.4

Virtual si override

Pentru a asigura faptul c a legarea apelului de metode se face la rulare si nu la compilare, e necesar ca n clasa de baz a s a se specice c a metoda Draw() este virtual a, iar n clasa derivat a pentru aceea si metod a trebuie s a se spun a c a este o suprascriere a celei din baz a:

118

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

class Shape{ public virtual void Draw(){...} } class Polygon{ public override void Draw(){...} } In urma execut arii metodei Main din clasa de mai sus, se va a sa: Polygon.Draw() Polygon.Draw() adic a sa apelat metoda corespunz atoare tipului efectiv de la rulare, n ecare caz. In cazul n care clasa Polygon este la r andul ei mo stenit a si se dore ste ca polimorsmul s a func tioneze n continuare va trebui ca n aceast a a treia clas a s a suprascrie (override ) metoda Draw(). Un astfel de comportament polimorc este benec atunci c and se folose ste o colec tie de obiecte de tipul unei clase de baz a: Shape[] painting = new Shape[10]; painting[0] = new Shape(); painting[1] = new Polygon(); ... foreach( Shape s in painting) s.Draw();

5.1.5

Modicatorul new pentru metode

Modicatorul new se folo seste pentru a indica faptul c a o metod a dintr-o clas a derivat a care are aceea si semn atur a cu una dintro clas a de baz a nu este o suprascriere polimorc a a ei, ci apare ca o nou a metod a. Este ca si cum metoda declarat a new ar avea nume diferit. S a presupunem urm atorul scenariu: compania A creaz a o clas a A care are forma: public class A{ public void M(){ Console.WriteLine(A.M()); } }

5.1. POLIMORFISMUL

119

O alt a companie B va crea o clas a B care mo stene ste clasa A. Compania B nu are nici o inuen ta asupra companiei A sau asupra modului n care aceasta va face modic ari asupra clasei A. Ea va deni n interiorul clasei B o metod a M() si una N() : class B: A{ public void M(){ Console.WriteLine(B.M()); N(); base.M(); } protected virtual void N(){ Console.WriteLine(B.N()); } } Atunci c and compania B compileaz a codul, compilatorul C# va produce urm atorul avertisment: warning CS0108: The keyword new is required on B.M() because it hides inherited member A.M() Acest avertisment va notica programatorul c a clasa B dene ste o metod a M(), care va ascunde metoda M() din clasa de baz a A. Aceast a nou a metod a ar putea schimba n telesul (semantica) lui M(), a sa cum a fost creat ini tial de compania A. Este de dorit n astfel de cazuri compilatorul s a avertizeze despre posibile nepotriviri semantice. Oricum, programatorii din B vor trebui s a pun a n orice caz specicatorul new naintea metodei B.M() pentru a elimina avertismentul. S a presupunem c a o aplica tie folose ste clasa B() n felul urm ator: class App{ static void Main(){ B b = new B(); b.M(); } } La rulare se va a sa: B.M() B.N() A.M()

120

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

S a presupunem c a A decide ad augarea unei metode virtuale N() n clasa sa, metod a ce va apelat a din M() : public class A { public void M() { Console.WriteLine(A.M()); N(); } protected virtual void N() { Console.WriteLine(A.N()); } } La o recompilare f acut a de B, este dat urm atorul avertisment: warning CS0114: B.N() hides inherited member A.N(). To make the current member override that implementation, add the override keyword. Otherwise, add the new keyword. In acest mod compilatorul avertizeaz a c a ambele clase ofer a o metod a N() a c aror semantic a poate s a difere. Dac a B decide c a metodele N() nu sunt semantic legate n cele dou a clase, atunci va specica new, inform and compilatorul de faptul c a versiunea sa este una nou a, care nu suprascrie polimorc metoda din clasa de baz a. Atunci c and codul din clasa App este rulat, se va a sa la ie sire: B.M() B.N() A.M() A.N() Ultima linie a sat a se explic a tocmai prin faptul c a metoda N() din B este declarat a new si nu override (dac a ar fost override, ultima linie ar fost B.N(), din cauza polimorsmului). Se poate ca B s a decid a c a metodele M() si N() din cele dou a clase sunt legate semantic. In acest caz, ea poate sterge deni tia metodei B.M, iar pentru a semnala faptul c a metoda B.N() suprascrie metoda omonim a din clasa p arinte, va nlocui cuv antul new cu override. In acest caz, metoda App.Main va produce:

5.1. POLIMORFISMUL A.M() B.N()

121

ultima linie ind explicat a de faptul c a B.N() suprascrie o metod a virtual a.

5.1.6

Metode sealed

O metod a de tip override poate declarat a ca ind de tip sealed, astfel mpiedic anduse suprascrierea ei ntro clas a derivat a din cea curent a: using System; class A { public virtual void F() { Console.WriteLine(A.F()); } public virtual void G() { Console.WriteLine(A.G()); } } 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()); } } Modicatorul sealed pentru B.F va mpiedica tipul C s a suprascrie metoda F.

122

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

5.1.7

Exemplu folosind virtual, new, override, sealed

S a presupunem urm atoare ierarhie de clase, reprezentat a n Fig. 5.1; o clas a X mo steneste o clas a Y dac a sensul s age tii este de la X la Y. Fiecare clas a are o metod a void foo() care determin a a sarea clasei n care este denit a si pentru care se vor specica new, virtual, override, sealed. S a
A

+foo(): void

+foo(): void

+foo(): void

+foo(): void

Figura 5.1: Ierarhie de clase presupunem c a clasa de test arat a astfel: public class Test { static void Main() { A[] x = new A[4]; x[0] = new A(); x[1] = new B(); x[2] = new C(); x[3] = new D(); A a = new A(); B b = new B();

5.1. POLIMORFISMUL C c = new C(); D d = new D(); /* secventa 1 */ for(int i=0; i<4; i++) { x[i].foo(); } /* secventa 2 */ a.foo(); b.foo(); c.foo(); d.foo(); } }

123

In func tie de specicatorii metodelor f() din ecare clas a, se ob tin ie sirile din tabelul 5.1: Tabelul 5.1: Efecte ale diferi tilor specicatori.

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

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

124

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI Tabelul 5.1 (continuare) B.f() C.f() D.f()

Metoda B.f nlocuie ste A.f Specicator Eroare de compilare deoarece deoarece B.f nu poate suprascris a de C.f

A.f()

virtual sealed override override override

5.2

Clase si metode abstracte

Deseori pentru o anumit a clas a nu are sens crearea de instan te, din cauza unei generalit a ti prea mari a tipului respectiv. Spunem c a aceast a clas a este abstract a, iar pentru a mpiedica efectiv crearea de instan te de acest tip, se va specica cuv antul abstract naintea metodei. In exemplele de anterioare, clasele Employee si Shape ar putea g andite ca ind abstracte: ele con tin prea pu tin a informa tie pentru a putea crea instan te utile. Analog, pentru o anumit a metod a din interiorul unei clase uneori nu se poate specica o implementare. De exemplu, pentru clasa Shape de mai sus, este imposibil s a se dea o implementare la metoda Draw(), tocmai din cauza generalit a tii acestei clase. Ar util dac a pentru aceast a metod a programatorul ar obligat s a dea implement ari specice ale acestei metode pentru diversele clase derivate. Pentru a se asigura tratarea polimorc a a acestui tip abstract, orice metod a abstract a este automat si virtual a. Orice metod a care este declarat a abstract a implic a declararea clasei ca ind abstract a. Exemplu: abstract class Shape { public abstract void Draw(); //remarcam lipsa implementarii si semnul punct si virgula } Orice clas a care este derivat a dintro clas a abstract a va trebui e s a nu aib a nici o metod a abstract a mo stenit a f ar a implementare, e s a se declare ca ind abstract a. Existen ta de metode neimplementate nu va permite instan tierea clasei respective.

5.3. TIPURI PARTIALE

125

5.3

Tipuri par tiale

Incep and cu versiunea 2.0 a platfomei .NET este posibil ca deni tia unei clase, interfe te sau structuri s a e f acut a n mai multe siere surs a. Deni tia clasei se ob tine din reuniunea p ar tilor componente, lucru f acut automat de c atre compilator. Aceast a spargere n fragmente este benec a n urm atoarele cazuri: atunci c and se lucreaz a cu proiecte mari, este posibil ca la o clas a s a trebuiasc a s a lucreze mai mul ti programatori simultan - ecare concentr andu se pe aspecte diferite. c and se lucreaz a cu cod generat automat, acesta poate scris separat stfel nc at programatorul s a nu interfereze accidental cu el. Situa tia este frecvent nt alnit a n cazul aplica tiilor de tip Windows Forms. De exemplu, pentru o form a nou creat a (numit a Form1) mediul Visual Studio va scrie un sier numit Form1.Designer.cs care con tine partea de ini tializare a controalelor si componentelor introduse de utilizator. Partea de tratare a evenimentelor, constructori, etc este denit a ntr-un alt sier (Form1.cs). Declararea unei p ar ti a unei clase se face folosind cuv antul cheie partial naintea lui class. Exemplu: //fisierul Browser1.cs public partial class Browser { public void OpenPage(String uri) {...} } //fisierul Browser2.cs public partial class Browser { public void DiscardPage(String uri) {...} } Urm atoarele sunt valabile pentru tipuri par tiale: cuv antul partial trebuie s a apara exact nainte cuvintelor: class, interface, struct dac a pentru o parte se specic a un anumit grad de acces, aceasta nu trebuie s a duc a la conicte cu declara tiile din alte p ar ti

126

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

dac a o parte de clas a este declarat a ca abstract a, atunci ntreaga clas a este considerat a abstract a dac a o parte declar a clasa ca ind sealed, atunci ntreaga clas a este considerat a sealed dac a o parte declar a c a mo stene ste o clas a, atunci ntr-o alt a parte nu se mai poate specica o alt a derivare p ar ti diferite pot s a declare c a se implementeaz a interfe te multiple acelea si c ampuri si metode nu pot denite n mai multe p ar ti. clasele imbricate pot s a e declarate n p ar ti diferite, chiar dac a clasa con tin atoare e denit a ntr-un singur sier: class Container { partial class Nested { void Test() { } } partial class Nested { void Test2() { } } } Urm atoarele elemente vor reunite pentru deni tia clasei: comentarii XML, interfe te, atribute, membri. Exemplu: partial class Earth : Planet, IRotate { } partial class Earth : IRevolve { } este echivalent cu: class Earth : Planet, IRotate, IRevolve { }

5.4. STRUCTURI

127

5.4

Structuri

Structurile reprezint a tipuri de date asem an atoare claselor, cu principala diferen ta c a sunt tipuri valoare (o astfel de variabil a va con tine direct valoarea, si nu o adres a de memorie). Sunt considerate versiuni u soare ale claselor, sunt folosite predilect pentru tipuri pentru care aspectul comportamental este mai pu tin pronun tat. Declara tia unei structuri se face astfel: atributeopt modicatori-de-structopt struct identicator :interfe teopt corp ;opt Modicatorii de structur a sunt: new, public, protected, internal, private. O structur a este automat derivat a din System.ValueType, care la r andul ei este derivat a din System.Object ; de asemenea, este automat considerat a sealed (nederivabil a). Poate ns a s a implementeze una sau mai multe interfe te. O structur a poate s a con tin a declara tii de constante, c ampuri, metode, propriet a ti, evenimente, indexatori, operatori, constructori, constructori statici, tipuri imbricate. Nu poate con tine destructor. La atribuire, se face o copiere a valorilor con tinute de c atre surs a n destina tie (indiferent de tipul c ampurilor: valoare sau referin ta ). 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 {

128

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI get { return yVal; } set { yVal = value; }

} public override string ToString( ) { return (String.Format({0}, {1}, xVal,yVal)); } public int xVal; public int yVal; } public class Tester { public static void MyFunc(Point loc) { loc.X = 50; loc.Y = 100; Console.WriteLine(In MyFunc loc: {0}, loc); } static void Main( ) { Point loc1 = new Point(200,300); Console.WriteLine(Loc1 location: {0}, loc1); MyFunc(loc1); Console.WriteLine(Loc1 location: {0}, loc1); } } Dup a cum este dat n exemplul de mai sus, crearea unei instan te se face folosind operatorul new ; dar n acest caz, nu se va crea o instan ta n memoria heap, ci pe stiv a. Transmiterea lui loc1 f ac anduse prin valoare, adic a metoda myFunc nu face dec at s a modice o copie de pe stiv a a lui loc1. La revenire, se va a sa tot valoarea original a, deoarece loc1 a r amas nemodicat: Loc1 location: 200, 300

5.4. STRUCTURI In MyFunc loc: 50, 100 Loc1 location: 200, 300

129

Deseori pentru o structur a se declar a c ampurile ca ind publice, pentru a nu mai necesare denirea accesorilor (simplicare implement arii). Al ti programatori consider a ns a c a accesarea membrilor trebuie s a se fac a precum la clase, folosind propriet a ti. Oricare ar alegerea, limbajul o sprijin a. Alte aspecte demne de re tinut: C ampurile nu pot ini tializate la declarare; altfel spus, dac a n exemplul de mai sus se scria: public int xVal = 10; public int yVal = 20; s-ar semnalat o eroare la compilare. Nu se poate deni un constructor implicit. Cu toate acestea, compilatorul va crea un astfel de constructor, care va ini tializa c ampurile la valorile lor implicite (0 pentru tipuri numerice sau pentru enumer ari, false pentru bool, null pentru tipuri referin ta ). Pentru tipul Point de mai sus, urm atoarea secven ta de cod este corect a: Point a = new Point(0, 0); Point b = new Point(); si duce la crearea a dou a puncte cu abcisele si ordonatele 0. Un constructor implicit este apelat atunci c and se creeaz a un tablou de structuri: Point[] points = new Points[10]; for( int i=0; i<points.Length; i++ ) { Console.WriteLine(points[i]); } va a sa de 10 ori puncte de coordonate (0, 0). De asemenea este apelat la ini tializarea membrilor de tip structur a ai unei clase. De men tionat pentru exemplul anterior c a se creeaz a un obiect de tip tablou n heap, dup a care n interiorul lui ( si nu pe stiv a!) se creeaz a cele 10 puncte (alocare inline).

130

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

Nu se poate declara destructor. Ace stia se declar a numai pentru clase. Dac a programatorul dene ste un constructor, atunci acesta trebuie s a dea valori ini tiale pentru c ampurile con tinute, altfel apare eroare la compilare. Dac a pentru instan tierea unei structuri declarate n interiorul unei metode sau bloc de instruc tiuni n general nu se apeleaz a new, atunci respectiva instan ta nu va avea asociat a nici o valoare (constructorul implicit nu este apelat automat!). Nu se poate folosi respectiva variabil a de tip structur a dec at dup a ce i se ini tializeaz a toate c ampurile: {//bloc de instructiouni Point p; Console.WriteLine(p); } va duce la apari tia erorii de compilare: Use of unassigned local variable p Dar dup a ni ste asign ari de tipul: p.xVal=p.yVal=0; a sarea este posibil a (practic, orice apel de metod a pe instan ta este acum acceptat). Dac a se ncearc a denirea unei structuri care con tine un c amp de tipul structurii, atunci va ap area o eroare de compilare: struct MyStruct { MyStruct s; } va genera un mesaj din partea compilatorului: Struct member MyStruct.s of type MyStruct causes a cycle in the structure layout Dac a o instan ta este folosit a acolo unde un object este necesar, atunci se va face automat o conversie implicit a c atre System.Object (boxing). Ca atare, utilizarea unei structuri poate duce (dar nu obligatoriu, ci n func tie de context) la un overhead datorat conversiei.

5.5. INTERFETE

131

5.4.1

Structuri sau clase?

Structurile pot mult mai eciente n alocarea memoriei atunci c and sunt re tinute ntrun tablou. De exemplu, crearea unui tablou de 100 de elemente de tip Point (de mai sus) va duce la crearea unui singur obiect (tabloul), iar cele 100 de instan te de tip structur a ar alocate inline n vectorul creat ( si nu referin te ale acestora). Dac a Point ar declarat ca si clas a, ar fost necesar a crearea a 101 instan te de obiecte n heap (un obiect pentru tablou, alte 100 pentru puncte), ceea ce ar duce la mai mult lucru pentru garbage collector si ar putea duce la fragmentarea heap-ului. Dar n cazul n care structurile sunt folosite n colec tii de tip Object (de exemplu un ArrayList ), se va face automat un boxing, ceea ce duce la overhead (consum suplimentar de memorie si cicli procesor). De asemenea, la transmiterea prin valoare a unei structuri, se va face copierea tuturor c ampurilor con tinute pe stiv a, ceea ce poate duce la un overhead semnicativ.

5.5

Interfe te

O interfa ta dene ste un contract. O clas a sau o structur a care implementeaz a o interfa ta ader a la acest contract. Rela tia dintre o interfa ta si un tip care o implementeaz a este deosebit a de cea existent a ntre clase (este un/o): este o rela tie de implementare. O interfa ta con tine metode, propriet a ti, evenimente, indexatori. Ea ns a nu va con tine implement ari pentru acestea, doar declara tii. Declararea unei interfe te se face astfel: atributeopt modicatori-de-interfa ta opt interface identicator baza-interfe teiopt corp-interfa ta ;opt Modicatorii de interfa ta sunt: new, public, protected, internal, private. O interfa ta poate s a mo steneasc a de la zero sau mai multe interfe te. Corpul interfe tei con tine declara tii de metode, f ar a implement ari. Orice metod a are gradul de acces public. Nu se poate specica pentru o metod a din interiorul unei interfe te: abstract, public, protected, internal, private, virtual, override, ori static. Exemplu: interface IStorable { void Read( ); void Write(); } O clas a care implementeaz a o astfel de interfa ta se declar a ca mai jos:

132

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

class Document: IStorable { public void Read(){/*cod*/} public void Write(){/*cod*/} //alte declaratii } O clas a care implementeaz a o interfa ta trebuie s a deneasc a toate metodele care se reg asesc n interfa ta respectiv a, sau s a declare metodele din interfa ta ca ind abstracte. Urm atoarele reguli trebuie respectate la implementarea de interfe te: 1. Tipul de retur al metodei din clas a trebuie s a coincid a cu tipul de retur al metodei din interfa ta 2. Tipul parametrilor formali din metod a trebuie s a e acela si cu tipul parametrilor formali din interfa ta 3. Metoda din clas a trebuie s a e declarat a public a si nestatic a. Aceste implement ari pot declarate folosind specicatorul virtual (deci subclasele clasei curente pot folosi new si override ). Exemplu: using System; interface ISavable { void Read(); void Write(); } public class TextFile : ISavable { public virtual void Read() { Console.WriteLine("TextFile.Read()"); } public void Write() { Console.WriteLine("TextFile.Write()"); } } public class ZipFile : TextFile {

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

133

134

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

TextFile.Write() ZipFile reference to ZipFile ZipFile.Read() ZipFile.Write() In exemplul de mai sus se folo seste operatorul as pentru a ob tine o referin ta la interfe te, pe baza obiectelor create. In general, se prefer a ca apelul metodelor care sunt implementate din interfa ta s a se fac a via o referin ta la interfa ta respectiv a, ob tinut a prin intermediul operatorului as (ca mai sus) sau dup a o testare prealabil a prin is urmat a de conversie explicit a, ca mai jos: if (textRef is ISavable) { ISavable is = (ISavable)textRef; is.Read();//etc } In general, dac a se dore ste doar r aspunsul la ntrebarea "este obiectul curent un implementator al interfe tei I ?", atunci se recomand a folosirea operatorului is. Dac a se stie c a va trebui f acut a si o conversie la tipul interfa ta , atunci este mai ecient a folosirea lui as. Arma tia se bazeaz a pe studiul codului IL rezultat n ecare caz. S a presupunem c a exista o interfa ta I av and metoda M() care este implementat a de o clas a C, care dene ste metoda M(). Este posibil ca aceast a metod a s a nu aib a o semnica tie n afara clasei C, ca atare a e de dorit ca metoda M() s a nu e declarat a public a. Mecanismul care permite acest lucru se nume ste implementare explicit a. Aceast a tehnic a permite ascunderea metodelor mo stenite dintr-o interfa ta , acestea devenind private (calicarea lor ca ind publice este semnalat a ca o eroare). Implementarea explicit a se ob tine prin calicarea numelui de metod a cu numele interef tei: interface IMyInterface { void F(); } class MyClass : IMyInterface { void IMyInterface.F() { //... } }

5.5. INTERFETE

135

Metodele din interfe te care sau implementat explicit nu pot declarate abstract, virtual, override, new. Mai mult, asemenea metode nu pot accesate direct prin intermediul unui obiect (obiect.NumeMetoda ), ci doar prin intermediul unei conversii c atre interfa ta respectiv a, deoarece prin implementare explicit a a metodelor aceste devin private si singura modalitate de acces a lor este upcasting-ul c atre interfa ta . Exemplu: using System; public interface IDataBound { void Bind(); } 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 = edit; Console.WriteLine("Apel (IDataBound) EditBox.Bind()..."); // Functioneaza deoarece s-a facut conversie la IDataBound bound.Bind(); } }

136

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

Este posibil ca un tip s a implementeze mai multe interfe te. Atunci c and dou a interfe te au o metod a cu aceea si semn atur a, programatorul are mai multe variante de lucru. Cel mai simplu, el poate s a furnizeze o singur a implementare pentru ambele metode, ca mai jos: interface IFriendly { void GreetOwner() ; } 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!" ) ; } } O alt a modalitate este s a se specice implicit care metod a este implementat a. class Dog : Pet, IAffectionate, IFriendly { public override void Eat() { Console.WriteLine( "Dog.Eat" ) ; } void IAffectionate.GreetOwner() {

5.5. INTERFETE Console.WriteLine( "Woof!" ) ; } void IFriendly.GreetOwner() { Console.WriteLine( "Jump up!" ) ; } } public class Pets { static void Main() { IFriendly mansBestFriend = new Dog() ; mansBestFriend.GreetOwner() ; (mansBestFriend as IAffectionate).GreetOwner() ; } } La ie sire se va a sa: Jump up! Woof! Dac a ns a n clasa Dog se adaug a metoda public void GreetOwner() { Console.WriteLine( "Woof!" ) ; }

137

atunci se poate face apel de tipul dog.GreetOwner() (variabila dog este instan ta de Dog); apelurile de metode din interfa ta r am an de asemenea valide. Rezultatul este a sarea mesajului Woof.

5.5.1

Clase abstracte sau interfe te?

At at interfe tele c at si clasele abstracte au comportamente similare si pot folosite n situa tii similare. Dar totu si ele nu se pot substitui reciproc. C ateva principii generale de utilizare a lor sunt date mai jos. Dac a o rela tie se poate exprima mai degrab a ca este un/o dec at altfel, atunci entitatea de baz a ar trebui g andit a ca o clas a abstract a. Un alt aspect este bazat pe obiectele care ar folosi capabilit a tile din tipul de baz a. Dac a aceste capabilit a ti ar folosite de c atre obiecte care nu sunt legate ntre ele, atunci ar indicat a o interfa ta .

138

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

Dezavantajul claselor abstracte este c a nu poate dec at baz a unic a pentru orice alt a clas a.

5.6

Tipul delegat

In programare deseori apare urm atoarea situa tie: trebuie s a se execute o anumit a ac tiune, dar nu se stie de dinainte care anume, sau chiar ce obiect va trebui efectiv utilizat. De exemplu, un buton poate sti c a trebuie s a anun te pe oricine este intresat despre faptul c a fost ap asat, dar nu va sti aprioric cum va tratat acest eveniment. Mai degrab a dec at s a se lege butonul de un obiect particular, butonul va declara un delegat, pentru care clasa interesat a de evenimentul de ap asare va da o implementare. Fiecare ac tiune pe care utilizatorul o execut a pe o interfa ta grac a declan seaz a un eveniment. Alte evenimente se pot declan sa independent de ac tiunile utilizatorului: sosirea unui email, terminarea copierii unor siere, sf ar situl unei interog ari pe o baz a de date, etc. Un eveniment este o ncapsulare a ideii c a se nt ampl a ceva la care programul trebuie s a r aspund a. Evenimentele si delega tii sunt str ans legate deoarece r aspunsul la acest eveniment se va face de c atre un event handler, care este legat de eveniment printr-un delegat, Un delegat este un tip referin ta folosit pentru a ncapsula o metod a cu un anumit antet (tipul parametrilor formali si tipul de retur). Orice metod a care are acest antet poate legat a la un anumit delegat. Intrun limbaj precum C++, acest lucru se rezolv a prin intermediul pointerilor la fun tii. Delega tii rezolv a aceea si problem a, dar ntro manier a orientat a obiect si cu garan tii asupra siguran tei codului rezultat, precum si cu o u soar a generalizare (vezi delega tii multicast). Un delegat este creat dup a urm atoarea sintax a: atributeopt modicatori-de-delegatopt delegate tip-retur identicator( listaparam-formaliopt ); Modicatorul de delegat poate : new, public, protected, internal, private. Un delegat se poate specica at at n interiorul unei clase, c at si n exteriorul ei, ind de fapt o declara tie de clas a derivat a din System.Delegate. Dac a este declarat n interiorul unei clase, atunci este si static (asem an ator cu statutul claselor imbricate). Exemplu:

public delegate int WhichIsFirst(object obj1, object obj2);

5.6. TIPUL DELEGAT

139

5.6.1

Utilizarea delega tilor pentru a specica metode la runtime

S a presupunem c a se dore ste crearea unei clase container simplu numit Pair care va con tine dou a obiecte pentru care va putea face si sortare. Nu se va sti aprioric care va tipul obiectelor con tinute, deci se va folosi pentru ele tipul object. Dar sortarea celor dou a obiecte se va face diferit, n func tie de tipul lor efectiv: de exemplu pentru ni ste persoane (clasa Student n cele ce urmeaz a) se va face dup a nume, pe c and pentru animale (clasa Dog ) se va face dup a alt criteriu: greutatea. Containerul Pair va trebui s a fac a fa ta acestor clase diferite. Rezolvarea se va da prin delega ti. Clasa Pair va deni un delegat, WhichIsFirst. Metoda Sort de ordonare va prelua ca (unic) parametru o instan ta a metodei WhichIsFirst, care va implementa rela tia de ordine, n func tie de tipul obiectelor con tinute. Rezultatul unei compara tii ntre dou a obiecte va de tipul enumerare Comparison, denit de utilizator: public enum Comparison { theFirstComesFirst = 0, //primul obiect din colectie este primul in ordinea sortarii theSecondComesFirst = 1 //al doilea obiect din colectie este primul in ordinea sortarii } Delegatul (tipul de metod a care realizeaz a compararea) se declar a astfel: //declarare de delegat public delegate Comparison WhichIsFirst( object obj1, object obj2); Clasa Pair se declar a dup a cum urmeaz a: 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;

140 }

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

//metoda publica pentru ordonarea celor doua obiecte //dupa orice criteriu public void Sort( WhichIsFirst theDelegatedFunc ) { if (theDelegatedFunc(thePair[0],thePair[1]) == Comparison.theSecondComesFirst) { object temp = thePair[0]; thePair[0] = thePair[1]; thePair[1] = temp; } } //metoda ce permite tiparirea perechii curente //se foloseste de polimorfism - vezi mai jos public override string ToString( ) { return thePair[0].ToString()+", "+thePair[1].ToString(); } } Clasele Student si Dog sunt: public class Dog { public Dog(int weight) { this.weight=weight; } //Ordinea este data de greutate public static Comparison WhichDogComesFirst( 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( )

5.6. TIPUL DELEGAT { 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: 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);

141

142

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI 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( ));

} }

5.6.2

Delega ti statici

Unul din aspectele neelegante ale exemplului anterior este c a e necesar ca n clasa Test s a se instan tieze delega tii care sunt necesari pentru a ordona obiectele din Pair. O modalitate mai bun a este s a se ob tin a delega tii direct din clasele Student si Dog. Acest lucru se ob tine prin crearea unui delegat static n interiorul ec arei clase: public static readonly WhichIsFirst OrderStudents = new WhichIsFirst(Student.WhichStudentComesFirst); (analog pentru clasa Dog ; de remarcat c a static readonly nu se poate nlocui cu const, deoarece ini tilizatorul nu este considerat expresie constant a). Declara tia de mai sus se folose ste astfel: ... studentpair.Sort(Student.OrderStudent); ... rezultatul ind identic. In [2] este dat a si o implementare de delegat ca proprietate static a, aceasta duc and la crearea unui delegat doar n cazul n care este nevoie de el1 .
1

Tehnic a numit a ini tializaer t arzie (lazy initialization)

5.6. TIPUL DELEGAT

143

5.6.3

Multicasting

Uneori este nevoie ca un delegat s a poat a apela mai mult de o singur a metod a. De exemplu, atunci c and un buton este ap asat, se poate s a vrei s a efectuezi mai mult de o sigur a ac tiune: s a scrii ntrun textbox un sir de caractere si s a nregistrezi ntrun sier faptul c a sa ap asat acel buton (logging). Acest lucru sar putea rezolva prin construirea unui vector de delega ti care s a con tin a toate metodele dorite, ns a s-ar ajunge la un cod greu de urm arit si inexibil; pentru un astfel de exemplu, a se vedea [2]. Mult mai simplu ar dac a unui delegat i s-ar putea atribui mai multe metode. Acest lucru se nume ste multicasting si este folosit intens la tratarea evenimentelor. Orice delegat care returnez a void este un delegat multicast, care poate tratat si ca un delegat single-cast. Doi delega ti multicast pot combina ti folosind semnul +. Rezultatul unei astfel de adun ari este un nou delegat multicast care la apelare va invoca metodele con tinute, n ordinea n care sa f acut adunarea. De exemplu, dac a Writer si Logger sunt delega ti care returneaz a void, atunci urm atoarea linie va produce combinarea lor ntrun singur delegat: myMulticastDelegate = Writer + Logger; Se pot ad auga delega ti multicast folosind operatorul +=, care va ad auga delegatul de la dreapta operatorului la delegatul multicast aat n st anga sa: myMulticastDelegate += Transmitter; presupun and c a Transmitter este compatibil cu myMulticastDelegate (are aceea si semn atur a). Operatorul = func tioneaz a invers fa ta de + =. Exemplu: 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);

144

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

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

5.6. TIPUL DELEGAT //Anunta utilizatorul ca se va adauga al treilea delegat Console.WriteLine( "\nmyMulticastDelegate += Transmitter"); //adauga al treilea delegat myMulticastDelegate += Transmitter; //invoca cele trei metode delagate myMulticastDelegate( "Second string passed to Collector"); //anunta utilizatorul ca se va scoate delegatul Logger Console.WriteLine( "\nmyMulticastDelegate -= Logger"); //scoate delegatul Logger myMulticastDelegate -= Logger; //invoca cele doua metode delegat ramase myMulticastDelegate( "Third string passed to Collector"); } } La ie sire vom avea: Writing string String passed to Writer Logging string String passed to Logger Transmitting string String passed to Transmitter myMulticastDelegate = Writer + Logger Writing string First string passed to Collector Logging string First string passed to Collector myMulticastDelegate += Transmitter Writing string Second string passed to Collector 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

145

146

CURS 5. CLASE, STRUCTURI, INTERFETE, DELEGATI

Curs 6 Metode anonime. Evenimente. Excep tii.


6.1 Metode anonime

Pentru a folosi un delegat a fost nevoie p an a acum de a se crea de ecare dat a o metod a ( si posibil si o nou a clas a care s a con tin a aceast a metod a). Exist a ns a cazuri n care corpul metodei este sucient de simplu pentru a nu necesita declararea explicit a a metodei. C# 2.0 introduce aceast a facilitate prin intermediul metodelor anonime. Varianta tradi tional a presupunea scrierea unui cod de forma: class SomeClass { delegate void SomeDelegate(); public void InvokeMethod() { SomeDelegate del = new SomeDelegate(SomeMethod); del(); } void SomeMethod() { MessageBox.Show("Hello"); } } Se poate deni o implementare folosind o metod a anonim a: class SomeClass { 147

148

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII. delegate void SomeDelegate(); public void InvokeMethod() { SomeDelegate del = delegate() { MessageBox.Show("Hello"); }; del(); }

} Metoda anonim a este denit a in-line si nu ca metod a membr a a unei clase. Compilatorul este sucient de inteligent pentru a infera tipul delegatului, pe baza declara tiei de variabil a delegat (del n exemplul anterior). O astfel de metod a anonim a se poate folosi oriunde este nevoie de o variabil a de tip delegat, de exemplu ca parametru al unei metode: class SomeClass { delegate void SomeDelegate(); public void SomeMethod() { InvokeDelegate(delegate(){MessageBox.Show("Hello");}); } void InvokeDelegate(SomeDelegate del) { del(); } } Exist a si cazuri n care se cere transmiterea de parametri metodei anonime. Parametrii (tip + nume) se declar a n interiorul parantezelor cuv antului delegate: class SomeClass { delegate void SomeDelegate(string str); public void InvokeMethod() { SomeDelegate del = delegate(string str) { MessageBox.Show(str);

6.2. EVENIMENTE }; del("Hello"); } }

149

Dac a se omit cu totul parantezele de dup a cuv antul delegate, atunci se declar a o metod a anonim a care este asignabil a unui delegat cu orice semn atur a: class SomeClass { delegate void SomeDelegate(string str); public void InvokeMethod() { SomeDelegate del = delegate { MessageBox.Show("Hello"); }; del("Parameter is ignored"); } } Remarc am c a nc a trebuie da ti parametri delegatului ce se apeleaz a; dac a delegatul declar a parametri de tip out, atunci varianta de mai sus nu se poate aplica.

6.2

Evenimente

Interfe tele grace actuale cer ca un anumit program s a r aspund a la evenimente. Un eveniment poate de exemplu ap asarea unui buton, terminarea transferului unui sier, selectarea unui meniu, etc; pe scurt, se nt ampl a ceva la care trebuie s a se dea un r aspuns. Nu se poate prezice ordinea n care se petrec evenimentele, iar la apari tia unuia se va cere reac tionarea din partea sistemului soft. Alte clase pot interesate n a r aspunde la aceste evenimente. Modul n care vor reac tiona va extrem de particular, iar obiectul care semnaleaz a evenimentul (ex: un obiect de tip buton, la ap asarea lui) nu trebuie s a stie modul n care se va r aspunde. Butonul va comunica faptul c a a fost ap asat, iar clasele interesate n acest eveniment vor reac tiona n consecin ta .

150

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

6.2.1

Publicarea si subscrierea

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

6.2.2

Evenimente si delega ti

Tratarea evenimentelor n C# se face folosind delega ti. Clasa ce public a dene ste un delegat pe care clasele abonate trebuie s a l implementeze. C and evenimentul este declan sat, metodele claselor abonate vor apelate prin intermediul delegatului (pentru care se prevede posibilitatea de a multicast, astfel nc at s a se permit a mai mul ti abona ti). Metodele care r aspund la un eveniment se numesc event handlers. Prin conven tie, un event handler n .NET Framework returneaz a void si preia doi parametri: primul parametru este sursa evenimentului (obiectul publicator); al doilea parametru are tip EventArgs sau derivat din acesta. Declararea unui eveniment se face astfel: atributeopt modicatori-de-evenimentopt event tip numeeveniment Modicator-de-eveniment poate abstract, new, public, protected, internal, private, static, virtual, sealed, override, extern. Tip este un handler de eveniment (delegat multicast). Exemplu: public event SecondChangeHandler OnSecondChange; Vom da mai jos un exemplu care va construi urm atoarele: o clas a Clock care folose ste un eveniment (OnSecondChange ) pentru a notica poten tialii abona ti atunci c and timpul local se schimb a cu o secund a. Tipul acestui eveniment este un delegat SecondChangeHandler care se declar a astfel: public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation );
1 2

Engl: publisher Engl: subscribers

6.2. EVENIMENTE

151

n conformitate cu metodologia de declarare a unui event handler, pomenit a mai sus. Tipul TimeInfoEventArgs este denit de noi ca o clas a derivat a din EventArgs : public class TimeInfoEventArgs : EventArgs { public TimeInfoEventArgs( int hour, int minute, int second ) { this.hour = hour; this.minute = minute; this.second = second; } public readonly int hour; public readonly int minute; public readonly int second; } Aceast a clas a va con tine informa tie despre timpul curent. Informa tia este accesibil a readonly. Clasa Clock va con tine o metod a Run() : 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);

152 }

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

} //modifica timpul curent in obiectul Clock this.second = dt.Second; this.minute = dt.Minute; this.hour = dt.Hour; } } Metoda Run creeaz a un ciclu innit care interogheaz a periodic ceasul sistem. Dac a timpul sa schimbat cu o secund a fa ta de timpul precedent, se vor notica toate obiectele abonate dup a care si va modica starea, prin cele trei atribuiri nale. Tot ce r am ane de f acut este s a se scrie ni ste clase care s a subscrie la evenimentul publicat de clasa Clock. Vor dou a clase: una numit a DisplayClock care va a sa pe ecran timpul curent si o alta numit a LogCurrentTime care ar trebui s a nregistreze evenimentul ntrun sier, dar pentru simplitate va a sa doar la dispozitivul curent de ie sire informa tia transmis a: public class DisplayClock { 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); }

6.2. EVENIMENTE //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( )); } }

153

De remarcat faptul c a evenimentele sunt ad augate folosind operatorul +=. Exemplul n ntregime este dat mai jos: using System; using System.Threading; //o clasa care va contine informatie despre eveniment //in acest caz va contine informatie disponibila in clasa Clock public class TimeInfoEventArgs : EventArgs { 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; } //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

154

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

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

6.2. EVENIMENTE 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) { 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( );

155

156

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII. //creaza un obiect DisplayClock care //va subscrie la evenimentul obiectului //Clock anterior creat DisplayClock dc = new DisplayClock( ); dc.Subscribe(theClock); //analog se creeaza un obiect de tip LogCurrentTime //care va subscrie la acelasi eveniment //ca si obiectul DisplayClock LogCurrentTime lct = new LogCurrentTime( ); lct.Subscribe(theClock); //porneste ceasul theClock.Run( );

} } La ie sire se va a sa: Current Logging Current Logging Current Logging Current Logging Time: 14:53:56 to file: 14:53:56 Time: 14:53:57 to file: 14:53:57 Time: 14:53:58 to file: 14:53:58 Time: 14:53:59 to file: 14:53:59

6.2.3

Comentarii

Sar putea pune urm atoarea ntrebare: de ce este nevoie de o astfel de redirectare de eveniment, c and n metoda Run() se poate a sa direct pe ecran sau ntrun sier informa tia cerut a? Avantajul abord arii anterioare este c a se pot crea oric ate clase care s a e noticate atunci c and acest eveniment se declan seaz a. Clasele abonate nu trebuie s a stie despre modul n care lucreaz a clasa Clock, iar clasa Clock nu trebuie s a stie despre clasele care vor subscrie la evenimentul s au. Similar, un buton poate s a publice un eveniment OnClick si orice num ar de obiecte pot subscrie la acest eveniment, primind o noticare atunci c and butonul este ap asat. Publicatorul si abona tii sunt decupla ti. Clasa Clock poate s a modice modalitatea de detectare a schimb arii de timp f ar a ca acest lucru s a impun a o schimbare n clasele abonate. De asemenea, clasele abonate pot s a si modice modul de tratare a evenimentului, n mod transparent fa ta de clasa Clock. Toate aceste caracteristici fac ntre tinerea codului extrem de facil a.

6.3. TRATAREA EXCEPTIILOR

157

6.3

Tratarea excep tiilor

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

6.3.1

Tipul Exception

In C# se pot arunca ca excep tii obiecte de tip System.Exception sau derivate ale acestuia. Exist a o ierarhie de excep tii care se pot folosi, sau se pot crea propriile tipuri excep tie. Enumer am urm atoarele metode si propriet a ti relevante ale clasei Exception : public Exception(), public Exception(string), public Exception(string, Exception) - constructori; ultimul preia un obiect de tip Exception (sau de tip clas a derivat a) care va ncapsulat n instan ta curent a; o excep tie poate deci s a con tin a n interiorul s au o instan ta a altei excep tii (cea care a fost de fapt semnalat a ini tial). public virtual string HelpLink {get; set;} ob tine sau seteaz a o leg atur a c atre un sier help asociat acestei excep tii; poate de asemenea o adresa Web (URL) public Exception InnerException {get;} returnez a excep tia care este ncorporat a n excep tia curent a
3

S i nu neap arat a ntregului proces!

158

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

public virtual string Message {get;} ob tine un mesaj care descrie excep tia curent a public virtual string Source {get; set;} ob tine sau seteaz a numele aplica tiei sau al obiectului care a cauzat eroarea public virtual string StackTrace {get;} ob tine o reprezetare string a apelurilor de metode care au dus la apari tia acestei excep tii public MethodBase TargetSite {get;} ob tine metoda care a aruncat 4 excep tia curent a

6.3.2

Aruncarea si prinderea excep tiilor

Aruncarea cu throw Aruncarea unei excep tii se face folosind instruc tiunea throw. Exemplu: throw new System.Exception(); Aruncarea unei excep tii opre ste execu tia metodei curente, dup a care CLR ncepe s a caute un manipulator de excep tie. Dac a un handler de excep tie nu este g asit n metoda curent a, atunci CLR va cur a ta stiva, ajung anduse la metoda apelant a. Fie undeva n lan tul de metode care au fost apelate se g ase ste un exception handler, e threadul curent este terminat de c atre CLR. 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...);
MethodBase este o clas a care pune la dispozi tie informa tii despre metodele si constructorii unei clase
4

6.3. TRATAREA EXCEPTIILOR Func2( ); Console.WriteLine(Exit Func1...); } public void Func2( ) { Console.WriteLine(Enter Func2...); throw new System.Exception( ); Console.WriteLine(Exit Func2...); } }

159

Se exemplic a apelul de metode: Main() apeleaz a Func1(), care apeleaz a Func2() ; aceasta va arunca o excep tie. Deoarece lipse ste un event handler care s a trateze aceast a excep tie, se va ntrerupe threadul curent ( si ind singurul, si ntregul proces) de c atre CLR, iar la ie sire vom avea: Enter Main... Enter Func1... Enter Func2... Exception occurred: System.Exception: An exception of type System.Exception was thrown at Test.Func2( ) in ...Test.cs:line 24 at Test.Func1( ) in ...Test.cs:line 18 at Test.Main( ) in ...Test.cs:line 12 Deoarece este aruncat a o excep tie, n metoda Func2() nu se va mai executa ultima linie, ci CLRul va ncepe imediat c autarea event handlerului care s a trateze excep tia. La fel, nu se execut a nici ultima linie din Func1() sau din Main(). Prinderea cu catch Prinderea si tratarea excep tiei se poate face folosind un bloc catch, creat prin intermediul instruc tiunii catch. Exemplu: using System; public class Test { public static void Main( ) {

160

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII. 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...); } } Se observ a c a sa folosit un bloc try pentru a delimita instruc tiunile care vor duce la apari tia excep tiei. In momentul n care se arunc a excep tia, restul instruc tiunilor din blocul try se ignor a si controlul este preluat de c atre blocul catch. Deoarece excep tia a fost tratat a, CLRul nu va mai opri procesul. La ie sire se va a sa: Enter Main... Enter Func1... Enter Func2... Entering try block... Exception caught and handled. Exit Func2... Exit Func1... Exit Main...

6.3. TRATAREA EXCEPTIILOR

161

Se observ a c a n blocul catch nu sa specicat tipul de excep tie care se prinde; asta nseamn a c a se va prinde orice excep tie se va arunca, indiferent de tipul ei. Chiar dac a excep tia este tratat a, execu tia nu se va relua de la instruc tiunea care a produs excep tia, ci se continu a cu instruc tiunea de dup a blocul catch. Uneori, prinderea si tratatarea excep tiei nu se poate face n func tia apelat a, ci doar n func tia apelant a. Exemplu: using System; public class Test { public static void Main( ) { Console.WriteLine(Enter Main...); Test t = new Test( ); t.Func1( ); Console.WriteLine(Exit Main...); } public void Func1( ) { Console.WriteLine(Enter Func1...); try { Console.WriteLine(Entering try block...); Func2( ); Console.WriteLine(Exiting try block...); } catch { Console.WriteLine(Exception caught and handled.); } Console.WriteLine(Exit Func1...); } public void Func2( ) { Console.WriteLine(Enter Func2...); throw new System.Exception( ); Console.WriteLine(Exit Func2...); } } La ie sire se va a sa:

162

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

Enter Main... Enter Func1... Entering try block... Enter Func2... Exception caught and handled. Exit Func1... Exit Main... Este posibil ca ntro secven ta de instruc tiuni s a se arunce mai multe tipuri de excep tii, n func tie de natura st arii ap arute. In acest caz, prinderea excep tiei printrun bloc catch generic, ca mai sus, nu este util a; am vrea ca n func tie de natura excep tiei aruncate, s a facem o tratare anume. Se sugereaza chiar s a nu se foloseasc a aceast a construc tie de prindere generic a, deoarece n majoritatea cazurilor este necesar s a se cunoasc a natura erorii (de exemplu pentru a scris a ntr-un sier de logging, pentru a consultat a mai t arziu). Acest lucru se face specic and tipul excep tiei care ar trebui tratate n blocul catch : using System; public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } //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) {

6.3. TRATAREA EXCEPTIILOR 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; }

163

} In exemplul de mai sus sa convenit ca o mp ar tire cu numitor 0 s a duc a la o execep tie System.DivideByZeroException, iar o mp ar tire cu num ar ator 0 s a duc a la apari tia unei excep tii de tip System.ArithmeticException. Este posibil a specicarea mai multor blocuri de tratare a excep tiilor. Aceste blocuri sunt parcurse n ordinea n care sunt specicate, iar primul tip care se potrive ste cu excep tia aruncat a ( n sensul c a tipul excep tie specicat este e exact tipul obiectului aruncat, e un tip de baz a al acestuia - din cauz a de upcasting) este cel care va face tratarea excep tiei ap arute. Ca atare, este important ca ordinea excep tiilor tratate s a e de la cel mai derivat la cel mai general. In exemplul anterior, System.DivideByZeroException este derivat din clasa System.ArithmeticException. Blocul nally Uneori, aruncarea unei excep tii si golirea stivei p an a la blocul de tratare a excep tiei poate s a nu e o idee bun a. De exemplu, dac a excep tia apare atunci c and un sier este deschis ( si nchiderea lui se poate face doar n metoda curent a), atunci ar util ca s a se nchid a sierul nainte ca s a e preluat controlul de c atre metoda apelant a. Altfel spus, ar trebui s a existe o garan tie c a un anumit cod se va executa, indiferent dac a totul merge normal sau apare o excep tie. Acest lucru se face prin intermediul blocului nally, care se va executa n orice situa tie. Existen ta acestui bloc elimin a necesitatea existen tei blocurilor catch (cu toate c a si acestea pot s a apar a).

164

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII. 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.); } } public double DoDivide(double a, double b) { if (b == 0) throw new System.DivideByZeroException( ); if (a == 0) throw new System.ArithmeticException( ); return a/b; } } In exemplul de mai sus, mesajul Close le here se va a sa indiferent de ce parametri se transmit metodei DoDivide(). La aruncarea unei excep tii se poate particulariza obiectul care se arunc a: if (b == 0) { DivideByZeroException e = new DivideByZeroException( );

6.3. TRATAREA EXCEPTIILOR e.HelpLink = http://www.greselifatale.com; throw e; } iar c and excep tia este prins a, se poate prelucra informa tia: catch (System.DivideByZeroException e) { Console.WriteLine( DivideByZeroException! goto {0} and read more, e.HelpLink); } Crearea propriilor excep tii

165

In cazul n care suita de excep tii predenite nu este sucient a, programatorul si poate construi propriile tipuri. Se recomand a ca acestea s a e derivate din System.ApplicationException, care este derivat a direct din System.Exception. Se indic a aceast a derivare deoarece astfel se face distinc tie ntre excep tiile aplica tie si cele sistem (cele aruncate de c atre CLR). Exemplu: using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message): base(message) { } } public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } public void TestFunc( ) { try { double a = 0;

166

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII. 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); }

} public double DoDivide(double a, double b) { if (b == 0) { DivideByZeroException e = new DivideByZeroException( ); e.HelpLink= http://www.greselifatale.com; throw e; } if (a == 0) { MyCustomException e = new MyCustomException( Cant have zero divisor); e.HelpLink = http://www.greselifatale.com/NoZeroDivisor.htm; throw e; } return a/b; }

6.3. TRATAREA EXCEPTIILOR } Rearuncarea excep tiilor

167

Este perfect posibil ca ntrun bloc de tratare a excep tiilor s a se se fac a o tratare primar a a excep tiei, dup a care s a se arunce mai departe o alt a excep tie, de acela si tip sau de tip diferit (sau chiar excep tia original a). Dac a se dore ste ca aceast a excep tie s a p astreze cumva n interiorul ei excep tia original a, atunci constructorul permite nglobarea unei referin te la aceasta; aceast a referin ta va accesibil a prin intermediul propriet a tii InnerException : using System; public class MyCustomException : System.ApplicationException { public MyCustomException(string message,Exception inner): base(message,inner) { } } public class Test { public static void Main( ) { Test t = new Test( ); t.TestFunc( ); } 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;

168 }

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

} } 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) { Exception ex = new Exception(E2 - Func2 caught divide by zero,e); throw ex; } } public void DangerousFunc3( ) { try { DangerousFunc4( ); } catch (System.ArithmeticException) { throw; } catch (System.Exception) {

6.3. TRATAREA EXCEPTIILOR Console.WriteLine(Exception handled here.);

169

} } public void DangerousFunc4( ) { throw new DivideByZeroException("E1 - DivideByZero Exception"); } }

6.3.3

Re ncercarea codului

Se poate pune ntrebarea: cum se procedeaz a dac a se dorec ste revenirea la codul care a produs excep tia, dup a tratarea ei? Exist a destule situa tii n care reexecutarea acestui cod este dorit a: s a ne g andim de exemplu la cazul n care ntr-o fereastr a de dialog se specic a numele unuei sier ce trebuie procesat, numele este introdus gre sit si se dore ste ca s a se permit a corectarea numelui. Un alt exemplu clasic este cazul n care autorul unei metode stie c a o opera tie poate s a e sueze periodic de exemplu din cauza unui timeout pe re tea dar vrea s a re ncerce opera tia de n ori nainte de a semnala eroare. In aceast a situa tie se poate deni o etichet a naintea blocului try la care s a se permit a salutl printrun goto. Urm atorul exemplu permite unui utilizator s a specice de maxim trei ori numele unui sier ce se proceseaz a, cu revenire n cazul erorii. using System; using System.IO; class Retry { 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();

170

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

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; } else { Console.Write("You have exceeded the maximum " + "retry limit ({0})", maxAttempts); }

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

6.3. TRATAREA EXCEPTIILOR

171

6.3.4

Compararea tehnicilor de manipulare a erorilor

Metoda standard de tratare a aerorilor a fost n general returnarea unui cod de eroare c atre metoda apelant a. Ca atare, apelantul are sarcina de a descifra acest cod de eroare si s a reac tioneze n consecin ta . Ins a a sa cum se arat a mai jos, tratarea excep tiilor este superioar a acestei tehnici din mai multe motive. Neconsiderarea codului de retur Apelul unei func tii care returneaz a un cod de eroare poate f acut si f ar a a utiliza efectiv codul returnat, scriind doar numele func tiei cu parametrii de apel. Dac a de exemplu pentru o anmit a procesare se apeleaz a metoda A (de exemplu o deschidere de sier) dup a care metoda B (citirea din sier), se poate ca n A s a apar a o eroare care nu este luat a n considerare; apelul lui B este deja sortit e secului, pentru c a buna sa func tionare depinde de efectele lui A. Dac a ns a metoda A arunc a o excep tie, atunci nici m acar nu se mai ajunge la apel de B, deoarece CLR-ul va pasa execu tia unui bloc catch/finally. Altfel spus, nu se permite o propagare a erorilor. Manipularea erorii n contextul adecvat In cazul n care o metod a A apeleaz a alte metode B1 , . . . Bn , este posibil ca oricare din aceste n metode s a cauzeze o eroare ( si s a returneze cod adecvat); tratarea erorii din exteriorul lui A este dicil a n acest caz, deoarece ar trebui s a se cerceteze toate codurile de eroare posibile pentru a determina motivul apari tiei erorii. Dac a se mai adauga si apelul de metod a Bn+1 n interiorul lui A, atunci orice apel al lui A trebuie s a includ a suplimentar si vericarea pentru posibilitatea ca Bn+1 s a cauzat o eroare. Ca atare, costul men tinerii codului cre ste permanent, ceea ce are un impact negativ asupra TCO-ului5 Folosind tratarea excep tiilor, arunc and excep tii cu mesaje de eroare explicite sau excep tii de un anumit tip (denit de programator) se poate trata mult mai convenabil o situa tie deosebit a. Mai mult dec at at at, introducerea apelului lui Bn+1 n interiorul lui A nu reclam a modicare suplimentar a, deoarece tipul de excep tie aruncat de Bn+1 este deja tratat (desigur, se presupune c a se dene ste un tip excep tie sau o ierarhie de excep tii creat a convenabil). U surin ta citirii codului Pentru compara tie, se poate scrie un cod care realizeaz a procesarea con tinutului unui sier folosind coduri de eroare returnate sau excep tii. In primul caz,
5

Total Cost of Ownership.

172

CURS 6. METODE ANONIME. EVENIMENTE. EXCEPTII.

solu tia va con tine cod de prelucrare a con tinutului mixat cu cod de vericare si reac tie pentru diferitele cazuri de excep tie. Codul n al doilea caz este mult mai scurt, mai u sor de n teles, de men tinut, de corectat si extins. Aruncarea de excep tii din constructori Nimic nu opre ste ca o situa tie deosebit a s a apar a ntrun apel de constructor. Tehnica veric arii codului de retur nu mai func tioneaz a aici, deoarece un constructor nu returneaz a valori. Folosirea excep tiilor este n acest caz aproape de ne nlocuit.

6.3.5

Sugestie pentru lucrul cu excep tiile

In Java, programatorii trebuie s a declare c a o metod a poate arunca o excep tie si s a o declare explicti ntro list a astfel nc at un apelant s a stie c a se poate se poate a stepta la primirea ei. Aceast a cunoa stere n avans permite conceperea unui plan de lucru cu ecare dintre ele, preferabil dec at s a se prind a oricare dintre ele cu un catch generic. In cazul .NET se sugereaz a s a se men tin a o documenta tie cu excep tiile care pot aruncate de ecare metod a.

Curs 7 Colec tii. Clase generice.


7.1 Colec tii

Un vector reprezint a cel mai simplu tip de colec tie. Singura sa decien ta este faptul c a trebuie cunoscut dinainte num arul de elemente con tinute. Spa tiul de nume System.Collections pune la dispozi tie un set de clase de tip colec tie. Clasele din acest spa tiu de nume reprezint a containere de elemente de tip Object care si gestioneaz a singure necesarul de memorie (cresc pe m asur a ce se adaug a elemente; pot de asemenea s a si reduc a efectivul de memorie alocat atunci c and num arul de elemente con tinute este prea mic). Exemplu: ArrayList myCollection = new ArrayList(); myCollection.Add(client); client = myCollection[0] as Client; Remarc am conversia explicit a pentru recuperarea unui element din colec tie. Elementele de baz a pentru lucrul cu colec tiile sunt un set de interfe te care ofer a o mul time consistent a de metode de lucru. Principalele colec tii mpreun a cu interfe tele implementate sunt: ArrayList : IList, ICollection, IEnumerable, ICloneable SortedList : IDictionary, ICollection, IEnumerable, ICloneable Hashtable : IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback, ICloneable BitArray : ICollection, IEnumerable, ICloneable 173

174

CURS 7. COLECTII. CLASE GENERICE.

Queue : ICollection, IEnumerable, ICloneable Stack : ICollection, IEnumerable, ICloneable CollectionBase : IList, ICollection, IEnumerable DictionaryBase : IDictionary, ICollection, IEnumerable ReadOnlyCollectionBase : ICollection, IEnumerable IEnumerable Implement arile aceste interfe te permit iterarea peste o colec tie de elemente. Unica metod a declarat a este metoda GetEnumerator : IEnumerator GetEnumerator () unde un obiect de tip IEnumerator este folosit pentru parcurgerea colec tiei un a sa numit iterator. ICollection Interfa ta ICollection este tipul de baz a pentru orice clas a de tip colec tie; se deriveaz a din IEnumerable si prezint a urm atoarele propriet a ti si metode: Count - proprietate de tip ntreg care returneaz a num arul de elemente con tinute n colec tie IsSynchronized - proprietate logic a ce indic a dac a colec tia este sincronizat a (sigur a pentru accesarea de c atre mai multe re de execu tie) SyncRoot - proprietate care returneaz a un obiect care poate folosit pentru a sincroniza accesul la colec tie CopyTo - metod a care permite copierea con tinutului colec tiei ntr-un vector IList Reprezint a o colec tie de obiecte care pot accesate individual printr-un index. Propriet a tile sunt: IsFixedSize - returneaza o valoare logic a indic and dac a lista are o dimensiune x a

7.1. COLECTII

175

IsReadOnly - returneaz a o valoare logic a indic and dac a lista poate doar citit a Item - returneaz a sau seteaz a elementul de la loca tia specicat a Metodele sunt: Add - adaug a un obiect la o list a Clear - gole ste lista Contains - determin a dac a colec tia con tine o anumit a valoare IndexOf - determin a pozi tia n list a a unei anumite valori Insert - insereaz a o valoare la o anumit a pozi tie Remove - sterge prima apari tie a unui obiect din list a RemoveAt - sterge un obiect aat la o anumit a loca tie IDictionary Interfa ta IDictionary reprezint a o colec tie de perechi (cheie, valoare). Permite indexarea unei colec tii de elemente dup a altceva dec at indici ntregi. Fiecare pereche trebuie s a aib a o cheie unic a. Propriet a tile sunt: IsFixedSize, IsReadOnly - returneaz a o valoare care precizeaz a dac a colec tia este cu dimensiune maxim a xat a, respectiv doar citibil a Item - returneaz a un element av and o cheie specicat a Keys - returneaz a o colec tie de obiecte con tin and cheile din dic tionar Values - returneaz a un obiect de tip colec tie care con tine toate valorile din dic tionar Metodele sunt: Add - adaug a o pereche (cheie, valoare) la dic tionar; dac a cheia exist a deja, se va face suprascrierea valorii asociate Clear - se sterge con tinutul unui dic tionar Contains - determin a dac a dictionarul con tine un element cu cheia specicat a

176

CURS 7. COLECTII. CLASE GENERICE.

GetEnumerator - returneaz a un obiect de tipul IDictionaryEnumerator asociat Remove - sterge elementul din dic tionar av and cheia specicat a

7.1.1

Iteratori pentru colec tii

Colec tiile (at at cele de tip list a, c at si cele dictionar) mo stenesc interfa ta IEnumerable care permite construirea unui obiect de tip enumerator instan ta a lui IEnumerable : interface IEnumerator { object Current {get;} bool MoveNext(); void Reset(); } Remarc am c a un asemenea iterator este de tip forward-only. Proprietatea Current returneaz a elementul curent al iter arii. Metoda MoveNext avanseaz a la urm atorul element al colec tiei, return and true dac a acest lucru s-a putut face si false n caz contrar; trebuie s a e apelat a cel pu tin o dat a naintea acces arii componentelor colec tiei. Metoda Reset reini tializeaz a iteratorul mut and pozi tia curent a naintea primului obiect al colec tiei. Pentru ecare clas a de tip colec tie, enumeratorul este implementat ca o clas a intern a. Returnarea unui enumerator se face prin apelul metodei GetEnumerator. Exemplu: se va apela n mod explicit metoda de returnare a iteratorului si mai departe acesta se folose ste pentru a sarea elementelor. ArrayList list = new ArrayList(); list.Add("One"); list.Add("Two"); list.Add("Three"); IEnumerator e = list.GetEnumerator(); while(e.MoveNext()) { Console.WriteLine(e.Current); } Apelul unui iterator este mecanismul esen tial pentru func tionara instruc tiunii foreach, care debuteaz a prin a apela intern metoda GetEnumerator iar trecerea la elementul urm ator se face cu metoda MoveNext. Altfel spus, exemplul de mai sus este echivalent cu:

7.1. COLECTII ArrayList list = new ArrayList(); list.Add("One"); list.Add("Two"); list.Add("Three"); foreach(String s in list) { Console.WriteLine(s); }

177

7.1.2

Colec tii de tip list a

Colec tiile de tip list a sunt: ArrayList, BitArray, Stack, Queue si CollectionBase. ArrayList Este o clas a concret a care stocheaz a o colec tie de elemente sub forma unui vector auto-redimensionabil. Suport a mai mul ti cititori concuren ti si poate accesat exact ca un vector: ArrayList list = new ArrayList(); list.Add(...); Console.WriteLine(list[0]); list[0] = "abc"; BitArray Acest tip de colec tie gestioneaz a un vector de elemente binare reprezentate ca booleeni, unde true reprezint a 1 iar false 0. Cele mai importante metode sunt: And, Or, Xor - produce un nou BitArray care con tine rezultatul aplic arii operanzilor respectivi pe elementele din colec tia curent a si alt a colec tie dat a ca argument. Dac a cele 2 colec tii nu au acela si num ar de elemente, se arunc a excep tie Not - returneaz a un obiect de tip BitArray care con tine valorile negate din colec tia curent a Stack Stack reprezint a o colec tie ce permite lucrul conform principiului LIFO Last In, First Out.

178 Queue

CURS 7. COLECTII. CLASE GENERICE.

Clasa Queue este nou ap arut a n versuinea 2.0 ce permite implementarea politicii FIFO - First In, First Out. CollectionBase Clasa CollectionBase reprezint a o clas a abstract a, baz a pentru o colec tie puternic tipizat a. Programatorii sunt ncuraja ti s a deriveze aceast a clas a dec at s a creeze una proprie de la zero.

7.1.3

Colec tii de tip dic tionar

Colec tiile de tip dic tionar (SortedList, Hashtable, DictionaryBase ) con tin obiecte care se manipuleaz a prin intermediul cheii asociate (care poate altceva dec at un indice numeric). Toate extind interfa ta IDictionary, iar ca enumeratorul este de tip IDictionaryEnumerator : interface IDictionaryEnumerator : IEnumerator { DictionaryEntry Entry {get;} object Key {get;} object Value {get;} } unde DictionaryEntry este denit ca: struct DictionaryEntry { public DictionaryEntry(object key, object value) { ... } public object Key {get; set;} public object Value {get; set;} ... } Invocarea enumeratorului se poate face e explicit: Hashtable htable = new Hashtable(); htable.Add("A", "Chapter I"); htable.Add("B", "Chapter II"); htable.Add("App", "Appendix"); IDictionaryEnumerator e = htable.GetEnumerator(); for ( ; e.MoveNext() ; ) Console.WriteLine(e.Key); e implicit:

7.2. CREAREA UNEI COLECTII foreach (DictionaryEntry s in htable) Console.WriteLine(s.Key); Hashtable

179

Reprezint a o colec tie de perechi de tip (cheie, valoare) care este organizat a pe baza codului de dispersie (hashing) al cheii. O cheie nu poate s a e nul a. Obiectele folosite pe post de chei trebuie s a suprascrie metodele Object.GetHashCode si Object.Equals. Obiectele folosite pe post de cheie trebuie sa e imuabile (s a nu suporte schimb ari de stare care s a altereze valorile returnate de cele 2 metode spuse anterior). SortedList Reprezint a o colec tie de perechi de tip (cheie, valoare) care sunt sortate dup a cheie si se pot accesa dup a cheie sau dup a index. DictionaryBase Reprezint a o clas a de baz a abstract a pentru implementarea unui dic tionar utilizator puternic tipizat (valorile s a nu e v azute ca object, ci ca tip specicat de programator).

7.2

Crearea unei colec tii

Vom exemplica n aceast a sec tiune modul n care se dene ste o colec tie ce poate iterat a. Sunt prezentate 2 variante: specice versiunilor 1.1 si respectiv 2.0 ale platformei .NET. In ambele cazuri clasa de tip colec tie va implementa intefa ta IEnumerable, dar va diferi modul de implementare.

7.2.1

Colec tie iterabil a (stil vechi)

using System; using System.Collections; class MyCollection : IEnumerable { private int[] continut = {1, 2, 3}; public IEnumerator GetEnumerator() { return new MyEnumerator( this ); }

180

CURS 7. COLECTII. CLASE GENERICE.

private class MyEnumerator : IEnumerator { private MyCollection mc; private int index = -1; public MyEnumerator( MyCollection mc ) { this.mc = mc; } public object Current { get { if (index < 0 || index >= mc.continut.Length) { return null; } else return mc.continut[index]; } } public bool MoveNext() { index++; return index < mc.continut.Length; } public void Reset() { index = -1; } } } Remarc am c a clasa imbricat a prime ste prin constructor o referin ta la obiectul de tip colec tie, deoarece orice clas a imbricat a n C# este automat si static a, neav and astfel acces la membrii nestatici ai clasei. Demonstra tia pentru iterarea clasei este: class TestIterator

7.2. CREAREA UNEI COLECTII { static void Main() { MyCollection col = new MyCollection(); foreach(int i in col) { Console.WriteLine(s); } } }

181

Instruc tiunea foreach va apela ini tial metoda GetEnumerator pentru a ob tine obiectul de iterare si apoi pentru acest obiect se va apela metoda MoveNext la ecare itera tie. Dac a se returneaz a true atunci se apeleaz a automat si metoda Current pentru ob tinerea elementului curent din colec tie; dac a se returneaz a false atunci execu tia lui foreach se termin a. Implementarea de mai sus permite folosirea simultan a a mai multor iteratori, cu p astrarea st arii specice. Defectele majore ale acestei implement ari sunt: 1. Complexitatea codului (num arul mare de linii). De si u sor de n teles si general acceptat a (ind de fapt un design pattern), abordarea presupune scrierea multor linii de cod, motiv pentru care programatorii evita aceasta facilitate, prefer and mecanisme alternative precum indexatorii. 2. Datorit a semn aturii metodei Current se returneaz a de ecare dat a un Object, pentru care se face e boxing si unboxing (dac a in colec tie avem tip valoare - cazul de mai sus), e downcasting (de la Object la tipul declarat in prima parte a lui foreach, dac a in colec tie avem tip referin ta ). In primul caz resursele suplimentare de memorie si ciclii procesor vor afecta negativ performan ta aplica tiei iar in al doilea caz apare o conversie explicit a care d auneaz a performan tei globale. Modalitatea de evitare a acestei probleme este ca s a nu se implementeze interfe tele IEnumerator si IEnumerable, ci scriind metoda Current astfel inc at s a returneze direct tipul de date necesar (int in cazul nostru). Acest lucru duce ins a la expunerea claselor imbricate (v azute ca ni ste clase auxiliare), ceea ce incalc a principiul incapsularii. In plus, cantitatea de cod r amane aceea si. Pentru prima problem a vom da varianta de mai jos. Pentru cea de a doua, rezolvarea se d a sub forma claselor generice.

182

CURS 7. COLECTII. CLASE GENERICE.

7.2.2

Colec tie iterabil a (stil nou)

Incepand cu C# 2.0 se poate deni un iterator mult mai simplu. Pentru aceasta se foloseste instruc tiunea yield. yield este folosit a ntr-un bloc de iterare pentru a semnala valoarea ce urmeaz a a returnat a sau oprirea iter arii. Are formele: yield return expression; yield break; In prima form a se precizeaz a care va valoarea returnat a; n cea de-a doua se precizeaz a oprirea iter arii (sf ar situl secven tei de elemente de returnat). Pentru exemplicare, prezent am o metod a al c arei rezultat este folosit pentru iterare. Valorile returnate de aceast a metod a sunt p atratele numerelor de la 1 la valoarea argumentului using System; using System.Collections; using System.Text; namespace TestCollection { class Program { static IEnumerable Squares(int number) { for (int i = 1; i <= number; i++) { yield return i*i; } } static void Main(string[] args) { foreach (int iterate in Squares(10)) { Console.WriteLine(iterate.ToString()); } } } }

7.2. CREAREA UNEI COLECTII

183

Remarc am c a are loc urm atorul efect: la ecare itera tie se returneaz a urm atoarea valoare din colec tie (colec tia este denit a de metoda Squares ). Astfel, se creeaz a impresia c a la ecare iterare din metoda Main se reia execu tia din metoda Squares de unde a r amas la apelul precedent; acest mecanism este diferit de cel al rutinelor (metodelor) nt alnite p an a acum, purt and numele de corutin a. Ca s a facem evident acest mecanism de continuare a execu tiei de la punctul de retur anterior, consider am exemplul: using System; using System.Collections.Generic; namespace Iterator { class DemoCorutina { static IEnumerable<int> Numere() { Console.WriteLine("Returnare 1"); yield return 1; Console.WriteLine("Returnare 2"); yield return 2; Console.WriteLine("Returnare 3"); yield return 3; } static void Main(string[] args) { foreach(int valoare in Numere()) { Console.WriteLine(valoare.ToString()); } } } } pentru care rezultatul a sat pe ecran este: Returnare 1 1 Returnare 2 2

184 Returnare 3 3

CURS 7. COLECTII. CLASE GENERICE.

deci n mod clar apelul pentru urm atoarea valoarea dat a de c atre metoda Numere se continu a de la punctul de ie sire anterior. De fapt, compilatorul va genera automat o implementare de metod a de tip IEnumerable (precum am f acut manual n sec tiunea 7.2.1), permi ta nduse astfel programatorului s a se concentreze pe designul metodei si mai pu tin pe stufoasele detaliile interne. Un alt aspect demn de re tinut este c a secven ta se construie ste pe m asur a ce datele din enumerare sunt parcurse. Clasa MyCollection de mai sus s-ar rescrie astfel: class MyCollection : IEnumerable { private int[] continut = { 1, 2, 3 }; public IEnumerator GetEnumerator() { for(int i=0; i<continut.Length; i++) { yield return continut[i]; } } } Pentru a demonstra utilitatea acestui tip de implementare, mai jos d am rezolvarea pentru urm atoarea problem a: plec anduse de la un arbore binar s a se scrie iteratorii pentru parcurgerea n inordine si preordine. Nu vom prezenta construirea efectiv a a arborelui, aceasta ind o problema separat a. Practic, se va implementa n mod recursiv o iterare peste arbore. Pentru nceput, deni tia tipului nod: using System; namespace TestIterTree { class TreeNode<T> { private T value; private TreeNode<T> left, right; public T Value { get

7.2. CREAREA UNEI COLECTII { return value; } set { this.value = value; } } public TreeNode<T> Left { get { return left; } set { left = value; } } public TreeNode<T> Right { get { return right; } set { this.right = value; } } } } Urmeaz a denirea arborelui si a celor doi iteratori: using System; using System.Collections.Generic; namespace TestIterTree {

185

186

CURS 7. COLECTII. CLASE GENERICE. class Tree<T> { private TreeNode<T> root; #region popularea arborelui cu valori public void AddValues(params T[] value) { Array.ForEach(value, Add); } #endregion #region tehnici de traversare public IEnumerable<T> InOrder() { return InOrder(root); } public IEnumerable<T> PreOrder() { return PreOrder(root); } #endregion #region Private helper methods private IEnumerable<T> InOrder(TreeNode<T> node) { if (node.Left != null) { foreach (T value in InOrder(node.Left)) { yield return value; } } yield return node.Value; if (node.Right != null) { foreach (T value in InOrder(node.Right)) { yield return value; } }

7.3. CLASE GENERICE } private IEnumerable<T> PreOrder(TreeNode<T> root) { yield return root.Value; if (root.Left != null) { foreach (T value in PreOrder(root.Left)) { yield return value; } } if (root.Right != null) { foreach (T value in PreOrder(root.Right)) { yield return value; } } } private void Add(T value) { //Implements adding a value to the tree } #endregion } }

187

Implementarea de mai sus sa f acut conform deni tiilor recursive pentru cele dou a tipuri de parcurgeri (al treilea tip de parcurgere se implementeaz a analog). Invit am cititorul s a compare aceste implement ari cu cele iterative clasice din teoria structurilor de date. Pe l ang a timpul scurt de implementare, se c a stig a n claritate si u surin ta n exploatare.

7.3

Clase generice

Vom prezenta n cele ce urmeaz a suportul .NET 2.0 pentru clase si metode generice; acestea sunt blocuri de cod parametrizate care permit scriere unui cod general, ce poate ulterior adaptat automat la cerintele specice ale programatorului.

188

CURS 7. COLECTII. CLASE GENERICE.

7.3.1

Metode generice

S a presupunem c a dorim s a scriem o metod a care s a realizeze interschimbarea valorilor a dou a variabile. Variantele sunt: 1. scrierea unei metode pentru ecare tip al variabilelor: neelegant, cod mult 2. scrierea unei metode care s a foloseasc a un Object pe post de variabil a auxiliar a; dac a se face apelul pentru 2 variabile de tip sir de caractere, apare eroarea Cannot convert from ref string to ref object . In plus, antetul metodei ar permite apel pentru un parametru de tip string si cel alalt de tip int, ceea ce nu ar trebui s a e admis la compilare. Singurul mod adecvat de rezolvare a problemei este folosirea unei metode generice, ca mai jos: void Swap<T>(ref T a, ref T b) { T aux; aux = a; a = b; b = aux; } Apelul acestei metode se face astfel: int x = 3, y=4; Swap<int>(ref x, ref y);//nu apare boxing/unboxing string a="a", b="b"; Swap<string>(ref a, ref b); Remarc am c a apelul se face specic and tipul efectiv pentru T. Aceast a specicare poate omis a dac a compilatorul poate deduce singur care este tipul efectiv T: bool b1=true, b2=false; Swap(ref b1, ref b2); Tipul generic T poate folosit si ca tip de retur.

7.3. CLASE GENERICE

189

7.3.2

Tipuri generice

Mecanismul de genericitate poate extins la clase si structuri. D am mai jos exemplu care modeleaz a no tiunea de punct ntrun spa tiu bidimensional. Genericitatea provine din faptul c a coordonatele pot de tip ntreg sau frac tionare. struct Point<T> { private T xPos; private T yPos; public Point(T xPos, T yPos) { this.xPos = xPos; this.yPos = yPos; } public T X { get { return xPos; } set { xPos = value; } } public T Y { get { return yPos; } set { yPos = value; } }

190

CURS 7. COLECTII. CLASE GENERICE.

public override string ToString() { return string.Format("({0}, {1})", xPos.ToString(), yPos.ToString()); } public void Reset() { xPos = default(T); yPos = default(T); } } Utilizarea efectiv a ar putea : Point<int> p = new Point(10, 10); Point<double> q = new Point(1.2, 3.4); Observ am c a: Metodele, de si cu caracter generic, nu se mai specic a drept generice (nu se mai folosesc simbolurile < si >), acest lucru ind implicit folosim o supra ncarcare a cuv antului cheie default pentru a aduce c ampurile la valorile implicite ale tipului respectiv: 0 pentru numerice, false pentru boolean, null pentru tipuri referin ta . Mai ad aug am faptul c a o clas a poate avea mai mult de un tip generic drept parametru, exemplele clasice ind colec tiile generice de tip dic tionar pentru care se specic a tipul cheii si al valorilor con tinute.

7.3.3

Constr angeri asupra parametrilor de genericitate

Pentru structura de mai sus este posibil s a se foloseasc a o instan tiere de tipul: Point<StringBuilder> r = null; ceea ce este aberant din punct de vedere semantic. Am dori s a putem face restri tionarea tipului parametrilor generici. Un asemenea mecanism exist a si permite 5 tipuri de restric tii:

7.3. CLASE GENERICE where T:struct where T:class where T:new() where T:NameOfBaseClass where T:NameOfInterface Exemple:

191 T trebuie s a e tip derivat din System.ValueType T trebuie s a nu e derivat din System.ValueType T trebuie s a aibe un constructor implicit (f ar a parametri) T trebuie s a e derivat (direct sau nu) din NameOfBaseClass T trebuie s a implementeze interfa ta NameOfInterface

class MyGenericClass<T> where T:new() specic a faptul c a parametrul T trebuie s a e un tip cu constructor implicit class MyGenericClass<T> where T:class, IDrawable, new() specic a faptul c a parametrul T trebuie s a e de tip referin ta , s a implementeze IDrawable si s a posede constructor implicit class MyGenericClass<T>:MyBase, ICloneable where T:struct descrie o clas a care este derivat a din MyBase, implementeaz a ICloneable iar parametrul T este de tip valoare (structur a sau enumerare). Clasele generice pot de asemenea clase de baz a pentru tipuri (generice sau nu): class MyList<T>... class MyStringList : MyList<String>...

7.3.4

Interfe te si delega ti generici

Interfe tele si delega tii pot declarate ca ind generice; de si nu pezint a cerin te sau particularit a ti fa ta de ceea ce s-a spus mai sus, le eviden tiem astfel doarece gradul nalt de abstractizare le face deosebit de utile n modelarea unui sistem soft complex. interface IMyFeature<T> { T MyService(T param1, T param2); } respectiv: delegate void MyGenericDelegate<T>(T arg);

192

CURS 7. COLECTII. CLASE GENERICE.

7.4
7.4.1

Colec tii generice


Probleme cu colec tiile de obiecte

Colec tiile, a sa cum au fost ele prezentate n sec tiunea 7.1 sunt utile, dar au c ateva puncte slabe. 1. s a presupunem c a pornim cu o list a de tip ArrayList la care ad aug am elemente de tip ntreg: ArrayList al = new ArrayList(); al.Add(1); al.Add(2); int x = (int)al[0]; Secven ta este corect a din punct de vedere sintactic, dar la rulare solicit a folosirea mecanismului de boxing si unboxing. De si pentru colec tii mici acest lucru nu are are efecte sesizabile, pentru un num ar mare de ad aug ari sau acces ari ale elementelor din list a avem un impact negativ ce trebuie luat n calcul. Am prefera ca tipurile colec tie s a suporte lucrul cu tipuri valoare f ar a costul suplimentar introdus de boxing/unboxing. 2. problema tipului efectiv stocat n colec tie: s a presupunem c a ntro list a ad aug am: al.Add(new Dog("Miki")); al.Add(new Dog("Gogu")); al.Add(new Matrix(3, 5)); Dog dog = (Dog)al[2]; Secven ta de sus este corect a din punct de vedere sintactic, dar la rulare va determina aruncarea unei excep tii de tipul InvalidCastException. E de dorit ca la compilare s a se poat a semnala aceast a gre seal a.

7.4.2

Colec tii generice

Clasele generice mpreun a cu colec tiile au fost combinate n biblioteca .NET Framework, duc and la apari tia unui nou spa tiu de nume, con tinut n System.Collections : System.Collections.Generic. Acesta con tine tipurile: ICollection<T>, IComparer<T>, IDictionary<K, V>, IEnumerable<T>, IEnumerator<T>, IList<T>, Queue<T>, Stack<T>, LinkedList<T>, List<T>. Exemplu de utilizare:

7.5. ELEMENTE SPECIFICE C# 3.0 List<int> myInts = new List<int>(); myInts.Add(1); myInts.Add(2); //myInts.Add(new Complex());//eroare de compilare

193

De si n secven ta de mai sus tipul listei este int, nu se apeleaz a la boxing/unboxing, deoarece lista este compus a din elemente de tip ntreg si nu din obiecte de tip Object.

7.5

Elemente specice C# 3.0

Sec tiunea con tine o prezentare a elementelor noi introduse de versiunea 3.0 a limbajului C#, aspecte ce se pot raporta la ceea ce s-a prezentat p an a acum. Prezentarea este completat a n capitolele ulterioare (LINQ, lambda expresii).

7.5.1

Propriet a ti implementate automat

Consider am clasa: class MyClass { private int myField; public int MyField { get { return myField; } set { myField = value; } } } Deseori se pune problema scrierii unor c ampuri private, pentru care accesarea se face prin intermediul propriet a tilor. Este contraindicat s a se expun a c ampurile ca ind publice, deoarece se sparge ncapsularea si nu se poate face databinding la c ampuri publice. Dac a un c amp se expune ca ind public si

194

CURS 7. COLECTII. CLASE GENERICE.

un cod client ncepe s a l acceseze, este imposibil ca ulterior s a se impun a cod de validare pentru accesul la el. Deoarece codul de tipul celui scris mai sus apare foarte des, s-a pus problema simplic arii lui. In C# 3.0 se scrie echivalent: class MyClass { public int MyField { get; set; } } Se folose ste aici mecanismul de implementare automat a a unei propriet a ti care ac tioneaz a asupra unor c ampuri private autodeclarate; aceast a proprietate returneaz a sau acceseaz a direct c ampul asociat. Particularit actile sunt urm atoarele: 1. nu se declar a c ampul privat; acesta este creat automat de compilator, pe baza propriet a tii auto-implementate; 2. nu se scriu implement ari pentru get si set; corpul lor este caracterul ;. get acceseaz a c ampul autodeclarat, set seteaz a valoarea c ampului cu ce se a a n dreapta semnului egal; 3. nu se poate accesa c ampul autodeclarat altfel dec at prin intermediul propriet a tii 4. proprietatea nu poate doar read-only sau write-only, ci read-write. Dac a ulterior se decide implementarea unui accesor, atunci si cel alalt trebuie implementat si c ampul privat trebuie declarat. Important este ns a c a se scrie un minim de cod pentru a genera un contract: c amp privat accesat prin proprietate public a.

7.5.2

Ini tializatori de obiecte

S a consider am clasa: class Person { private string firstName;

7.5. ELEMENTE SPECIFICE C# 3.0 public string FirstName { get { return firstName; } set { firstName = value; } } private string lastName; public string LastName { get { return lastName; } set { lastName = value; } } private int age; public int Age { get { return age; } set { age = value; } } }

195

Se poate scrie urm atoarea secven ta de cod care ini tializeaz a o persoan a cu datele cuvenite: Person p = new Person(); p.FirstName = "Rafael"; p.Age = 25; p.LastName = "Popescu"; In C# 3.0 se poate scrie mai succint: Person p = new Person { FirstName = "Rafael", Age = 25, LastName = "Popescu" }; cu acela si rezultat1 ,2 . Intr-un astfel de caz se face mai nt ai apelarea constructorului implicit (indiferent de cine anume l scrie - compilatorul sau programatorul) si apoi se face accesarea propriet a tilor, n ordinea scris a la ini tializator. Membrii pentru care se face ini tializare trebuie s a e publici; n particular, ei
In unele lucr ari se folose ste: Person p = new Person(){FirstName="Rafael", Age=25, LastName="Popescu" }; deci cu paranteze rotunde dup a numele clasei folosite de operatorul new. 2 Cele dou a secven te nu sunt totu si echivalente, a sa cum se arat a n http://community.bartdesmet.net/blogs/bart/archive/2007/11/22/ c-3-0-object-initializers-revisited.aspx
1

196

CURS 7. COLECTII. CLASE GENERICE.

pot si c ampuri, dar acest lucru nu este ncurajat de principiul ncapsul arii. Putem avea inclusiv propriet a ti auto-implementate (sec tiunea 7.5.1). Dac a se scrie un constructor care preia un parametru dar nu si unul care s a e implicit, de exemplu: public Person(String firstName) { FirstName = firstName; } atunci se poate nc a folosi mecansimul de ini tializare: Person r = new Person("Rafael") {LastName="Popescu", Age = 25 }; Exemplul se poate dezvolta mai departe prin exemplicarea construirii unor obiecte mai complexe: Person p = new Person{ FirstName = "Rafael", LastName = "Popescu", Age={25}, Address = new Address{ City = "Brasov", Country = "Romania", Street = "Iuliu Maniu" } }

7.5.3

Ini tializatori de colec tii

Se d a secven ta de cod: List<String> list = new List<String>(); list.Add("a"); list.Add("b"); list.Add("c"); In C# 3.0 ea este echivalent a cu: List<String> list = new List<String>(){"a", "b", "c"}; ceea ce aduce aminte de o tr as atur a similar a de la ini tializarea tablourilor; mai exact codul de mai jos:

7.5. ELEMENTE SPECIFICE C# 3.0 String[] x = new String[3]; x[0] = "a"; x[1] = "b"; x[2] = "c"; este nc a din prima versiune de C# echivalent a cu: String[] x = new String[]{"a", "b", "c"};

197

Ini tializarea colec tiilor vine s a ofere acela si mecanism ca si n cazul tablourilor. Exemplul poate completat cu popularea unei colec tii de obiecte compuse: List<Person> persons = new List<Person>(){ new Person{FirstName = "Rafael", LastName="Popescu", Age=25}, new Person{FirstName = "Ioana", LastName="Ionescu", Age=23} };

198

CURS 7. COLECTII. CLASE GENERICE.

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

ADO.NET reprezint a o parte component a a lui .NET Framework ce permite aducerea, manipularea si modicarea datelor. In mod normal, o surs a de date poate s a e o baz a de date, dar de asemenea un sier text sau Excel sau XML sau Access. Lucrul se poate face e conectat, e deconectat de la sursa de date. De si exist a variate modalit a ti de lucru cu bazele de date, ADO.NET se impune tocmai prin faptul c a permite lucrul deconectat de la baza de date, integrarea cu XML, reprezentarea comun a a datelor cu posibilitatea de a combina date din variate surse, toate pe baza unor clase .NET. Faptul c a se permite lucrul deconectat de la sursa de date rezolv a urm atoarele probleme: men tinerea conexiunilor la baza de date este o opera tie costisitoare. O bun a parte a l a timii de band a este men tinut a ocupat a pentru ni ste proces ari care nu necesit a neap arat conectare continu a probleme legate de scalabilitatea aplica tiei: se poate ca serverul de baze de date s a lucreze u sor cu 5-10 conexiuni men tinute, dar dac a num arul acestora cre ste aplica tia poate s a reac tioneze extrem de lent pentru unele servere se impun clauze asupra num arului de conexiuni ce se pot folosi simultan. Toate acestea fac ca ADO.NET s a e o tehnologie mai potrivit a pentru dezvoltarea aplica tiilor Internet dec at cele precedente (e.g. ADO, ODBC). Pentru o prezentare a metodelor de lucru cu surse de date sub platform a Windows se poate consulta [7]. 199

200

CURS 8. ADO.NET

Vom exemplica n cele ce urmeaz a preponderent pe baz a de date Microsoft SQL 2005 Express Edition, ce se poate desc arca gratuit de pe site-ul Microsoft.

8.2

Furnizori de date n ADO.NET

Din cauza existen tei mai multor tipuri de surse de date (de exemplu, a mai multor produc atori de servere de baze de date) e nevoie ca pentru ecare tip major de protocol de comunicare s a se foloseasc a o bibliotec a specializat a de clase. Toate aceste clase implementeaz a ni ste interfe te bine stabilite, ca atare trecerea de la un SGBD la altul se face cu eforturi minore (dac a codul este scris tin and cont de principiile program arii orientate pe obiecte). Exist a urm atorii furnizori de date1 (lista nu este complet a): Tabelul 8.1: Furnizori de date. Nume furnizor Prex API ODBC Data Odbc Provider OleDb Data OleDb Provider Oracle Data Provider SQL Data Provider Borland Data Provider MySql Oracle Sql Bdp MySql Descriere Surse de date cu interfa ta ODBC (baze de date vechi) Surse de date care expun o interfa ta OleDb, de exemplu Access si Excel sau SQL Sever versiune mai veche de 7.0 SGBD Oracle Pentru interac tiune cu Microsoft SQL Server 7.0, 2000, 2005 Acces generic la SGBD-uri precum Interbase, SQL Server, IBM DB2, Oracle SGBD MySql

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

8.3

Componentele unui furnizor de date

Fiecare furnizor de date ADO.NET const a n patru componente: Connection, Command, DataReader, DataAdapter. Arhitectura ADO.NET este prezentat a n gura 8.1
1

Engl: Data Providers

8.3. COMPONENTELE UNUI FURNIZOR DE DATE

201

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. Ele con tin date specice conexiunii, cum ar loca tia sursei de date, numele si parola contului de acces, etc. In plus au metode pentru deschiderea si nchiderea conexiunilor, pornirea unei tranzac tii sau setarea perioadei de time-out. Stau la baza oric arei acces ari de servicii de pe server.

8.3.2

Clasele Command

Sunt folosite pentru a executa diferite comenzi pe baza de date (SELECT, INSERT, UPDATE, DELETE) si pentru a furniza un obiect de tip DataReader

202

CURS 8. ADO.NET

sau pentru a umple un DataSet prin intermediul unui obiect DataAdapter. Pot folosite pentru apelarea de proceduri stocate aate pe server. Ele permit scrierea de interog ari SQL parametrizate sau specicarea parametrilor pentru procedurile stocate.

8.3.3

Clasele DataReader

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

8.3.4

Clasele DataAdapter

Ultima component a principal a a unui furnizor de date .NET este DataAdapter. Func tioneaz a ca o punte ntre sursa de date si obiecte de tip DataSet deconectate, permi ta nd efectuarea opera tiilor pe baza de date. Con tin referin te c atre obiecte de tip Connection si deschid / nchid singure conexiunea la baza de date. In plus, un DataAdapter con tine referin te c atre patru comenzi pentru selectare, stergere, modicare si ad augare la baza de date.

8.3.5

Clasa DataSet

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

8.4

Obiecte Connection

Clasele de tip Connection pun la dispozi tie tot ceea ce e necesar pentru conectarea la baze de date. Este primul obiect cu care un programator ia contact atunci c and ncearc a s a foloseasc a un furnizor de date .NET. Inainte ca o comand a s a e executat a pe o baz a de date trebuie stabilite datele de conectare si deschis a conexiunea.

8.4. OBIECTE CONNECTION

203

Orice clas a de tip conexiune (din orice furnizor de date) implementeaz a intefa ta IDbConnection. De exemplu, clasele SqlConnection (folosit a pentru conectare la server Microsoft SQL Server 2000) sau OleDbConnection (folosit a pentru conectare la siere .mdb din Access sau Excel) implementeaz a IDbConnection. Pentru deschiderea unei conexiuni se poate proceda ca mai jos: using System.Data.SqlClient;//spatiul de nume SqlClient ... SqlConnection cn = new SqlConnection(@"Data Source= localhost\sqlexpress;Database=Northwind;User ID=sa; Password=parola"); cn.Open(); ... Mai sus sa specicat numele calculatorului pe care se a a instalat severul SQL (localhost) precum si al numelui de instan ta pentru acest server (express), baza de date la care se face conectarea (Northwind), contul SQL cu care se face accesul (sa) si parola pentru acest cont (parola). Pentru conectarea la un sier Access Northwind.mdb aat n directorul c:\lucru se folose ste un obiect de tipul OleDbConnection sub forma: using System.Data.OleDb;//spatiul de nume OleDb ... OleDbConnection cn = new OleDbConnection( @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source= C:\Lucru\Northwind.mdb"); cn.Open(); ... S-a specicat furnizorul de date (Microsoft.Jet.OLEDB.4.0 pentru sier Access) precum si locul unde se a a sursa de date (C:\Lucru\Northwind.mdb). Vom enumera principalele propriet a ti, metode si evenimente pentru un obiect de tip Connection.

8.4.1

Propriet a ti

1. ConnectionString : de tip String, cu accesori de get si set ; aceast a proprietate dene ste un string care permite identicarea tipului si loca tiei sursei de date la care se face conectarea si eventual contul si parola de acces. Acest string con tine lista de parametri necesari pentru conectare sub forma numeParametru=valoare, separa ti prin punct si virgul a. Parametrii sunt:

204

CURS 8. ADO.NET provider : se specic a furnizorul de date pentru conectarea la sursa de date. Acest furnizor trebuie precizat doar dac a se folose ste OLE DB .NET Data Provider, ns a nu se specic a pentru conectare la SQL Server. Data Source (sinonim cu server ): se specic a numele serverului de baze de date sau numele sierului de date. Initial Catalog (sinonim cu Database ): specic a numele baze de date. Baza de date trebuie s a se g aseasc a pe serverul dat n Data Source. User ID (sinonim cu uid ): specica un nume de utilizator care are acces de loginare la server. Password (sinonim cu pwd ): specic a parola contului de mai sus.

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

8.4. OBIECTE CONNECTION

205

8.4.2

Metode

1. Open() : deschide o conexiune la baza de date 2. Close(), Dispose() : nchid conexiunea si elibereaz a toate resursele alocate pentru ea 3. BeginTransaction() : pentru executarea unei tranzac tii pe baza de date; la sf ar sit se apeleaz a Commit() sau Rollback(). 4. ChangeDatabase() : se modic a baza de date la care se vor face conexiunile. Noua baz a de date trebuie s a existe pe acela si server ca precedenta. 5. CreateCommand() : creeaz a un obiect de tip Command valid (care implementeaz a interfa ta IDbCommand ) asociat cu conexiunea curent a.

8.4.3

Evenimente

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

8.4.4

Stocarea stringului de conexiune n sier de congurare

In general este contraindicat ca stringul de conexiune s a e scris direct n cod; modicarea datelor de conectare (de exemplu parola pe cont sau sursa de date) ar nsemna recompilarea codului. .NET Framework permite men tinerea unor perechi de tipul chei-valoare, specice aplica tiei; sierul este de tip XML. Pentru aplica tiile Web sierul se nume ste web.cong, pentru aplica tiile de tip consol a sierul de congurare are extensia cong si numele aplica tiei, iar pentru aplica tiile Windows acest sier nu exist a implicit, dar se adaug a: Project->Add new item->Application Conguration File, implicit acesta av and numele App.cong. Elementul r ad acin a mpreun a cu declara tia de XML sunt: <?xml version="1.0" encoding="utf-8" ?> <configuration> </configuration>

206

CURS 8. ADO.NET

In interiorul elementului r ad acin a conguration se va introduce elementul appSettings, care va con tine oric ate perechi cheie-valoare n interiorul unui atribut numit add, precum mai jos: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="constring" value="Data Source=localhost\sqlexpress;database=Northwind; User ID=sa;pwd=parola"/> </appSettings> </configuration> Clasele neceasre pentru accesarea sierului de congurare se g asesc n spa tiul de nume System.Conguration. Pentru a se putea folosi acest spa tiu de nume trebuie s a se adauge o referin ta la assembly-ul care con tine deaceast a clas a: din Solutin explorer click dreapta pe proiect->Add reference. . . ->se alege tab-ul .NET si de acolo System.Conguration. Utilizarea stringului de conexiune denit anterior se face astfel: using System; using System.Data; using System.Data.SqlClient; using System.Configuration; public class UsingConfigSettings { public static void Main() { SqlConnection con = new SqlConnection( ConfigurationManager.AppSettings["constring"]; //se lucreaza cu conexiunea con.Close(); } } Este posibil ca ntr-o aplica tie s a se foloseasc a mai multe conexiuni, motiv pentru care se sugereaz a ca n loc de varianta precedent a s a se foloseasc a elementul XML <connectionStrings>: <configuration> <appSettings>...</appSettings> <connectionStrings> <add name ="SqlProviderPubs" connectionString =

8.4. OBIECTE CONNECTION "Data Source=localhost\sqlexpress;uid=sa;pwd=; Initial Catalog=Pubs"/> <add name ="OleDbProviderPubs" connectionString = "Provider=SQLOLEDB.1;Data Source=localhost;uid=sa;pwd=; Initial Catalog=Pubs"/> </connectionStrings> </configuration> Preluarea unui string de conexiune se face prin: string cnStr = ConfigurationManager.ConnectionStrings["SqlProviderPubs"] .ConnectionString;

207

8.4.5

Gruparea conexiunilor

Gruparea conexiunilor2 reprezint a reutilizarea resurselor de tip conexiune la o baz a de date. Atunci c and se creeaz a o grupare de conexiuni se genereaz a automat mai multe obiecte de tip conexiune, acest num ar ind egal cu minimul setat pentru gruparea respectiv a. O nou a conexiune este creat a dac a toate conexiunile sunt ocupate si se cere conectare. Dac a dimensiunea maxim a setat a a grup arii este atins a, atunci nu se va mai crea o conexiune nou a, ci se va pune ntro coad a de a steptare. Dac a a steparea dureaz a mai mult dec at este precizat n valoarea de Timeout se va arunca o excep tie. Pentru a returna o conexiune la grupare trebuie apelat a metoda Close() sau Dispose() pentru acea conexiune. Sursele de date .NET administreaz a automat gruparea de conexiuni, degrev andu-l pe programator de aceast aspect ce nu tine de logica aplica tiei. La dorin ta comportamentul implicit se poate modica prin intemediul con tinutului stringului de conectare.

8.4.6

Mod de lucru cu conexiunile

Se cere ca o conexiune s a e ntotdeauna nchis a ( si dac a se poate, c at mai repede posibil). Ca atare, este de preferat ca s a se aplice o schem a de lucru de tipul: IDBConnection con = ... try {
2

Engl: connection pooling

208 //deschidere conexiune //lucru pe baza } catch(Exception e) { //tratare de exceptie } finally { con.Close(); }

CURS 8. ADO.NET

Deoarece se garanteaz a ca blocul nally este executat indiferent dac a apare sau nu o excep tie, n cazul de mai sus se va nchide n mod garantat conexiunea. In acela si scop se mai poate folosi si instruc tiunea using (sec tiunea 3.5.8).

8.5

Obiecte Command

Un clas a de tip Command dat a de un furnizor .NET trebuie s a implementeze interfa ta IDbCommand, ca atare toate vor asigura un set de servicii bine specicat. Un asemenea obiect este folosit pentru a executa comenzi pe baza de date: SELECT, INSERT, DELETE, UPDATE sau apel de proceduri stocate (dac a SGBD-ul respectiv stie acest lucru). Comanda se poate executa numai dac a s-a deschis o conexiune la baza de date. Exemplu: SqlConnection con = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString; SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", con); Codul care utilizeaz a o comand a pentru lucrul cu siere mdb sau xls ar foarte asem an ator, cu diferen ta c a n loc de SqlCommand se folose ste OleDbCommand, din spa tiul de nume System.Data.OleDb. Nu trebuie modicat altceva, doarece loca tia sursei de date se specic a doar la conexiune. Se observ a c a obiectul de conexiune este furnizat comenzii create. Enumer am mai jos principalele propriet a ti si metode ale unui obiect de tip comand a.

8.5.1

Propriet a ti

1. CommandText : de tip String, cu ambii accesori; con tine comanda SQL sau numele procedurii stocate care se execut a pe sursa de date.

8.5. OBIECTE COMMAND

209

2. CommandTimeout : de tip int, cu ambii accesori; reprezint a num arul de secunde care trebuie s a e a steptat pentru executarea interog arii. Dac a se dep a seste acest timp, atunci se arunc a o excep tie. 3. CommandType : de tip CommandType (enumerare), cu ambii accesori; reprezint a tipul de comand a care se execut a pe sursa de date. Valorile pot : CommandType.StoredProcedure - interpreteaz a comanda con tinut a n proprietatea CommandText ca o un apel de procedur a stocat a denit a n baza de date. CommandType.Text - interpreteaz a comanda ca ind o comand a SQL clasic a; este valoarea implicit a. CommandType.TableDirect - momentan disponibil numai pentru furnizorul OLE DB (deci pentru obiect de tip OleDbCommand); dac a proprietatea CommandType are aceast a valoare, atunci proprietatea CommandText este interpretat a ca numele unui tabel pentru care se aduc toate liniile si coloanele la momentul execut arii. 4. Connection - proprietate de tip System. Data. [.NET Data Provider]. PrexConnection, cu ambii accesori; con tine obiectul de tip conexiune folosit pentru legarea la sursa de date; Prex este prexul asociat furnizorului respectiv (a se vedea tabelul 8.1). 5. Parameters - proprietate de tip System.Data.[.NET Data Provider]. PrexParameterCollection, read-only; returneaz a o colec tie de parametri care s-au transmis comenzii; aceast a list a a fost creat a prin apelul metodei CreateParameter().Prex reprezint a acelasi lucru ca mai sus. 6. Transaction - proprietate de tip System.Data.[.NET Data Provider]. PrexTransaction, read-write; permite accesul la obiectul de tip tranzac tie care se cere a executat pe sursa de date.

8.5.2

Metode

1. Constructori - un obiect de tip comand a poate creat si prin intermediul apelului de constructor; de exemplu un obiect SqlCommand se poate ob tine astfel: SqlCommand cmd; cmd = new SqlCommand(); cmd = new SqlCommand(string CommandText);

210

CURS 8. ADO.NET cmd = new SqlCommand(string CommandText, SqlConnection con ); cmd = new SqlCommand(string CommandText, SqlConnection con, SqlTransaction trans);

2. Cancel() - ncearc a s a opreasc a o comand a dac a ea se a a n execu tie. Dac a nu se a a n execu tie, sau aceast a ncercare nu are nici un efect nu se int ampl a nimic. 3. Dispose() - distruge obiectul comand a. 4. ExecuteNonQuery() - execut a o comand a care nu returneaz a un set de date din baza de date; dac a comanda a fost de tip INSERT, UPDATE, DELETE, se returneaz a num arul de nregistr ari afectate. Dac a nu este denit a conexiunea la baza de date sau aveasta nu este deschis a, se arunc a o excep tie de tip InvalidOperationException. Exemplu: SqlConnection con = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString); SqlCommand cmd = new SqlCommand(); cmd.CommandText = "DELETE FROM Customers WHERE CustomerID = SEVEN"; cmd.Connection = con; con.Open(); Console.WriteLine(cmd.ExecuteNonQuery().ToString()); con.Close() In exemplul de mai sus se returneaz a num arul de nregistr ari care au fost sterse. 5. ExecuteReader() - execut a comanda con tinut a n proprietatea CommandText si se returneaz a un obiect de tip IDataReader (e.g. SqlDataReader sau OleDbDataReader ). Exemplu: se ob tine con tinutul tabelei Customers ntrun obiect de tip SqlDataReader (se presupune c a baza de date se stocheaz a pe un server MSSQL): SqlConnection con = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString); SqlCommand cmd = new SqlCommand();

8.5. OBIECTE COMMAND cmd.CommandText = "SELECT * FROM Customers"; cmd.Connection = con; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); con.Close();

211

Metoda ExecuteReader() mai poate lua un argument op tional de tip enumerare CommandBehavior care descrie rezultatele si efectul asupra bazei de date: CommandBehavior.CloseConnection - conexiunea este nchis a atunci c and obiectul de tip IDataReader este nchis. CommandBehavior.KeyInfo - comanda returnez a metadate despre coloane si cheia primar a. CommandBehavior.SchemaOnly - comanda returnez a doar informa tie despre coloane. CommandBehavior.SequentialAccess - d a posibilitatea unui DataReader s a manipuleze nregistr ari care con tin c ampuri cu valori binare de mare ntindere. Acest mod permite nc arcarea sub forma unui ux de date folosind GetChars() sau GetBytes(). CommandBehavior.SingleResult - se returneaz a un singur set de rezultate CommandBehavior.SingleRow - se returneaz a o singur a linie. De exemplu, dac a n codul anterior nainte de while ob tinerea obiectului reader sar face cu: SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleRow); atunci sar returna doar prima nregistrare din setul de date. 6. ExecuteScalar() - execut a comanda con tinut a n proprietatea CommandText ; se returneaz a valoarea primei coloane de pe primul r and a setului de date rezultat; folosit pentru ob tinerea unor rezultate de tip agregat (SELECT COUNT(*) FROM CUSTOMERS, de exemplu).

212

CURS 8. ADO.NET

7. ExecuteXmlReader() - returneaz a un obiect de tipul XmlReader ob tinut din rezultatul interog arii pe sursa de date. Exemplu: SqlCommand custCMD=new SqlCommand("SELECT * FROM Customers FOR XML AUTO, ELEMENTS", con); System.Xml.XmlReader myXR = custCMD.ExecuteXmlReader();

8.5.3

Utilizarea unei comenzi cu o procedur a stocat a

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

8.5.4

Folosirea comenzilor parametrizate

Exist a posibilitatea de a rula cod SQL (interog ari sau proceduri stocate) pe server care s a e parametrizate. Orice furnizor de date .NET permite crearea obiectelor parametru care pot ad augate la o colec tie de parametri

8.5. OBIECTE COMMAND

213

ai comenzii. Valoarea acestor parametri se specic a e prin numele lor (cazul SqlParameter ), e prin pozitia lor (cazul OleDbParameter ). Exemplu: vom aduce din tabela Customers toate nregistr arile care au n c ampul Country valoarea USA. SqlConnection con = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString); SqlCommand cmd = new SqlCommand("SELECT * FROM Customers WHERE Country=@country",con); SqlParameter param = new SqlParameter("@country", SqlDbType.VarChar); param.Value = "USA"; cmd.Parameters.Add( param ); con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); con.Close(); Pentru parametrul creat sa setat tipul lui (ca ind tip sir de caractere SQL) si valoarea. De re tinut faptul c a numele parametrului se prexeaz a cu caracterul @ n cazul lucrului cu SQL Server. In cazul n care un parametru este de ie sire, acest lucru trebuie spus explicit folosind proprietatea Direction a parametrului respectiv: SqlCommand cmd = new SqlCommand( "SELECT * FROM Customers WHERE Country = @country; " + "SELECT @count = COUNT(*) FROM Customers WHERE Country = @country", con); SqlParameter param = new SqlParameter("@country", SqlDbType.VarChar); param.Value = "USA"; cmd.Parameters.Add( param ); cmd.Parameters.Add(new SqlParameter("@count", SqlDbType.Int)); cmd.Parameters["@count"].Direction = ParameterDirection.Output; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) {

214 Console.WriteLine("{0} - {1}", reader.GetString(0), reader.GetString(1)); } reader.Close(); Console.WriteLine("{0} - {1}", "Count", cmd.Parameters["@count"].Value.ToString()); con.Close(); Remarc am urm atoarele:

CURS 8. ADO.NET

este posibil ca ntro comand a s a se execute mai multe interog ari pentru parametrul de ie sire numit @count trebuie f acut a declarare de direc tie; implicit un parametru este de intrare parametrii de ie sire sunt accesibili doar dup a nchiderea obiectului de tip DataReader

8.6

Obiecte DataReader

Un obiect de tip DataReader este folosit pentru a citi date dintr-o surs a de date. Caracteristicile unei asemenea clase sunt: 1. implementeaz a ntotdeauna interfa ta IDataReader 2. se lucreaz a conectat la sursa de date - pe toat a perioada c at este accesat un DataReader necesit a conexiune activ a 3. este de tip read-only; dac a se dore ste modicarea datelor se poate folosi un DataSet + DataAdapter sau comenzi INSERT, DELETE sau UPDATE trimise prin obiect de tip Command ; 4. este de tip forward-only - metoda de modicare a pozi tiei curente este doar n direc tia nainte; orice re ntoarcere presupune reluarea nregistr arilor (dac a programatorul nu implementeaz a el singur un mecanism de coad a) Avantajele utiliz arii acestui tip de obiecte sunt: accesul conectat, performan tele bune, consumul mic de resurse si tipizarea puternic a.

8.6. OBIECTE DATAREADER

215

8.6.1

Propriet a ti

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

8.6.2

Metode

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

216

CURS 8. ADO.NET

8. Read() - determin a trecerea la urm atoarea nregistrare, dac a aceasta exista a; n acest caz ea returneaz a true. Metoda trebuie chemat a cel pu tin o dat a, deoarece ini tial pozi tia curent a este naintea primei nregistr ari.

8.6.3

Crearea si utilizarea unui DataReader

Nu se creaz a un obiect de tip DataReader prin apel de constructor, ci prin intermediul unui obiect de tip Command, folosind apelul ExecuteReader() (a a se specic a instruc tiunea se vedea sec tiunea 8.3.2). Pentru comanda respectiv care determin a returnarea setului de date precum si obiectul de conexiune. Aceast a conexiune trebuie s a e deschis a naintea apelului ExecuteReader(). Trecerea la urm atoarea nregistrare se face folosind metoda Read(). C and se dore ste ncetarea lucrului se nchide readerul si conexiunea. Omiterea nchiderii obiectului de tip reader va duce la imposibilitatea reutiliz arii conexiunii ini tiale. Dup a ce se nchide acest DataReader este necesar a si n chiderea explicit a a conexiunii (acest lucru nu mai e mandatoriu doar dac a la apelul metodei ExecuteReader sa specicat CommandBehavior.CloseConnection ). Dac a se ncearc a refolosirea conexiunii f ar a ca readerul s a fost nchis se va arunca o excep tie InvalidOperationException. Exemplu: SqlConnection conn = new SqlConnection ( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString); SqlCommand selectCommand = new SqlCommand("select * from ORDERS", conn); conn.Open (); OleDbDataReader reader = selectCommand.ExecuteReader ( ); while ( reader.Read () ) { object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } reader.Close (); conn.Close (); Este perfect posibil ca un obiect de tip DataReader s a aduc a datele prin apelul unei proceduri stocate (de fapt invocarea acestei proceduri este f acut a de c atre obiectul de tip Command ).

8.6. OBIECTE DATAREADER

217

Urm atoarele observa tii trebuie luate n considerare atunci c and se lucreaz a cu un obiect DataReader : Metoda Read() trebuie s a e ntotdeauna apelat a naintea oric arui acces la date; pozi tia curent a la deschidere este naintea primei nregistr ari. Intotdeauna apela ti metoda Close() pe un DataReader si pe conexiunea asociat a c at mai repede posibil; n caz contrar conexiunea nu poate reutilizat a Procesarea datelor citite trebuie s a se fac a dup a nchiderea conexiunii; n felul acesta conexiunea se las a liber a pentru a putea reutilizat a.

8.6.4

Utilizarea de seturi de date multiple

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

8.6.5

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

S a consider am urm atoarea secven ta de cod: while ( reader.Read () ) {

218

CURS 8. ADO.NET

object id = reader["OrderID"]; object date = reader["OrderDate"]; object freight = reader["Freight"]; Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight ); } Dup a cum se observ a, este posibil ca valorile c ampurilor dintro nregistrare s a e accesate prin intermediul numelui coloanei (sau a indicelui ei, pornind de la 0). Dezavantajul acestei metode este c a tipul datelor returnate este pierdut (ind returnate obiecte de tip Object ), trebuind f acut a un downcasting pentru a utiliza din plin facilit a tile tipului respectiv. Pentru ca acest lucru s a nu se nt ample se pot folosi metodele GetXY care returneaz a un tip specic de date: 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 secven tei anterioare este c a dac a se ncearc a aducerea valorii unui c amp pentru care tipul nu este dat corect se arunc a o excep tie InvalidCastException ; altfel spus, accesul la date se face sigur din punct de verere al tipului datelor. Pentru a evita folosirea unor constante magice ca indici de coloan a (precum mai sus: 0, 3, 7), se poate folosi urm atoarea strategie: indicii se ob tin folosind apel de metod a GetOrdinal la care se specic a numele coloanei dorite: private int OrderID; private int OrderDate; private int Freight; ... OrderID = reader.GetOrdinal("OrderID"); OrderDate = reader.GetOrdinal("OrderDate"); Freight = reader.GetOrdinal("Freight"); ... reader.GetDecimal ( Freight ); ...

Curs 9 ADO.NET (2)


9.1 Obiecte DataAdapter

La fel ca si Connection, Command, DataReader, obiectele de tip DataAdapter fac parte din furnizorul de date .NET specic ec arui tip de surs a de date. Scopul clasei este s a permit a umplerea unui obiect DataSet cu date si reectarea schimb arilor efectuate asupra acestuia napoi n baza de date (DataSet permite lucrul deconectat de la baza de date). Orice clas a de tipul DataAdapter (de ex SqlDataAdapter si OleDbDataAdapter) este derivat a din clasa DbDataAdapter (clas a abstract a). Pentru orice obiect de acest tip trebuie specicat a minim comanda de tip SELECT care s a populeze un obiect de tip DataSet ; acest lucru este stabilit prin intermediul propriet a tii SelectCommand de tip Command (SqlCommand, OleDbCommand, . . . ). In cazul n care se dore ste si modicarea informa tiilor din sursa de date (inserare, modicare, stergere) trebuie specicate obiecte de tip comand a via propriet a tile: InsertCommand, UpdateCommand, DeleteCommand. Exemplu: mai jos se preiau nregistr arile din 2 tabele: Authors si TitleAuthor si se trec ntrun obiect de tip DataSet pentru a procesate ulterior. using System; using System.Data; using System.Data.SqlClient; class DemoDataSource { static void Main() { SqlConnection conn = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] 219

220

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

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

9.1.1

Metode

1. Constructori de la cei implici ti p an a la cei n care se specic a o comand a de tip SELECT si conexiunea la sursa de date. Pentru un obiect de tip SqlDataAdapter se poate crea o instan ta n urm atoarele moduri: SqlDataAdapter da = new SqlDataAdapter(); sau: SqlCommand cmd = new SqlCommand("SELECT * FROM Employees"); SqlDataAdapter da = new SqlDataAdapter(cmd); sau: String strCmd = "SELECT * FROM Employees"; String strConn = "..."; SqlDataAdapter da = new SqlDataAdapter(strCmd, strConn);

2. Fill() metod a polimorc a, permi ta nd umplerea unei tabele dintr un obiect de tip DataSet cu date. Permite specicarea obiectului DataSet n care se depun datele, eventual a numelui tablei din acest DataSet, num arul de nregistrare cu care s a se nceap a popularea (prima av and indicele 0) si num arul de nregistr ari care urmeaz a a aduse. Returneaz a de ecare dat a num arul de nregistr ari care au fost aduse din baz a. In clipa n care se apeleaz a Fill() se procedeaz a astfel:

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

221

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

9.1.2

Propriet a ti

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

AddWithKey - ca mai sus, dar adaug a si informa tii relativ la cine este cheia primar a Ignore - se ignor a lipsa coloanei respective, ceea ce duce la pierdere de date pe DataSet

Error - se genereaz a o excep tie de tipul InvalidOperationException.

9.2

Clasa DataSet

Clasa DataSet nu mai face parte din biblioteca unui furnizor de date ADO.NET, ind parte din .NET Framework. Ea poate s a con tin a reprezent ari tabelare ale datelor din baz a precum si diferite restric tii si rela tii existente. Marele ei avantaj este faptul c a permite lucrul deconectat de la sursa de date,

222

CURS 9. ADO.NET (2)

elimin and necesitatea unei conexiuni permanente precum la DataReader. In felul acesta, un server de aplica tii sau un client oarecare poate apela la serverul de baze de date doar c and preia datele sau c and se dore ste salvarea lor. Func tioneaz a n str ans a leg atur a cu clasa DataAdapter care ac tioneaz a ca o punte ntre un DataSet si sursa de date. Remarcabil este faptul c a un DataSet poate face abstrac tie de sursa de date, procesarea datelor desf a sur anduse independent de ea. Figura 9.1 con tine o vedere par tial a asupra clasei DataSet.

Figura 9.1: Structura unui DataSet

9.2.1

Con tinut

Prezent am succint con tinutul unui DataSet : 1. Colec tia Tables con tine 0 sau mai multe obiecte DataTable. Fiecare DataTable este compus a dintr-o colec tie de linii si coloane. 2. Colec tia Relations con tine 0 sau mai multe obiecte de tip DataRelation, folosite pentru marcarea leg aturilor p arintecopil. 3. Colec tia ExtendedProperties con tine propriet a ti denite de utilizator.

9.2.2

Clasa DataTable

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

9.2. CLASA DATASET

223

DataSet ca elemente ale colec tiei Tables. Un DataTable con tine o colec tie Columns de coloane, Rows de linii si Constraints de constr angeri. DataColumn Un obiect DataColumn dene ste numele si tipul unei coloane care face parte sau se adaug a unui obiect DataTable. Un obiect de acest tip se ob tine prin apel de constructor sau pe baza metodei DataTable.Columns.Add. Exemplu: DataColumn myColumn = new DataColumn("title", Type.GetType("System.String")); Denirea unei coloane ca ind de tip autonumber ( n vederea stabilirii ei ca si cheie pe o tabel a) se face astfel: DataColumn idColumn = new DataColumn("ID", Type.GetType("System.Int32")); idColumn.AutoIncrement = true; idColumn.AutoIncrementSeed = 1; idColumn.AutoIncrementStep = 1; idColumn.ReadOnly = true; DataRow Un obiect de tip DataRow reprezint a o linie dintr-un obiect DataTable. Orice obiect DataTable con tine o proprietate Rows ce da acces la colec tia de obiecte DataRow con tinut a. Pentru crearea unei linii se poate apela metoda NewRow pentru o tabel a a c arei schem a se cunoa ste. Mai jos este dat a secven ta de cod care creeaz a o linie nou a pentru o tabel a si o adaug a acestesia: DataRow tempRow; tempRow = myTable.NewRow(); tempRow["Name"] = "Book"; tempRow["Category"] = 1; myTable.Rows.Add(tempRow); Constr angeri Constr angerile sunt folosite pentru a descrie anumite restric tii aplicate asupra valorilor din coloane. In ADO.NET exist a dou a tipuri de constr angeri: de unicitate si de cheie str ain a. Toate obiectele de constr angere se a a n colec tia Constraints a unei tabele.

224

CURS 9. ADO.NET (2)

UniqueConstraint - precizeaz a c a ntr-o anumit a coloan a valorile sunt unice. Incercarea de a seta valori duplicate pe o coloana pentru care s-a precizat restric tia duce la aruncarea unei excep tii. Este necesar a o asemenea coloan a n clipa n care se folose ste metoda Find pentru proprietatea Rows : n acest caz trebuie s a se specice o coloan a pe care avem unicitate.

ForeignKeyConstraint - specic a ac tiunea care se va efectua atunci c and se sterge sau modic a valoarea dintro anumit a coloan a. De exemplu se poate decide c a dac a se sterge o nregistrare dintr-o tabel a atunci s a se stearg a si nregistr arile copil. Valorile care se pot seta pentru o asemenea constr angere se specic a n propriet a tile ForeignKeyConstraint.Delete si ForeignKeyConstraint.UpdateRule : Rule.Cascade - ac tiunea implicit a, sterge sau modic a nregistr arile afectate Rule.SetNull - se seteaz a valoare de null pentru nregistr arile afectate Rule.SetDefault - se seteaz a valoarea implicit a denit a n baz a pentru c ampul respectiv Rule.None - nu se execut a nimic Exemplu: ForeignKeyConstraint custOrderFK=new ForeignKeyConstraint ("CustOrderFK",custDS.Tables["CustTable"].Columns["CustomerID"], custDS.Tables["OrdersTable"].Columns["CustomerID"]); custOrderFK.DeleteRule = Rule.None; //Nu se poate sterge un client care are comenzi facute custDS.Tables["OrdersTable"].Constraints.Add(custOrderFK); Mai sus s-a declarat o rela tie de tip cheie str ain a ntre dou a tabele (CustTable si OrdersTable, care fac parte dintr-un DataSet). Restric tia se adaug a la tabla copil. Stabilirea cheii primare O cheie primar a se dene ste ca un vector de coloane care se atribuie propriet a tii PrimaryKey a unei tabele (obiect DataTable ). DataColumn[] pk = new DataColumn[1]; pk[0] = myTable.Columns["ID"]; myTable.PrimaryKey = pk;

9.2. CLASA DATASET

225

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

9.2.3

Rela tii ntre tabele

Proprietatea Relations a unui obiect de tip DataSet con tine o colec tie de obiecte de tip DataRelation folosite pentru a gura rela tiile de tip p arinte copil ntre dou a tabele. Aceste rela tii se precizeaz a n esen ta ca niste perechi de array-uri de coloane sau chiar coloane simple din cele dou a tabele care se rela tioneaz a, de exemplu sub forma: myDataSet.Relations.Add(DataColumn, DataColumn); //sau myDataSet.Relations.Add(DataColumn[], DataColumn[]); concret: myDataSet.Relations.Add( myDataSet.Tables["Customers"].Columns["CustomerID"], myDataSet.Tables["Orders"].Columns["CustomerID"]);

9.2.4

Popularea unui DataSet

De si un obiect DataSet se poate popula prin crearea dinamic a a obiectelor DataTable, cazul cel mai des nt alnit este acela n care se populeaz a prin intermediul unui obiect DataAdapter. O dat a ob tinut un asemenea obiect (care con tine cel pu tin o comand a de tiop SELECT ) se poate apela metoda Fill() care prime ste ca parametru DataSet -ul care se umple si op tional numele tabelei care va con tine datele: //defineste comanda de selectare din baza de date String mySqlStmt ="SELECT * FROM Customers"; String myConString = ConfigurationManager.ConnectionStrings["constring"] .ConnectionString; //Construieste obiectele de conexiune + comanda SELECT SqlConnection myConnection = new SqlConnection(myConString); SqlCommand myCommand = new SqlCommand(mySqlStmt, myConnection);

226

CURS 9. ADO.NET (2)

//Construieste obiectul DataAdapter 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 construc tia myDataSet.Tables["Customers"] sau folosind indici ntregi (prima tabel a are indicele 0). Acela si DataSet se poate popula n continuare cu alte tabele pe baza aceluia si sau a altor obiecte DataAdapter.

9.2.5

Clasa DataTableReader

Incep and cu versiunea 2.0 a lui ADO.NET s-a introdus clasa DataTableReader care permite manipularea unui obiect de tip DataTable ca si cum ar un DataReader : ntr-o manier a forward-only si read-only. Crearea unui obiect de tip DataTableReader se face prin: DataTableReader dtReader = dt.CreateDataReader(); iar folosirea lui: while (dtReader.Read()) { for (int i = 0; i < dtReader.FieldCount; i++) { Console.Write("{0} = {1} ", dtReader.GetName(i), dtReader.GetValue(i).ToString().Trim()); } Console.WriteLine(); } dtReader.Close();

9.2. CLASA DATASET

227

9.2.6

Propagarea modic arilor c atre baza de date

Pentru a propaga modic arile efectuate asupra con tinutului tabelelor dintr-un DataSet c atre baza de date este nevoie s a se deneasc a adecvat obiecte comand a de tip INSERT, UPDATE, DELETE. Pentru cazuri simple se poate folosi clasa SqlCommandBuilder care va construi singur a aceste comenzi. Clasa CommandBuilder Un obiect de tip CommandBuilder (ce provine din furnizorul de date) va analiza comanda SELECT care a adus datele n DataSet si va construi cele 3 comenzi de update n func tie de aceasta. E nevoie s a se satisfac a 2 condi tii atunci c and se uzeaz a de un astfel de obiect: 1. Trebuie specicat a o comand a de tip SELECT care s a aduc a datele dintr-o singur a tabel a 2. Trebuie specicat a cel pu tin cheia primar a sau o coloan a cu constr angere de unicitate n comanda SELECT. Pentru cea de a doua condi tie se poate proceda n felul urm ator: n comanda SELECT se specic a si aducerea cheii, iar pentru obiectul DataAdapter care face aducerea din baz a se seteaz a proprietatea MissingSchemaAction pe valoarea MissingSchemaAction.AddWithKey (implicit este doar Add ). Fiecare linie modicat a din colec tia Rows a unei tabele va avea modicat a valoarea propriet a tii RowState astfel: DataRowState.Added pentru o linie nou a ad augat a, DataRowState.Deleted dac ae stears a si DataRowState.Modied dac a a fost modicat a. Apelul de update pe un dataReader va apela comanda necesar a pentru ecare linie care a fost modicat a, n func tie de starea ei. Ar at am mai jos modul de utilizare a clasei SqlCommandBuilder pentru ad augarea, modicarea, stergerea de nregistr ari din baza de date. SqlConnection conn = new SqlConnection( ConfigurationManager.ConnectionStrings["constring"] .ConnectionString); da = new SqlDataAdapter("SELECT id, name, address FROM customers",conn); da.MissingSchemaAction = MissingSchemaAction.AddWithKey; da.Fill(ds); SqlCommandBuilder cb = new SqlCommandBuilder(da); //determina liniile care au fost schimbate

228 DataSet dsChanges = ds.GetChanges(); if (dsChanges != null) { // modifica baza de date da.Update(dsChanges); //accepta schimbarile din dataset ds.AcceptChanges(); }

CURS 9. ADO.NET (2)

In clipa n care se creeaz a obiectul SqlCommandBuilder automat se vor completa propriet a tile InsertCommand, DeleteCommand, UpdateCommand ale dataAdapter-ului. Se determin a apoi liniile care au fost modicate (prin interogarea st arii lor) si se ob tine un nou DataSet care le va con tine doar pe acestea. Comanda de Update se d a doar pentru acest set de modic ari, reduc and astfel tracul spre serverul de baze de date. Update folosind comenzi SQL Atunci c and interog arile de aducere a datelor sunt mai complexe (de exemplu datele sunt aduse din mai multe table, printr-un join) se pot specica propriile comenzi SQL prin intermediul propriet a tilor InsertCommand, DeleteCommand UpdateCommand ale obiectului DataAdapter. Pentru ecare linie dintro tabel a care este modicat a/ad augat a/ stears a se va apela comanda SQL corespunz atoare.Aceste comenzi pot fraze SQL parametrizate sau pot denumi proceduri stocate aate n baz a. S a presupunem c a s-a denit un DataAdapter legat la o baz a de date. Instruc tiunea de selec tie este SELECT CompanyName, Address, Country, CustomerID FROM Customers unde CustomerID este cheia. Pentru inserarea unei noi nregistr ari sar putea scrie codul de mai jos: //da=obiect DataAdapter da.InsertCommand.CommandText = @"INSERT INTO Customers ( CompanyName, Address, Country) VALUES (@CompanyName, @Address, @Country); SELECT CompanyName, Address, Country, CustomerID FROM Customers WHERE (CustomerID = scope_indentity())"; Update-ul efectiv se face prin prima instruc tiune de tip Update. Valorile pentru ace sti parametri se vor da la runtime, de exemplu prin alegerea lor dintr-un tabel. Valoarea pentru cheia CustomerID nu s-a specicat,

9.3. TRANZACTII IN ADO.NET

229

deoarece ( n acest caz) ea este de tip AutoNumber (SGBD-ul este cel care face managementul valorilor acestor c ampuri, nu programatorul). scope_indentity() este o func tie predenit a ce returneaz a id-ul noii nregistr ari ad augate n tabel a. Ultima instruc tiune va duce la reactualizarea obiectului DataSet, pentru ca acesta s a con tin a modicarea efectuat a (de exemplu ar putea aduce valorile implicite puse pe anumite coloane). Pentru modicarea con tinutului unei linii se poate declara instruc tiunea de UPDATE astfel: da.UpdateCommand.CommandText = @"UPDATE Customers SET CompanyName = @CompanyName, Address = @Address, Country = @Country WHERE (CustomerID = @ID)";

9.3

Tranzac tii n ADO.NET

O tranzac tie este un set de opera tii care se efectueaz a e n ntregime, e deloc. S a presupunem c a se dore ste trecerea unei anumite sume de bani dintr-un cont n altul. Opera tia presupune 2 pa si: 1. scade suma din primul cont 2. adaug a suma la al doilea cont Este inadmisibil ca primul pas s a reu seasc a iar al doilea s a e sueze. Tranzac tiile satisfac ni ste propriet a ti str anse sub numele ACID: atomicitate - toate opera tiile din tranzac tie ar trebui s a aib a succes sau s a e sueze mpreun a consisten t a - tranzac tia duce baza de date dintr-o stare stabil a n alta izolare - nici o tranzac tie nu ar trebui s a afecteze o alta care ruleaz a n acela si timp durabilitate - schimb arile care apar n tipul tranzac tiei sunt permanent stocate pe un mediu. Sunt trei comenzi care se folosesc n context de tranzac tii: BEGIN - nainte de executarea unei comenzi SQL sub o tranzac tie, aceasta trebuie s a e ini tializat a COMMIT - se spune c a o tranzac tie este terminat a c and toate schimb arile cerute sunt trecute n baza de date

230

CURS 9. ADO.NET (2)

ROLLBACK - dac a o parte a tranzac tiei e sueaz a, atunci toate opera tiile efectuate de la nceputul tranzac tiei vor neglijate Schema de lucru cu tranzac tiile sub ADO.NET este: 1. deschide conexiunea la baza de date 2. ncepe tranzac tia 3. execut a comenzi pentru tranzac tie 4. dac a tranzac tia se poate efectua (nu sunt excep tii sau anumite condi tii sunt ndeplinite), efectueaz a COMMIT, altfel efectueaz a ROLLBACK 5. nchide conexiunea la baza de date Sub ADO.NET acest lucru s-ar face astfel: SqlConnection myConnection = new SqlConnection(myConnString); myConnection.Open(); SqlCommand myCommand1 = myConnection.CreateCommand(); SqlCommand myCommand2 = myConnection.CreateCommand(); SqlTransaction myTrans; myTrans = myConnection.BeginTransaction(); //Trebuie asignate ambele obiecte: conexiune si tranzactie //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)

9.4. LUCRUL GENERIC CU FURNIZORI DE DATE { myTrans.Rollback(); } finally { myConnection.Close(); }

231

Comanda de ROLLBACK se poate executa si n alte situa tii, de exemplul comanda efectuat a dep a ste ste stocul disponibil.

9.4

Lucrul generic cu furnizori de date

In cele expuse p an a acum, s-a lucrat cu un furnizor de date specic pentru SQL Server 2005. In general e de dorit s a se scrie cod care s a func tioneze f ar a modic ari majore pentru orice furnizor de date; mai exact, am prefera s a nu e nevoie de rescrierea sau recompilarea codului. Incep and cu versiunea 2.0 a lui ADO.NET se poate face acest lucru u sor, prin intermediul unei clase DbProviderFactory (un Abstract factory ). Mecanismul se bazeaz a pe faptul c a avem urm atoarele clase de baz a pentru tipurile folosite ntr-un furnizor de date: DbCommand : clas a de baz a abstract a pentru obiectele de tip Command DbConnection : clas a de baz a abstract a pentru obiectele de tip Connection DbDataAdapter : clas a de baz a abstract a pentru obiectele de tip DataAdapter DbDataReader : clas a de baz a abstract a pentru obiectele de tip DataReader DbParameter : clas a de baz a abstract a pentru obiectele de tip parametru DbTransaction : clas a de baz a abstract a pentru obiectele de tip tranzac tie Crearea de obiecte specice (de exemplu obiect SqlCommand ) se face folosind clase derivate din DbProviderFactory ; o schi ta a acestei clase este: public abstract class DbProviderFactory { ... public virtual DbCommand CreateCommand(); public virtual DbCommandBuilder CreateCommandBuilder(); public virtual DbConnection CreateConnection();

232 public public public public } virtual virtual virtual virtual

CURS 9. ADO.NET (2) DbConnectionStringBuilder CreateConnectionStringBuilder(); DbDataAdapter CreateDataAdapter(); DbDataSourceEnumerator CreateDataSourceEnumerator(); DbParameter CreateParameter();

Tot ceea ce trebuie f acut este s a se ob tin a o clas a concret a derivat a din DbProviderFactory si care la apeluri de tip Create... s a returneze obiecte concrete, adecvate pentru lucrul cu sursa de date. Concret: static void Main(string[] args) { // Obtine un producator pentru SqlServer DbProviderFactory sqlFactory = DbProviderFactories.GetFactory("System.Data.SqlClient"); ... // Obtine un producator pentru Oracle DbProviderFactory oracleFactory = DbProviderFactories.GetFactory("System.Data.OracleClient"); ... } Se va evita, reste, codicarea numelui furnizorului de date n cod (precum mai sus) si se vor integra n siere de congurare. Aceste siruri de caractere exemplicate mai sus sunt denite n sierul machine.cong din directorul unde s-a f acut instalarea de .NET (%windir%\Microsoft.Net\Framework\ v2.0.50727\cong). Exemplu: sierul de congurare este: <configuration> <appSettings> <!-- Provider --> <add key="provider" value="System.Data.SqlClient" /> <!-- String de conexiune --> <add key="cnStr" value= "Data Source=localhost;uid=sa;pwd=;Initial Catalog=Pubs"/> </appSettings> </configuration> Codul C#: static void Main(string[] args) {

9.5. TIPURI NULABILE

233

string dp = ConfigurationManager.AppSettings["provider"]; string cnStr = ConfigurationManager.ConnectionStrings["constring"] .ConnectionString; DbProviderFactory df = DbProviderFactories.GetFactory(dp); DbConnection cn = df.CreateConnection(); cn.ConnectionString = cnStr; cn.Open(); DbCommand cmd = df.CreateCommand(); cmd.Connection = cn; cmd.CommandText = "Select * From Authors"; DbDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); while (dr.Read()) Console.WriteLine("-> {0}, {1}", dr["au_lname"], dr["au_fname"]); dr.Close();

9.5

Tipuri nulabile

Pentru tipurile valoare este mandatorie stabiilirea unei valori; o variabil a de tip valoare nu poate s a re tin a null. Altfel spus, codul urm ator va genera eroare de compilare static void Main(string[] args) { bool myBool = null; int myInt = null; } In contextul lucrului cu baze de date este perfect posibil ca rezulattul unei interog ari s a aduc a null pentru un anumit c amp. Pentru a rezolva aceast a incompatibilitate (tipuri cu valoare nenul a n C# care trebuie s a poat a lucra cu null-urile provenite din baz a), s-au introdus tipurile nulabile. Acesta reprezint a un mecansim de extindere a tipurilor de tip valoare astfel nc at s a suporte si valoarea de nul. De exemplu, pentru a putea declara o variabil a de tip int care s a poat a avea si valoare nul a se va scrie: int? intNulabil = null;

234 i=3; i=null;

CURS 9. ADO.NET (2)

Nu vom putea deni ca nulabile tipurile referin ta , deoarece acestea suport a implicti null: //eroare de compilare string? s = null Lucrul cu tipurile nulabile se face exact ca si cu tipurile valoare: class DatabaseReader { //campuri nulabile public int? numbericValue; public bool? boolValue = true; public int? GetIntFromDatabase() { return numbericValue; } public bool? GetBoolFromDatabase() { return boolValue; } } In contextul tipurilor nulabile s-a introdus operatorul ?? care permite asignarea unei valori pentru o variabil a de tip nulabil dac a valoarea returnat a este nul a: static void Main(string[] args) { DatabaseReader dr = new DatabaseReader(); int? myData = dr.GetIntFromDatabase() ?? 100; Console.WriteLine("Value of myData: {0}", myData); }

Curs 10 LINQ (I)


10.1 Generalit a ti

Language Integrated Query (LINQ, pronun tat precum link) permite interogarea unor colec tii de date folosind o sintax a integrat a n platforma .NET. Prin intermediul unor operatori se pot interoga colec tii de forma: vectori, colec tii, clase enumerabile, documente XML, baze de date rela tionale. Datele rezultate sunt v azute ca obiecte; are loc o mapare (asociere, traducere) a unor date neobiectuale ntrun format u sor de folosit n limbajele obiectuale din cadrul plaformei. Trebuie men tionat c a sintaxa este unitar a, independent de natura sursei de date. Listing 10.1: Interogare LINQ var query = from e in employees where e . i d == 1 s e l e c t e . name sau: var r e s u l t s = Listing 10.2: Alt a interogare LINQ from product in p r o d u c t s C o l l e c t i o n where product . U n i t P r i c e < 100 s e l e c t new { product . ProductName , product . U n i t P r i c e } ;

foreach ( var r e s u l t in r e s u l t s ) { Console . WriteLine ( r e s u l t ) ; } Este introdus a interfa ta IQueryable<T>, care permite implementarea unor furnizori de date n modul specic sursei de date considerate. Expresia 235

236

CURS 10. LINQ (I)

folosit a pentru interogare este tradus a ntr-un arbore de expresie. Dac a colec tia implementeaz a IEnumerable<T>, atunci se folose ste motorul de execu tie LINQ local, integrat n platform a; dac a colec tia implementeaz a IQueryable<T>, atunci se folose ste implementarea bazat a pe arborele de expresie; aceast a implementare este dat a de c atre furnizoare de LINQ. Furnizoarele de LINQ sunt scrise n mod specic ec arei surse de date, dar datorit a respect arii unor interfe te specicate, detaliile de implementare sunt irelevante pentru cel care folose ste cod LINQ n interogare. LINQ a fost introdus n versiunea 3.5 a lui .NET Framework. Const a ntrun set de unelte care sunt folosite pentru lucrul cu date si extensii aduse limbajului. Este alc atuit din: LINQ to Objects se aduc date din colec tii care implementeaz a IEnumerable<T>; datele interogate sunt deja n memoria procesului; LINQ to XML converte ste documentele XML ntro colec tie de obiecte de tip XElement; LINQ to SQL permite convertirea interog arilor LINQ n comenzi SQL; LINQ to DataSets spre deosebire de LINQ to SQL care a venit ini tial doar cu suport pentru SQL Server, LINQ to DataSets folose ste ADO.NET pentru comunicarea cu baze de date; LINQ to Entities solu tie Object/Relational Mapping de la Microsoft ce permite utilizarea de Entities introduse n ADO.NET 3.0 pentru a specica declarativ structura obiectelor ce modeleaz a domeniul si folose ste LINQ pentru interogare. La ora actual a exist a urm atorii furnizori de date LINQ: 1. LINQ to MySQL, PostgreSQL, Oracle, Ingres, SQLite si Microsoft SQL Server 2. LINQ to CSV 3. LINQ to Google 4. LINQ to NHibernate 5. LINQ to System Search Se lucreaz a la PLINQ, Parallel LINQ, un motor ce folose ste paralelizarea de cod pentru executare mai rapid a a interog arilor, n cazul unui sistem multinucleu sau multiprocesor.

10.2. MOTIVATIE

237

10.2

Motiva tie

Vom prezenta dou a motive pentru care LINQ este util: codul stufos, neproductiv utilizat pentru accesarea n modul clasic a datelor; nepotrivirea paradigmelor obiectualrela tionale.

10.2.1

Codul clasic ADO.NET

Pentru accesarea datelor dintr-o baz a de date rela tional a, folosind ADO.NET se scrie de regul a un cod de forma: Listing 10.3: Interogare clasic a folosind ADO.NET using ( S q l C o n n e c t i o n c o n n e c t i o n = new S q l C o n n e c t i o n ( " . . . " ) ) { c o n n e c t i o n . Open ( ) ; SqlCommand command = c o n n e c t i o n . CreateCommand ( ) ; command . CommandText = @"SELECT Name , Country FROM Customers WHERE City = @City" ; command . Parameters . AddWithValue ( "@City" , " P a r i s " ) ; using ( SqlDataReader r e a d e r = command . ExecuteReader ( ) ) { while ( r e a d e r . Read ( ) ) { string name = r e a d e r . G e t S t r i n g ( 0 ) ; string c o u n t r y = r e a d e r . G e t S t r i n g ( 1 ) ; ... } } } Se remarc a urm atoarele: cantitatea de cod scris a; codul de sus este unul des folosit, scrierea lui n repetate r anduri este contraproductiv a; interog arile sunt exprimate prin intermediul unei fraze scrise ntre ghilimele, ca sir de caractere, deci automat nu se poate verica prin compilarea corectitudinea codului SQL con tinut; este adev arat, ns a, c a se pot folosi aici proceduri stocate care sunt compilate deja pe server;

238

CURS 10. LINQ (I)

slaba tipizare a parametrilor: dac a tipul acestora nu coincide cu ce se a a n baza de date? dac a num arul de parametri este incorect (asta se semnaleaz a numai la rulare, ca eroare; de preferat ar fost s a se depisteze acest lucru la compilare); de cele mai multe ori trebui folosit un dialect de SQL specic produc atorului serverului de baze de date; codul SQL nu este portabil; totodat a: mixarea de limbaje C# si SQL face codul greu de urm arit. O expresie LINQ care care demonstreaz a dep a sirea acestor probleme este: Listing 10.4: Cod LINQ from customer in c u s t o m e r s where customer . Name . S t a r t s W i t h ( "A" ) && customer . Orders . Count > 10 o r d e r b y customer . Name s e l e c t new { customer . Name , customer . Orders }

10.2.2

Nepotrivirea de paradigme

Paradigma este o constructie mental a larg acceptat a, care ofer a unei comunit a ti sau unei societ a ti pe perioad a ndelungat a o baz a pentru crearea unei identit a ti de sine (a activit a tii de cercetare de exemplu) si astfel pentru rezolvarea unor probleme sau sarcini 1 . Exist a o diferen ta sesizabil a ntre programarea orientat a pe obiecte, utilizat a n cadrul limbajelor folosite pentru implementarea aplica tiilor si modul de stocare si reprezentare a datelor: XML sau baze de date rela tionale. Translatarea unui graf de obiecte din reprezentarea obiectual a ntro alt a reprezentare este greoaie: programatorul trebuie s a n teleag a si particularit a tile structurilor de date folosite pentru persistarea lor, pe l ang a cunoa sterea limbajului n care lucreaz a. Problema pe care LINQ o abordeaz a este rezolvarea urm atoarelor inegalit a ti: Data != Objects Relational data != Objects XML data != Objects XML data != Relational data
1

Wikipedia

10.3. LINQ TO OBJECTS: EXEMPLIFICARE

239

Toate aceste nepotriviri necesit a efort de adaptare din partea programatorului. Modelarea obiectualrela tional a este problema cea mai des nt alnit a, cu urm atoarele aspecte: 1. tipurile de date folosite de c atre modelele rela tionale si modelele obiectuale nu sunt acelea si; de exemplu, multitudinea de tipuri sir de caractere folosite n specicarea coloanelor, n timp ce n .NET exist a doar tipul String; 2. modelele rela tionale folosesc normalizarea (pentru eliminarea redundan tei si a anomaliilor de inserare, stergere, modicare), n timp ce n modelarea obiectual a nu trebuie s a treac a prin a sa ceva; n schimb, modelarea obiectual a folose ste agregarea sau mo stenirea, mecanisme care nu si au un echivalent direct n modelarea rela tional a 3. modele de programare diferite: pentru SQL se folose ste un limbaj declarativ care specic a ce se prelucreaz a, n timp ce limbajele de programare folosite sunt de regul a imperative arat a cum se face prelucrarea 4. ncapsulare ascunderea detaliilor si legarea laolalt a datelor cu metodele care prelucreaz a datele; Toate aceste probleme se manifest a ncep and cu maparea ntre obiecte si datele persistate. Aceea si problem a apare dac a ne referim la XML, care favorizeaz a un model ierarhic, semistructurat. Programatorul trebuie s a scrie mereu un cod care s a faciliteze legarea acestor universuri diferite. LINQ vine cu o propunere de rezolvare.

10.3

LINQ to Objects: exemplicare

LINQ to Objects este folosit pentru interogarea datelor care se a a deja n memorie. Un prim exemplu este: using System ; using System . Linq ; s t a t i c c l a s s HelloWorld { s t a t i c void Main ( ) { string [ ] words = { " h e l l o " , " w o n d e r f u l " , " l i n q " , " b e a u t i f u l " , " world " } ;

240 var shortWords = from word in words where word . Length <= 5 s e l e c t word ; foreach ( var word in shortWords ) Console . WriteLine ( word ) ; } } Un exemplu mai complex este:

CURS 10. LINQ (I)

Listing 10.5: LINQ peste colec tie generic a, folosind expresie LINQ L i s t <Person> p e o p l e = new L i s t <Person> { new Person ( ) { ID = 1 , IDRole = 1 , LastName = " Anderson " , FirstName = "Brad" }, new Person ( ) { ID = 2 , IDRole = 2 , LastName = "Gray" , FirstName = "Tom" } }; var query = from p in p e o p l e where p . ID == 1 s e l e c t new { p . FirstName , p . LastName } ; Interogarea din nal poate scris a si altfel: Listing 10.6: LINQ peste colec tie generic a, folosind apeluri de metode var query = p e o p l e . Where ( p => p . ID == 1 ) . S e l e c t ( p => new { p . FirstName , p . LastName } ) ;

10.4

Mecanisme utilizate de LINQ

Pentru a putea folosit, LINQ a necesitat extinderea limbajelor din cadrul platformei.

10.4. MECANISME UTILIZATE DE LINQ

241

10.4.1

Inferen ta tipului

Consider am metoda: void f() { int x = 3; MyClass y = new MyClass(); bool z = true; .... } Pentru ecare din declara tiile cu ini tializare de mai sus se poate spune c a valoarea asociat a d a sucient a informa tie despre tipul de date corespunz ator variabilelor. Tocmai din acest motiv n C# 3.0 se poate scrie astfel: void f() { var x = 3; var y = new MyClass(); var z = true; .... } var nu reprezint a un nou tip de date, ci pur si simplu arat a c a la compilare se poate deduce tipul actual al variabilelor locale respective. Ca atare, var este de fapt o scurt atur a, prin care se ob tine ceea ce s-a scris prima oar a. Dac a dup a declarare si ini tializare se scrie: x = false; compilatorul semnaleaz a eroare, deoarece s-a f acut deja inferarea tipului de dat a pentru x, si anume int, iar false nu este compatibil cu int. Limbajul r am ane deci puternic tipizat, ecare variabil a av and un tip asignat. Folosirea acestui nou cuv ant cheie se supune condi tiilor: se poate folosi doar pentru variabile locale se poate folosi atunci c and compilatorul poate s a infereze tipul de dat a asociat variabilei; acest lucru se nt ampl a conform situa tiilor de mai sus, sau pentru o iterare de forma: int[] sir = {1, 2, 3}; foreach(var i in sir)

242 { ... }

CURS 10. LINQ (I)

odat a ce compilatorul determin a tipul de date, acesta nu mai poate schimbat. Mecanismul nu reprezint a la prima vedere un mare pas, dar este cu adev arat util atunci c and se lucreaz a cu alte mecanisme din C# 3.0: tipuri anonime si programarea bazat a pe LINQ.

10.4.2

Tipuri anonime

Acest mecanism permite declararea unor variabile de un tip care nu este denit aprioric. Se omite declararea numelui de clas a si a componen tei acesteia. Exemplu: var p = new {FirstName="Rafael", Age=25}; Pentru propriet a tile care sunt pomenite n expresia de ini tializare se face deducerea tipului n mod automat (pe baza aceluia si mecanism de la variabile anonime). Propriet a tile sunt publice si read-only.

10.4.3

Metode par tiale

Metodele par tiale reprezint a metodele care sunt declarate n mai multe p ar ti ale unei clase. Clasa con tin atoare poate s a e sau nu par tial a. Cele dou a p ar ti sunt una n care se dene ste metoda par tial a (cu tip de retur si parametri, dar f ar a corp) si alta n care se implementeaz a (cu corp complet). Exemplu: //fisierul MyClass1.cs partial class MyClass { //definitie de metoda partial void f(int x); } //fisierul MyClass2.cs partial class MyClass { //implementare de metoda

10.4. MECANISME UTILIZATE DE LINQ partial void f(int x) { Console.WriteLine(x.ToString()); } }

243

Compilatorul va pune la un loc cele dou a declara tii de metode par tiale si va rezulta o metod a ntreag a. Regulile care trebuie respectate pentru crearea de metode par tiale sunt: 1. metoda pentru care se folose ste implementare par tial a trebuie s a returneze void; 2. parametrii nu pot de tip output; 3. metoda nu poate avea specicatori de acces; ea este privat a 4. metoda trebuie s a e declarat a at at la denire c at si la implementare ca ind par tial a. Ad ag am c a o astfel de metod a par tial a poate s a apar a ntro structur a sau ntro clas a (declarate ca par tiale). Nu este obligatoriu ca ambele declara tii s a apar a n p ar ti diferite ale clasei. Dac a o implementare de metod a par tial a lipse ste, atunci orice apel la ea este ignorat.

10.4.4

Metode de extensie

Metodele de extensie permit scrierea de metode asociate cu clase, alte clase dec at cele n care sunt denite. S a consider am o clas a, n cazul c areia ecare obiect men tine un sir de numere. O posibil a deni tie ar : class LotOfNumbers { private int[] numbers; public LotOfNumbers(int[] numbers) { this.numbers = new int[numbers.Length]; numbers.CopyTo(this.numbers, 0); } public int NumbersNo

244 { get { if (numbers == null) { return 0; } else { return numbers.Length; } } }

CURS 10. LINQ (I)

public int Sum() { if (numbers == null) { throw new Exception("No number inside"); } int result = 0; foreach (int x in numbers) { result += x; } return result; } } Ne propunem s a ad aug am o metod a la aceast a clas a, care s a returneze media elementelor sirului. S a presupunem c a nu avem acces la sursa codului C#. Singura modalitate de a extinde clasa LotOfNumbers este ca s a se scrie o metod a de extensie: static class ExtendsLotOfNumbers { public static double Average(this LotOfNumbers data) { return (double)data.Sum() / data.NumbersNo; } }

10.5. OPERATORI LINQ Utilizarea metodei de extensie se face cu:

245

class Program { static void Main() { LotOfNumbers numbers = new LotOfNumbers(new int[]{1, 2, 3}); Console.WriteLine(numbers.Average().ToString()); } } Am reusit astfel s a strecur am o metod a n interirorul unei clase al c arei cod surs a nu este accesibil. Putem utiliza acest mecanism dac a: 1. clasa n care se face implementarea metodei de extensie este static a; 2. metoda care implementeaz a extensia este static a 3. metoda de extensie are primul parametru de tipul clasei pentru care se face extinderea, iar tipul parametrului formal este prexat cu this. Este posibil ca o metod a de extensie s a aibe mai mult de un parametru.

10.4.5

Expresii lambda

Aceste expresii simplic a scrierea delega tilor si a metodelor anonime. Intrun exemplu anterior sa folosit: Where ( p => p . ID == 1 ) care sar citi: obiectul p produce expresia logic a p.ID egal cu 1. Ar putea rescris a echivalent, astfel: Listing 10.7: Expresie lambda rescris a cu delega ti Func<Person , bool> f i l t e r = delegate ( Person p ) { return p . ID == 1 ; } ; var query = p e o p l e . Where ( f i l t e r ) . S e l e c t ( p => new { p . FirstName , p . LastName } ) ;

10.5

Operatori LINQ

246

CURS 10. LINQ (I) Tabelul 10.1: Operatori LINQ.

Opera tie Aggregate

Descriere Aplic a o func tie peste o secven ta Calculeaz a media peste o secven ta calculeaz a num arul de elemente dintr-o secven ta Max, Min, Sum Calculeaz a maximul, minimul, suma elementelor dintr-o secven ta Concatenare Concat Concateneaz a elementele a dou a secven te Conversie AsEnumerable Converte ste o secven ta la un IEnumerable<T> AsQueryable Converte ste o secven ta la un IQueryable<T> Cast converte ste un element al unei secven te la un tip specicat OfType Filtreaz a elementele unei secven te, return andule doar pe cele care au un tip specicat ToArray transform a n vector ToDictionary transform a n dic tionar ToList transform a n colec tie ToLookup creeaz a un obiect de tip Lookup<K, T> dintr-o secven ta ToSequence returneaz a argumentul transformat ntrun IEnumerable<T> Ob tinere de element DefaultIfEmpty d a un element implicit dac a secven ta este goal a ElementAt returneaz a elementul de la pozi tia specicat a ElementAtOrDefault returneaz a elementul de la pozi tia specicat a sau o valoare implicit a, dac a la pozi tia specicat a nu se a a nimic First, Last primul, respectiv ultimul element dintro secven ta FirstOrDefault ca mai sus, dar cu returnare de valoare LastOrDefault implicit a dac a primul, respectiv ultimul elem din colec tie nu este diponibil Single returneaz a elementul din colec tie,

Operator Aggregate Average Count/LongCount

10.5. OPERATORI LINQ

247 Tabelul 10.1 (continuare)

Opera tie

Operator SingleOrDefault

Egalitate Generare

SequenceEqual Empty Range Repeat

Grupare Jonc tiune

GroupBy GroupJoin Join OrderBy OrderByDescending Reverse ThenBy, ThenByDescending Skip SkipWhile Take TakeWhile

Sortare

Parti tionare

Proie tie

Select SelectMany

Cuanticatori

All Any Contains

Descriere presupus a a format dintrun singur element ca mai sus, sau element implicit dac a elementul singular nu este g asit n secven ta veric a dac a dou a secven te sunt egale returneaz a o secven ta goal a de tipul specicat Genereaz a o secven ta de numere aate ntre dou a capete specicate Genereaz a o secven ta prin repetarea de un num ar de ori specicat a unei valori specicate Grupeaz a elementele unei secven te O jon tiune grupat a a dou a secven te pe baza unor chei care se potrivesc Jon tiune interioar a a dou a secven te Ordoneaz a elementele unei secven te pe baza unora sau a mai multo chei Ca mai sus, dar cu sortare descresc atoare inverseaz a ordinea elementelor dintr-o secven ta pentru specicarea de chei suplimentare de sortare Produce elementele unei secven te care se a a dup a o anumit a pozi tie Sare peste elementele unei secven te care ndeplinesc o anumit a condi tie Ia primele elemente dintr-o secven ta Ia elementele de la nceputul unei secven te, at ata timp c at ele respect ao anumit a condi tie dene ste elementele care se iau n secven ta Preia elemente dintr-o secven ta con tin and secven te Veric a dac a toate elementele unei secven te satisfac o condi tie dat a Veric a dac a vreun elementl al unei secven te satisface o condi tie dat a Veric a dac a o secven ta con tine un element

248

CURS 10. LINQ (I)

Tabelul 10.1 (continua Opera tie Restric tie Mul time Operator Where Distinct Except Intersect Union Descriere Filtreaz a o secven ta pe baza unei condi tii Returneaz a elementele distincte dintro colec tie Efectueaz a diferen ta a dou a secven te Returneaz a intersec tia a dou a mul timi Produce reuniunea a dou a secven te

10.6

LINQ to Objects

Exemplele de mai jos sunt preluate din [8] si sunt folosite pentru exemplicarea codului ce folose ste LINQ. Se pleac a de la clase Person, Role si Salary, denite ca mai jos: Listing 10.8: Clase pentru exemplicarea LINQ-ului c l a s s Person { public int ID { get ; set ; } public int IDRole { get ; set ; } public string LastName { get ; set ; } public string FirstName { get ;

10.6. LINQ TO OBJECTS set ; } } c l a s s Role { public int ID { get ; set ; } public string R o l e D e s c r i p t i o n { get ; set ; } } class Salary { public int IDPerson { get ; set ; } public int Year { get ; set ; } public double S a l a r y Y e a r { get ; set ; } }

249

250

CURS 10. LINQ (I)

10.6.1

Filtarea cu Where

Pentru operatorul Where sau denit dou a metode de extensie: public s t a t i c IEnumerable<T> Where<T>( t h i s IEnumerable<T> s o u r c e , Func<T, bool> p r e d i c a t e ) ; public s t a t i c IEnumerable<T> Where<T>( t h i s IEnumerable<T> s o u r c e , Func<T, int , bool> p r e d i c a t e ) ; Prima form a folose ste un predicat (condi tie) care pentru un obiect de tipul T returneaz a un boolean, iar n al doilea caz se folose ste la condi tie obiectul si indicele s au n secven ta . S a presupunem c a avem o colec tie de obiecte de tip Person construit a astfel: L i s t <Person> p e o p l e = new L i s t <Person> { new Person { ID = 1 , IDRole = 1 , LastName = " Anderson " , FirstName = "Brad" }, new Person { ID = 2 , IDRole = 2 , LastName = "Gray" , FirstName = "Tom" }, new Person { ID = 3 , IDRole = 2 , LastName = " Grant " , FirstName = "Mary" }, new Person { ID = 4 , IDRole = 3 , LastName = "Cops" , FirstName = "Gary" } };

10.6. LINQ TO OBJECTS

251

Ob tinerea obiectelor de tip Person care au prenumele Brad se face cu expresia LINQ : var query = from p in p e o p l e where p . FirstName == "Brad" select p; Elementele care sunt aduse de c atre interogarea anterioar a pot iterate cu: foreach ( Person x in query ) { Console . WriteLine ( " { 0 } , {1} " , x . FirstName , x . LastName ) ; } Pentru expresia de interogare de mai sus se poate scrie echivalent: var query = p e o p l e . Where ( p => p . FirstName == "Brad" ) ; adic a o variant a n care se folosesc metodele de extensie. Dac a se dore ste folosirea n cadrul condi tiei de ltrare a pozi tiei elementului curent n lista people, se poate scrie: var query = p e o p l e . Where ( ( p , i n d e x ) => p . IDRole == 1 && i n d e x % 2 == 0 ) ; si utilizarea metodei de extensie este singura posibilitate de a referi indicele d epozi tie.

10.6.2

Operatorul de proiec tie

Pentru a determina ce con tine ecare element care compune o secven ta , se folose ste operatorul Select, denit de metodele de extensie: public s t a t i c IEnumerable<S> S e l e c t <T, S>( t h i s IEnumerable<T> s o u r c e , Func<T, S> s e l e c t o r ) ; public s t a t i c IEnumerable<S> S e l e c t <T, S>( t h i s IEnumerable<T> s o u r c e , Func<T, int , S> s e l e c t o r ) ; Selectarea se poate face cu: var query = from p in p e o p l e s e l e c t p . FirstName ; sau: var query = p e o p l e . S e l e c t ( p => p . FirstName ) ; Se poate de asemenea ca la ecare element selectat s a se produc a un obiect de un tip nou, chiar tip anonim:

252 var query = p e o p l e . Select ( ( p , i n d e x ) => new { P o s i t i o n=index , p . FirstName , p . LastName } );

CURS 10. LINQ (I)

10.6.3

Operatorul SelectMany

Are metodele de extensie: public s t a t i c IEnumerable<S> SelectMany<T, S>( t h i s IEnumerable<T> s o u r c e , Func<T, IEnumerable<S>> s e l e c t o r ) ; public s t a t i c IEnumerable<S> SelectMany<T, S>( t h i s IEnumerable<T> s o u r c e , Func<T, int , IEnumerable<S>> s e l e c t o r ) ; S a presupunem c a specic am si ni ste obiecte rol: L i s t <Role> r o l e s = new L i s t <Role> { new Role { ID = 1 , R o l e D e s c r i p t i o n = "Manager" } , new Role { ID = 2 , R o l e D e s c r i p t i o n = " D e v e l o p e r " } } ; Dac a dorim rolul persoanei av and id-ul 1, atunci putem scrie: var query = from p in p e o p l e where p . ID == 1 from r in r o l e s where r . ID == p . IDRole s e l e c t new { p . FirstName , p . LastName , r . RoleDescription }; Echivalent, se poate folosi metoda SelectMany: var query = p e o p l e . Where ( p => p . ID > 1 ) . SelectMany ( p => r o l e s

10.6. LINQ TO OBJECTS . Where ( r => r . ID == p . RoleID ) . S e l e c t ( r => new { p . FirstName , p . LastName , r . RoleDescription } ) );

253

SelectMany permite gestiunea unei alte secven te elemente; dac a s-ar folosi Select n loc de SelectMany, atunci sar ob tine o secven ta de elemente de tip IEnumerable<T>.

10.6.4

Jonc tiuni

Operatorul Join poate folosit pentru a aduce date din dou a colec tii, date care sunt puse n coresponden ta pe baza unei chei. Se bazeaz a pe metoda de extensie: public s t a t i c IEnumerable<V> Join<T, U, K, V>( t h i s IEnumerable<T> o u t e r , IEnumerable<U> i n n e r , Func<T, K > outerKeySelector , Func<U, K > innerKeySelector , Func<T, U, V> r e s u l t S e l e c t o r ) ; De exemplu, pentru a aduce lista numelor, prenumelor si a descrierilor de roluri pentru colec tiile persons si roles putem folosi: var query = from p in p e o p l e j o i n r in r o l e s on p . IDRole e q u a l s r . ID s e l e c t new { p . FirstName , p . LastName , r . RoleDescription }; Folosind operatori (metode) LINQ, se poate scrie: query = p e o p l e . J o i n ( r o l e s , p => p . IDRole , r o l e => r o l e . ID , ( p , r ) => new { p . FirstName , p . LastName , r . RoleDescription

254 } );

CURS 10. LINQ (I)

10.6.5

Grupare

Gruparea datelor se face cu GroupBy; se grupeaz a elementele unei secven te pe baza unui selector. Exemplu: plec and de la var query = from m in typeof ( int ) . GetMethods ( ) s e l e c t m. Name ; Dac a se a seaz a elementele din colec tia query, se poate observa c a metoda ToString este a sat a de 4 ori, Equals de cou a ori etc. Gruparea acestora2 se poate face astfel: var q = from m in typeof ( int ) . GetMethods ( ) group m by m. Name i n t o gb s e l e c t new {Name = gb . Key } ; Dac a dorim s a a sa m si o valoare agregat a la nivel de grup, atunci putem specica suplimentar n operatorul de selec tie: var q = from m in typeof ( int ) . GetMethods ( ) group m by m. Name i n t o gb s e l e c t new {Name = gb . Key , O v e r l o a d s = gb . Count ( ) } ; Pentru grupare se poate specica si un comparator (implementare de interfa ta IEqualityComparer) care s a spun a care este criteriul de determinare a elementelor egale ce formeaz a un grup.

10.6.6

Ordonare

Sunt folosi ti operatorii: OrderBy, OrderByDescending, ThenBy, ThenByDescending si Reverse. Operatorii OrderBy si OrderByDescending produc sortarea cresc atoare, respectiv descresc atoare a unor secven te, pe baza unei chei de sortare. Exemplu: var q = from m in typeof ( int ) . GetMethods ( ) o r d e r b y m. Name s e l e c t new { Name = m. Name } ; Folosind metode se poate scrie:
2

A nu se confunda exemplul cu eliminarea duplicatelor.

10.6. LINQ TO OBJECTS q = typeof ( int ) . GetMethods ( ) . OrderBy ( method => method . Name ) . S e l e c t ( method => new { Name = method . Name } ) ; Pentru sortarea descresc atoare expresia LINQ este: var q = from m in typeof ( int ) . GetMethods ( ) o r d e r b y m. Name d e s c e n d i n g s e l e c t new { Name = m. Name } ; sau prin operatori LINQ: q = typeof ( int ) . GetMethods ( ) . OrderByDescending ( method => method . Name ) . S e l e c t ( method => new { Name = method . Name } ) ;

255

Dac a se dore ste specicarea mai multor chei dup a care s a se fac a sortarea n ordine lexicograc a, atunci se poate specica prin ThenBy si ThenByDescending apelate ca metode care sunt criteriile suplimentare de sortare. Dac a se folose ste expresie LINQ, atunci se poate scrie mai simplu: var query = from p in p e o p l e o r d e r b y p . FirstName , p . LastName select p; Exprimarea echivalent a cu operatori LINQ este: var query = p e o p l e . OrderBy ( p => p . FirstName ) . ThenBy ( p => p . LastName ) ; Pentru personalizarea sort arii, se poate folosi o implementare de interfa ta IComparer<T>. Inversarea ordinii elementelor dintro secven ta se face cu Reverse, apelat a ca metod a: var q = ( from m in typeof ( int ) . GetMethods ( ) s e l e c t new { Name = m. Name } ) . R e ve r s e ( ) ; sau: var q = typeof ( int ) . GetMethods ( ) . S e l e c t ( method => new { Name = method . Name } ) . R e ve r s e ( ) ;

256

CURS 10. LINQ (I)

10.6.7

Agregare

Exist a operatorii de agregare: Count, LongCount, Sum, Min, Max, Average, Aggregate. Metoda Count() returneaz a un ntreg, iar LongCount() un long (intreg pe 64 de biti, cu semn) reprezent and num arul de elemente din secven ta . Metoda Sum calculeaz a suma unor valori numerice dintro secven ta . Metodele sunt: public s t a t i c Numeric Sum( t h i s IEnumerable<Numeric> s o u r c e ) ; public s t a t i c Numeric Sum<T>( t h i s IEnumerable<T> s o u r c e , Func<T, Numeric> s e l e c t o r ) ; unde Numeric se refer a la un tip de date de forma: int, int?, long, long?, double, double?, decimal, sau decimal?. Exemplu: int [ ] numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ; var query = numbers . Sum ( ) ; Console . WriteLine ( query . ToString ( ) ) ; Dac a dorim s a determin am care este suma salariilor anuale primite de c atre ecare salariat n parte, atunci putem forma prima oar a grupuri bazate pe numele de familie al salariatului si apoi se poate face suma salariilor de-a lungul anilor. Presupunem c a salariile sunt date astfel: L i s t <S a l a r y > s a l a r i e s = new L i s t <S a l a r y > { new S a l a r y { IDPerson = 1 , Year = 2 0 0 4 , SalaryYear = 10000.00 } , new S a l a r y { IDPerson = 1 , Year = 2 0 0 5 , SalaryYear = 15000.00 } }; var query = from p in p e o p l e j o i n s in s a l a r i e s on p . ID e q u a l s s . IDPerson s e l e c t new

10.6. LINQ TO OBJECTS {p . FirstName , p . LastName , s . SalaryYear }; Apoi: var querySum = from q in query group q by q . LastName i n t o gp s e l e c t new { LastName = gp . Key , T o t a l S a l a r y = gp . Sum( q => q . S a l a r y Y e a r ) };

257

Pentru operatorii Min, Max, Average, ace stia trebuie apela ti pentru o colec tie de valori numerice. Operatorul Aggregate permite denirea unei metode care s a e folosit a pentru sumarizarea datelor. Declara tia metodei este: public s t a t i c T Aggregate<T>( t h i s IEnumerable<T> s o u r c e , Func<T, T, T> f u n c ) ; public s t a t i c U Aggregate<T, U>( t h i s IEnumerable<T> s o u r c e , U seed , Func<U, T, U> f u n c ) ; A doua metod a sepcic a prin intermediul valorii seed care este valoarea de nceput cu care se porne ste agregarea. Dac a nu se specic a nicio valoare pentru seed, atunci primul element din colec tie este luat drept seed. Pentru nmul tirea valorilor cuprinse ntrun tablou se poate proceda astfel: int [ ] numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ; var query = numbers . Aggregate ( ( a , b ) => a b ) ;

Valoarea a sat a este 9! = 362880. Specicarea unei valori pentru seed se face (exemplu): int [ ] numbers = { 9 , 3 , 5 , 4 , 2 , 6 , 7 , 1 , 8 } ; var query = numbers . Aggregate ( 5 , ( a , b ) => ( ( a < b ) ? ( a b ) : a ) ) ;

10.6.8

Parti tionare

Se folosesc pentru extragerea unei anumite p ar ti dintr-o secven ta . Operatorii sunt: Take, Skip, TakeWhile, SkipWhile. Vom exemplica doar o parte din

258 ace stia: int var var var var

CURS 10. LINQ (I)

[ ] numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ; query = numbers . Take ( 5 ) ; // s e c v e n t a 1 , 2 , 3 , 4 , 5 query2 = numbers . Skip ( 5 ) ; // s e c v e n t a 6 , 7 , 8 , 9 query3 = numbers . TakeWhile ( ( n , i n d e x ) => n + i n d e x < 4 ) ; // 1 , 2 query4 = numbers . SkipWhile ( ( n , i n d e x ) => n + i n d e x < 4 ) ; // 3 , 4 ,

10.6.9

Concatenarea

Se face cu Concat, care preia dou a secven te si produce o a treia, reprezent and concatenarea lor, f ar a eliminarea duplicatelor: int [ ] x = { 1 , 2 , 3 } ; int [ ] y = { 3 , 4 , 5 } ; var c o n c a t = x . Concat ( y ) ; // c o n c a t = 1 , 2 , 3 , 3 , 4 , 5

10.6.10

Referirea de elemente din secven te

Se pot folosi: First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault si DefaultIfEmpty. Pentru First si deriva tii lui exist a formele: public s t a t i c T F i r s t <T>( t h i s IEnumerable<T> s o u r c e ) ; public s t a t i c T F i r s t <T>( t h i s IEnumerable<T> s o u r c e , Func<T, bool> p r e d i c a t e ) ; public s t a t i c T F i r s t O r D e f a u l t <T>( t h i s IEnumerable<T> s o u r c e ) ; public s t a t i c T F i r s t O r D e f a u l t <T>( t h i s IEnumerable<T> s o u r c e , Func<T, bool> p r e d i c a t e ) ; Dac a nu se specic a predicatul, atunci se va returna primul element din secven ta , altfel primul element din secven ta care satisface condi tia exprimat a prin predicat. int [ ] numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ; var query = numbers . F i r s t ( ) ; query = numbers . F i r s t ( n => n % 2 == 0 ) ;

10.6. LINQ TO OBJECTS

259

Varianta cu OrDefault este util a dac a se crede c a predicatul poate s a nu e satisf acut de niciun element al secven tei. Se returneaz a valoarea implicit a pentru tipul de date considerat: 0 pentru tip numeric, false pentru boolean, null pentru tip referin ta . Dac a se folose ste varianta f ar a OrDefault, atunci e posibil ca aplica tia s a se opreasc a cu eroare asta n cazul n care niciun element al colec tiei nu respect a condi tia.

260

CURS 10. LINQ (I)

Curs 11 LINQ (II): Linq to SQL


Linq to SQL vine cu o solu tie pentru accesarea datelor stocate ntrun server de baze de date SQL Server 2000, 2005 sau 2008; se permite si implementarea unor furnizori de Linq pentru alte servere de baze de date1 . Linq to SQL dene ste o asociere ntre tabelele din baza de date si clase C#, clase numite entit a ti. Suplimentar, se pune la dispozi tie o clas a DataContext care mediaz a comunicarea ntre baza de date si clasele entitate. DataContext men tine starea obiectelor, transform a dintr-un obiect ntro nregistrare si invers.

11.1

Ob tinerea unui context de date

Ob tinerea obiectului de tip DataContext se poate face ad aug and un obiect de tip LINQ to SQL Classes, prin Add new item la nivel de augarea unui sier cu extensia aplica tie, precum n gura 11.1. Va rezulta ad dbml la aplica tie; acesta con tine o clas a derivat a din DataContext. Prin ad augarea tabelelor din fereastra de Server Explorer se vor crea si clasele entitate aferente tabelelor (gurile 11.2 si 11.3). Ob tinerea unui obiect asociate tabelei Person se face prin: PersonDataContext pdc = new PersonDataContext ( ) ; Trebuie avut n vedere faptul c a clasa asociat a contextului de date implementeaz a interfa ta IDisposable, deci un asemenea obiect poate si ar trebui s a e disponibilizat de ndat a ce nu mai este nevoie de serviciile lui. Acest lucru se face e prin instruc tiunea using, e prin bloc try-finally: using ( PersonDataContext pdc = new PersonDataContext ( ) ) {
1

Vezi de exemplu : furnizor pentru MySql, Oracle, PostgreSQL.

261

262

CURS 11. LINQ (II): LINQ TO SQL

Figura 11.1: Ad agarea unui sier DBML la aplica tie

Figura 11.2: Selectarea tabelor pe baza c arora se va construi sierul DBML

11.1. OBTINEREA UNUI CONTEXT DE DATE

263

Figura 11.3: Clasele C# rezultate dup a ad augarea de tabele pe suprafa ta sierului DBML Table<Person> p e r s o n T a b l e = pdc . GetTable<Person > ( ) ; } // sau PersonDataContext pdc = new PersonDataContext ( ) ; try { ... } finally { pdc . D i s p o s e ( ) ; } Ob tinerea unui obiect asociat tabelei People se face prin: Table<Person> p e r s o n T a b l e = pdc . GetTable<Person > ( ) ; iar ob tinerea de obiecte de tip Person, c ate unul asociat ec arei nregistr ari se face cu: var a l l P e r s o n s = from p in p e r s o n T a b l e select p; Mai mult, orice interogare efectuat a cu expresii Linq sau operatori Linq poate efectuat a asupra tabelelor. De exemplu, pentru o jonc tiune ntre tabelele Person si Salary putem scrie: var p e r s o n s W i t h S a l a r i e s = from p in pdc . P e r s o n s j o i n s in pdc . S a l a r i e s on p . i d e q u a l s s . IDPerson

264

CURS 11. LINQ (II): LINQ TO SQL s e l e c t new { p . FirstName , p . LastName , s . Year , s . S a l a r y Y e a r } ;

foreach ( var i t e r in p e r s o n s W i t h S a l a r i e s ) { Console . WriteLine ( " FirstName : {0} LastName : {1} Year : {2} S a l a r y Y e a r : {3} " , i t e r . FirstName , i t e r . LastName , i t e r . Year . ToString ( ) , i t e r . S a l a r y Y e a r . ToString ( ) ) ; } Pentru a vizualiza interogarea SQL care este formulat a spre a se trimite c atre server, se poate scrie: pdc . Log = Console . Out ; O alt a variant a este folosirea obiectului de context de date n conjunc tie cu metoda GetCommand care prime ste interogarea pentru care se vrea vizualizarea comenzii SQL: Console . WriteLine ( pdc . GetCommand ( p e r s o n s W i t h S a l a r i e s ) . CommandText ) ;

11.2
11.2.1

Ad augarea, modicarea si stergerea de nregistr ari n tabel a


Ad augarea

Pentru ad augarea unei noi nregistr ari, se poate crea un obiect de tipul entitate - clas a furnizat a automat de c atre DBML - iar apoi apelarea metodei InsertOnSubmit pentru tabela n care se dorec ste inserarea. Ad augarea la setul de nregistr ari se face numai c and se apeleaz a metoda SubmitChanges pentru contextul de date: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { Person p = new Person { FirstName = " i n s e r t e d " , LastName = " from code " , IDRole = 1 } ; pdc . P e r s o n s . InsertOnSubmit ( p ) ; pdc . SubmitChanges ( ) ; } Dac a pentru obiectul p se stabile ste rolul nu prin asigmare direct a a valorii cheii str aine, ci prin crearea unui obiect nou de tip Role, atunci se face

265 11.2. ADAUGAREA, MODIFICAREA S I S TERGEREA DE INREGISTRARI IN TABELA inserare automat a a acestui nou obiect de rol si apoi inserarea nregistr arii de persoan a, cu referin ta c atre rolul creat: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { pdc . Log = Console . Out ; Person p = new Person { FirstName = " i n s e r t e d " , LastName = " from code " , Role = new Role { R o l e D e s c r i p t i o n = " t e s t e r " } } ; pdc . P e r s o n s . InsertOnSubmit ( p ) ; pdc . SubmitChanges ( ) ; }

11.2.2

Modicarea unei nregistr ari

Modicarea unei nregistr ari se poate face prin ob tinerea obiectului care va modicat, se modic a propriet a tile lui si n nal apelul metodei SubmitChanges pentru contextul de date: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { pdc . Log = Console . Out ; Person p e r s o n = pdc . P e r s o n s . Where ( p => p . i d == 1 ) . S i n g l e ( ) ; p e r s o n . LastName = " M o d i f i e d " ; pdc . SubmitChanges ( ) ; } Men tion am doar c a se poate face modicarea unui obiect care a fost adus ntrun context deschis si nchis anterior, dar cu reata sare la context.

11.2.3

S tergerea unei nregistr ari

Printro manevr a asem an atoare cu cea de mai sus, se preia nregistrarea care trebuie stears a si apoi pentru obiectul rezultat se face stergerea via metoda DeleteOnSubmit pentru obiectul reprezent and tabelul din care se sterge: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { pdc . Log = Console . Out ; Person p e r s o n = pdc . P e r s o n s . Where ( p => p . i d == 9 ) . DefaultIfEmpty ( ) . S i n g l e ( ) ; i f ( p e r s o n != null ) {

266

CURS 11. LINQ (II): LINQ TO SQL pdc . P e r s o n s . DeleteOnSubmit ( p e r s o n ) ;

} pdc . SubmitChanges ( ) ; }

11.3

Op tiuni de nc arcare a datelor

S a presupunem c a dorim s a aducem lista de rolurim iar pentru ecare rol persoanele care fac parte din el: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { pdc . Log = Console . Out ; var q u e r y R o l e s = from r o l e in pdc . R o l e s select role ; foreach ( var r o l e in q u e r y R o l e s ) { foreach ( var p e r s o n in r o l e . P e r s o n s ) { Console . WriteLine ( " p e r s o n : FirstName { 0 } , LastName {1} " , p e r s o n . FirstName , p e r s o n . LastName ) ; } } } Se poate observa c a la ecare executare a ciclului foreach exterior se va apela c ate o instruc tiune SQL care aduce persoanele care fac parte din rolul respectiv. Dat a ind abilitatea SQL-ului de a face jonc tiuni, ne dorim s a mbun at ac tim acest ecomportament. Se poate ob tine o nc arcare complet aa datelor prin utilizarea obiectului DataLoadOptions: using ( PersonDataContext pdc = new PersonDataContext ( ) ) { pdc . Log = Console . Out ; DataLoadOptions d l o = new DataLoadOptions ( ) ; d l o . LoadWith<Role >( r o l e => r o l e . P e r s o n s ) ; pdc . LoadOptions = d l o ; var q u e r y R o l e s = from r o l e in pdc . R o l e s select role ; foreach ( var r o l e in q u e r y R o l e s ) { foreach ( var p e r s o n in r o l e . P e r s o n s )

11.3. OPTIUNI DE INCARCARE A DATELOR {

267

Console . WriteLine ( " r o l e : {0} p e r s o n : FirstName { 1 } , LastName {2} " , r o l e . R o l e D e s c r i p t i o n , p e r s o n . FirstName , p e r s o n . LastName ) ; } } }

268

CURS 11. LINQ (II): LINQ TO SQL

Curs 12 Atribute. Fire de execu tie


12.1 Atribute

O aplica tie .NET con tine cod, date si metadate. Metadata este o dat a despre assembly (versiune, producator, etc), tipuri de date con tinute, etc stocate mpreun a cu codul compilat. Atributele sunt folosite pentru a da o extra-informa tie compilatorului de .NET. Java folose ste o combina tie de semne /** si @ pentru a include informa tie relativ la clase, metode, c ampuri sau parametri. Aceste comentarii ns a nu vor incluse n bytecod-ul nal, doar n eventuala documenta tie. Folosind atributele, aceast a informa tie poate stocat a n codul compilat si reutilizat a ulterior la runtime. Unde ar utile aceste atribute? Un exemplu de utilizare a lor ar urm arirea bugurilor care exist a ntrun sistem sau urm arirea stadiului proiectului. De asemenea, anumite atribute predenite sunt utilizate pentru a specica dac a un tip este sau nu serializabil, care sunt por tiunile de cod scoase din circula tie, informa tii despre versiunea assembly-ului, etc.

12.1.1

Generalit a ti

Atributele sunt de dou a feluri: intrinseci (predenite) si denite de utilizator. Cele intrinseci sunt integrate n platforma .NET si sunt recunoscute de CLR. Atributele denite de utilizator sunt create n func tie de dorin tele acestuia. Atributele se pot specica pentru: assemblyuri, clase, constructori, delega ti, enumer ari, evenimente, c ampuri, interfe te, metode, module, parametri, propriet a ti, valori de retur, structuri. Tinta unui atribut specic a cui anume i se va aplica un atribut anume. Tabelul 12.1 con tine tintele posibile si o scurt a descriere a lor.

269

270

CURS 12. ATRIBUTE. FIRE DE EXECUTIE Tabelul 12.1: Tintele atributelor. Tint a All Assembly ClassMembers Class Constructor Delegate Enum Event Field Interface Method Module Parameter Property ReturnValue Struct Descriere Orice element Un assembly Orice membru al unei clase O clas a Un constructor Un delegat O enumerare Un eveniment Un c amp O interfa ta O metod a Un modul Un parametru O proprietate O valoare de retur O structur a

Aplicarea atributelor se face prin specicarea numelui lor ntre paranteze drepte; mai multe atribute e se specic aunul deasupra celuilalt, e n interiorul acelora si paranteze, desp ar tite prin virgul a. Exemplu: [Serializable] [Webservice] echivalent cu: [Serializable, Webservice] In cazul atributelor ce se specic a pentru un assembly, forma lor este: [assembly:AssemblyDelaySign(false)] in cazul acestor din urm a atribute, specicarea lor se face dup a toate declara tiile using, dar naintea oric arui cod. In general, specicarea unui atribut se face prin scrierea lui imediat naintea elementului asupra c aruia se aplic a: using System; [Serializable] class ClasaSerializabila { //definitia clasei }

12.1. ATRIBUTE

271

12.1.2

Atribute predenite
Tabelul 12.2: Atribute predenite.

a c ateva dintre ele: Tabelul 12.2 prezint

Atribut System.SerializableAttribute [Serializable] System.NonSerializedAttribute [NonSerialized] System.Web.Services.WebServiceAttribute [WebService] System.Web.Services.WebMethodAttribute [WebMethod] System.AttributeUsageAttribute [AttributeUsage] System.ObsoleteAttribute [Obsolete] System.Reection.AssemblyVersionAttribute [AssemblyVersion] System.Attribute.CLSCompliant [CLSCompliant] System.Runtime.InteropServices. DllImportAttribute [DllImport]

Descriere Permite unei clase s a e serializat a pe disc sau ntro re tea Permite unor membri s a nu e salva ti pe re tea sau pe disc Permite specicarea unui nume si a unei descrieri pentru un serviciu Web Marcheaz a o metod a ca ind expus a ca parte a unui serviciu Web Dene ste parametrii de utilizare pentru atribute Marcheaz a o secven ta ca ind scoas a din uz Specic a num arul de versiune al unui assembly Indic a dac a un element de program este compatibil cu CLS specic a loca tia DLL care con tine implementarea pentru o metod a extern a

Intre parantezele drepte se arat a cum se specic a acel atribut. Exemplul de mai jos folose ste atributul System.ObsoleteAttribute. using System; namespace AttributeSample1 { class Class1 { [STAThread] static void Main(string[] args) { int s1 = AddTwoNumbers(2, 3); int s2 = AddNumbers(2, 3); int s3 = AddNumbers(2, 3, 4);

272 }

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

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

12.1. ATRIBUTE

273

My2DPoint.Y = 200; Stream WriteStream = File.Create("Point2D.bin"); BinaryFormatter BinaryWrite = new BinaryFormatter(); 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); } }

12.1.3

Exemplicarea altor atribute predenite

Atributul Conditional Acest atribut se ata seaz a la o metod a pentru care se dore ste ca atunci c and compilatorul o nt alne ste la un apel, dac a un anumit simbol nu e denit atunci nu va chemat a. Este folosit a pentru a omite anumite apeluri de metode ( de exemplu cele folosite la etapa de debugging). Exemplu: using System; using System.Diagnostics; namespace CondAttrib { class Thing { private string name; public Thing(string name) { this.name = name; SomeDebugFunc(); SomeFunc(); } public void SomeFunc()

274

CURS 12. ATRIBUTE. FIRE DE EXECUTIE { Console.WriteLine("SomeFunc"); } [Conditional("DEBUG")] public void SomeDebugFunc() { Console.WriteLine("SomeDebugFunc"); }

} public class Class1 { [STAThread] static void Main(string[] args) { Thing t = new Thing("T1"); } } } Denirea unui anumit simbol (de exemplu DEBUG) se poate face n dou a moduri: prin folosirea unei directive de preprocesor de tipul #dene: #define DEBUG prin folosirea parametrilor din linia de comanda sau a posibilit a tilor mediului integrat de dezvoltare. De exemplu, pentru a se deni un anumit simbolul din Visual Studio se procedeaz a astfel: clic dreapta n Solution Explorer pe numele proiectului, selectare Properties apoi Conguration Properties. Pe linia Conditional Compilation Constants se adaug a simbolul dorit. Acest lucru va avea ca efect folosirea op tiunii dene a compilatorului. Dac a la compilare simbolul nu este denit atunci compilatorul va ignora apelurile de metode calicate cu atributul Conditional. Acest lucru se poate verica at at urm arind execu tia programului, c at si din inspec tia codului IL rezultat la compilare. Atributul CLSCompliant Acest atribut se aplic a pe assemblyuri. Dac a un assembly este marcat ca ind CLSCompliant, orice tip expus public n assembly care nu este compatibil CLS trebuie s a e marcat cu CLSCompliant(false). De exmplu,

12.1. ATRIBUTE

275

CLS prevede faptul c a tipurile de date ntregi ar trebui s a e cu semn. O clas a poate s a con tin a membri (c ampuri, metode) de tip unsigned, dar dac a respectiva clas a este declarat a ca ind CLSCompliant atunci acestea ar trebui declarate ca ind ne-vizibile din afara assemblyului. Dac a ele sunt expuse n afara assemblyului, unele limbaje .NET s-ar putea s a nu le poat a folosi efetiv. Pentru a ajuta dezvoltatorii .NET s a evite asemenea situa tii, platforma pune la dispozi tie atributul CLSCompliant cu care se controleaz a r aspunsul compilatorului la expunerea entit a tilor ne-compatibile cu CLS; astfel, se va genera o eroare sau se vor ignora asemenea cazuri. Exemplu: [assembly:CLSCompliant(true)] namespace DemoCLS { public class ComplianceTest { //Tipul uint nu ste compatibil CLS //deaorece acest camp este privat, regulile CLS nu se aplica private uint a = 4; //deoarece acest camp uint este public, avem //incompatibilitate CLS public uint B = 5; //Acesta este modul corect de expunere a unui uint: //tipul long este compatibil CLS public long A { get { return a; } } } } Dac a se compileaz a assembly-ul de mai sus atunci apare o eroare: Type of DemoCLS.ComplianceTest.B is not CLS-compliant Pentru ca aceast a eroare s a nu mai apar a putem s a declar am B ca ind invizibil din exteriorul assembly-ului sau s a ad aug am inaintea lui B atributul: [CLSCompliant(false)] Meritul acestui atribut este c a anun ta programatorul despre eventualele neconcordan te cu Common Language Specications.

276

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

12.1.4

Atribute denite de utilizator

In general, atributele predenite acoper a marea majoritate a situa tiilor care cer utilizarea de atribute. Eventualizatea ca utilizatorul s a doreasc a crearea propriilor sale atribute este prev azut a de c atre platforma .NET, d anduse posibilitatea denirii lor. Exist a c ateva situa tii n care e benec a denirea de noi atribute. Exemplul cel mai des nt alnit este acela n care pentru a se men tine informa tii despre un cod la care lucreaz a mai multe echipe, se dene ste un atribut care s a serveasc a la pasarea de informa tie relativ la por tiuni din cod. Un alt exemplu este utilizarea unui sistem de urm arire a stadiului de dezvoltare a codului, care ar folosi informa tia stocat a n atribute. Un atribut utilizator este o clas a denit a ca p an a acum. Se cere a derivat a din System.Attribute e direct, e indirect. Sunt c a tiva pa si care trebuie parcur si pentru realizara unui atribut: specicarea tintei, derivarea adecvat a a clasei atribut, denumirea clasei n conformitate cu anumite reguli (recomandat, dar nu obligatoriu) si denirea clasei. Pasul 1. Declararea tintei Primul pas n crearea unui atribut utilizator este specicarea domeniului s au de aplicabilitate, adic a a elementelor c arora li se poate ata sa. Tintele sunt cele din tabelul 12.1, care sunt de fapt valori din enumerarea AttributeTargets. Specicarea tintei se face prin intermediul unui (meta)atribut AttributeUsage. Pe l ang a tinta propriuzis a, se mai pot specica valorile propriet a tilor Inherited si AllowMultiple. Proprietatea Inherited este de tip boolean si precizeaz a dac a atributul poate mo stenit de c atre clasele derivate din cele c areia i se aplic a. Valoarea implicit a este true. Proprietatea AllowMultiple este de asemenea de tip boolean si specic a dac a se pot utiliza mai multe instan te ale atributului pe un acela si element. Valoarea implicit a este false. Vom deni mai jos tinta atributului pe care l vom construi ca ind assemblyul, clasa, metoda, cu posibilitate de repetare a sa: [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] Pasul 2. Declararea unei clase atribut Pentru declarearea unei clase atribut, urm atoarele reguli trebuie s a e avute n vedere:

12.1. ATRIBUTE

277

(obligatoriu) O clas a atribut trebuie s a deriveze direct sau indirect System.Attribute (obligatoriu) O clas a atribut trebuie s a e declarat a ca ind public a (recomandat) Un atribut ar trebui s a aib a suxul Attribute. Vom declara clasa atribut CodeTrackerAttribute. Acest atribut s-ar putea folosi n cazul n care s-ar dori urm arirea dezvolt arii codului n cadrul unui proiect de dimensiuni foarte mari, la care particip a mai multe echipe. Acest atribut utilizator va inclus n codul compilat si distribuit altor echipe (care nu vor avea acces la cod). O alternativ a ar folosirea documentat tiei XML generate dup a comentariile din cod. [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] public class CodeTrackerAttribute : System.Attribute { //cod } Pasul 3. Declararea constructorilor si a propriet a tilor Acest pas const a n denirea efectiv a membrilor clasei. Vom considera c atributul pe care l cre am va purta trei informa tii: numele programatorului care a ac tionat asupra respectivei unit a ti de cod, faza n care se a a, op tional note. Primele dou a atribute sunt mandatorii si vor preluate prin constructor, al treilea poate setat prin intermediul unei propriet a ti. using System; [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] public class CodeTrackerAttribute : System.Attribute { private string name; private string phase; private string notes; public CodeTrackerAttribute(string name, string phase) { this.name = name; this.phase = phase; }

278

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

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

12.2. FIRE DE EXECUTIE

279

12.2

Fire de execu tie

Firele de execu tie1 sunt responsabile cu multitaskingul n interiorul unei aplica tii. Clasele si interfe tele responsabile pentru crearea aplica tiilor multir se g asesc n spa tiul de nume System.Threading. Vom discuta n cele ce urmeaz a despre managementul threadurilor si despre sincronizare. De cele mai multe ori crearea de threaduri este necesar a pentru a da utilizatorului impresia c a programul execut a mai multe ac tiuni simultan, n cadrul unei aplica tii. Pentru ca o interfa ta utilizator s a poat a folosit a f ar a a se a stepta ncheierea unei anumite secven te de instruc tiuni, e nevoie de mai multe re de execu tie (unele pentru procesarea efectiv a a informa tiei, altele care s a r aspund a ac tiunilor utilizatorului). Pentru probleme de calcul numeric, exprimarea procesului de calcul se poate face foarte natural sub form a de re de exec tie. De asemenea, strategia de rezolvare divide et impera se preteaz a natural la o lucrul cu re de execu tie. In sf ar sit, se poate benecia de sisteme cu procesoare hyperthreading, multiprocesor, multicore sau combina tii ale acestora.

12.3
12.3.1

Managementul threadurilor
Pornirea threadurilor

Cea mai simpl a metod a de a crea un r de exec tie este de a crea o instan ta a clasei Thread, al c arei constructor preia un singur argument de tip delegat. In BCL este denit tipul delegat ThreadStart, care este folosit ca prototip pentru orice metod a care se vrea a lansat a ntrun r de execu tie. Declara tia de ThreadStart este: public delegate void ThreadStart(); Crearea unui r de execu tie se face pe baza unei metode care returneaz a void si nu preia nici un parametru: ThreadStart ts = new ThreadStart(MyFunc); Thread myThread = new Thread( ts ); Un exemplu simplu este: using System; using System.Threading;
1

Engl: threads; vom folosi alternativ termenii thread si r de execu tie

280

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

class SimpleThreadApp { public static void WorkerThreadMethod() { Console.WriteLine("[WorkerThreadMethod] Worker " + "thread started"); } public static void Main() { ThreadStart worker = new ThreadStart(WorkerThreadMethod); Console.WriteLine("[Main] Creating worker thread"); Thread t = new Thread(worker); t.Start(); Console.WriteLine( "[Main] Have requested the start of worker thread"); Console.ReadLine(); } } P an a la linia t.Start() exist a un singur thread: cel dat de pornirea metodei Main. Dup a t.Start() sunt 2 threaduri: cel anterior si t. Primul esaj din Main va tip arit naintea celui din threadul t; n func tie de cum anume se planic a threadurile pentru execu tie, mesajul de dup a t.Start() poate s a apar a nainte sau dup a mesajul tip arit de metoda WorkerThreadMethod(). De remarcat c a simpla creere a rului de execu tie nu determin a si pornirea lui: acest lucru se nt ampl a dupa apelul metodei Start denit a n clasa Thread. Exemplul urm ator porne ste dou a re de execu tie. Func tiile care con tin codul ce se va executa n c ate un thread sunt Increment() si Decrement(): using System; using System.Threading; class Tester { static void Main( ) { Tester t = new Tester( ); t.DoTest( ); } public void DoTest( )

12.3. MANAGEMENTUL THREADURILOR { // creeaza un thread pentru Thread t1 = new Thread( new // creeaza un thread pentru Thread t2 = new Thread( new // porneste threadurile t1.Start( ); t2.Start( ); } 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); } } } Incrementer ThreadStart(Incrementer) ); Decrementer ThreadStart(Decrementer) );

281

La ie sire se vor mixa mesajele tip arite de primul thread cu cele tip arite de cel deal doilea thread: ... Incrementer: Incrementer: Incrementer: Incrementer: Incrementer: Decrementer: Decrementer: Decrementer: Decrementer: ...

102 103 104 105 106 1000 999 998 997

282

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

Perioada de timp alocat a ec arui thread este determinat a de c atre planicatorul 2 de re de execu tie si depinde de factori precum viteza procesorului, gradul lui de ocupare, etc. O alt a modalitate de ob tinere a unei referin te la un thread este prin apelul propriet a tii statice Thread.CurrentThread pentru rul de execu tie curent. In exemplul de mai jos se ob tine obiectul Thread asociat rului de execu tie curent, i se seteaz a numele (proprietate read/write de tip String) si se a seaz a pe ecran: Thread current = Thread.CurrentThread; current.Name = "My name"; Console.WriteLine("nume={0}", current.Name );

12.3.2

Metoda Join()

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

12.3.3

Suspendarea relor de execu tie

Se poate ca n anumite cazuri s a se doreasc a suspendarea unui r de execu tie pentru o scurt a perioad a de timp. Clasa Thread ofer a o metod a static a supra nc arcat a Sleep(), care poate prelua un parametru de tip int reprezent and milisecundele de adormire, iar a doua variant a preia un argument de tip TimeSpan, care reprezint a cea mai mic a unitate de timp care poate specicat a, egal a cu 100 nanosecunde. Pentru a cere rului de execu tie curent s a se suspende pentru o secund a, se execut a n cadrul acestuia:
2

Engl: thread scheduler

12.3. MANAGEMENTUL THREADURILOR Thread.Sleep(1000);

283

In acest fel se semnaleaz a planicatorului de re de execu tie c a poate lansa un alt thread. Dac a n exemplul de mai sus se adaug a un apel Thread.Sleep(1) dup a ecare WriteLine(), atunci ie sirea se schimb a dramatic: Iesire (extras) Incrementer: 0 Incrementer: 1 Decrementer: 1000 Incrementer: 2 Decrementer: 999 Incrementer: 3 Decrementer: 998 Incrementer: 4 Decrementer: 997 Incrementer: 5 Decrementer: 996 Incrementer: 6 Decrementer: 995

12.3.4

Omor area threadurilor

De obicei, un r de execu tie moare dup a ce se termin a de executat. Se poate totu si cere unui r de execu tie s a si nceteze execu tia folosind metoda Abort(). Acest lucru va duce la aruncarea unei excep tii n interiorul rului de execu tie c aruia i se cere suspendarea: ThreadAbortedException, pe care rul respectiv o poate prinde si procesa, permi ta ndui eliberarea de resurse alocate. Ca atare, se recomand a ca o metod a care se va lansa ca r de execu tie s a se compun a dintrun bloc try care con tine instruc tiunile utile, dup a care un bloc catch si/sau nally care vor efectua eliberarea de resurse. Exemplu: using System; using System.Threading; class Tester { static void Main( ) { Tester t = new Tester( ); t.DoTest( );

284

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

} 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( ); } Console.WriteLine("All my threads are done."); } // 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);

12.3. MANAGEMENTUL THREADURILOR } } catch (ThreadAbortedException) { Console.WriteLine( Thread {0} interrupted! Cleaning up..., Thread.CurrentThread.Name); } finally { Console.WriteLine(Thread {0} Exiting. , Thread.CurrentThread.Name); }

285

} // 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..., Thread.CurrentThread.Name); } finally { Console.WriteLine( Thread {0} Exiting. , Thread.CurrentThread.Name); } } } Ie sire: Started thread Thread1

286

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

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 Thread Thread1. Decrementer: 987 Thread Thread3. Incrementer: 4 Thread Thread1. Decrementer: 986 Thread Thread3. Incrementer: 5 // ... Thread Thread1. Decrementer: 1 Thread Thread3. Incrementer: 997

12.3.5

Sugerarea priorit a tilor relor de execu tie

Un r de execu tie se lanseaz a implicit cu prioritatea ThreadPriorityLevel.Normal. Dar schedulerul poate inuen tat n activitatea sa prin setarea de diferite

12.3. MANAGEMENTUL THREADURILOR

287

nivele de prioritate pentru re; aceste nivele fac parte din enumerarea ThreadPriorityLevel : ThreadPriorityLevel.TimeCritical, ThreadPriorityLevel.Highest, ThreadPriorityLevel.AboveNormal, ThreadPriorityLevel.Normal, ThreadPriorityLevel.BelowNormal, ThreadPriorityLevel.Lowest, ThreadPriorityLevel.Idle. Prioritatea este descresc atoare n lista prezentat a. Pe baza priorit a tii procesului care con tine rele de execu tie si a priorit a tii relor, se calculeaz a un nivel de prioritate (de ex. pe ma sini Intel valori ntre 0 si 31) care determin a prioritatea n ansamblul sistemului de operare a rului respectiv. Setarea unei anumite priorit a ti se face folosind proprietatea Priority : myThread.Priority = ThreadPriorityLevel.Highest;

12.3.6

Fire n fundal si re n prim-plan

Relativ la proprietatea boolean a IsBackground, trebuie f acut a precizarea c a un r de execu tie poate s a se execute n fundal (background) sau n prim plan (foreground). Diferen ta dintre cele dou a posibilit a ti o constituie faptul c a dac a un proces are m acar un r de execu tie n foreground, CLR va men tine aplica tia n execu tie. O dat a ce toate rele de execu tie de tip foreground se termin a, CLR va executa Abort() pentru ecare r de execu tie de tip background (dac a mai exist a a sa ceva) si termin a procesul. Exemplu: using System; using System.Threading; class Test { static void Main() { 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();

288

CURS 12. ATRIBUTE. FIRE DE EXECUTIE 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 men tine procesul n execu tie p an a c and se termin a ciclul s au while. C and acesta se termin a, procesul este oprit, chiar dac a ciclul while din rul de execu tie din background nu si-a terminat execu tia.

12.4

Sincronizarea

Sincronizarea se ocup a cu controlarea accesului la resurse partajate de mai multe re de execu tie. De exemplu, se poate cere ca utilizarea unei resurse anume s a se fac a la un moment dat de c atre un singur r de execu tie. Vom discuta aici trei mecanisme de sincronizare: clasa Interlock, instruc tiunea C# lock si clasa Monitor. Exemplele se vor baza pe acces la o resurs a partajat a, ca mai jos: public void Incrementer( )

12.4. SINCRONIZAREA {

289

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); } } C ampul counter se ini tializeaz a cu 0. S a presupunem c a pornim dou a re de execu tie pe baza metodei Incrementer() de mai sus. Este posibil s a se nt ample urm atoarele: primul thread va citi valoarea lui counter (0) si o va atribui unei variabile temporare, pe care o va incrementa apoi. Al doilea r se activeaz a si el, va citi valoarea (nemodicat a) lui counter si va atribui aceast a valoare unei variabile temporare. Primul r de execu tie si termin a munca, apoi asigneaz a valoarea variabilei temporare (1) lui counter si o a seaz a. Al doilea thread face exact acela si lucru. Se tip are ste astfel 1, 1. La urm atoarele itera tii se va a sa 2, 2, 3, 3, etc, n locul lui 1, 2, 3, 4. Exemplu: using System; using System.Threading;

290

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

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 } } Ie sire: Started thread ThreadOne Started thread ThreadTwo Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer:

1 2 3 3 4 4 5 5

12.4. SINCRONIZAREA Thread ThreadTwo. Incrementer: 6 Thread ThreadOne. Incrementer: 6

291

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

12.4.1

Clasa Interlocked

Incrementarea si decrementarea unei valori este o situa tie at at de des nt alnit a, nc at C# pune la dispozi tie o clas a special a Interlocked pentru o rezolvare rapid a. Clasa include dou a metode statice, Increment() si Decrement(), care incrementeaz a sau decrementeaz a o valoare, ns a sub un control sincronizat. Putem modica metoda Incrementer() de mai sus astfel: public void Incrementer( ) { try { while (counter < 1000) { Interlocked.Increment(ref counter); // simuleaza o sarcina in aceasta metoda Thread.Sleep(1); // asigura valoarea decrementata // si afiseaza rezultatul Console.WriteLine( "Thread {0}. Incrementer: {1}", Thread.CurrentThread.Name, counter); } } //blocurile catch si finally raman neschimbate } Ie sirea este cea dorit a: Started thread ThreadOne Started thread ThreadTwo Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer: Thread ThreadOne. Incrementer: Thread ThreadTwo. Incrementer:

1 2 3 4

292 Thread Thread Thread Thread ThreadOne. ThreadTwo. ThreadOne. ThreadTwo.

CURS 12. ATRIBUTE. FIRE DE EXECUTIE Incrementer: Incrementer: Incrementer: Incrementer: 5 6 7 8

12.4.2

Instruc tiunea lock

Exist a situa tii c and vrem s a bloc am alte variabile dec at cele de tip int. Un lock marcheaz a o sec tiune critic a a codului, produc and astfel sincronizare pentru un obiect. La utilizare, se specic a un obiect pentru care se stabile ste un lock, dup a care o instru tiune sau un grup de instruc tiuni. Lockul este nl aturat la sf ar situl instruc tiunii/blocului de instru tiuni. Sintaxa este: lock(expresie) { instructiuni } Exemplu: metoda Incrementer() se va modica dup a cum urmeaz a: public void Incrementer( ) { try { while (counter < 1000) { lock (this) { int temp = counter; temp++; Thread.Sleep(1); counter = temp; } // atribuie valoarea decrementata // si afiseaza rezultatul Console.WriteLine( "Thread {0}. Incrementer: {1}", Thread.CurrentThread.Name, counter); } } //blocurile catch si finally raman neschimbate } Rezultatele sunt a sate exact ca la sec tiunea 12.4.1.

12.4. SINCRONIZAREA

293

12.4.3

Clasa Monitor

Clasa Monitor con tine metode pentru a controla sincronizarea relor de execu tie, permi ta nd declararea unei zone critice n care la un moment dat doar un thread trebuie s a opereze. Atunci c and se dore ste s a se nceap a sincronizarea, se va apela metoda Enter, d and obiectul pentru care se va face blocarea: Monitor.Enter( obiect ); Dac a monitorul este nedisponibil, atunci nseamn a c a un alt thread este ntro regiune critic a a obiectului respectiv. Se mai poate folosi de asemenea metoda Wait(), care elibereaz a monitorul, dar blocheaz a threadul, inform and CLR c a atunci c and monitorul devine din nou liber, threadul curent ar vrea s a si continue execu tia (este ad augat ntro coad a de a steptare format a din re de execu tie blocate pe obiect). Terminarea zonei critice se face folosind metoda Exit() a clasei Monitor. Metoda Pulse() semnaleaz a c a a avut loc o schimbare de stare, n urma c areia este posibil ca un alt r de execu tie care a steapt a va putea s a e continuat (ordinea de selectare a relor ce vor executate ind ordinea introducerii n coada de a steptare). Inrudit a este metoda PulseAll care anun ta toate obiectele blocate pe un anumit obiect de schimbarea de stare. S a presupunem c a avem o clas a MessageBoard unde re individuale pot citi si scrie mesaje. Vom sincroniza accesul la aceast a clas a astfel nc at doar un thread s a poat a ac tiona la un moment dat. Clasa MessageBoard va avea o metod a Reader() si una Writer(). Metoda Reader() determin a dac a stringul message con tine vreun mesaj valabil, iar metoda Writer() va scrie n acest string. Dac a nu sunt mesaje n timpul citirii, threadul Reader() va intra n stare de a steptare folosind Wait() p an a c and metoda Writer() scrie un mesaj si transmite un mesaj via Pulse() pentru a trezi alte threaduri. using System; using System.Threading; class MessageBoard { private String messages = no messages ; public void Reader() { try { Monitor.Enter(this);

294

CURS 12. ATRIBUTE. FIRE DE EXECUTIE //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..., 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

12.4. SINCRONIZAREA ThreadStart(myMessageBoard.Writer)); writer.Name = WriterThread:; reader.Start(); writer.Start(); } } Ie sirea este: ReadrThread: no messages ReaderThread: waiting... WriterThread: Done writing message... ReaderThread: Greetings Caroline and Marianne!

295

Dup a cum se vede mai sus, threadul reader este pornit primul, el blocheaz a clasa obeictul de tip MessageBoard, ceea ce nseamn a c a are acces exclusiv la variabila message. Dar deoarece message nu con tine nici un mesaj, thread-ul reader elibereaz a obiectul pe care la blocat mai nainte prin apelul Wait(). Threadul writer va putea acum s a blocheze obiectul pentru a scrie mesajul. Apoi el cheam a metoda Pulse() pentru a semnala rului reader c a a ap arut o schimbare de stare a obiectului indicat wde this.

296

CURS 12. ATRIBUTE. FIRE DE EXECUTIE

Curs 13 Nout a ti n C# 4.0


13.1 Parallel Linq

Parallel Linq (PLINQ) permite executarea de interog ari n mod paralel, pentru stitua tiile n care exist a un sistem de tip multioprocesor, multicore sau cu suport de hyperthreading. Pentru a transforma o interogare clasic a ntruna paralelizat a, trebuie ad augat a specicarea AsParallel la sursa de date peste care se execut a interogarea. Mai exact, plec and de la interogarea Linq: var result = from x in source where [conditie] select [ceva] se ob tine interogarea PLINQ: var result = from x in source.AsParallel() where [conditie] select [ceva] Echivalent, se poate face transformarea folosind metode Linq: //varianta var result //varianta var result neparalela = source.Where(x => [conditie]).Select(x => [ceva]); cu paralelism = source.AsParallel().Where(x => [conditie]).Select(x => [ceva]);

Prin ad augarea acestei metode AsParallel() se folosesc metode din clasa ParallelEnumerable, n timp ce n Linq-ul clasic se folosesc metode din clasa Enumerable. 297

298

TI CURS 13. NOUTA IN C# 4.0 Prezent am urm atorul exemplu preluat din [9]:

using using using using using using

System; System.Collections; System.Collections.Generic; System.Linq; System.Text; System.Diagnostics;

namespace TestPLINQ { class Program { static void Main() { Stopwatch sw = new Stopwatch(); sw.Start(); DoIt(); sw.Stop(); Console.WriteLine("Elapsed = " + sw.ElapsedMilliseconds.ToString()); } private static bool isPrime(int p) { int upperBound = (int)Math.Sqrt(p); for (int i = 2; i <= upperBound; i++) { if (p % i == 0) return false; } return true; } static void DoIt() { IEnumerable<int> arr = Enumerable.Range(2, 10000000); var q = from n in arr where isPrime(n) select n.ToString(); IList list = q.ToList();

13.2. PARAMETRI CU NUME S I PARAMETRI OPTIONALI Console.WriteLine(list.Count.ToString()); } } }

299

Pentru un sistem oarecare, timpul mediu de rulare este de aproximativ 17 secunde; rescriind interogarea pentru ob tinerea variabilei q astfel: var q = from n in arr.AsParallel() where isPrime(n) select n.ToString(); timpul mediu ob tinut este de aproximativ 10 secunde. Se pot congura aspecte precum gradul de paralelism, controlul ordinii, op tiuni de utilizare de buere, dac a anumite por tiuni s a se ruleze secven tial etc prin folosirea metodelor de extensie precum AsOrdered, AsUnordered sau prin folosirea enumer arii ParallelQueryMergeOptions.

13.2

Parametri cu nume si parametri op tionali

Parametrii op tionali permit precizarea unor valori implicite pentru parametrii unor metode; dac a ace stia nu sunt preciza ti la apel, atunci valorile trimise metodei sunt cele declarate implicit. Exemplu: class Program { static void Main(string[] args) { MyMethod(3); MyMethod(3, 4); } private static void MyMethod(int p, int q = 100) { Console.WriteLine("p= {0}, q={1}", p, q); } } De si avem o singur a metod a, aceasta suport a cele dou a apeluri. Se poate chiar s a avem mai mul ti de un parametru cu valoare implicit a:

300

TI CURS 13. NOUTA IN C# 4.0

private static void MyMethod(int p=1, int q = 100) { Console.WriteLine("p= {0}, q={1}", p, q); } In cazul n care avem m acar un astfel de parametru cu valoarea implicit a, acesta trebuie s a e prezent dup a parametrii cu valori obligatorii. Astfel, ncercarea de a scrie: private void MyMethod(int p=1, int q){...} duce la apari tia erorii de compilare: Optional parameters must appear after all required parameters Valorile furnizate pentru parametrii cu valori implicite trebuie s a e constante sau s a aibe valori de forma default(T). In ceea ce prive ste folosirea parametrilor cu nume, s a presupunem c a avem urm atoarea metod a: public void M(int x, int y = 5, int z = 7){...} Dac a vrem ca la un apel s a nu preciz am valoarea lui y, dar s a o preciz am pe a lui z, am tenta ti s a folosim: M(1, ,-1) ceea ce n cazul n care ar permis, ar duce la un cod greu de citit, n care abilitatea de num arare a virgulelor ar crucial a. In locul acestei variante, s-a introdus posibilitatea de a preciza parametrii prin nume: M(1, z:3); //sau M(x:1, z:3) //sau chiar: M(z:3, x:1) Ordinea de evaluare a expresiilor date pentru parametri este dat a de ordinea de precizare a numelor parametrilor, deci n ultimul exemplu expresia 3 este evaluat a nainte de expresia 1. Mai preciz am si c a parametrii op tionali si cu nume se pot folosi si pentru constructori sau indexatori.

13.3. TIPURI DE DATE DINAMICE

301

13.3

Tipuri de date dinamice

Tipurile dinamice permit tratarea unui obiect f ar a a crispa ti de provenien ta acestuia: obiect creat clasic, sau prin COM sau prin reectare. Unui astfel de obiect i s pot transmite mesaje (=apeluri de metode), iar legitimitatea acestor apeluri este vericat a la rulare. Altfel zis, pentru tipurile de date dinamice se renun ta la tipizarea static a specic a platformelor .NET de versiune anterioar a, dar cu riscul de a primi erori doar la rularea aplica tiei. Acest tip de date dinamic se declar a prin cuv antul cheie dynamic. Plec and de la un astfel de obiect, se pot apela diferite metode: dynamic x = MyMethod(); x.AnotherMethod(3); La rulare se veric a dac a tipul de date aferent variabilei x are metoda AnotherMethod care s a permit a apel cu un parametru de tip ntreg. Exemplu: static void Main(string[] args) { dynamic x = "abc"; x = 3; Console.WriteLine(x.CompareTo(10));//metoda CompareTo este din //tipul System.Int32 } Remarc am c a tipul unei variabile dinamice nu este setat odat a pentru totdeauna. Alt exemplu este: class ExampleClass { public ExampleClass() { } public ExampleClass(int v) { } public void exampleMethod1(int i) { } public void exampleMethod2(string str) { } } //.... static void Main(string[] args) { ExampleClass ec = new ExampleClass();

302

TI CURS 13. NOUTA IN C# 4.0 // The following line causes a compiler error if exampleMethod1 has only // one parameter. //ec.exampleMethod1(10, 4); dynamic dynamic_ec = new ExampleClass(); // The following line is not identified as an error by the // compiler, but it causes a run-time exception. dynamic_ec.exampleMethod1(10, 4); // The following calls also do not cause compiler errors, whether // appropriate methods exist or not. dynamic_ec.someMethod("some argument", 7, null); dynamic_ec.nonexistentMethod();

} Se pot declara ca ind dinamici si parametrii unei metode: public static void Log(dynamic x) { Console.WriteLine(x.ToString()); } static void Main(string[] args) { var x = 3; string y = "abc"; Log(x); Log(y); }

13.4

COM Interop

COM Interop este o facilitate existent a n versiuni mai vechi ale lui .NET framework care permite codului managed s a apeleze cod unmanaged de tip Component Object Model, cum ar aplica tiile Word sau Excel. De exemplu, pentru utilizarea unei celule de Excel, varianta veche de cod ce trebuia scris a era: ((Excel.Range)excel.Cells[1, 1]).Value2 = "Hello";

13.4. COM INTEROP pe c and n .NET 4 a se scrie mai inteligibil astfel: excel.Cells[1, 1].Value = "Hello"; sau n loc de Excel.Range range = (Excel.Range)excel.Cells[1, 1]; se scrie: Excel.Range range = excel.Cells[1, 1]; Exemplu: using using using using using System; System.Collections.Generic; System.Linq; System.Text; Excel = Microsoft.Office.Interop.Excel;

303

namespace TestOffice { class Program { static void Main(string[] args) { var excel = new Excel.Application(); // Make the object visible. excel.Visible = true;

// Create a new, empty workbook and add it to the collection returned // by property Workbooks. The new workbook becomes the active workbook. // Add has an optional parameter for specifying a praticular template. // Because no argument is sent in this example, Add creates a new workbo excel.Workbooks.Add(); excel.Cells[1, 1].Value = "Hello"; var processes = Process.GetProcesses() .OrderByDescending(p => p.WorkingSet64) .Take(10); int i = 2; foreach (var p in processes)

304 {

TI CURS 13. NOUTA IN C# 4.0

excel.Cells[i, 1].Value = p.ProcessName; // no casts excel.Cells[i, 2].Value = p.WorkingSet64; // no casts i++; }

Excel.Range range = excel.Cells[1, 1]; // no casts dynamic chart = excel.ActiveWorkbook.Charts. Add(After: excel.ActiveSheet); // named and optional chart.ChartWizard( Source: range.CurrentRegion, Title: "Memory Usage in " + Environment.MachineName); //named chart.ChartStyle = 45; chart.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlBitmap, Excel.XlPictureAppearance.xlScreen); } } }

13.5

Covarian ta si contravarian ta

S a presupunem c a avem secven ta de cod: var strList = new List<string>(); List<object> objList = strList; Linia a doua, dac a ar permis a, ar predispune la erori, deoarece s-ar permite mai departe: objList.Add(new MyClass()); deci sar nc alca punctul de plecare pentru colec tie: elementele ar trebui s a e toate de tip String. Ca atare, acest lucru nu ar avea sens s a e permis. Incep and cu .NET 4, se permite ns a conversia c atre interfe te generice sau delega ti generici pentru care tipul generic este mai general dec at argumentul generic dinspre care se face conversie. De exemplu, se poate scrie: IEnumerable<object> myCollection = strList; Aceasta este covarian ta si se ob tine prin declara tiile:

S 13.5. COVARIANT A I CONTRAVARIANT A public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); } public interface IEnumerator<out T> : IEnumerator { bool MoveNext(); T Current { get; } }

305

unde cuv antul out are un alt sens dec at la transmiterea de parametri: n C# 4.0, n acest context, semnic a faptul c a tipul T poate s a apar a doar n pozi tia de ie sire a unei metode din interfa ta . Interfa ta IEnumerable devine astfel covariant a n T si se poate face conversie de la IEnumerable<B> la IEnumerable<A> pentru tipul B derivat din A. Acest lucru este util pentru o situa tie de genul: var result = strings.Union(objects); unde se face reuniune ntre o cole tie de stringuri si una de obiecte. Contravarian ta permite conversii n sens invers ca la covarian ta . De exemplu, prin declara tia: public interface IComparer<in T> { public int Compare(T left, T right); } prin arat a contravarian ta a tipului T, deci se poate face ca un IComparer<object> s a e considerat drept un IComparer<String>. Are sens, deoarece dac a un IComparer poate s a compare orice dou a obiecte, atunci cu siguran ta poate s a compare si doua string-uri.

306

TI CURS 13. NOUTA IN C# 4.0

Curs 14 Fluxuri
Pentru aplica tiile reale, datele care se prelucreaz a nu se mai preiau din tastatur a, ci se acceseaz a din siere de date. C#, precum alte limbaje anterioare, pune la dispozi tie o abstractizare numit a ux 1 care permite manipularea datelor, indiferent de sursa acestora. Intrun astfel de ux, pachete de date urmeaz a unele dup a altele. In C# sierele si directoarele se manipuleaz a prin intermediul unor clase predenite. Acestea permit crearea, redenumirea, manipularea, stergerea sierelor si a directoarelor. Manipularea con tinutului sierelor se face prin intermediul stream-urilor cu sau f ar a buer; de asemenea exist a un suport puternic pentru streamuri asincrone (prelucrarea unui sier se face de c atre un r de execu tie creat automat). Datorit a abstractiz arii, nu exist a diferen te mari ntre lucrul cu siere aate pe discul local si datele aate pe re tea; ca atare se va vorbi despre uxuri bazate pe protocoale TCP/IP sau web. In sf ar sit, serializarea este legat a de uxuri, ntruc at permit nge tarea unui obiect (care poate urmat a de transportul lui pe re tea).

14.1

Sistemul de siere

Clasele care se folosesc pentru manipularea sierelor si a directoarelor se a a n spa tiul de nume System.IO. Func tionalitatea lor este aceea si cu a comenzilor disponibile ntrun sistem de operare: creare, stegere, redenumire, mutare de siere sau directoare, listarea con tinutului unui director, listarea atributelor sau a diferi tilor timpi pentru siere sau directoare, etc. Clasele pe care vom folosi sunt: Directory, DirectoryInfo, File, FileInfo.
1

Engl: Stream

307

308

CURS 14. FLUXURI

14.1.1

Lucrul cu directoarele: clasele Directory si DirectoryInfo

Clasa Directory con tine metode statice pentru crearea, mutarea, explorarea directoarelor. Clasa DirectoryInfo con tine doar membri nestatici si permite aarea diferitelor informa tii pentru un director anume. Tabelul 14.1 con tine principalele metode ale clasei Directory, iar tabelul 14.2 con tine metodele notabile din clasa DirectoryInfo. Tabelul 14.1: Metode ale clasei Directory. Metoda CreateDirectory() Explica tie Creeaz a directoarele si subdirectoarele specicate prin parametru Delete() S terge un director si con tinutul s au Exists() Returneaz a true dac a stringul specicat reprezint a numele unui director existent GetCreationTime() Returneaz a / seteaz a data si timpul SetCreationTime() cre arii unui director GetCurrentDirectory() returneaz a / seteaz a directorul curent SetCurrentDirectory() GetDirectories() Returneaz a un sir de nume de subdirectoare GetDirectoryRoot() Returneaz a numele r ad acinii unui director specicat GetFiles() Returneaz a un sir de stringuri care con tine numele sierelor din directorul specicat GetLastAccesTime() returneaz a / seteaz a timpul ultimului acces SetLastAccesTime() pentru un director GetLastWriteTime() Returneaz a / seteaz a timpul c and directorul SetLastWriteTime() specicat a fost ultima oar a modicat GetLogicalDrives() Returneaz a numele tuturor unit a tilor logice sub forma drive:\ GetParent() Returneaz a directorul curent pentru calea specicat a Move() Mut a un director (cu con tinut) ntro cale specicat a Tabelul 14.2: Metode ale clasei DirectoryInfo. Metoda sau proprietate Attributes CreationTime Explica tie Returneaz a sau seteaz a atributele sierului curent Returneaz a sau seteaz a timpul de creare al sierului curent

14.1. SISTEMUL DE FI SIERE

309 Tabelul 14.2 (continuare)

Metoda sau proprietate Exists Extension FullName LastAccessTime LastWriteTime Parent Root Create() CreateSubdirectory() Delete() GetDirectories() GetFiles() MoveTo()

Explica tie true dac a directorul exist a Extensia directorului Returneaz a calea absolut a a sierului sau a directorului Returneaz a sau seteaz a timpul ultimului acces Returneaz a sau seteaz a timpul ultimei scrieri Directorul p arinte al directorului specicat R ad acina c aii corespunz atoare Creeaz a un director Creeaz a un subdirector n calea specicatu a S terge un DirectoryInfo si con tinutul s au Returneaz a un vector de tip DirectoryInfo cu subdirectoare Returneaz a lista sierelor din director Mut a un DirectoryInfo si con tinutul s au ntrun nou loc

Exemplul urm ator folose ste clasa DirectoryInfo pentru a realiza explorarea recursiv a a unui director cu enumerarea tuturor subdirectoarelor con tinute. Se creeaz a un obiect de tipul pomenit pe baza numelui de director de la care se va ncepe explorarea. O metod a va a sa numele directorului la care sa ajuns, dup a care se apeleaz a recursiv metoda pentru ecare subdirector (ob tinut via GetDirectories() ). using System; using System.IO; class Tester { public static void Main( ) { Tester t = new Tester( ); //choose the initial subdirectory string theDirectory = @c:\WinNT; // call the method to explore the directory, // displaying its access date and all // subdirectories DirectoryInfo dir = new DirectoryInfo(theDirectory); t.ExploreDirectory(dir);

310

CURS 14. FLUXURI // completed. print the statistics Console.WriteLine(\n\n{0} directories found.\n, dirCounter);

} // Set it running with a directoryInfo object // for each directory it finds, it will call // itself recursively private void ExploreDirectory(DirectoryInfo dir) { indentLevel++; // push a directory level // create indentation for subdirectories for (int i = 0; i < indentLevel; i++) Console.Write(" "); // two spaces per level // print the directory and the time last accessed Console.WriteLine("[{0}] {1} [{2}]\n", indentLevel, dir.Name, dir.LastAccessTime); // get all the directories in the current directory // and call this method recursively on each DirectoryInfo[] directories = dir.GetDirectories( ); foreach (DirectoryInfo newDir in directories) { dirCounter++; // increment the counter ExploreDirectory(newDir); } indentLevel--; // pop a directory level } // static member variables to keep track of totals // and indentation level static int dirCounter = 1; static int indentLevel = -1; // so first push = 0 }

14.1.2

Lucrul cu sierele: clasele FileInfo si File

Un obiect DirectoryInfo poate de asemenea s a returneze o colec tie a tuturor sierelor con tinute, sub forma unor obiecte de tip FileInfo. Inrudit a cu clasa FileInfo (care are membri nestatici) este clasa File (care are doar membri statici). Tabelele 14.3 si 14.4 con tin metodele pentru ecare clas a:

14.1. SISTEMUL DE FI SIERE Tabelul 14.3: Metode ale clasei File. Metoda AppendText()

311

Explica tie Creeaz a un obiect StreamWriter care adaug a text la sierul specicat Copy() Copiaz a un sier existent ntrun alt sier Create() Creeaz a un sier n calea specicat a CreateText() Creeaz a un StreamWriter care scrie un nou sier text Delete() S terge sierul specicat Exists() Returneaz a true dac a sierul corespunz ator exist a GetAttributes() Returneaz a / seteaz a FileAttributes pentru sierul SetAttributes() specicat GetCreationTime() Returneaz a / seteaz a data si timpul cre arii pentru sierul SetCreationTime() specicat GetLastAccessTime() Returneaz a sau seteaz a timpul ultimului acces la sier SetLastAccessFile() GetLastWriteTime() Returneaz a / seteaz a timpul ultimei modic ari a sierului SetLastAccessTime() Move() Mut a un sier la o nou a loca tie; poate folosit pentru redenumire OpenRead() Metod a return and un FileStream pe un sier OpenWrite() Creaz a un Stream de citire / scriere Tabelul 14.4: Metode ale clasei FileInfo. Metoda Attibutes CreationTime Directory Exists Extension FullName LastAccessTime LastWriteTime Length Name AppendText Explica tie Returneaz a sau seteaz a atributele sierului curent Returneaz a sau seteaz a timpul de creare al sierului curent Returneaz a o instan ta a directorului curent true dac a sierul exist a Returneaz a extensia sierului Calea absolut a p an a la sierul curent Returneaz a sau seteaz a timpul ultimului acces Returneaz a sau seteaz a timpul c and sa modicat ultima oar a sierul curent Returneaz a dimensiunea sierului Returneaz a numele instan tei curente Creaz a un StreamWriter care va permite ad augarea

312

CURS 14. FLUXURI Tabelul 14.4 (continuare)

Metoda sau proprietatea CopyTo() Create() Delete() MoveTo() OpenRead() OpenText() OpenWrite()

Explica tie la sier Copieaz a sierul curent ntrun alt sier Creaz a un nou sier S terge un sier Mut a un sier la o loca tie specicat a; poate folosit a pentru redenumire Creaz a un sier readonly respectiv StreamReader(text) sau FileStream (readwrite)

Exemplul anterior este modicat pentru a a sa informa tii despre siere: numele, dimensiunea, data ultimei modic ari: using System; using System.IO; class Tester { public static void Main( ) { Tester t = new Tester( ); // choose the initial subdirectory string theDirectory = @c:\WinNT; // call the method to explore the directory, // displaying its access date and all // subdirectories DirectoryInfo dir = new DirectoryInfo(theDirectory); t.ExploreDirectory(dir); // completed. print the statistics Console.WriteLine(\n\n{0} files in {1} directories found.\n, fileCounter,dirCounter); } // Set it running with a directoryInfo object // for each directory it finds, it will call // itself recursively private void ExploreDirectory(DirectoryInfo dir) { indentLevel++; // push a directory level // create indentation for subdirectories for (int i = 0; i < indentLevel; i++)

14.1. SISTEMUL DE FI SIERE

313

Console.Write( ); // two spaces per level // print the directory and the time last accessed Console.WriteLine([{0}] {1} [{2}]\n, indentLevel, dir.Name, dir.LastAccessTime); // get all the files in the directory and // print their name, last access time, and size FileInfo[] filesInDir = dir.GetFiles( ); foreach (FileInfo file in filesInDir) { // indent once extra to put files // under their directory for (int i = 0; i < indentLevel+1; i++) Console.Write( ); // two spaces per level Console.WriteLine({0} [{1}] Size: {2} bytes, file.Name, file.LastWriteTime file.Length); fileCounter++; } // get all the directories in the current directory // and call this method recursively on each DirectoryInfo[] directories = dir.GetDirectories( ); foreach (DirectoryInfo newDir in directories) { dirCounter++; // increment the counter ExploreDirectory(newDir); } indentLevel--; // pop a directory level } // static member variables to keep track of totals // and indentation level static int dirCounter = 1; static int indentLevel = -1; // so first push = 0 static int fileCounter = 0; } Exemplul urm ator nu introduce clase noi, ci doar exemplic a crearea unui director, copierea de siere n el, stergerea unora dinre ele si n nal stergerea directorului: using System; using System.IO; class Tester {

314

CURS 14. FLUXURI

public static void Main( ) { // make an instance and run it Tester t = new Tester( ); string theDirectory = @c:\test\media; DirectoryInfo dir = new DirectoryInfo(theDirectory); t.ExploreDirectory(dir); } // Set it running with a directory name private void ExploreDirectory(DirectoryInfo dir) { // make a new subdirectory string newDirectory = newTest; DirectoryInfo newSubDir = dir.CreateSubdirectory(newDirectory); / get all the files in the directory and // copy them to the new directory FileInfo[] filesInDir = dir.GetFiles( ); foreach (FileInfo file in filesInDir) { string fullName = newSubDir.FullName + \\ + file.Name; file.CopyTo(fullName); Console.WriteLine({0} copied to newTest, file.FullName); } // get a collection of the files copied in filesInDir = newSubDir.GetFiles( ); // delete some and rename others int counter = 0; foreach (FileInfo file in filesInDir) { string fullName = file.FullName; if (counter++ %2 == 0) { file.MoveTo(fullName + .bak); Console.WriteLine({0} renamed to {1}, fullName,file.FullName); } else { file.Delete( ); Console.WriteLine({0} deleted.,

14.2. CITIREA S I SCRIEREA DATELOR fullName); } } newSubDir.Delete(true); // delete the subdirectory } }

315

14.2

Citirea si scrierea datelor


Tabelul 14.5: Clase pentru lucrul cu streamuri

Clasele disponibile pentru lucrul cu streamuri sunt:

Clasa Stream BinaryReader

Descriere Manipulare generic a a unei secven te de octe ti; clas a abstract a Cite ste tipuri de date primitive ca voalori binare ntro codicare specic a BinaryWriter Scrie tipuri de date primitive ntrun ux binar; de asemenea scrie stringuri folosind o anumit a codicare BueredStream Ata seaz a un buer unui stream de intrare / ie sire. Clas a sealed FileStream Ata seaz a un stream unui sier, permi ta nd opera tii sincrone sau asincrone de citire si scriere. MemoryStream Creaz a un stream pentru care citirea / stocarea de date se face n memorie NetworkStream Creeaz a un stream folosind TCP/IP TextReader Permite citire de caractere, n mod secven tial. Clas a abstract a. TextWriter Permite scriere secven tial a ntrun sier. Clas a abstract a. StreamReader Implementeaz a o clas a TextReader care cite ste caractere dintrun stream folosind o codicare particular a StreamWriter Implementeaz a o clas a TextWriter care scrie caractere ntrun stream folosind o codicare particular a StringReader Implementeaz a un TextReader care cite ste dintrun string StringWriter Scrie informa tie ntrun string. Informa tia este stocat a ntrun StringBuilder

14.2.1

Clasa Stream

Clasa Stream este o clas a abstract a din care se deriveaz a toate celelalte clase de lucru cu streamuri. Metodele con tinute permit citire de octe ti,

316

CURS 14. FLUXURI

nchidere, golire de buer, etc. Pe l ang a acestea, clasa Stream permite at at opera tii sincrone, c at si asincrone. Intro opera tie de intrare / ie sire sincron a, dac a se ncepe o opera tie de citire sau scriere dintrun / ntrun ux, atunci programul va trebui s a a stepte p an a c and aceast a opera tie se termin a. Sub platforma .NET se poate face ns a op opera tie de intrare / ie sire n mod asincron, astfel permi ta nd altor re de execu tie s a se execute. Metodele folosite pentru a n cepe o astfel de intrare asincron a sunt: BeginRead(), BeginWrite(), EndRead(), EndWrite(). O dat a ce o astfel de opera tie se termin a, o metod a callback se va executa automat. O list a a metodelor si propriet a tilor care sunt denite n clasa Stream este dat a n tabelul 14.6. Tabelul 14.6: Metode si propriet a ti ale clasei Stream Clasa Descriere Incepe o citire asincron a BeginRead() a BeginWrite() Incepe o scriere asincron Inchide streamul curent si elibereaz a resursele asociate cu el Close() (socketuri, le handles, etc) EndRead() A steapt a pentru o terminare de citire asincron a. EndWrite() A steapt a pentru o terminare de scriere asincron a. Flush() C and este suprascris a n tro clas a derivat a, gole ste buerele asociate straemului curent si determin a scrierea lor. Read() C and este suprascris a ntro clas a derivat a, cite ste o secven ta de octe ti si incrementeaz a indicatorul de pozi tie curent a n stream ReadByte() Cite ste un byte din stream si incrementeaz a indicatorul de pozi tie curent; dac a este la sf ar sit de sier, returneaz a -1 Seek() C and este suprascris a ntro clas a derivat a, seteaz a pozi tia curent a n interiorul streamului SetLength() C and este suprascris a ntro clas a derivat a, seteaz a lungimea streamului curent Write() C and este suprascris a ntro clas a derivat a, scrie o secven ta de octe ti n streamul curent si incrementeaz a corespunz ator indicatorul pozi tiei curente n stream WriteByte() Scrie un byte la pozi tia curent a din stream si incrementeaz a indicatorul de pozi tie curent a CanRead() C and este suprascris a ntro clas a derivat a, returneaz a o valoarea care indic a dac a streamul curent poate citit CanWrite() C and este suprascris a ntro clas a derivat a, returneaz a

14.2. CITIREA S I SCRIEREA DATELOR

317 Tabelul 14.6 (continuare)

Metoda CanSeek

Length Position

Descriere o valoarea care indic a dac a streamul curent suport a scriere C and este suprascris a ntro clas a derivat a, returneaz ao valoarea care indic a dac a se poate face pozi tionare aleatoare n streamul curent C and este suprascris a ntro clas a derivat a, returneaz a dimensiunea n octe ti a sierului C and este suprascris a ntro clas a derivat a, returneaz a sau seteaz a pozi tia curent a n interiorul streamului

14.2.2

Clasa FileStream

Exist a mai multe metode de ob tinere a unui obiect de tip FileStream. Clasa prezint a nou a constructori supra nc arca ti. Enumerarea FileMode este folosit a pentru a se specica modul de deschidere a unui stream: (Append, Create, CreateNew, Open, OpenOrCreate, Truncate ). Exemplu: mai jos se creeaz a un sier nou (dac a nu exist a) sau se suprascrie unul existent: FileStream f = new FileStream( @C:\temp\a.dat, FileMode.Create ); De asemenea se mai poate ob tine o instan ta a clasei FileStream din clasa File : FileStream g = File.OpenWrite(@c:\temp\test.dat); //deschidere doar pentru citire Asem an ator, se poate folosi o instan ta a clasei FileInfo : FileInfo fi = new FileInfo(@c:\temp\test.dat); FileStream fs = fi.OpenRead(); //deschidere doar pentru citire

14.2.3

Clasa MemoryStream

Un MemoryStream si ia datele din memorie, v azut a ca un vector de octe ti. Exist a sapte constructori pentru aceast a clas a, dar care pot grupa ti n dou a categorii. Primul tip de constructor preia un array de octe ti pentru care poate face citire si op tional scriere. Caracteristic este c a tabloul nu va putea redimensionat:

318 byte[] b = {1, 2, 3, 4}; MemoryStream mem = new MemoryStream(b);

CURS 14. FLUXURI

O alt a variant a de constructor nu prime ste un vector de octe ti, dar va putea scrie ntrun tablou redimensionabil. Op tional, se specic a un int ca parametru al constructorului care determin a dimensiunea ini tial a a tabloului de octe ti. Datele sunt ad augate folosind metoda Write() : using System; using System.IO; public class MemTest { public static void Main() { MemoryStream mem = new MemoryStream(); byte[] bs = {1, 2, 3, 4, 5, 6}; mem.Write(bs, 0, bs.Length); mem.Seek(3, SeekOrigin.Begin); byte b = (byte)mem.ReadByte(); Console.WriteLine(Value: {0}, b.ToString()); //se va afisa 4 } } E de preferat s a se utilizeze obiecte de tip MemoryStream n scopul de a accesa informa tia din memoria RAM, mai degrab a dec at de pe disc sau din re tea. De exemplu se poate nc arca un sier de pe disc n memorie, astfel nc at analiza lui se poate face mai repede.

14.2.4

Clasa BueredStream

C# pune la dispozi tie o clas a BueredStream pentru a asigura o zon a tampon n cazul opera tiilor de intrareie sire. Constructorul acestei clase prime ste o instan ta a clasei Stream. Exemplu: FileStream fs = new FileStream(@c:\temp\a.dat, FileMode.Open); BufferedStream bs = new BufferedStream(fs); De men tionat aici metoda Flush(), care for teaz a golirea buerului asociat streamului.

14.2. CITIREA S I SCRIEREA DATELOR

319

14.2.5

Clasele BinaryReader si BinaryWriter

Cele dou a clase sunt folosite pentru a accesa date mai complexe dec at un byte: de exemplu, pentru manipularea datelor de tip boolean, sau Decimal, sau int cu semn pe 64 de bi ti. Tabelul 14.7 con tine metodele puse la dispozi tie de c atre clasa BinaryWriter : Tabelul 14.7: Metodele clasei BinaryReader Metoda PeekChar() Read() ReadBoolean() ReadBytes() ReadChar() ReadChars() Descriere Returneaz a urm atorul caracter disponibil, f ar a a avansa indicatorul de pozi tie curent a Cite ste caractere din ux si avanseaz a pozi tia curent a din acel stream Cite ste un Boolean din stream si avanseaz a pozi tia curent a cu un byte Cite ste un num ar precizat de octe ti ntrun vector si avanseaz a pozi tia curent a Cite ste urm atorul caracter si avanseaz a pozi tia curent a corespunz ator cu codicarea folosit a pentru caracter Cite ste mai multe caractere ntrun vector si avanseaz a pozi tia curent a cu num arul de caractere dimensiunea de reprezentare pentru caracter Cite ste un decimal si avanseaz a pozi tia curent a cu 16 octe ti Cite ste o variabil a n virgul a mobil a si avanseaz a cu 8 octe ti Cite ste un ntreg cu semn pe 16 bi ti si avanseaz a cu 2 octe ti Cite ste un ntreg cu semn pe 32 de bi ti si avanseaz a cu 4 octe ti Cite ste un ntreg cu semn pe 64 de bi ti si avanseaz a cu 8 octe ti Cite ste un byte cu semn si avanseaz a cu un byte Cite ste o valoare n virgul a mobil a pe 4 octe ti si avanseaz a pozi tia curent a cu 4 octe ti Cite ste un string, prexat cu lungimea sa, codicat a ca un ntreg reprezentat pe grupe de c&ate 7 bi ti (MSDN) Cite ste un ntreg f ar a semn reprezentat pe 16 bi ti si avanseaz a cu 2 octe ti Cite ste un ntreg f ar a semn reprezentat pe 32 de bi ti si avanseaz a cu 4 octe ti Cite ste un ntreg f ar a semn reprezentat pe 64 de bi ti si avanseaz a cu 8 octe ti

readDecimal() ReadDouble() ReadInt16() ReadInt32() ReadInt64() ReadSByte() ReadSingle() ReadString() ReadUInt16 ReadUInt32 ReadUInt64

320

CURS 14. FLUXURI

Clasa BinaryWriter are o metod a Write() supra nc arcat a, care poate apelat a pentru scrierea diferitelor tipuri de date. O men tiune la scrierea de stringuri si de caractere / v ectori de caractere: caracterele pot codicate n mai multe moduri (ex: ASCII, UNICODE, UTF7, UTF8), codicare care se poate transmite ca argument pentru constructor. A se vedea MSDN si RFCurile de pe Interner.

14.2.6

Clasele TextReader, TextWriter si descendentele lor

Pentru manipularea sirurilor de caractere aate n siere, dar nu numai, C# pune la dispozi tie clasele abstracte TextReader, TextWriter. Clasa TextReader are subclasele neabstracte StreamReader si StringReader. Clasa TextWriter are subclasele neabstracte StreamWriter, StringWriter, System.Web.HttpWriter, System.Web.UI.HtmlTextWriter, System.CodeDom.Compiler.IndentedTextWriter. Descrieri si exemple sunt date mai jos: 1. Clasele StreamReader si StreamWriter - sunt folosite pentru a cit sau scrie siruri de caractere. Un obiect de tip StreamReader se poate ob tine via un constructor: StreamReader sr = new StreamReader(@C:\temp\siruri.txt); sau pe baza unui obeiect de tip FileInfo : FileInfo fi = new FileInfo(@c:\temp\siruri.txt); StreamReader sr = fi.OpenText(); Obiectele de tip StreamReader pot citi c ate o linie la un moment dat folosind metoda ReadLine(). Exemplu: using System; using System.IO; class Test { static void Main() { StreamReader sr = new StreamReader(c:\temp\siruri.txt); String line; do

14.2. CITIREA S I SCRIEREA DATELOR {

321

line = sr.ReadLine(); Console.WriteLine(line); //daca line==null, atunci se va afisa o linie vida }while( line!=null); } } 2. Clasele StringReader si StringWriter - permit ata sarea unor uxuri la siruri de caractere, folosite pe post de surse de date. Exemplu: string myString = 1234567890; StringReader sr = new StringReader(myString); using System; using System.IO; using System.Xml; class Test { static void Main() { XmlDocument doc = new XmlDocument(); String entry = <book genre=biography + ISBN=12345678><title>Yeager</title> + </book>; doc.LoadXml(entry);//salvare in doc. xml StringWriter writer = new StringWriter(); doc.Save(writer); string strXml = writer.ToString(); Console.WriteLine(strXml); } } va a sa: <?xml version=1.0 encoding=utf-16> <book genre=biography ISBN=12345678> <title>Yeager</title> </book>

322

CURS 14. FLUXURI In loc ca salvarea din documentul xml s a se fac a ntrun sier text, se face ntrun obiect de tip StringWriter(), al c arui con tinut se va a sa.

3. IndentedTextWriter dene ste metode care insereaz a taburi si p astreaz a eviden ta niveluilui de identare. Este folosit de deriuv ari ale claseiCodeDom, folosit a pentru generare de cod. 4. HtmlTextWriter scrie o secven ta HTML pe o pagin a Web. Este folosit de exemplu n script-uri C#, n cazul aplica tiilor ASP.NET 5. HttpWriter - prea pu tin a informa tie!!!

14.3

Operare sincron a si asincron a

Exemplele folosite p an a acum au folosit un mod de operare sincron, adic a atunci c and se face o opera tie de intrare/ie sire, ntregul program este blocat p an a c and se tranziteaz a toate datele specicate. Streamurile C# permit si acces asincron, permi ta nd altor re de execu tie s a e rulate. Pentru semnalarea nceputului unei opera tii de citire sau scriere asincrone, se folosesc BeginRead() si BeginWrite(). Metoda BeginRead are prototipul: public override IAsyncResult BeginRead( byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject ); Vectorul de bytes reprezint a buerul n care se vor citi datele; al doilea si al treilea parametru determin a byteul la care s a se va scrie, respectiv num arul maxim de octe ti care se vor citi. Al patrulea parametru este un delegat, folosit pentru a semnala (mai exact, a face ni ste prelucr ari) sf ar situl citirii. Se poate transmite null pentru acest parametru, dar programul nu va noticat de terminarea citirii. Ultimul parametru este un obiect care va folosit pentru a distinge ntre aceast a cerere de citire asincron a si altele. using System; using System.IO; using System.Threading; using System.Text; public class AsynchIOTester { private Stream inputStream;

S 14.3. OPERARE SINCRONA I ASINCRONA // delegated method private AsyncCallback myCallBack; // buffer to hold the read data private byte[] buffer; // the size of the buffer const int BufferSize = 256; // constructor AsynchIOTester( ) { // open the input stream inputStream = File.OpenRead( @"C:\test\source\AskTim.txt"); // allocate a buffer buffer = new byte[BufferSize]; // assign the call back myCallBack = new AsyncCallback(this.OnCompletedRead); } public static void Main( ) { // create an instance of AsynchIOTester // which invokes the constructor AsynchIOTester theApp = new AsynchIOTester( ); // call the instance method theApp.Run( ); } void Run( ) { inputStream.BeginRead( buffer, // holds the results 0, // offset buffer.Length, // (BufferSize) myCallBack, // call back delegate null); // local state object // do some work while data is read for (long i = 0; i < 500000; i++) { if (i%1000 == 0) {

323

324

CURS 14. FLUXURI

Console.WriteLine("i: {0}", i); } } } // call back method void OnCompletedRead(IAsyncResult asyncResult) { int bytesRead = inputStream.EndRead(asyncResult); // if we got bytes, make them a string // and display them, then start up again. // Otherwise, were done. if (bytesRead > 0) { String s = Encoding.ASCII.GetString(buffer, 0, bytesRead); Console.WriteLine(s); inputStream.BeginRead( buffer, 0, buffer.Length, myCallBack, null); } } } Ie sirea ar : i: 47000 i: 48000 i: 49000 Date: January 2001 From: Dave Heisler To: Ask Tim Subject: Questions About OReilly Dear Tim, Ive been a programmer for about ten years. I had heard of OReilly books,then... Dave, You might be amazed at how many requests for help with school projects I get; i: 50000 i: 51000 i: 52000 Cele dou a re de execu ctie lucreaz a deci concurent.

14.4. STREAMURI WEB

325

14.4

Streamuri Web

C# con tine clase g andite pentru a u sura interoperarea cu webul. Aducerea informa tiei de pe web se face n doi pa si: primul pas const a n a face o cerere de conectare la un server; dac a cererea se poate face, atunci n pasul al doilea const a n ob tinerea informa tiei propriuzise de la server. Cei doi pa si se fac respectiv cu clasele HttpWebRequest, respectiv HttpWebResponse Un obiect HttpWebRequest poate ob tinut prin metoda static a Create() din clasa WebRequest : string page = http://www.cetus-links.org/index.html; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(page); Pe baza obiectului HttpWebRequest ob tinut se va crea un obiect HttpWebResponse : HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse(); In nal, se ob tine un stream prin metoda GetResponseStream() : StreamReader streamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.ASCII);

14.5

Serializarea

Serializarea reprezint a posibilitatea de a trimite un obiect printrun stream. Pentru aceasta, C# folose ste metodele Serialize() si Deserialize() ale clasei BinaryFormatter. Metoda Serialize() are nevoie de 2 parametri: un stream n care s a scrie si obiectul pe care s a l serializeze. Metoda de deserializare cere doar un stream din care s a citeasc a, si din care s a refac a un object (care poate convertit la tipul corespunz ator).

14.5.1

Crearea unui obiect serializabil

Pentru ca o clas a denit a de utilizator s a suport serializarea, este nevoie de a preciza atributul [Serializable] n fa ta declara tiei clasei respective, atribut denit n clasa System.SerializableAttribute. Tipurile primitive sunt automat serializabile, iar dac a tipul denit de utilizator con tine alte tipuri, atunci acestea la r andul lor trebuie s a poat a serializate. Exemplu:

326 using System; [Serializable] public class BookRecord { public String title; public int asin; public BookRecord(String title, int asin) { this.title = title; this.asin = asin; } }

CURS 14. FLUXURI

14.5.2

Serializarea

Codul pentru serializarea unui obiect de tipul declarat mai sus este: using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO; public class SerializeObject { public static void Main() { BookRecord book = new BookRecord( "Building Robots with Lego Mindstorms", 1928994679); FileStream stream = new FileStream(@"book.obj", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(stream, book); stream.Close(); } }

14.5.3

Deserializarea unui obiect

Deserializarea se face astfel: using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO;

14.5. SERIALIZAREA public class DeserializeObject { public static void Main() { FileStream streamIn = new FileStream( @"book.obj", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); BookRecord book = (BookRecord)bf.Deserialize(streamIn); streamIn.Close(); Console.Write(book.title + " " + book.asin); } }

327

14.5.4

Date tranziente

Uneori este nevoie ca anumite c ampuri ale unui obiect s a nu e salvate: parola unui utilizator, num arul de cont al unui client, etc. Acest lucru se face specic and atributul [NonSerialized] pentru c ampul respectiv: [NonSerialized] int secretKey;

14.5.5

Opera tii la deserializare

Uneori este nevoie ca o deserializare s a e automat urmat a de o anumit a opera tie. De exemplu, un c amp care nu a fost salvat (tranzient) va trebui s a e ref acut n mod calculat. Pentru acest lucru, C# pune la dispozi tie interfa ta IDeserializationCallback, pentru care trebuie s a se implementeze metoda OnDeserialization. Aceast a metod a va apelat a automat de c atre CLR atunci c and se va face deserializarea obiectului. Exemplul de mai jos ilustreaz a acest mecanism: using System; using System.Runtime.Serialization; [Serializable] public class BookRecord: IDeserializationCallback { public String title; public int asin; [NonSerialized] public int sales_rank; public BookRecord(String title, int asin) {

328 this.title = title; this.asin = asin; sales_rank = GetSalesRank(); } public int GetSalesRank() { Random r = new Random(); return r.Next(5000); } public void OnDeserialization(Object o) { sales_rank = GetSalesRank(); } }

CURS 14. FLUXURI

Mecanismul poate folosit n special atunci c and serializarea unui anumit c amp, care ar duce la mult spa tiu de stocare consumat, ar putea calculat n mod automat la deserializare (spa tiu vs. procesor).

Bibliograe
[1] C# in depth, Manning, Jon Skeet, 2008 [2] Programming C#, OReilly, Jesse Liberty, 4th edition, 2005 [3] Microsoft C# 2005 Step by Step, John Sharp, Microsoft Press, 2005 [4] Core C# and .NET, Stephen C. Perry, Prentice Hall, 2005 [5] Pro ASP.NET in C# 2005, Mathew MacDonald and Mario Szpuszta, Apress, 2005 [6] C# Language Specication, ECMA TC39/TG2, Octombrie 2002 [7] Professional ADO.NET Programming, Wrox, 2001 [8] LINQ for Visual C# 2008, Fabio Claudio Ferracchiati, Apress, 2008 [9] PLINQ, Daniel Moth, 2009.

329

S-ar putea să vă placă și