Sunteți pe pagina 1din 165

Cuprins

3. Dezvoltare de aplicaii .NET...................................................................................3 Introducerea n .NET Framework.............................................................................3 Caracterizarea platformei .NET............................................................................3 Date........................................................................................................................5 Tipuri de date.......................................................................................................5 Tipuri de date predefinite.....................................................................................7 Variabile n C#.....................................................................................................8 Constante...........................................................................................................10 Utilizarea enumerrilor.......................................................................................14 Tablouri unidimensionale...................................................................................17 Tablouri bidimensionale i multidimensionale....................................................19 Dirijarea execuiei (instruciuni)............................................................................25 Clasificarea instruciunilor..................................................................................25 Instruciuni de salt..............................................................................................26 Instruciuni de selecie.......................................................................................26 Instruciuni de iterare.........................................................................................29 Tipuri de date create de utilizator (Clase i structuri)...........................................33 Descrierea claselor.............................................................................................34 Membri statici i nestatici...................................................................................37 Transmiterea de parametri................................................................................39 Constructori i destructori..................................................................................42 Proprieti..........................................................................................................45 Indexatori...........................................................................................................46 Constructori statici.............................................................................................47 Cmpuri destinate doar pentru citire (readonly)................................................48 Utilizarea structurilor..........................................................................................49 Realizarea conceptului de motenire.................................................................50 Motenirea de implementare.............................................................................53 Motenirea de interfa......................................................................................59 Testarea la egalitate..........................................................................................62 Construcii auxiliare..............................................................................................64 Comentarii..........................................................................................................64 Comentarii de documentare...............................................................................65

Spaii de nume...................................................................................................71 Directivele preprocesorului................................................................................73 Caracterizare operatori.........................................................................................78 Clasificare operatori...........................................................................................78 Conversia datelor...............................................................................................80 Suprancarcarea operatorilor..............................................................................83 Conversia datelor de tip clas i structur.........................................................87 Crearea interfeei utilizator...................................................................................89 Crearea de aplicaii Windows (cu interfa grafic)............................................90 Componente i controale.................................................................................101 Controale de tip container................................................................................116 Validarea informaiei introduse........................................................................120 Utilizarea meniurilor.........................................................................................125 Crearea de componente i controale...............................................................131 Creare de aplicaii MDI.....................................................................................135 Delegai...............................................................................................................137 Delegai simpli.................................................................................................137 Delegai multipli...............................................................................................142 Delegai i evenimente....................................................................................143 Delegai i fire de execuie...............................................................................145 Grafic.................................................................................................................149 Folosirea GDI+.................................................................................................149

3. Dezvoltare de aplicaii .NET


Introducerea n .NET Framework

Caracterizarea platformei .NET

Platforma .NET este un produs Microsoft. ntrebarea ar fi de ce firma Microsoft, avnd o clas important de produse, a mers pe calea crerii unui nou produs, mai exact a unei noi tehnologii. Crearea unei noi tehnologii poate duce deseori la schimbri ideologice. Aa s-a i ntmplat n cazul platformei .NET. Firma Microsoft a venit i cu o serie de schimbri conceptuale, ce a implicat mari eforturi. Desigur c aceast direcie de elaborare nu a fost un capriciu, ci o necesitate, fiindc n cadrul produselor Microsoft s-au acumulat treptat o serie de probleme. Baza n dezvoltarea tuturor produselor Microsoft a fost de la nceputuri o serie de biblioteci de funcii, care formeaz nucleul Windows API (Windows Application Programming Interfaces), numit prescurtat Win API. Iniial, Win API consta din cteva sute de funcii, care treptat a crescut pn la mii de funcii. Aceast cretere s-a datorat implementrii unor noi posibiliti de interaciune la nivel de dispozitive. Aceast transformare permanent a instrumentarului a purtat nu doar un caracter extensiv, dar i unul ideologic. Trecerea de la modelul 16 la modelul 32, sau de la modelul 32 la modelul 64, a necesitat i transformri de principiu. n asemenea situaii, una dintre problemele importante i complicate este problema compatibilitii. n procesul dezvoltrii instrumentarului Win API, se acumulau treptat o serie de probleme interne, soluionarea crora cerea din ce n ce un efort mai mare. Firma Microsoft a contientizat aceste probleme i a neles c micarea pe calea veche de dezvoltare va cere eforturi din ce n ce mai mari i poate duce la o situaie de impas. De aceea Microsoft a mers pe calea unor schimbri ideologice, care ar soluiona problemele aprute i, pe de alt parte, ar permite o dezvoltare mai eficient i mai rapid a aplicaiilor. Astfel s-a venit cu platforma .NET, aceasta posednd o serie de avantaje: - are o orientare pe obiecte; - uurina dezvoltrii codului, fiindc exist nite biblioteci puternice; - permite dezvoltarea att a unor aplicaii desktop, ct i a unor aplicaii Web; - mecanisme puternice de prelucrare a datelor;
3

o securitate sporit; existena unui mediu de dezvoltare cu multe funcionaliti; uurina amplasrii aplicaiilor; permite portabilitate sporit. O aplicaie executabil creat n baza tehnologiei .NET are o serie de particulariti, care o deosebesc de aplicaiile executabile obinuite create n baza Win API i poate fi rulat doar pe calculatoare pe care este instalat platforma .NET (.NET FrameWork). Pregtirea unei aplicaii executabile .NET pentru a fi rulat trece mai multe etape. Codificarea modulului-surs ntr-un limbaj de programare. n .NET sunt implementate mai multe limbaje (C#, C++, J#, Visual Basic, Jscript). Apoi urmeaz etapa de compilare-redactare, fiind obinut un modul executabil, de regul cu extensiunea .exe. Aceasta este prima etap a compilrii. Dar fiierul executabil nu poate fi rulat direct. A fost menionat deja c este necesar instalarea platformei .NET, care conine un element foarte important numit motor de executare (CLR Common Language RunTime), fiind un nucleu pentru executarea codului. Necesitatea acestui nucleu rezult din faptul c fiierul executabil este codificat ntr-un limbaj de nivel jos numit Limbaj Intermediar (LI). De aceea compilarea final are loc n procesul executrii codului. Aceasta este a doua etap a compilrii. O astfel de compilare se numete momentan (JIT Just In Time). Algoritmul acestei etape este urmtorul: se ia un fragment n LI i se compileaz rescriind fragmentul n coduri de calculator, fragmentul obinut se memoreaz i, totodat, se execut. Acest mod de tratare a compilrii n dou etape duce la un ir ntreg de eficientizri. Este, n primul rnd, o eficientizare n comparaie cu unele versiuni ale Java i, totodat, o eficien n crearea unui cod optimizat. Este o optimizare real dependent i de tipul procesorului. Acest tip de optimizare este posibil, cci a doua etap a compilrii are loc pe un calculator concret i informaia despre parametrii reali ai calculatorului poate fi uor utilizat. Realizarea compilrii prin compilare la LI duce la o interoperabilitate lingvistic, ceea ce presupune derivarea unei clase dintr-o clas creat n alt limbaj, existena n cadrul unei clase a instanelor de obiecte a unei clase create n alt limbaj, transmiterea de obiecte n calitate de parametri, iar obiectele sunt realizate n baz de clase create n diferite limbaje. Interoperabilitatea ridic o serie de probleme legate de tipurile de date. Fie, de exemplu, limbajele Visual Basic i C#. Limbajul Visual Basic are tipul Integer pe cnd C# nu are aa tip. n schimb C# are tipul int, iar Visual Basic nu-l are. De aceea la compilare trebuie s se in cont de aceste deosebiri ca s se obin un cod compatibil cu orice limbaj al platformei .NET. Deci trebuie s existe o politic de mapare ntre tipurile de date utilizate n cadrul platformei, politic ce trebuie s fie
-

foarte strict. Aceast politic este exprimat prin CTS (Common Type System), o ideologie de legare a tuturor tipurilor ntr-un sistem integru. A fost deja menionat ideea c platforma .NET are implementate o serie de limbaje de programare. Printre acestea pot fi enumerate: C#, C++, J#, Visual Basic, Jscript, care pot fi numite limbaje native, fiindc sunt incluse n platforma .NET. Fiecare programator poate s aleag acel limbaj care i convine mai bine pentru dezvoltarea unei aplicaii concrete. Dar limbajele cu care poate lucra platforma .NET nu sunt limitate doar la cele care au fost enumerate anterior. Platforma .NET este deschis pentru insluderea de noi limbaje. Pe lng limbajele native pot fi amintite i compilatoare cu care poate lucra platforma .NET, cum ar fi limbajele F#, Fortran, PERL. ns nici aceste limbaje nu reprezint limita de includere. Orice limbaj poate fi inclus la necesitate n componena platformei .NET. Pentru aceasta limbajul trebuie s respecte o serie de cerine stricte numite CLS (Common Language Specification), reprezentnd un set de reguli stricte, respectate ca un limbaj s poat lucra cu succes n cadrul platformei .NET. nc un moment interesant referitor la platforma .NET ar fi tendina de a pstra i a ncerca s integreze experiena trecut. Una dintre tehnologiile trecute ar fi COM (Component Obiect Model). Platforma .NET are mecanisme ce permit integrarea aplicaiilor i serviciilor de tip COM. Un element ce d rapiditate n dezvoltarea aplicaiilor n cadrul platformei .NET este existena unei biblioteci comune pentru toate limbajele platformei. Biblioteca se numete prescurtat FCL (Foundation Class Library), iar toate clasele i tot ce ine de ele pot fi utilizate n acelai mod din orice limbaj al platformei. Adic numele clasei, metodele, proprietile, cmpurile i alte caracteristici sunt aceleai care nu ar fi limbajul. Doar modul de utilizare ine cont de sintaxa limbajului.

Date
Tipuri de date Pentru a descrie datele pe care le prelucreaz, limbajul C# utilizeaz tipuri de date, printre ele existnd i o clas bogat de tipuri de date predefinite. Tipurile de date utilizate se mpart n dou categorii importante: 1) Tipuri de date orientate la valoare (valorice). 2) Tipuri de date orientate la referen (refereniale). n tabelul ce urmeaz sunt prezentate cele mai importante particulariti ale datelor din aceste categorii:
5

Valorice Sunt memorate n stiv Sunt dinamice

Refereniale Sunt memorate n dirijat (managed heap) Sunt stabile

memoria

Pentru a observa o particularitate ce deosebete datele valorice de cele refereniale, sunt prezentate dou exemple asemntoare ca prelucrare. n exemplul nti sunt utilizate date de tip int, care sunt date valorice. n urmtorul exemplu sunt utilizate date de tipul clasei Vector, care sunt date refereniale.
int i, j; . . . i=20; j=i; i=17; Console.WriteLine({0}

{1}, i, j);

Rezultatul afirii este: 17 20


Vector x, y; . . . x.c1=20; y=x; x.c1=17; Console.WriteLine({0}

{1}, x.c1, y.c1);

Rezultatul afirii este: 17 17 Din rezultatul afirii se observ c datele valorice diferite au un comportament independent, pe cnd datele refereniale diferite nu totdeauna au un comportament independent. Dup aplicarea operatorului de atribuire n contextul a dou variabile refereniale, ele ncep s arate la aceeai entitate:
Memorie stiv (variabile) x y Memorie dirijat (obiecte) 20, . . .

Ambele variabile x, y sunt plasate n memoria stiv, iar obiectul de tip vector legat de variabila x este plasat n memoria dirijat. Dup aplicarea operatorului de atribuire (y=x;), variabila y va arta la obiectul la care arat variabila x. Evident c dac este schimbat valoarea componentei c1 a vectorului x, acelai lucru se va obine i pentru variabila y, dac n cadrul clasei nu este redefinit situaia dat.

Not: Orice tip referenial de date va avea aceast proprietate, dac n cadrul tipului de date nu este redefinit situaia dat. Tipuri de date predefinite Dup cum a fost deja menionat, limbajul C# are un set bogat de tipuri de date predefinite. Aceste tipuri sunt divizate n diferite categorii. Astfel printre tipurile predefinite, exist att tipuri de date valorice, ct i tipuri de date refereniale. Tipurile de date valorice sunt, la rndul lor, de mai multe feluri. Aadar, sunt opt tipuri de date predefinite ntregi. n tabelul ce urmeaz sunt prezentate unele caracteristici importante ale acestor tipuri:
Common Type System System.Byte System.Uint16 System.Uint32 System.Uint64 System.Sbyte System.Int16 System.Int32 System.Int64 Tip predefinit byte ushort uint ulong sbyte short int long Dimensiune (binari) 8 16 32 64 8 16 32 64 Domeniu de valori 0255 065535 04294967295 018446744073709551615 (0264-1) -128127 -3276832767 -21474836482147483647 -9223372036854775808 9223372036854775807 (-263263-1)

De asemenea, sunt trei tipuri de date predefinite cu virgul mobil. Tabelul ce urmeaz prezint unele caracteristici importante ale acestor tipuri:
Common Type Tip System predefinit System.Single float System.Double double System.Decimal decimal Dimensiune (binari) 32 64 128 Domeniu de valori 1.510-453.41038 5.010-3241.710308 1.010-287.91028

Mai sunt, de asemenea, dou tipuri speciale unul pentru caracterizarea valorilor caracteriale i altul pentru caracterizarea valorilor de adevr. Tabelul ce urmeaz vine cu unele caracteristici importante ale acestor tipuri:
Common Type System System.Boolean System.Char Tip predefinit bool char Domeniu de valori false, true Setul de simboluri (Unicode)

Acestea sunt toate tipurile predefinite valorice. Tipurile predefinite refereniale sunt reprezentate prin dou tipuri de date. Tabelul ce urmeaz prezint unele caracteristici ale acestor tipuri de date:
7

Common Type System System.Object System.String

Tip predefinit object string

Descriere Tip rdcin de la care sunt derivate toate tipurile. ir de caractere Unicode.

Tipul de date object este important prin faptul c este cel mai general tip de date de la care sunt derivate, direct sau indirect, absolut toate tipurile de date, inclusiv i cele predefinite. De aceea poate fi considerat printele tuturor tipurilor de date. Un alt reprezentant al tipurilor de date refereniale este string, care aduce comoditate n prelucrarea irurilor de caractere. n contextul tipului string poate fi aplicat operatorul + care are semnificaia concatenrii a dou iruri de caractere:
sir1+sir2

n continuare, este adus un exemplu referitor la string, dar asemntor cu exemplul referitor la tipul de date Vector:
string s1=Salut; string s2; s2=s1; s1=La revedere; Console.WriteLine({0}

{1}, s2, s1);

Rezultatul afirii este: Salut La revedere Se observ c rezultatul afirii este diferit, cu toate c tipul string este referenial i parc ar trebui ca s1 i s2 s ofere acelai rezultat. Din specificul de realizare intern a tipului de date string se obine o abatere de la regula general. Variabile n C# Definirea variabilelor n C# se face n felul urmtor:
tip numeVariabila;

unde numeVariabila este un identificator corect. Crearea unui identificator corect se bazeaz pe urmtoarele reguli: 1) Primul simbol este o liter sau simbolul de subliniere _ . 2) Celelalte simboluri sunt litere, cifre sau simbolul de subliniere _ . 3) Simbolurile pot fi codificate. Codul este precedat de \u (u de la unicod), constnd din 4 cifre hexazecimale.
Identificatorul \u0041i1 este acelai cu identificatorul Ai1.

Un identificator poate fi precedat de simbolul @ . Astfel pot fi utilizate n calitate de identificatori i cuvinte-cheie. 5) Literele pot fi oricare din codificarea unicod. Iniializarea unei variabile poate fi fcut la nivel de clas sau la nivel local n cadrul unei funcii. Poate fi efectuat iniializarea la definire
4)

tip numeVariabila=valoare;

sau prin atribuire:


numeVariabila=valoare;

La nivel de clas iniializarea nu este strict necesar. n caz c nu este fcut o iniializare explicit, valoarea variabilei va fi fixat implicit cu zero. La nivel de funcie variabila trebuie neaprat iniializat. n caz contrar, ea nu se consider a fi iniializat i apare eroare dac se ncearc utilizarea variabilei respective. Dac ar fi de comparat limbajele C, C++ cu C#, n raport cu definirea de variabile cu acelai nume la diferite niveluri, atunci n C, C++ fragmentul ce urmeaz este perfect corect:
{ int i; i=20; . . . { int i; i=10; . . . } . . . }

pe cnd n C# codul anterior la nivel local va genera eroare. Tot va genera eroare i fragmentul ce urmeaz:
static void Main() { int i; for(int j=0; j<12; j++) { int i=10; // va fi eroare . . . } }

O alt situaie legat de definirea variabilelor cu acelai nume ar fi descris de urmtorul fragment de cod:
. . . for(int i=0; i<10; i++){ . . . } for(int i=0; i<20; i++){ . . . } . . .

Acest fragment nu va genera erori din motiv c domeniile unde sunt definite variabilele cu acelai nume nu se intersecteaz. Tot de definirea variabilelor cu acelai nume este legat i situaia descris de urmtorul fragment de cod:
class cl { . . . static int i=20; static void Main() { int i=10; . . . Console.WriteLine(i); . . . } . . . }

n acest caz, fragmentul dat se va compila fr erori, deoarece o variabil i este definit la nivel de clas, iar alt variabil i este definit la nivel local. La afiare se va afia valoarea variabilei locale i, adic 10. Dac e de dorit accesul la cmpul i, va trebui de concatenat prin punct numele clasei cu numele variabilei cl.i, ceea ce va da acces la valoarea 20. Constante Dup variabilitate, datele pot fi clasificate n date constante i date variabile. Din denumiri este clar c date constante sunt datele care nu se schimb pe parcursul ntregului program, iar date variabile sunt datele care pot s se schimbe n procesul execuiei programului. n continuare, vor fi caracterizate mai detaliat constantele. Datele constante sunt exprimate prin constante de tip i constante literale. Constantele de tip, sunt definite ca nite variabile, fiind iniializate la definire i pstreaz aceast valoare pe parcursul ntregului program. Constantele pot fi definite la nivel de clas, fiind cmpuri ale clasei. Totodat, constantele pot fi definite i la nivel local n cadrul unor funcii, fiind vizibile pe parcursul execuiei funciilor corespunztoare. n caz general, o constant este definit n felul urmtor:
[modificatori] const tip numeConstanta=valoare;

unde numeConstanta este un identificator corect, valoare este valoarea de iniializare a constantei, iar elementul modificatori determin gradul de acces

10

la constanta dat, dac ea este un cmp al clasei sau lipsesc n cazul constantelor de nivel local. De exemplu:
public const int suta=100;

Valorile de iniializare sunt n caz general expresii formate din constante, ns adeseori sunt, pur i simplu, constante literale. Constantele literale pot fi descrise prin intermediul urmtoarei diagrame:
constante

numerice

caracteriale

ir de caractere

ntregi

reale

simbolice

codificate

notaie cu punct zecimale hexazecimale

notaie tiinific

Dup cum poate fi vzut din diagram, constantele literale sunt constante numerice, constante caracteriale i constante exprimate prin iruri de caractere.
Constante Constante Constante iruri numerice caracteriale de caractere 12; -345; a; A; Student eminent 3.14; \u1034; -45.237 \x41

Constantele numerice se mpart n dou clase mari: constante numerice ntregi i reale.
Constante Constante numerice ntregi numerice reale 127; -8345; 100000; 32.154; -245.2134; -237346 1.2e-3

Constantele numerice ntregi pot fi clasificate dup sistemul de numeraie utilizat la codificarea lor, obinnd constante zecimale i hexazecimale. Pentru a face deosebire ntre constantele scrise n diferite sisteme de numeraie, este utilizat prefixul 0x n cazul constantelor hexazecimale. Constantele zecimale sunt scrise fr vreun prefix.
Constante numerice ntregi zecimale hexazecimale
11

2345; -345; 451237

0x126; 0x1f2d; 0xfefe2

ntruct exist mai multe tipuri de date ntregi, cu diferite domenii de valori, exist i posibilitatea de a schimba i clasa constante. Pentru aceasta sunt utilizai civa modificatori care sunt aplicai unei valori scriind modificatorul necesar dup valoarea dat. Modificatorii sunt exprimai prin urmtoarele simboluri: l, u, L, U. Modificatorii l i L trec valorile la clasa constantelor ntregi de tip long. Fiindc sunt i tipuri de date ntregi fr semn, modificatorii u i U trec valorile la clasa constantelor ntregi fr semn, adic pozitive.
Constante numerice ntregi scurte 0243; -0x291; 31446 Constante numerice ntregi cu semn 0236; -0x2a2; 323670 Constante numerice ntregi lungi 0243l; -0x291L; 31446l Constante numerice ntregi fr semn 0236u; -0x2a2U; 323670u

Constantele numerice reale sunt de dou tipuri: constante reale n notaie cu punct fix i constante reale n notaie tiinific. Constantele reale n notaie tiinific sunt utilizate pentru scrierea valorilor de urmtorul tip 1.2*1030.
Constante numerice reale n notaie cu punct fix 1.25; -82.175; 100.00011145; -25714.6 Constante numerice reale n notaie tiinific 32.1e-4; -217.21e+34; 17.2E-3

De asemenea, exist mai multe tipuri de date cu virgul mobil, de aceea exist i posibilitatea de a concretiza clasa constantei cu virgul mobil. Pentru aceasta sunt utilizai civa modificatori care sunt aplicai unei valori scriind modificatorul necesar dup valoarea dat. Modificatorii sunt exprimai prin urmtoarele simboluri: f, d, m, F, D, M. Modificatorii f i F trec constantele la clasa constantelor cu virgul mobil de tip float. Modificatorii d i D trec constantele la clasa constantelor cu virgul mobil de tip double. Modificatorii m i M trec constantele la clasa constantelor cu virgul mobil de tip decimal. Constantele caracteriale reprezint caractere alfanumerice i coduri de dirijare. Pentru scrierea constantelor caracteriale, sunt folosite ghilimele unare ntre care este plasat simbolul alfanumeric sau codul necesar. Constantele caracteriale sunt divizate n constante caracteriale simbolice i constante caracteriale codificate. Constantele caracteriale codificate sunt scrise utiliznd codul ASCII al simbolului codificat, scris n sistem unicod sau n sistem hexazecimal de numeraie. Pentru o
12

scriere corect a constantei codificate, secvena de cod unicod este precedat de simbolurile \u, iar cifrele hexazecimale sunt precedate de simbolurile \x.
Constante caracteriale a; =; A; $; @; Constante simbolice codificate \x41; \u0101; \u0012; \xD;

Codurile de dirijare sunt nite expresii constnd din mai multe simboluri, avnd menirea de a genera anumite operaii de formatare n procesul afirii la ecran sau la imprimant a informaiei alfanumerice. Codurile de dirijare sunt caracterizate prin prezena simbolului \ care preced expresia, reprezentnd codurile respective. n tabelul de mai jos, sunt enumerate o serie de caractere de dirijare, descriindu-se i operaiile de formatare caracteristice lor:
Coduri de dirijare \a \b \r \n \t \v \f \\ \ \ Operaii de formatare caracteristice Alarma Retur cu un simbol Retur la nceput de linie Linie nou Tabulare orizontal Tabulare vertical Foaie nou Simbolul \ Simbolul Simbolul

Fiindc simbolurile \ au semnificaii auxiliare, pentru a prezenta chiar simbolul reprezentat de ele, este utilizat o secven de dirijare. Secvenele date sunt enumerate n tabelul anterior. Constantele simbolice codificate sunt tot o form de coduri de dirijare. Constantele reprezentnd iruri de caractere constau din secvene de caractere alfanumerice i coduri de dirijare luate n ghilimele.
Constante iruri de caractere Student Universitatea de Stat Constante iruri de caractere continnd coduri de dirijare \tUniversitatea\n de Stat\n C:\\Documente\\Doc

n ncheiere va fi menionat faptul c toate operaiile valabile pentru un tip de date sunt valabile i pentru constantele de tipul dat.

13

Utilizarea enumerrilor O enumerare este un tip de date ce reunete o serie de valori ntregi, care vor fi valorile posibile ale variabilelor de tip enumerare, fiecare dintre valori fiind legat cu o etichet simbolic. O enumerare reprezint o submulime de valori ntregi. Forma general de prezentare a unei enumerri este urmtoarea:
enum nume_enum [: tip] { et1, et2, ..., etk }

unde nume_enum este un identificator, propus de programator, ce reprezint denumirea enumerrii; et1, et2, ..., etk sunt identificatorii, ce descriu etichetele simbolice, iar tip determin tipul valorilor ntregi asociate cu etichetele. A fost menionat deja faptul c fiecrei etichete i corespunde o valoare ntreag. Astfel eticheta et1 reprezint valoarea ntreag 0, etichetei et2 i corespunde valoarea 1, iar ultima etichet etk reprezint valoarea ntreag k-1. Iat un exemplu ce concretizeaz forma general de prezentare:
enum culori { rosu, verde, albastru }

unde eticheta rosu este echivalent cu valoarea 0, lui verde i corespunde 1, iar albastru exprim valoarea 2. Orice variabil de tip enumerare poate avea n calitate de valoare oricare dintre valorile ntregi legate de etichetele simbolice. De aceea o form posibil de iniializare ar fi urmtoarea:
nume_var_enum=nume_enum.nume_eticheta;

Trebuie de remarcat faptul c exist un control strict al formei de atribuire. De aceea nu este permis atribuirea direct a valorii numerelor corespunztoare etichetei. n contextul exemplului anterior, poate fi fcut urmtoarea atribuire:
c1=culori.verde;

O alt form general de utilizare a enumerrilor este orientat la posibilitatea de definire a variabilelor i este urmroarea:
nume_enum lista_descr;

unde lista_descr este lista descriptorilor variabilelor definite. Aceast form permite definirea variabilelor n orice loc al programului, n care este vizibil descrierea
14

enumerrii, n baza creia sunt definite variabilele. De exemplu, n baza enumerrii culori mai pot fi definite o serie de alte variabile
culori c2, c3, te[10];

Mai exist o form de descriere a unei enumerri, care permite un control mai riguros asupra valorilor reunite de enumerare i a corespondenei dintre valori i etichetele simbolice. Forma general de prezentare n acest caz este urmtoarea:
enum nume_enum { et1[=val1], et2[=val2], ..., etk[=valk] }

unde val1, val2, ..., valk sunt valorile ntregi asociate cu etichetele simbolice, iar celelalte elemente din descriere au fost deja caracterizate anterior. Valorile asociate cu etichetele corespunztoare sunt opionale i de aceea ele pot lipsi din descriere. Orice enumerare este guvernat de urmtoarele reguli generale: dac valoarea asociat cu prima etichet lipsete din descriere, atunci eticheta este asociat cu valoarea 0; dac valoarea asociat cu o etichet oarecare lipsete din descriere, atunci eticheta este asociat cu o valoare cu unu mai mare dect valoarea etichetei precedente; pot exista mai multe etichete asociate cu aceeai valoare. Din exemplul
zile { luni=1, marti=1, miercuri=1, joi=1, vineri=1, sambata, duminica=2 }

se poate observa c toate etichetele corespunztoare zilelor de lucru sunt asociate cu valoarea 1, iar cele corespunztoare zilelor de odihn sunt asociate cu valoarea 2. Tot din exemplul acesta, se poate vedea c eticheta sambata obine valoarea 2 prin incrementarea valorii etichetei vineri. Exemplu. S se alctuiasc un program care permite afiarea unui salut n dependen de timpul zilei.
using System;

15

namespace Salutare { enum salut { dimineata, ziua, seara } class Program { static void Main(string[] args) { salut s; DateTime timp = DateTime.Now; if (timp.Hour >= 2 && timp.Hour < 11) s = salut.dimineata; else if(timp.Hour >= 11 && timp.Hour < 17) s = salut.ziua; else s = salut.seara; Console.WriteLine("Buna {0}!!!", s); Console.ReadLine(); } } }

Trebuie de concretizat faptul c orice enumerare utilizat ntr-un program are menirea de uurare a activitii de citire i deci de nelegere a codului-surs i are drept scop final comoditatea n procesul de elaborare a programelor. Un exemplu simplu, dar elocvent, care confirm cele spuse ar fi urmtorul. S presupunem c este elaborat un program cu orientare la circulaia rutier. Inevitabil, va trebui de modelat n acest program i lucrul unui semafor. O posibil variant ar fi utilizarea pentru culorile semaforului a unor indici numerici: de exemplu, indicele 1 va fi legat cu verde. n acest context, s-ar putea scrie:
. . . if(culoare==1) { . // unele actiuni concrete . } . . .

n procesul elaborrii programului, poate nu va fi foarte complicat de reinut c indicele 1 este legat de culoarea verde. Dar dac peste o perioad, cnd unele lucruri se mai uit, programul va trebui revzut, atunci nelegerea acestui fragment
16

de cod poate genera unele dificulti. Mult mai simplu ar fi fost dac era descris o enumerare pentru culorile semaforului, utiliznd-o apoi n program.
enum culoriSemafor {verde, galben, rosu}; . . . if(culoare==verde) { . // unele actiuni concrete . } . . .

Acest fapt ar fi uurat procesul de nelegere a codului-surs i de reamintire a ideilor realizate n program. Tablouri unidimensionale Orice limbaj de programare se consider srac, dac are doar tipuri de date simple (predefinite). Pe lng tipuri de date simple, care nu au o structur intern, deseori apare necesitatea de utilizare n algoritmi a unor tipuri de date compuse, care constau, la rndul lor, din alte date. Cu ct limbajul are mai multe tipuri de date, cu att crete mai mult puterea lui de exprimare i deci uurina de a scrie algoritmii necesari de prelucrare a datelor. Limbajul C# are mecanisme de descriere a unor tipuri de date compuse, iar n baza lor de definire a unor variabile necesare n contextul scrierii de programe. Un tablou unidimensional este o reuniune de elemente de acelai tip reunite sub acelai nume, ocupnd o locaie de memorie contigu (elementele urmeaz unul dup altul fr guri). Accesul la elementele tabloului se efectueaz cu ajutorul unui indice. Definirea unui tablou unidimensional poate fi realizat n felul urmtor
tip [] nume;

unde tip descrie tipul elementelor reunite n tablou, nume este numele tabloului, fiind un identificator corect, care reprezint numele comun al elementelor reunite. Pentru a face alocare de memorie pentru elementele tabloului este utilizat operatorul new:
nume=new tip[dim];

unde dim este dimensiunea tabloului. De exemplu:


int [] tab_i = new int[17];

reprezint un tablou de 17 elemente de tip int.

17

Mrimea n octei a fragmentului de memorie ocupat de tablou depinde de tipul elementelor reunite i de dimensiunea tabloului, putnd fi exprimat astfel:
dimensiune*sizeof(tip).

Un moment important referitor la tablouri este posibilitatea de a accesa un element concret al tabloului. Accesarea unui element este realizat folosind numele tabloului i un numr numit indice al elementului:
nume[ind] 0 ind dim-1

Accesarea unui element al tabloului poate fi fcut n scopul plasrii unei valori n element sau n scopul folosirii valorii din element. Elementele tabloului pot fi utilizate n orice expresii, fiind restricionate doar de tipul de date al lor. Plasarea primei valori n element va fi numit iniializare a elementului. Iniializarea elementelor tabloului poate fi fcut i n procesul definirii tabloului. Pentru aceasta este folosit expresia
tip [] nume=new tip[N]{val1, val2, ..., valN};

unde numrul de iniializatori este strict egal cu dimensiunea tabloului. Este posibil i o astfel de sintax:
tip [] nume=new tip[]{val1, val2, ..., valN};

care permite definirea unui tablou fr dimensiuni. Desigur c tabloul definit nu este chiar un tablou fr dimensiuni. Dimensiunea lui este egal cu numrul valorilor de iniializare. De exemplu:
float [] tab_f = new float[]{ 4, -7.5, 3.2 };

genereaz un tablou de 3 elemente float, adic dimensiunea lui este egal cu 3. Ultima expresie de iniializare poate fi simplificat astfel:
tip [] nume={val1, val2, ..., valN};

Exemplu. S se alctuiasc un program care permite colectarea temperaturilor zilnice lunare i permite gsirea temperaturilor medii, maximale i minimale n luna respectiv.
using System; namespace tablou { class Program { static void Main () { int sumaTemp = 0, i, min, max, zile; float med; int temp [31]; Console.WriteLine(numarul de zile:);

18

zile=int.Parse(Console.ReadLine()); for(i=0; i<zile; i++) { Console.WriteLine(temperatura la amiaza zilei {0}, i+1); temp[i]=int.Parse(Console.ReadLine()); } min = temp[0]; max = temp[0]; for(i=0; i<zile; i++) { if(min > temp[i]) min = temp[i]; if(max < temp[i]) max = temp[i]; sumaTemp += temp[i]; } med = ((float)sumaTemp) / zile; Console.WriteLine(Temperatura medie: {0}, med); Console.WriteLine(Temperatura maximala: {0}, max); Console.WriteLine(Temperatura minimala: {0}, min); } } }

Tablouri bidimensionale i multidimensionale nainte de a trece la definirea general a tablourilor multidimensionale, va fi tratat mai nti un caz particular al lor, i anume tablourile bidimensionale, care au o utilizare suficient de frecvent n diverse aplicaii. Un tablou bidimensional este un tablou unidimensional fiecare element al cruia este un tablou unidimensional. Urmnd aceast definiie pot fi evideniate dou cazuri: a) elementele tabloului, fiind tablouri unidimensionale, sunt de aceiai dimensiune; b) elementele tabloului, fiind tablouri unidimensionale, sunt de dimensiuni diferite. De aceea sunt dou tipuri de tablouri bidimensionale: a) tablouri regulate; b) tablouri neregulate (eng. jagged). Definirea unui tablou bidimensional regulat poate fi realizat astfel:
tip [ , ] nume;

unde tip descrie tipul elementelor reunite n tablou, nume reprezint numele tabloului, fiind numele comun al elementelor reunite. Pentru a face alocare de memorie pentru elementele tabloului este utilizat operatorul new:
nume=new tip[dim1, dim2];

19

unde dim1 i dim2 reprezint dimensiunile tabloului. Pentru accesarea unui element al tabloului bidimensional regulat, este utilizat numele tabloului i doi indici
nume[ind1, ind2]

indicii satisfcnd relaiile


0 ind1 dim1-1, 0 ind2 dim2-1

Exist o legtur de asemnare ntre tablourile bidimensionale regulate i matrice. Datorit acestei asemnri, foarte des tablourile bidimensionale regulate mai por fi numite i matrice i pentru o reprezentare intuitiv este folosit forma matriceal. n caz general, unui tablou bidimensional i va corespunde o matrice cu dim1 linii, fiecare linie coninnd dim2 elemente. Dac este necesar elementele tabloului bidimensional regulat pot fi iniializate n procesul definirii tabloului. Pentru a iniializa elementele tabloului, vor fi utilizate construcii ierarhice cu dou niveluri create n baza acoladelor, care permit gruparea valorilor de iniializare. Iat schema general:
tip [ , ] nume = new tip[M, N]{{v11, {v21, . . {vM1, v12,..., v1N}, v22,..., v2N}, ., vM2,..., vMN}};

Schema aceasta de iniializare ine cont de definiia unui tablou bidimensional. Numrul de iniializatori coincide cu numrul de elemente ale tabloului. Din acest motiv dimensiunile M i N pot fi omise:
tip [ , ] nume = new tip[ , ]{{v11, {v21, . . {vM1, v12,..., v1N}, v22,..., v2N}, ., vM2,..., vMN}};

Exist desigur i forma simplificat de iniializare a elementelor unui tablou, fcndu-se unele omiteri n schema precedent:
tip [ , ] nume = {{v11, {v21, . . {vM1, tip [][] nume; v12,..., v1N}, v22,..., v2N}, ., vM2,..., vMN}};

Definirea unui tablou bidimensional neregulat (jagged) poate fi realizat astfel: unde tip descrie tipul elementelor reunite n tablou, nume reprezint numele tabloului, fiind numele comun al elementelor reunite. Pentru a face alocare de memorie pentru elementele tabloului este utilizat operatorul new n dou etape: mai nti este determinat numrul de elemente (linii), iar apoi este determinat dimensiunea fiecrui element (linii) n parte.
20

nume=new tip[N][]; nume[0]= new tip[dim1]; nume[1]= new tip[dim2]; . . . nume[N-1]= new tip[dimN];

unde N reprezint numrul de linii ale tabloului bidimensional, iar dim1, dim2, ..., dimN reprezint respectiv dimensiunile fiecrei linii. Pentru accesarea unui element al tabloului bidimensional neregulat, este utilizat numele tabloului i doi indici:
nume[i][j]

i pentru acest tip de tablou exist posibilitatea de iniializare a elementelor tabloului n procesul definirii lui. Pentru a iniializa elementele tabloului, n cazul cel mai general vor fi utilizate construcii ierarhice cu dou niveluri create n baza acoladelor, care permit gruparea valorilor de iniializare. Iat schema general:
tip [][] nume=new tip[N][]{new new . new tip[]{v11, v12,..., v1k1}, tip[]{v21, v22,..., v2k2}, . ., tip[]{vN1, vN2,..., vNkN}};

unde k1, k2,..., kN reprezint dimensiunile elementelor. Acest model de iniializare poate fi puin simplificat, omind unele componente:
tip [][] nume=new tip[][]{new new . new tip[]{v11, v12,..., v1k1}, tip[]{v21, v22,..., v2k2}, . ., tip[]{vN1, vN2,..., vNkN}};

sau
tip [][] nume={new new . new tip[]{v11, v12,..., v1k1}, tip[]{v21, v22,..., v2k2}, . ., tip[]{vN1, vN2,..., vNkN}};

Dimensiunile depind de forma de iniializare i de numrul de iniializatori i vor fi determinate n procesul compilrii. n continuare este prezentat un exemplu de iniializare a elementelor la definire:
float [][] tf={{2.3,-4.5},{6.7},{1.2,3.8,8.4}};

Schematic acest fapt poate fi reprezentat sub form de matrice astfel:


2.3 6.7 1.2 -4.5 3.8 8.4

Exemplu. S se alctuiasc un program care modeleaz o serie de operaii cu matricele utiliznd tablouri bidimensionale (citire, adunare, afiare).

21

using System; namespace tablou { class Program { const int nlA = 3; //numar linii matrice A, B, S const int ncA = 4; //numar coloane matrice A, B, S static void Main () { int [,] A=new int[nlA, ncA], B=new int[nlA, ncA], S=new int[nlA, ncA]; int i, j, k ; Console.WriteLine(Elementele matricelor A si B:); for(i=0; i<nlA; i++) //citire elem. matrice A, B for(j = 0; j< ncA; j++) { Console.WriteLine(A[{0},{1}]=, i, j); A[i,j]=int.Parse(Console.ReadLine()); Console.WriteLine(B[{0},{1}]=, i, j); B[i,j]=int.Parse(Console.ReadLine()); } for(i=0; i<nlA; i++) //adunare matrice A, B for(j = 0; j<ncA; j++) S[i,j]=A[i,j] + B[i,j]; for (i=0; i<nlA; i++) //afisare matrice A { for (j = 0; j<ncA; j++) Console.Write({0,5:D}, A[i,j]); Console.Write(\n); } Console.ReadLine(); } } }

n continuare, tablourile multidimensionale vin ca o generalizare a tablourilor bidimensionale. Un tablou multidimensional de dimensiunea n este un tablou unidimensional, fiecare element al cruia este un tablou de dimensiunea n-1. De asemenea ca i n cazul tablourilor bidimensionale tablourile multidimensionale sunt de dou tipuri

22

regulate i neregulate (eng. jagged). Definirea unui tablou multidimensional regulat de dimensiunea n poate fi fcut astfel:
tip [,,...,] nume;

unde tip descrie tipul elementelor reunite n tablou, nume reprezint numele tabloului. Alocarea de memorie pentru amplasarea elementelor se realizeaz cu ajutorul operatorului new:
nume = new tip[dim1, dim2, ...,dimn];

unde dim1, dim2,..., dimn reprezint dimensiunile tabloului. Dimensiunile sunt exprimate prin expresii numerice ntregi. Accesarea unui element al tabloului multidimensional regulat este efectuat prin urmtoarea expresie
nume[ind1, ind2, ..., indn]

unde ind1, ind2, ..., indn satisfac urmtoarele condiii


0 0 . 0 . ind1 dim1-1 ind2 dim2-1 . indn dimn-1

Deci primul element al unui tablou multidimensional are numele


nume[0, 0, ..., 0]

iar ultimul element are numele


nume[dim1-1, dim2-1, ..., dimn-1]

Pentru a iniializa elementele unui tablou n dimensional n momentul definirii tabloului, vor fi utilizate construcii ierarhice cu n niveluri create n baza acoladelor, grupnd valorile de iniializare. Numrul de iniializatori trebuie s coincid cu numrul de elemente ale tabloului. Schema general are urmtoarea form:
tip [,,...,] nume=new tip[N,...,M]{{v11,v12,,v1M}, . . ., {v21,v22,,v2M}, . . ., {vN1,vN2,,vNM}};

Schema de iniializare poate fi simplificat puin prin omiterea unor componente (dimensiunile i operatorul new), obinnd urmtoarele construcii:
tip [,,...,] nume=new tip[,...,]{{v11,v12,,v1M}, . . ., {v21,v22,,v2M}, . . ., {vN1,vN2,,vNM}};
23

sau
tip [,,...,] nume = {{v11,v12,,v1M}, . . ., {v21,v22,,v2M}, . . ., {vN1,vN2,,vNM}};

Pentru a ilustra modul dat de iniializare, n continuare este propus un exemplu n care vor fi iniializate la definire elementele urmtorului tablou 3 dimensional
float [,,] tf3=new float[2,3,2]{ {{1.1,1.2}, {1.3,1.4}, {1.5,1.6}}, {{2.1,2.2}, {2.3,2.4}, {2.5,2.6}} };

Aplicnd schema simplificat, exemplul anterior poate fi rescris astfel:


float [,,] tf3={{{1.1,1.2}, {1.3,1.4}, {1.5,1.6}}, {{2.1,2.2}, {2.3,2.4}, {2.5,2.6}}};

Familia tablourile multidimensionale neregulate, avnd aceiai dimensiune, este mult mai bogat. Aceasta se datoreaz faptului c un tablou neregulat poate fi obinut din diverse combinaii de subtablouri regulate de diferite dimensiuni. Pentru a nelege aceast ideie va fi cercetat cazul unui tablou tridimensional neregulat. Pot fi evideniate urmtoarele cazuri: a)
tip [][][] nume;

b)
tip [,][] nume;

c)
tip [][,] nume;

unde tip descrie tipul elementelor reunite n tablou, nume reprezint numele tabloului. Alocarea de memorie pentru fiecare dintre cazurile date poate fi realizat cu ajutorul operatorului new n felul urmtor: a)
nume=new tip[N][][]{new tip[M][]{ new tip[d11], ..., new tip[d1M]}, . . ., new tip[K][]{new tip[dN1], ..., new tip[dNK]} };

b)
nume=new tip[N,M][]{ {new tip[d11], ..., new tip[d1M]},

24

. . ., {new tip[dN1], ..., new tip[dNM]} };

c)
nume=new tip[N][,]{ new tip[L1,C1], ..., new tip[LN,CN] };

n contunuare sunt prezentate o serie de exemple referitoare la cazurile expuse anterior:


int[,][] ti3n1 = { { { new new new new int[3, 3][] int[2], new int[4], new int[3], new { int[3], new int[2] }, int[2], new int[2] }, int[3], new int[2] }};

int[][,] ti3n2 = new int[3][,] { new int[2, 2], new int[3, 3], new int[3, 4] }; int[][][] ti3n3 = new int[3][][] new int[3][]{ new new int[2][]{ new new int[2][]{ new { int[2],new int[3], new int[2]}, int[3],new int[4] }, int[5],new int[3] }};

Dirijarea execuiei (instruciuni)

Clasificarea instruciunilor Vocabularul limbajului C# conine o gam bogat de instruciuni, care mpreun cu alte construcii ale limbajului permit descrierea diferiilor algoritmi de prelucrare a datelor. Toate instruciunile pot fi clasificate n urmtoarele categorii: instruciuni de selecie (if, switch), ele permit alegerea unei ci de prelucrare din dou sau mai multe ci posibile; instruciuni de iterare (for, foreach, while, do-while), ele permit executarea repetat a unui grup de instruciuni;

25

instruciuni de salt (goto, break, continue, return), ele permit transmiterea necondiionat a dirijrii ntr-un anumit punct al programului; instruciuni etichete (case, default), ele permit desemnarea locului de trecere n procesul prelucrrii; instruciuni expresii (;), ele permit organizarea aciunilor concrete de prelucrare a datelor; instruciuni bloc, gruparea mai multor instruciuni ntre acolade {}, permite obinerea unei instruciuni bloc, care mai poate fi numit i instruciune compus.

Instruciuni de salt

Sunt cteva instruciuni de salt, fiecare avnd sensul su specific de executare, dar ideea general care le reunete este faptul c ele transmit dirijarea n anumit punct al programului, fr testarea unor condiii. Una dintre instruciunile din aceast categorie este instruciunea goto. Intruciunea goto transmite execuia n alt loc al programului. Uneori ea este denumit i instruciune de salt necondiionat, dat fiind faptul c saltul este efectuat fr a testa anumite condiii. Pentru a putea fi efectuat actiunea de salt, locul saltului trebuie marcat printr-un identificator urmat de dou puncte (:), numit etichet. Forma general a instruciunii este:
goto nume_eticheta;

Conform principiilor programrii structurate, este descurajat aplicarea instruciunii goto, deoarece poate fi surs de erori. Totui, cnd crem construcii ierarhice din blocuri de instruciuni incluse unele n altele, putem efectua cu uurin salturi de pe unele niveluri interioare ale ierarhiei n exteriorul ierarhiei utiliznd instruciunea goto. Instruciuni de selecie Pentru a putea alege o cale din mai multe ci posibile de prelucrare, sunt puse la dispoziie o serie de instruciuni, numite instruciuni de selecie. O instruciune din aceast categorie este instruciunea if-else. Forma general a acestei instruciuni este
26

if (expresie_condiional) instruciune_simpl_sau_compus1 else instruciune_simpl_sau_compus2

Deoarece ramura else este opional, adic poate lipsi, mai exist o form simplificat a instruciunii
if (expresie_condiional) instruciune_simpl_sau_compus

Execuia instruciunii const din urmtorii pai: a) este estimat expresie_condiional; b) dac se obine adevr, este executat blocul instruciune_simpl_sau_compus1; are loc trecerea la urmtoarea instruciune dup instruciunea if-else; c) dac se obine fals, este executat blocul instruciune_simpl_sau_compus2; are loc trecerea la urmtoarea instruciune dup instruciunea if-else. Dac ramura else lipsete, sunt obinui urmtorii pai de execuie: a) este estimat expresie_condiional; b) dac se obine adevr, este executat blocul instruciune_simpl_sau_compus; are loc trecerea la urmtoarea instruciune dup instruciunea if; c) dac se obine fals, are loc trecerea la urmtoarea instruciune dup instruciunea if. Poate fi obinut o descriere mai sugestiv a acestor variante ale instruciunii utiliznd elemente grafice pentru descrierea schemelor-bloc. n cele ce urmeaz, sunt prezentate descrierile grafice respective. Instruciunile if-else pot fi incluse una n alta obinnd instruciuni imbricate.
if(exp_cond0) { . . if(exp_cond1) { . . } . . }

27

Un caz particular al instruciunilor imbricate este scria else-if care poate fi reprezentat schematic n felul urmtor:
if(exp_cond1) instr_s_c1 else if(exp_cond2) instr_s_c2 . . else if(exp_condk) instr_s_ck else instr_s_c0

Execuia scriei else-if se supune regulilor de execuie a instruciunii if-else, reducndu-se la evalurile consecutive ale expresiilor condiionale exp_cond1, exp_cond2, , exp_condk. Estimarea la adevr a vreunei expresii exp_condi duce la executarea mulimii de instruciuni instr_s_ci. Dac toate expresiile condiionale sunt estimate la fals, atunci este executat mulimea de instruciuni instr_s_c0, n caz c exist ultima ramur else. Apoi are loc trecerea la urmtoarea instruciune dup scria else-if. Un alt reprezentant al categoriei instruciunilor de selecie este instruciunea switch(). Ea are o asemnare cu scria else-if, cnd expresiile condiionale sunt de tip testare la egalitate. Forma general a instruciunii poate fi descris de urmtoarea sintax:
switch(expresie) { case exp_const1: instr_s_c1 case exp_const2: instr_s_c2 . . case exp_constk: instr_s_ck default: instr_s_c0 }

Modul de execuie a instruciunii se reduce la urmtorii pai: este evaluat valoarea parametrului expresie;

28

dac valoarea obinut este egal cu una dintre valorile expresiilor constante exp_const1, exp_const2, , exp_constk, va ncepe execuia blocului de instruciuni, corespunztor constantei date; - dac valoarea obinut nu este egal nici cu una dintre valorile expresiilor constante exp_const1, exp_const2, , exp_constk i exist instruciunea default, atunci va ncepe execuia blocului de instruciuni instr_s_c0, corespunztor acestei ramuri. Trebuie de subliniat faptul c parametrul expresie i expresiile constante sunt toate de tip ntreg sau de tip string, utilizarea altor tipuri de date n contextul instruciunii switch genernd erori. De regul, blocurile de instruciuni instr_s_c0, instr_s_c1, instr_s_c2, , instr_s_ck conin n calitate de ultim instruciune o instruciune de salt, care deseori este instruciunea break. Dac n blocurile date de instruciuni nu sunt instruciuni de salt, atunci firul de execuie va trece n urmtoarea ramur i acest proces de trecere dintr-o ramur n alta se va opri doar la ntlnirea unei instruciuni de salt. Instruciunea de salt break transmite dirijarea la urmtoarea instruciune dup switch. Instruciunile etichete case i default nu sunt ordonate n conformitate cu valorile expresiilor constante. Un operator care poate fi cercetat n contextul instruciunilor de selecie este operatorul ternar ?:. El are o mare similitudine cu instruciunea if-else. Forma general de utilizare poate fi urmrit dup descrierea:
-

(exp_condiional) ? exp1 : exp2

Dac exp_condiional este estimat la valoarea de adevr, atunci este evaluat expresia exp1, n caz contrar, este evaluat expresia exp1. De exemplu:
b=(a>0) ? 10 : 20;

expresie echivalent cu urmtoarea instruciune


if(a>0) b=10; else b=20;

Desigur c instruciunea if-else are un domeniu de aplicabilitate mult mai larg dect operatorul ternar, care este utilizat doar n contextul expresiilor ce genereaz valori. De aceea, n contextul operatorului ternar pot fi utilizate doar funcii care returneaz valori ca n exemplul urmtor:
s=(s==null) ? Console.ReadLine() : s;

Dac vor fi utilizate funcii care nu returneaz valori, vor fi generate erori sintactice. Instruciuni de iterare

29

Exist foarte muli algoritmi, a cror realizare necesit executarea repetat a unei mulimi de instruciuni. Pentru a organiza mai simplu n programe astfel de fragmente, care la execuie se repet, toate limbajele au instruciuni specializate, numite instruciuni de iterare. De regul, sunt mai multe forme ale instruciunilor de iterare, acomodate la diferite situaii. O instruciune care poate fi utilizat natural pentru situaii cnd este cunoscut a priori numrul de repetri ale mulimii de instruciuni este instruciunea for. Totui, aceast instruciune depete cu mult cadrul acestor situaii. Forma general de utilizare a instruciunii se supune urmtoarei descrieri:
for (iniializare; condiie; incrementare) insctruciune_simpl_sau_compus

Primul rnd al descrierii constituie antetul instruciunii, iar ceea ce urmeaz formeaz corpul instruciunii. Blocul iniializare permite fixarea valorilor iniiale a unor variabile, care vor fi utilizate n cadrul ciclului. Blocul condiie impune numrul de repetri efectuate la executarea instruciunii, iar incrementare permite schimbarea valorilor unor variabile, nainte de urmtoarea iteraie. Execuia instruciunii parcurge urmtorii pai: este executat blocul iniializare; este evaluat expresia condiie; a) dac se obine adevr este executat blocul instruciune_simpl_sau_compus; este executat blocul incrementare; trecere la pasul b); b) dac se obine fals, are loc trecerea la urmtoarea instruciune dup instruciunea for. Pentru a face iniializarea mai multor variabile, expresiile de iniializare sunt enumerate prin virgul:
var1=val1, var2=val2,..., vark=valk

O enumerare similar este utilizat i la incrementarea mai multor variabile. De exemplu:


int i, j; for (i=0, j=0; i+j <10; i++, j+=2) Console.WriteLine(({0} {1}), i, j);

Vor fi afiate perechi de numere de la (0 0) pn la (3 6). Exist cicluri for din care lipsesc unele blocuri sau chiar toate. Ciclul for fr blocul de iniializare:
. for ( ;i<k; i++) s+=i; .

30

Ciclul for fr blocul de condiie:


. for (j=-10 ; ; j++) { . . } . .

Lipsa condiiei este echivalent cu o condiie adevrat. Ciclul for fr blocul de incrementare:
. for (i=0 ;i<5;) { . . } .

Ciclul for fr toate blocurile, care este numit, de regul, ciclu infinit:
. for(;;) { . . } .

Instruciunea while este destinat pentru repetarea unui set de intruciuni fr a cunoate preliminar numrul de repetri. Numrul de repetri este determinat de o condiie: ct timp condiia va fi adevrat, setul de instruciuni va trece printr-o executare repetat. Forma general a instruciunii este:
while (condiie) instruciune_simpl_sau_compus

Instruciunea este executat n conformitate cu urmtorii pai: este evaluat expresia condiie; a) dac se obine adevr, este executat blocul instruciune_simpl_sau_compus; trecere la pasul a); b) dac se obine fals, are loc trecerea la urmtoarea instruciune dup instruciunea while. Ciclul for poate fi modelat utiliznd ciclul while:

31

(iniializare); while (condiie) { insctruciune_simpl_sau_compus (incrementare); }

De asemenea, se poate afirma c ciclul while poate fi modelat utiliznd ciclul for
for ( ;condiie; ) instruciune_simpl_sau_compus

Att ciclul for, ct i ciclul while fac parte din categoria de cicluri cu precondiie, fiindc mai nti este testat condiia i, n caz de adevr, vor fi executate instruciunile din cadrul ciclului. Se poate ntmpla ca instruciunile din cadrul ciclului s nu fie executate niciodat. Un alt reprezentant al instruciunilor de iterare este instruciunea do-while. Forma general de utilizare este prezentat de urmtoarea descriere:
do instruciune_simpl_sau_compus while (condiie);

Executarea ciclului do-while parcurge urmtorii pai: a) este executat blocul instruciune_simpl_sau_compus; este evaluat expresia condiie; b) dac se obine adevr, trecere la pasul a); c) dac se obine fals, are loc trecerea la urmtoarea instruciune dup instruciunea do-while. Instruciunea do-while este o intruciune de ciclu cu postcondiie, fiindc mai nti sunt executate instruciunile din corpul ciclului, iar apoi este testat condiia i, n caz de adevr, vor fi executate din nou instruciunile din corpul ciclului. Chiar dac condiia este evaluat la valoarea fals, instruciunile din corpul ciclului vor fi executate mcar o singur dat. Instruciunea do-while poate fi modelat prin instruciunea while, deci i prin for:
instruciune_simpl_sau_compus while (condiie) instruciune_simpl_sau_compus

O instruciune de iterare care poate fi utilizat doar n contextul coleciilor de date este foreach. Forma general de utilizare a instruciunii se supune urmtoarei descrieri:

32

foreach(tip numeVar in numeColectie) insctruciune_simpl_sau_compus

unde elementul tip reprezint tipul fiecrui element al coleciei, numeVar este variabila n care este citit valoarea curent din colecie, in este cuvnt-cheie, iar numeColectie este colecia elementele creea sunt parcurse. Variabila ciclului este definit doar n cadrul instruciunii. Execuia instruciunii parcurge urmtorii pai: a) are loc trecerea la elementul urmtor al coleciei; b) dac aciunea de trecere nu eueaz, este citit elementul curent al coleciei; este executat blocul instruciune_simpl_sau_compus; trecere la pasul a); c) dac aciunea de trecere eueaz, are loc trecerea la urmtoarea instruciune dup instruciunea foreach. Instruciunea de iterare foreach permite doar citirea elementelor coleciei fr posibilitatea de a schimba valoarea lor. Pentru a putea schimba valoarea unor elemente din colecie este utilizat oricare alt instruciune de iterare. n contextul instruciunilor de iterare, sunt aplicate dou instruciuni de salt. Instruciunea break executat n cadrul ciclului ntrerupe firul ciclului, trecnd la instruciunea urmtoare dup ciclu, dac break nu este subordonat intruciunii switch. Dac break se gsete ntr-o instruciune switch subordonat unui ciclu, atunci break va ntrerupe doar execuia lui switch. Instruciunea continuie ntrerupe iteraia curent a ciclului, ncepnd o nou iteraie. Reluarea iteraiei presupune i executarea blocului de incrementare, dac aceasta o cere logica ciclului ca n cazul instruciunii for.

Tipuri de date create de utilizator (Clase i structuri)


Pe lng tipurile predefinite, limbajul C# permite crearea de tipuri noi care ar putea descrie mai bine entitile necesare din anumite domenii de problem. Toate tipurile necesare sunt create n baza a dou concepte: 1) clase; 2) structuri. Aceste dou concepte sunt foarte asementoare din punctul de vedere al unor aspecte, dar sunt diferite din punctul de vedere al altora. Pentru a vedea asemnrile, vor fi efectuate aciuni paralele cu clase i cu structuri. Asemnrile dintre clase i structuri se manifest la nivel de:
33

descriere a tipului
class AngajatClass { public string Nume; public string Prenume; public string Adresa; } struct { public public public } AngajatStruct string Nume; string Prenume; string Adresa;

creare de obiecte
AngajatClass Ion = new AngajatClass(); AngajatStruct Vasile = new AngajatStruct();

utilizare a funcionalitii i caracteristicilor tipului


Ion.Prenume=Ion; Ion.Nume=Sirbu; Ion.Adresa= Bd.Mircea cel Batrin, 10; Vasile.Prenume=Vasile; Vasile.Nume=Virtosu; Vasile.Adresa= Str.Grenoble, 60;

Deosebirile dintre aceste concepte se reduc la urmtoarele:


Clasele reprezint abloane Structurile reprezint avnd menirea crerii de abloane avnd menirea tipuri de date refereniale. crerii de tipuri de date orientate la valoare. Clasele sunt tipuri de date Structurile sunt tipuri de n baza crora pot fi create date n baza crora nu pot fi alte tipuri de date prin create alte tipuri de date motenire. prin motenire. Clasele pot fi obinute prin Structurile pot fi obinute motenire de la o clas prin motenire de la i/sau interfee. interfee.

Deoarece clasele sunt mai complexe din punctul de vedere al posibilitilor i al implementrii, ele sunt mai des aplicate pentru crearea de tipuri de date. De aceea nti vor fi caracterizate mai detaliat clasele, iar apoi se va face o caracteristic mai amnunit i a structurilor. Descrierea claselor Pentru a acomoda procesul de programare la domeniul de problem, ar fi bine venit posibilitatea de a putea genera tipuri de date care modeleaz entiti din domeniul de problem. Aceste tipuri vor fi nite tipuri abstracte de date, avnd anumite proprieti i funcionaliti. O clas este anume expresia posibilitii de generare a unui tip abstract de date, care va fi ca un tipar, n baza cruia vor fi create variabilele necesare n procesul elaborrii algoritmilor, ce rezolv diferite sarcini din domeniul de problem.
34

n forma cea mai general o clas poate fi descris astfel


[modificator] class nume_clasa { //membrii clasei };

unde membrii clasei sunt de trei tipuri: a) date membre; b) funcii membre; c) tipuri de date interne. Cu ajutorul datelor membre pot fi descrise atributele care caracterizeaz proprietile claselor de obiecte reale, modelate n cadrul programelor create. Funciile membre, desigur, permit descrierea funcionalitii claselor de obiecte din domeniul de problem. Forma general de descriere a unei clase este urmtoarea:
[modificatori] class nume_clasa //identificator { [modificatori1] membru1; [modificatori2] membru2; - - - - - - - - - - [modificatorin] membrun; }

Componentele din descriere, cum ar fi modificatori1, modificatori2, ..., modificatorin, reprezint nite modificatori care dicteaz, n primul rnd, gradul de acces la membrii clasei i, totodat, i alte particulariti ale membrilor. Fiecare membru al clasei se afl sub aciunea unui modificator de acces. Modificatorii de acces sunt descrii prin cuvintele-cheie private, protected, internal, internal protected i public. Dac modificatorul de acces nu este specificat, se subnelege private.
Funcii membre ale clasei date private protected internal internal protected public + + + + + Funcii membre ale claselor derivate din asamblajul dat + + + + Funcii membre ale claselor derivate din alte asamblaje + + + Funcii membre ale claselor din asamblajul dat + + + Funcii membre ale claselor din alte asamblaje +

Datele membre sunt de urmtoarele tipuri: a) cmpuri;


35

b) constante; c) evenimente. Un cmp descrie caracteristici simple sau compuse ale unor obiecte concrete de anumite tipuri. Un cmp este declarat n felul urmtor:
[modificatori] tip nume_camp;

Cmpul poate fi descris oferindu-i, totodat, i o valoare iniial, adic iniializndu-l, ceea ce se realizeaz astfel:
[modificatori] tip nume_camp=initializator;

unde nume_camp este un indentificator pentru denumirea cmpului, tip descrie tipul cmpului dat, iar initializator reprezint procedura de iniializare. Iniializatorul este exprimat diferit n dependen de tipul cmpului. Pentru tipuri predefinite, poate fi scris astfel
[modificatori] tip nume_camp=valoare;

iar pentru tipuri definite de utilizator poate fi scris


[modificatori] tip nume_camp=new tip(. . .);

Dac cmpul nu este iniializat prin expresii de iniializare, atunci el va fi iniializat implicit prin zero dac cmpul este orientat la valoare sau prin null dac cmpul este referenial. Constantele sunt, de fapt, constante de tip. De aceea ele sunt descrise similar cu cmpurile iniializate, adugnd modificatorul const n faa descrierii, adic astfel:
[modificatori] const tip nume_camp=valoare;

Evenimentele sunt situaii de producere a ceva important n raport cu clasa dat. Descrierea unui eveniment se realizeaz n felul urmtor:
[modificatori] event numeDelegat numeEveniment;

Funcionalitatea clasei este exprimat prin funcii membre, numite i metode ale clasei. Fiecare funcie membr este descris n cadrul clasei prin definirea complet a funciei membre, ceea ce se numete implementare a funciei. Implementarea funciei este efectuat n interiorul clasei
[modificatori] { //instruciuni } tip_r nume_fm([tip1 par1[, tip2 par2[, ..., tipk park]]])

unde elementul modificatori reprezint lista modificatorilor aplicai funciei membr; nume_fm este identificator corect ce reprezint numele funciei membre; par1, par2, ..., park sunt identificatorii ce reprezint numele parametrilor fictivi, iar tip1, tip2, ..., tipk descriu tipurile acestor parametri. Functiile membre se mpart n urmtoarele categorii:
36

a) metode; b) constructori (funcii care particip n procesul de creare a obiectelor); c) destructori (funcii care particip n procesul de nimicire a obiectelor); d) proprieti (funcii care realizeaz acces la anumite cmpuri); e) indexatori (funcii care permit parcurgerea datelor din cadrul obiectului dup analogia elementelor unui tablou). f) operatori (care modeleaz anumii operatori ai limbajului (+ - * / .a.) pentru utilizare n cadrul clasei). n baza unei clase pot fi create nite exemplare concrete, ele purtnd denumirea de obiecte. Procesul de creare a obiectelor n baza unei clase se numete instaniere, iar exemplarul poart denumirea de instan a clasei. Poate fi observat o analogie ntre procesul de definire a unor variabile n baza unui tip predefinit i procesul de instaniere a unor obiecte n baza unei clase, care este, de asemenea, un proces de definire a unor variabile de tip clasa dat:
int i1, i2, i3; nume_clasa ob1, ob2, ob3;

Orice variabil nainte de a fi utilizat trebuie iniializat. Tot aa orice obiect trebuie iniializat. Un mod de iniializare a unui obiect poate fi realizat utiliznd operatorul new i constructorul necesar:
nume_obiect = new nume_clasa(lista_par_reali);

O clas const din mai muli membri i n procesul prelucrrii obiectelor membrii clasei pot fi implicai n acest proces, fiind accesai. Operatorul care permite accesul la un membru accesibil al clasei este operatorul . (punct). O form general de aplicare a acestui operator este urmtoarea:
nume_ob.nume_membru

Membri statici i nestatici Unul dintre modificatorii care poate fi aplicat la membrii unui tip de date este modificatorul static. Un membru static este mai degrab un membru interpretat la nivel de clas i nu la nivel de obiect al clasei date. Dac membrul static este un cmp, atunci valoarea acestui cmp va fi aceeai pentru toate obiectele create, spre deosebire de cmpurile nestatice, care au valori diferite pentru fiecare obiect. De aceea accesarea membrilor nestatici se face prin intermediul numelui obiectului pentru care se acceseaz membrii dai, iar accesarea membrilor statici se face prin intermediul numelui clasei n care sunt descrii, adic
nume_ob.nume_membru

pentru membri nestatici i


nume_tip.nume_membru

37

pentru membri statici. O funcie static foarte important este funcia Main(). Fiecare aplicaie trebuie s aib un punct de intrare. Anume prin funcia Main() este desemnat punctul potenial de intrare ntr-o aplicaie. O aplicaie poate consta din mai multe clase. Ca s existe punct de intrare n aplicaie, mcar o clas trebuie s conin funcia static Main(). Dac mai multe clase conin cte o funcie static Main(), atunci la compilare va fi desemnat o singur funcie Main(), care va reprezenta punctul de intrare n aplicaie. Forma general de descriere a funciei Main() este urmtoarea:
static void Main(string [] args) { . . . }

unde string [] args este un tablou prin intermediul cruia pot fi transmii parametri din exterior n funcia Main(). Dac nu este necesitate de transmitere a parametrilor din exterior, atunci acest element poate fi omis obinnd o form mai simpl.
static void Main() { . . . }

n continuare, urmeaz o mic exemplificare cu cmpuri statice i nestatice.


using System; namespace ExempluStatic { class staticNestatic { public int nsi; public static int si; } class Program { static void Main() { staticNestatic ob1=new staticNestatic(); staticNestatic ob2=new staticNestatic(); ob1.nsi=10; staticNestatic.si=100; Console.WriteLine({0} {1}, ob1.nsi, staticNestatic.si); ob2.nsi=20; Console.WriteLine({0} {1}, ob2.nsi, staticNestatic.si); }

38

} }

Fiindc funciile de asemenea pot fi membri statici, n continuare este prezentat un exemplu n care sunt realizate dou operaii aritmetice simple, una fiind funcie nestatic, iar alta fiind funcie nestatic.
using System; using System.Text; namespace OperatiiMatematice { class Matematica { private double x; public Matematica(double x) { this.x = x; } public double Inmultire(double y) { return x * y; } public static double Patrat(double y) { return y * y; } } class Program { static void Main(string[] args) { Matematica m = new Matematica(-256.32d); double i, p; i = m.Inmultire(435.35d); p = Matematica.Patrat(217.94); Console.WriteLine(i); Console.WriteLine(p); } } }

Transmiterea de parametri Transmiterea de parametri n funciile membre se face prin dou metode: a) prin valoare; b) prin referin.

39

Aceste dou metode de transmitere duc la comportamente diferite ale parametrilor transmii n funcii. Schimbarea n cadrul funciei a valorii parametrului transmis prin valoare nu este sesizat dup terminarea execuei funciei la parametrul real transmis n funcie. Pe de alt parte schimbarea n cadrul funciei a valorii parametrului transmis prin referin este sesizat dup terminarea execuei funciei la parametrul real transmis n funcie. Pentru a observa particularitile transmiterii de parametri de diferite tipuri, va fi prezentat un mic exemplu n care se vor ncerca nite schimbri asupra unor valori de diferite tipuri n cadrul unei funcii membre.
using System; namespace Parametri { class Program { static void Schimb(int [] tab, int i) { tab[0]=100; i=100; } static void Main() { int[] t={0, 1, 2, 3, 4}; int j=0; Console.WriteLine({0} {1}, j, t[0]); Schimb(t, j); Console.WriteLine({0} {1}, j, t[0]); }

} }

Rezultatul afirii va fi:


0 0 0 100

De aici se vede c schimbarea n cadrul funciei asupra elementului tabloului s-a produs pe cnd schimbarea valorii celeilalte variabile nu s-a produs. Aceasta se explic prin faptul c tabloul este un tip referenial, pe cnd tipul predefinit int este orientat la valoare. De aceea primul parametru este transmis prin referin, iar cel de al doilea este transmis prin valoare. De aici este formulat regula general: parametrii sunt transmii implicit n funcii dup felul tipurilor de date care i descriu, adic tipurile refereniale impun parametrilor transmiterea prin referin, iar tipurile orientate la valoare impun parametrilor transmiterea prin valoare. Dar nu totdeauna este util transmiterea parametrilor orienai la valoare prin valoare. Uneori este necesar transmiterea lor prin referin. Pentru asemenea

40

situaii, este prevzut cuvntul-cheie ref, care este aplicat n definirea funciei parametrului respectiv, impunnd transmiterea lui prin referin:
[modificatori] tipr nume_fun(..., ref tip nume_par, ...) { . . . }

Pentru funcia Schimb() din exemplul precedent ar putea fi urmtoarea definire


static void Schimb(int [] tab, ref int i) { tab[0]=100; i=100; }

Apelarea funciei care conine parametri fictivi descrii cu ajutorul cuvntuluicheie ref se va face descriind i parametrii reali corespunztori tot cu ajutorul cuvntului ref. Un exemplu utiliznd funcia Schimb() ar fi:
Schimb(t, ref j);

Din specificul parametrilor transmii prin referin se poate trage concluzia c ei pot fi folosii ca mijloc de a transmite valori n exteriorul funciei, adic un fel de valori returnate din funcie. Dar parametrii transmii implicit prin referin i cei transmii prin referin cu ajutorul cuvntului ref trebuie s aib anumite valori atunci cnd ajung n funcie, n caz contrar, este generat o eroare. Dar dac parametrii sunt introdui n funcie doar ca metod de returnare a valorilor, n acest caz, nu este necesar ca ei s fie iniializai cu anumite valori, care, de fapt, nu vor fi utilizate. Pentru asemenea situaii, vine n ajutor cuvntul-cheie out, care permite descrierea parametrilor fictivi transmii prin referin, dar care nu trebuie neaprat iniializai. Apelarea funciei care conine parametri fictivi descrii cu ajutorul cuvntului-cheie out se va face descriind i parametrii reali corespunztori tot cu ajutorul cuvntului out. n continuare, vine un mic exemplu care descrie aceste idei.
. . . static void Suma(int a, int b, out int suma) { suma=a + b; } static void Main() { int x = 109, y = 20, s; Suma(x, y, out s); Console.WriteLine({0} + {1} = {2}, x, y, s); } . . .

41

Constructori i destructori n procesul lucrului cu obiectele create n baza unor clase, una din cerine ar fi ca ele s aib anumit stare nainte de a fi implicate n operaiile efectuate. Adic, n mare parte, sunt necesare o serie de lucrri de pregtire a obiectelor. Dac uneori o pregtire incorect a obiectelor genereaz erori nesemnificative, n unele cazuri, totui urmrile pot fi mai grave. Pentru a evita, mcar parial, astfel de situaii, n cadrul claselor sunt utilizate nite funcii membre cu destinaie special, numite constructori. Constructorul este o funcie membr care are menirea de a pregti obiectele create pentru operaiile ulterioare. De aceea la procesul de creare a oricrui obiect particip i constructorul clasei. Exemple de aciuni puse n responsabilitatea constructorilor pot fi urmtoarele: iniializarea cmpurilor cu anumite valori; alocarea de memorie i iniializarea cmpurilor de tip pointer; deschiderea unor fiiere s.a. Constructorul unei clase are urmtoarele particulariti: numele constructorului este identic cu numele clasei; constructorul nu returneaz nici o valoare i deci, spre deosebire de celelalte funcii, i lipsete din antet sau prototip (signatur) descrierea tipului valorii returnate; constructorul nu poate fi apelat explicit dup modelul unei funcii membre obinuite. Este prezentat urmtoarea schem sintactic, care descrie utilizarea constructorilor:
class nume_cl { . . . [modificatori] nume_cl([tip1 p1[,[,tipk pk]]]) { . . . //instructiuni . . . } . . . }

Ar putea fi evideniate urmtoarele categorii de constructori: 1) constructori implicii; 2) constructori fr parametri; 3) constructori cu parametri; 4) constructori de copiere.

42

O clas poate s nu aib nici un constructor, poate avea unul sau mai muli constructori. Chiar dac programatorul nu definete n interiorul clasei un constructor, sistemul va crea automat un constructor, numit constructor implicit. Trebuie de subliniat faptul c constructorul implicit efectueaz operaii de iniializare a cmpurilor de tip valoarea cu valoarea de iniializare specificat sau cu zero dac nu este specificat nici o valoare de iniializare, iar cmpurile refereniale pot fi iniializate prin intermediul constructorului corespunztor sau sunt iniializate cu valoarea null. Constructorul de copiere permite construirea unui obiect n baza unui obiect existent. Dac programatorul nu definete un constructor de copiere, sistemul va genera un constructor de copiere, care permite obinerea unei copii perfecte a unui obiect. Dac o clas conine mai muli constructori i dac unii constructori sunt nite cazuri particulare ale altor constructori pentru anumite seturi de parametri, atunci pentru a simplifica descrierea clasei ar fi bine de a exprima unii constructori prin ali constructori. De exemplu, fie clasa Auto:
class Auto { string descriere; int numarUsi; public Auto(string d, int nu) { descriere=d; numarUsi=nu; } public Auto(string d) { descriere=d; numarUsi=4; } }

Se observ c al doilea constructior este un caz particular al primului constructor. De aceea el va fi exprimat prin primul astfel:
public Auto(string d):this(d,4) { }

n baza constructorilor pot fi create obiectele necesare. Procesul de instaniere depinde de constructorul utilizat, de aceea exist cteva forme de instaniere. 1) Prima form este urmatoarea:
nume_cl n_ob1=new nume_cl()[, n_ob2=new nume_cl() [, [, n_obk=new nume_cl()]]];

Ea este valabil atunci cnd clasa are fie constructor implicit, fie constructor fr parametri. 2) O alt variant are urmtoarea form:

43

nume_cl n_ob1=new nume_cl(pr11,, pr1n1)[, n_ob2=new nume_cl(pr21,, pr2n2) [, [, n_obk=new nume_cl(prk1,, prknk)]]];

Ea este valabil atunci cnd clasa are unul sau mai muli constructori cu parametri. 3) Ultima form de instaniere este urmatoarea:
nume_cl n_ob1=n_ob1[, n_ob2=n_ob2[, [, n_obk=n_obk]]];

unde obiectele n_ob1, n_ob2, , n_obk exist deja i n baza lor sunt create obiectele n_ob1, n_ob2, , n_obk. Aceast form funcioneaz n baza constructorului de copiere creat fie de sistem, fie de programator. O alt form echivalent, care este posibil doar n cazul cnd clasa este dotat cu un constructor de copiere, ar fi urmtoarea:
nume_cl n_ob1=new nume_cl(n_ob1)[, n_ob2=new nume_cl(n_ob2)[, [, n_obk=new nume_cl(n_obk)]]];

Ordinea de execuie a constructorilor coincide cu ordinea apariiei expresiilor de definire a obiectelor. O alt categorie de erori posibile n procesul de prelucrare a obiectelor ar putea fi neeliberarea unor resurse, legate de obiecte, atunci cnd obiectele i termin existena lor. n asemenea situaii, are loc o risip de resurse, fiindc, de fapt, este pierdut complet controlul asupra lor, odat cu lichidarea obiectului. Astfel de situaii pot fi ocolite, utiliznd nite funcii membre cu destinaie special, numite destructori. Destructorul este o funcie membru care are menirea de a efectua o serie de operaii n contextul distrugerii obiectului. De aceea la procesul de distrugere a oricrui obiect particip i destructorul clasei. Exemple de aciuni puse n responsabilitatea destructorilor pot fi urmtoarele: eliberarea memoriei asociate cu obiectul; nchiderea unor fiiere .a. Destructorul unei clase are urmtoarele particulariti: numele destructorului este identic cu numele clasei, fiind precedat de simbolul ~; similar cu constructorul, destructorul nu returneaz nici o valoare; destructorul nu are parametri fictivi; destructorul nu poate fi apelat explicit dup modelul unei funcii membre obinuite. Apelarea lui are loc automat, atunci cnd obiectul ajunge la sfritul domeniului su de scop. Este valabil urmtoarea schem, care descrie utilizarea destructorilor:
class nume_cl { . . . ~nume_cl() { . . . //instructiuni . . .

44

} . . . }

Clasa poate i s nu aib destructor. n acest caz, este creat un destructor implicit, care, de fapt, nu efectueaz anumite operaii n procesul de distrugere a obiectului. Clasa poate avea cel mult un destructor definit de programator. Destructorul se execut n ordine invers executrii constructorului. Proprieti Proprietile apar ca necesitate de a accesa cmpurile unei clase dup modelul accesrii directe a cmpurilor publice. Dat fiind faptul c declararea de cmpuri publice nu este salutat, se recurge la crearea unor funcii membre speciale care ar permite accesul necesar, ele fiind numite proprieti. Accesul la cmpuri poate fi de citire cnd este necesar a citi valoarea cmpului i poate fi de scriere cnd este necesar a schimba valoarea cmpului. Deci i proprietile sunt create n corespundere cu aceste dou tipuri de acces. De aceea pot fi proprieti doar de citire sau proprieti doar de scriere sau proprieti i de citire, i de scriere. Proprietile sunt definite i dup anumite principii sintactice. Ele pot consta din acceptorii get{} sau set{} pentru a implementa respectiv citirea sau scrierea. Descrierea general a unei proprieti este urmatoarea:
class nume_cl { . . . [modificatori] tipr nume_pr { get { . . . } set { . . . } } . . . }

Iat un exemplu de creare a unei proprieti n clasa dreptunghi


. . . class dreptunghi { private int latime; private int lungime; public int Latime { get {
45

} set { } } }

return latime;

latime=value;

. . . static void Main() { dreptunghi d1=new dreptunghi(); d1.Latime=100; Console.WriteLine({0}, d1.Latime); } . . .

Indexatori Indexatorii apar ca necesitate de a accesa informaia stocat n cmpurile unei clase dup modelul accesrii elementelor unui tablou. Pentru aceasta se recurge la crearea unor funcii membre speciale care ar permite accesul dat, ele fiind numite indexatori. Accesul la cmpuri poate fi de citire cnd este necesar a citi valoarea cmpului i poate fi de scriere cnd este necesar a schimba valoarea cmpului. De aceea i indexatorii sunt creai n corespundere cu aceste dou tipuri de acces. Deci pot fi indexatori care permit doar citirea sau indexatori care permit doar scrierea sau indexatori care permit i citirea, i scrierea. Indexatorii sunt definii i dup anumite principii sintactice. Ei pot consta din acceptorii get{} sau set{} pentru a implementa respectiv citirea sau scrierea. Descrierea general a unui indexator este urmatoarea:
class nume_cl { . . . [modificatori] tipr this[tip1 ind1, tip2 ind2, , tipn indn] { get { . . . } set { . . . } } . . . }

46

unde ind1, ind2, ..., indn sunt indicii fictivi de care depinde indexatorul, iar tip1, tip2, ..., tipn sunt respectiv tipurile lor. Un indexator depinde minimum de un indice fictiv. Cel mai simplu indexator este cel ce realizeaz modelul unui tablou unidimensional i deci va avea un singur indice fictiv de tip int. Iat un exemplu de creare a unui indexator n clasa colectie
. . . class colectie { private [] int lungimi; public colectie(int dim) { lungimi=new int[dim]; } public int this[int i] { get { return lungimi[i]; } set { lungimi[i]=value; } } } . . . static void Main() { colectie c=new colectie(10); for(int i=0; i<10; i++) c[i]=i; for(int i=0; i<10; i++) Console.WriteLine({0}, c[i]); } . . .

Constructori statici Un caz particular al funciilor membre statice sunt constructorii statici. O clas poate s nu aib constructor static sau poate avea doar unul singur. Menirea unui constructor static ar fi, de regul, iniializarea cmpurilor statice (static) sau a cmpurilor statice destinate doar pentru citire (readonly). O particularitate a constructorilor statici este faptul c ei nu au parametri. Totodat, un constructor static nu are aplicat un modificator de acces. Acest fapt nu are mare sens din motiv c el este executat de sistem doar o singur dat. Momentul apelrii nu este

47

suficient de determinat, astfel nct s se poat plasa n constructorul static linii de cod care presupun o executare dependent de anumite momente de timp. De exemplu, s se presupun c constructorul static al unei clase se va executa naintea constructorului static al altei clase. Ce este cunoscut cu siguran este faptul c el este executat naintea primei accesri a unui oarecare membru al clasei cu constructor static.
using System; namespace constructorStatic { class Preferinte { private static readonly ConsoleColor culoare; static Preferinte() { DateTime acum = DateTime.Now; if(acum.Day==DayOfWeek.Sunday || acum.Day==DayOfWeek.Saturday) culoare=ConsoleColor.Red; else culoare=ConsoleColor.Green; } public static Culoare { get { return culoare; } } } class Program { static void Main() { Console.ForegroundColor = Preferinte.Culoare; Console.WriteLine(Console.ForegroundColor); } } }

Cu toate c constructorul static nu are parametri, aceasta nu limiteaz existena i a unui constructor nestatic fr parametri, dac este necesar. Cmpuri destinate doar pentru citire (readonly) Posibilitatea de a defini cmpuri constante nu ofer soluii pentru toate situaiile posibile ntlnite n procesul de programare. Constantele au
48

particularitatea de a putea fi doar citite i de a nu putea fi schimbate pe parcursul execuiei programului, dar ele au dezavantajul de a trebui s fie iniializate nainte de a executa programul. ns exist situaii cnd un cmp va conine anumit informaie care nu va fi schimbat pe parcursul execuiei programului, adic va fi doar pentru citire, dar valoarea de iniializare a cmpului va fi tiut doar n procesul de rulare a programului. Pentru astfel de situaii, cmpurile constante nu mai sunt bune. De aceea este introdus o noiune nou, i anume cmpuri doar pentru citire. Ele vor avea particularitile de a putea fi doar citite, de a nu putea fi schimbate pe parcursul execuiei programului i de a fi iniializate o singur dat. Cmpurile doar pentru citire sunt definite aplicnd cuvntul-cheie readonly:
[modificatori] readonly tip nume_cimp;

Aplicnd modificatorul static, se obine categoria cmpurilor doar pentru citire statice. Ca rezultat sunt cmpuri doar pentru citire de dou tipuri statice i nestatice. Iniializarea cmpurilor doar pentru citire se face n procesul rulrii programului i poate fi realizat doar n cadrul constructorilor: cele statice n constructorul static, iar cele nestatice n constructori de instan (nestatici). n paragraful precedent a fost deja prezentat un exemplu, n care a fost utilizat un cmp doar pentru citire static. Utilizarea structurilor Cu toate asemnrile dintre clase i structuri enumerate anterior, ele au totui unele particulariti distinctive. Clasele, fiind tipuri refereniale de date, au un mecanism de dirijare a memoriei destul de sofisticat. De aceea n unele situaii, cnd sunt modelate nite tipuri de date relativ simple, ar fi o irosire de resurse utilizarea claselor. n acest caz, ar fi mai raional utilizarea structurilor care au un mecanism de dirijare a memoriei mai simplu din motiv c ele creeaz tipuri de date orientate la valoare i de aceea utilizeaz memoria de tip stiv. Un exemplu simplu ar fi tipul de date Dreptunghi constnd din dou cmpuri publice. El poate fi realizat sub form de clas:
class DreptunghiClasa { public int lungime; public int latime; }

Dar, desigur, fiind att de simplu, ar fi mai raional descrierea lui sub form de structur:
struct DreptunghiStruct {

49

public int lungime; public int latime; }

Dat fiind faptul c structurile sunt tipuri de date orientate la valoare, ele au particularitatea de a permite definirea de variabile care nu sunt create neaprat cu ajutorul constructorului pentru a putea fi utilizate. Astfel urmtorul cod este perfect valabil pentru structuri.
DreptunghiStruct d; d.lungime=10;

Un cod echivalent se obine aplicnd constructorul


DreptunghiStruct d = new DreptunghiStruct(); d.lungime=10;

Dac s-ar ncerca acelai lucru n baza unei clase dup cum urmeaz
DreptunghiClasa d; d.lungime=10;

ar fi semnalat eroare. Obiectul de tip clas neaprat este definit cu ajutorul constructorului nainte de a putea fi folosit
DreptunghiClasa d = new DreptunghiClasa(); d.lungime=10;

Aa ceva este posibil din motiv c la definirea unei variabile n baza unei structuri pentru variabil se aloc spaiu, fiind definite toate cmpurile ei. Este ca i cnd sunt executai pe rnd constructorii pentru fiecare cmp al structurii. Astfel, dac variabila exist, nseamn c i cmpul component exist, putnd fi utilizat. O serie de particulariti ale structurilor sunt legate de constructori. Cmpurile sunt iniializate de constructorul implicit doar prin zerografiere. Cmpurile unei structuri nu admit expresii de iniializare. Totodat, n cadrul unei structuri nu este admis crearea unui constructor fr parametri, care ar suprancrca constructorul implicit. Fa de constructorii cu parametri nu exist restricii. Un alt dezavantaj al structurilor ar putea fi transmiterea prin valoare a unei structuri n calitate de parametru unei funcii. Fiind un tip de date orientat la valoare, transmiterea prin valoare duce la crearea copiei parametrului respectiv, ceea ce ar putea rpi din timpul de execuie a programului. ns acest dezavantaj poate fi lichidat transmind structura prin referin cu ajutorul cuvntului-cheie ref. Realizarea conceptului de motenire Obiectele din domeniul de problem nu sunt statice unul fa de altul, ci interacioneaz ntre ele. Vom spune c obiectele se afl n anumite relaii. Sunt

50

mai multe tipuri de relaii, dar un tip foarte important ar fi relaiile de motenire, cnd un obiect transmite o serie de proprieti i operaii altui obiect. Fiindc obiectele de acelai tip reprezint o clas de obiecte, se poate vorbi i despre relaii ntre clase, generalizndu-le pe cele enumerate anterior la nivel de clase. Utiliznd relaia de motenire n procesul programrii, se obin aplicaii eficiente din punctul de vedere al mrimii codului obinut i al lipsei de erori. S presupunem c trebuie de realizat un sistem de gestiune n cadrul sistemului de fiiere. n domeniul de problem pot fi evideniate o serie de obiecte caracteristice. Ca exemplu ar putea fi evideniate obiectele Program i Document. Dac ar fi proiectate tipuri abstracte de date corespunztoare acestor obiecte, evideniind proprietile i operaiile caracteristice lor, s-ar putea propune urmtoarele elemente: Document Nume Dimensiune Data Tip Copiere Nimicire Redenumire Afiare Imprimare Program Nume Dimensiune Data TipSistemOperare Copiere Nimicire Redenumire Lansare

Cercetnd atent schiele propuse, se poate observa c exist o serie de proprieti i o serie de operaii comune pentru ambele obiecte. Definind funciile membre ale ambelor clase, vom avea o serie de funcii din diferite clase realizate identic. Deci se obine o dublare de cod. O cale mai eficient ar fi posibilitatea de evideniere i implementare a unei clase generale, care ar putea transmite caracteristicile ei altor clase mai concretizate. Astfel n cazul exemplului nostru sar putea evidenia obiectul general numit Fisier, care va conine caracteristicile cele mai importante ale obiectelor ce in de sistemul de fiiere. Fiier Nume Dimensiune Data Copiere Nimicire Redenumire
51

Transmiterea de proprieti i funcionaliti dintr-un tip de date numit tip de baz ntr-un tip numit tip derivat se numete motenire singular. Procesul de transmitere se numete derivare. La nivel de diagram, motenirea singular are urmtoarea reprezentare:
tip_baza

Document Tip Afisare Imprimare

Program TipSistemOperare Lansare

tip_der1

tip_der2

...

tip_derk

La nivel de descriere prin construcii ale limbajului C#, avem urmtoarea descriere general:
[modificatori] descr_tip1 tip_baza { . . . } //--------------[modificatori] descr_tip2 tip_der1 : tip_baza { . . . } ... //--------------[modificatori] descr_tipk tip_derk : tip_baza { . . . }

unde descr_tip1, descr_tip2, , descr_tipk sunt cuvinte-cheie care permit descrierea unui tip de date (class, struct, interface), iar tip_baza, tip_der1, , tip_derk sunt denumirile tipurilor de date implicate n procesul de motenire. n C# exist dou moduri de motenire: motenire de clas (de implementare); motenire de interfa. Cu toate c exist noiunea de motenire de clas multipl i exist limbaje care au implementat aceast noiune, totui limbajul C# permite doar motenirea

52

de clas singular. Cu toate acestea, totui este realizat motenirea multipl, dar de interfa. Cu referin la structuri, vor fi evideniate urmtoarele restricii: structura nu poate fi un tip derivat prin derivarea de clas; structura nu poate juca rolul tipului de baz n procesul de derivare; structura poate fi un tip derivat prin derivarea de interfa. Schema ce urmeaz descrie posibilitatea de motenire multipl de la mai multe interfee:
. . . [modificatori] descr_tip tip_der : interf1, interf2, ..., interfn { . . . } . . .

Un obiect creat n baza clasei derivate are o serie de caracteristici dependente de clasa de baz. Este important a ti gradul de acces la aceti membri din punctul de vedere al clasei derivate. tiind modificatorii de acces ai membrilor clasei de baz, poate fi determinat gradul de accesibilitate la membrii dai n raport cu clasa derivat. Tabelul de mai jos descrie gradele respective de acces:
Modificator de acces private al membrilor din protected clasa de baz public Grad de acces la membrii clasei de baz din clasa derivat inaccesibil protected public

Motenirea de implementare Motenirea de implementare este realizat doar prin mostenirea singular de clas. Concretiznd schemele generale pentru cazul clasei, se obine urmtoarea decsriere:
. . . [modificatori] class nume_clBaza { . . . } [modificatori] class nume_clDer : nume_clBaza { . . . } . . .

Un obiect creat n baza clasei derivate are o serie de caracteristici dependente de clasa de baz. De aceea, n procesul de creare a acestui obiect, va

53

participa att constructorul clasei de baz, ct i constructorul clasei derivate. Acest fapt este vizibil i la nivel de descriere general a constructorului clasei derivate:
[modificatori] cl_der(lista_pf_cd) : base(lista_pf_cb) { . //instructiuni . }

Fiindc procesul de creare a unui obiect n baza clasei derivate depinde de doi constructori, este bine a ti ordinea execuiei lor: mai ntai va lucra constructorul clasei de baz, iar apoi va lucra constructorul clasei derivate. Ordinea de execuie a destructorilor este invers: mai inti lucreaz destructorul din clasa derivat, iar apoi lucreaz destructorul din clasa de baz. n procesul de motenire are importan nu doar relaia dintre constructori i relaia dintre destructori, ci i relaia dintre alte tipuri de metode, care aparin clasei de baz i clasei derivate. Metode virtuale (Modificatorul virtual) Poate fi evideniat categoria important a metodelor virtuale. O metod virtual are proprietatea de a putea fi schimbat n cadrul tipurilor derivate din clasa de baz, schimbndu-i sensul dup necesitate n clasa derivat. O metod virtual este introdus prin intermediul cuvntului-cheie virtual:
[modificatori] class nume { . . . [modificatori] virtual tipr nume_fv(lista_pf) { . . . } . . . }

Dac metoda virtual este schimbat n una dintre clasele derivate, atunci la schimbare metoda dat este introdus prin intermediul cuvntului-cheie override:
. . . [modificatori] override tipr nume_fv(lista_pf) { . . . } . . .

54

Pentru ca s fie realizat conceptul de polimorfism, n clasa derivat prototipul (signatura) funciei trebuie s fie identic cu prototipul descris n clasa de baz. La cea mai mic deosebire dintre prototipuri, polimorfismul nu va fi realizat. Pentru a beneficia de polimorfism, trebuie folosit un obiect de tip clasa de baz. n acest obiect pot fi plasate obiecte de tip clas de baz sau obiecte de tipul uneia dintre clasele derivate. Apelnd prin intermediul acestui obiect funcii virtuale, va fi obinut efectul de polimorfism. Deci polimorfismul este obinut din diversitatea formelor de realizare a unei funcii virtuale, fie realizarea din clasa de baz, fie realizrile din clasele derivate. Dac obiectul conine un obiect de tipul clasei de baz, atunci la apelarea unei funcii virtuale va fi apelat anume funcia realizat n clasa de baz. Dac ns obiectul conine un obiect de tipul unei clase derivate, atunci la apelarea unei funcii virtuale va fi apelat funcia realizat anume n clasa derivat. Deci obiectul dat este sensibil la tipul obiectului pe care l conine, determinnd i apelnd funcia care corespunde obiectului dat. n continuare, sunt reprezentate schematic ideile expuse mai sus:
cl_baza baza; cl_der1 ob_der1=new cl_der1(. . .); cl_der2 ob_der2=new cl_der2(. . .); . . . cl_derk ob_derk=new cl_derk(. . .); baza=ob_der1; baza.nume_fv(lista_pr);// apelare metoda din cl_der1 baza=ob_der2; baza.nume_fv(lista_pr);// apelare metoda din cl_der2 . . . baza=ob_derk; baza.nume_fv(lista_pr);// apelare metoda din cl_derk

Modificatorul new O alt situaie poate apare atunci cnd n clasa derivat este definit o metod cu acelai nume ca i n clasa de baz. Acest fapt poate fi intenionat sau ntmpltor. ntmpltor aceast situaie poate apare n procesul unei dezvoltri ndelungate a unor programe. De exemplu, la un anumit moment, se dorete reproiectarea clasei de baz prin adugarea unei metode noi, care ntmpltor exist deja n clasa derivat. Regulile limbajului nu interzic existena unor astfel de metode, att doar c la nivel de mediu de dezvoltare apare o prentmpinare i ca ea s dispar se cere o confirmare a programatorului c el este de acord cu o astfel de situaie, cerndu-i-se aplicarea modificatorului new metodei date:
. . . [modificatori] new tipr numeMetoda(lista_pf)

55

{ . . . } . . .

Modificatorul sealed n procesul proiectrii claselor, sunt admise ierarhii de motenire cu mai multe niveluri. Funciile virtuale pot fi scimbate la orice nivel al ierarhiei de motenire. Dar uneori apare situaia de a interzice posibilitatea de schimbare a funciilor virtuale ncepnd cu un anumit nivel al ierarhiei de motenire. Pentru astfel de situaii este prevzut modificatorul sealed:
. . . [modificatori] sealed override tipr numeMetoda(lista_pf) { . . . } . . .

Modificatorul sealed poate fi aplicat nu doar la nivel de metod ci i la nivel de clas:


[modificatori] sealed class nume { . . . }

n acest caz, clasa dat nu va mai putea participa n procesul de motenire n calitate de clas de baz. Acest fapt poate fi folosit de firmele productoare de soft pentru a nu permite dezvoltarea de clase noi prin derivare din clasele propuse pe pia de ele. Drept exemplu pot o serie de clase elaborate de firma Microsoft i care fac parte din platforma .NET. Modificatorul abstract O categorie de metode virtuale speciale sunt metodele abstracte. Prototipurile acestor metode sunt descrise cu ajutorul modificatorului abstract:
. . . [modificatori] abstract tipr nume_f(lista_pf); . . .

Clasele care conin mcar o funcie abstract sunt numite i ele clase abstracte. n acest caz, este necesar a aplica modificatorul abstract i la nivel de clasa:
[modificatori] abstract class nume

56

{ . . . }

Pot exista clase abstracte care conin doar funcii abstracte. Caracteristica principal a funciilor abstracte const n faptul c ele nu sunt realizate n clasa lor de declarare. Deci, clasa conine doar descrierea prototipului funciei, fr a fi definit i corpul funciei. Aa o situaie poate fi deseori explicat prin faptul c noiunea descris de clas este una foarte abstract i la nivelul dat de abstractizare este imposibil de a defini algoritmic comportamentul operaiei anunate de funcia abstract. Drept rezultat al existenei n clasa abstract de funcii nedefinite este interzis de a crea obiecte n baza unei clase abstracte. O clas abstract poate fi folosit doar ca element de creare a unor noi clase, utiliznd mecanismul de motenire. Deci n procesul de generare a unei clase derivate clasa de baz poate fi i clas abstract. Dac n clasa derivat obinut sunt definite toate funciile abstracte motenite din clasa de baz, atunci obinem o clas obinuit n baza creia putem defini obiecte. Funciile abstracte fac parte din categoria funciilor virtuale, de aceea definirea lor n cadrul clasei derivate se face aplicndu-le modificatorul cunoscut override. Dac ns n clasa derivat obinut rmne nedefinit mcar o funcie abstract, atunci clasa derivat va fi n continuare una abstract. Trebuie de subliniat faptul c pot exista situaii cnd n procesul de motenire sunt implicate doar clase abstracte. Clasele abstracte sunt asemantoare cu interfeele. Totui exist o serie de particulariti ale claselor abstracte care le deosebesc de interfee: Clasele abstracte pot avea cmpuri, pe cnd interfeele nu. Unele funcii ale clasei abstracte pot fi definite, pe cnd nici o interfaa nu poate conine funcii definite. Membrii clasei abstracte sunt descrii prin modificatori de acces, pe cnd membrii intefeei sunt lipsii de modificatori. Modificatori aplicabili n contextul tipurilor de date O serie de modificatori au fost caracterizai deja. n continuare, va fi fcut o sintez a tuturor modificatorilor aplicabili n contextul tipurilor de date. Tabelul ce urmeaz conine informaia necesar:
Nr Modificator d/o 1 public 2 private Aplicabil Oricrui tip oricrui membru Oricrui tip oricrui membru
57

Descriere succint i Impune accesibilitate din orice loc al programului i Impune accesibilitate doar n cadrul clasei

protected

Membrilor clasei, tipurilor incluse

internal

Membrilor clasei, tipurilor incluse Membrilor clasei, tipurilor incluse

protected internal

new

Funciilor membre

virtual

Funciilor membre

override

Funciilor membre

abstract

Tipurilor de date i funciilor membre

10

sealed

Tipurilor de date i funciilor membre

11

partial

Tipurilor de date
58

din care face parte Impune accesibilitate n cadrul clasei din care fac parte i n cadrul claselor derivate din aceast clas. Impune accesibilitate n cadrul asamblajului din care fac parte Impune accesibilitate n cadrul asamblajului din care fac parte sau n cadrul claselor derivate din clas din care fac parte Permite existena n clasele de baz i derivat a funciilor membre cu acelai nume Permite existena n clasele de baz i derivat a funciilor membre cu acelai prototip, care pot avea forme diferite Permite schimbarea n clasa derivat a funciilor membre virtuale Permite existena funciilor membre fr realizare i a tipurilor de date care conin astfel de funcii Impune imposibilitatea motenirii de la tipul dat sau imposibilitatea schimbrii funciei virtuale Arat divizarea

realizrii de date

unui

tip

Motenirea de interfa O interfa poate fi definit ca o reuniune de signaturi a unor metode i descrieri de evenimente, oferind astfel o schem general de funcionalitate. Exist situaii cnd sunt cunoscute aciunile generale admise n anumite contexte generale, dar concretizarea aciunii la nivel algoritmic este posibil doar ntr-un context concret. n astfel de situaii, pot fi utilizate interfeele, descriind aciunile date la nivel de prototipuri de funcii. Totodat, interfeele pot fi privite i ca un mecanism de standardizare, impunnd prin intermediul lor respectarea acelorai condiii. Ele sunt ca un contract pe care l respect cei care implementeaz interfaa dat. Dac nu ar fi implementate n cadrul altor tipuri de date, interfeele nu ar avea mare valoare. Dat fiind posibilitatea de descriere a unor funcionaliti generale, exist deja multe interfee descrise la nivelul platformei .NET. Totodat, orice programator poate s-i declare interfeele care i sunt necesare n procesul soluionrii unor probleme concrete. O interfa este descris utiliznd cuvntul-cheie interface n felul urmtor:
[modificatori] interface numeInterfata { tipr1 numeMetoda1. . .; . . . tiprk numeMetodak. . .; event numeDelegat1 numeEv1; . . . event numeDelegatm numeEvm; }

unde tipr1, ..., tiprk sunt tipurile valorilor returnate de metode, numeMetoda1, ..., numeMetodak sunt denumirile metodelor ce fac parte din interfaa, iar numeEv1, ..., numeEvm sunt denumirile evenimentelor, create n baza delegailor numeDelegat1, ..., numeDelegatm, ce intr n componena interfeei. Nici o descriere nu are modificator de acces. Modificatorii de acces vor apare doar la implementarea metodei. Metodele pot fi de urmtoarele tipuri: funcii, proprieti, indexatori. O interfa nu poate conine operatori. Numele unei interfee poate fi orice identificator corect, ns s-a format deja o tradiie de a ncepe denumirea interfeei cu simbolul I. De exemplu, la nivel de .NET este descris interfaa foarte simpl ca i construcie System.IDisposable.IDisposable:
public interface IDisposable

59

{ }

void Dispose();

Menirea ei este de a implementa mecanismul de eliberare a resurselor neutilizate. O alt interfa important este interfaa IEnumerable, destinat crerii de colecii care pot fi parcurse cu ajutorul ciclului foreach. Implementarea metodelor unei interfee este realizat n cadrul unei structuri sau a unei clase. Acest moment este organizat ca un proces de motenire a interfeei de ctre structur sau clas. Va fi caracterizat n continuare doar cazul clasei, fiindc cazul structurii sau al clasei sunt similare. Deci n caz general se poate scrie:
. . . class nume_cl [: interf1[, interf2[, ...[, interfn]]]] { . . . } . . .

Clasa dat va fi obligat s implementeze metodele fiecrei interfee de la care motenete. Iat n cadrul clasei vor fi aplicai modificatori de acces la metodele implementate. Este interesant faptul c o interfa poate moteni de la alte interfee:
. . . interface nume_interf [: interf1[, interf2[, ...[, interfn]]]] { . . . } . . .

Interfaa derivat obinut este echivalent cu reuniunea tuturor metodelor din toate interfeele. Un moment interesant i important este faptul c metodele interfeelor sunt privite ca nite metode virtuale, ceea ce permite realizarea de polimorfism de interfa. Deci interfaa va juca rolul unui tip de baz, permind variabile de tip interfa. n rest polimorfismul este obinut dup schema tradiional: tip de baz tip derivat. Exemplu. De alctuit un program n care este declarat o interfa care descrie un cont bancar, utiliznd-o pentru derivarea unor clase, care modeleaz conturi bancare.
using System; namespace Cont { public interface IContbancar { void Adaug(decimal s);

60

} public interface ITransferbancar : IContbancar { bool Transfer(IContbancar cont, decimal s); } public class Victoria : IContbancar { private decimal suma; public Victoria(decimal s) { suma = s; } public void Adaug(decimal s) { suma += s; } public bool Extrag(decimal s) { if (suma >= s) { suma -= s; return true; } else return false; } public decimal Balanta { get { return suma; } } } public class Mobias : ITransferbancar { private decimal suma; public Mobias(decimal s) { suma = s; } public void Adaug(decimal s) { suma += s; } public bool Extrag(decimal s) { if (suma >= s)

bool Extrag(decimal s); decimal Balanta { get; }

61

{ } else

suma -= s; return true;

return false; } public decimal Balanta { get { return suma; } } public bool Transfer(IContbancar cont, decimal s) { bool rez; rez= Extrag(s); if (rez) cont.Adaug(s); return rez; } } class Program { static void Main(string[] args) { IContbancar cv; ITransferbancar cm; cv = new Victoria(1000m); cm = new Mobias(2500m); cv.Adaug(500m); Console.WriteLine(cv.Balanta); Console.WriteLine(cm.Balanta); cm.Transfer(cv, 1500); Console.WriteLine(cv.Balanta); Console.WriteLine(cm.Balanta); } }

Testarea la egalitate Pentru a efectua testarea la egalitate, sunt realizate mai multe posibiliti. n primul rnd, chiar n cadrul clasei object exist mai multe metode de comparare. Unele dintre ele sunt statice, iar altele virtuale, putnd fi suprancrcate.

62

Astfel una dintre metode este ReferenceEquals(), fiind o metod static, orientat la compararea la egalitate a adreselor a dou obiecte A i B, returneaz true sau false. Ea are urmtorul prototip:
static bool ReferenceEquals(object A, object B);

Dac ambele obiecte au valoarea null, este returnat valoarea true. Dac un obiect are valoarea null, iar cellalt este diferit de null, este returnat valoarea false. Dac ambele obiecte sunt diferite de null, este returnat valoarea true dac ele reprezint aceeai adres, sau false dac ele reprezint adrese diferite. O alt funcie este funcia nestatic Equals(). Ea are urmtorul prototip:
virtual bool Equals(ob);

Ca i n cazul funciei precedente, ea compar referinele a dou obiecte returnnd true dac sunt egale sau returnnd false dac sunt neegale. Fiind ns virtual, ea poate fi suprancrcat n cadrul unor clase, fcndu-se astfel acomodarea la specificul de comparare a obiectelor generate n baza claselor respective. Deci, dac este necesar, varianta suprancrcat a funciei ar putea compara obiectele la nivel de valoare. O astfel de suprancrcare ar putea fi necesar la crearea coleciilor de tip dicionar. Fiind funcie nestatic, este apelat astfel:
ob1.Equals(ob2)

O alt variant de funcie, dar deja static, este funcia Equals()cu urmtorul prototip:
static bool Equals(ob1, ob2);

Ca i n cazul funcilor precedente, ea compar referinele a dou obiecte. Compararea are loc n felul urmtor: dac ambele obiecte sunt null, ea returneaz true, dac un obiect este null, iar cellalt este diferit de null, funcia returneaz false i dac ambele obiecte sunt nenule, atunci este apelat varianta virtual a funciei. Din acest mod de execuie, se poate trage concluzia c dac este suprancrcat funcia virtual, este obinut i efectul de suprancrcare a funciei statice. O alt posibilitate de testare a egalitii este operatorul ==. Trebuie de subliniat faptul c dac se dorete o utilizare a lui n cadrul unei clase, el trebuie suprancrcat n aceast clas. Pentru tipurile predefinite orientate la valoare, acest operator face compararea la nivel de valoare. Pentru tipul de date string, operatorul == este suprancrcat ca s fac compararea corect a irurilor de caractere. Structurile sunt tipuri de date orientate la valoare, ns n cadrul lor operatorul == trebuie suprancrcat pentru a putea fi utilizat la nivel de comparare a valorilor. Este interesant faptul c funciile date pot fi utilizate i n contextul variabilelor de tipuri orientate la valoare, cum ar fi multe dintre tipurile predefinite.

63

ns rezultatele aplicrii vor fi deseori neateptate. De exemplu, fie variabila v1 de tip long. Este aplicat funcia ReferenceEquals() astfel:
long.ReferenceEquals(v1,v1)

Se poate atepta ca funcia s returneze true, avnd aceeai variabil pe ambele poziii. Totui funcia va returna false. Motivul este c variabila v1 va fi transformat la un tip referenial (operaia boxing). Dar aceast operaie va fi efectuat separat de dou ori obinnd ca rezultat dou obiecte diferite, avnd deci i referine diferite. De aceea o astfel de utilizare a funciei ReferenceEquals() nu are prea mult sens. Sens are utilizarea uneia dintre funciile Equals(), fiindc ele sunt acomodate pentru compararea tipurilor orientate la valoare. Astfel urmtoarele expresii
long.Equals(v1,v1)

sau
v1.Equals(v1)

dau acelai rezultat, i anume true. i n cazul structurilor funciile Equals() sunt acomodate pentru a face compararea orientat la valoare.

Construcii auxiliare
Comentarii Un element opional, dar important ntr-un program, sunt comentariile utilizate pentru o documentare mai bun a programului. Exist comentarii bloc, care pot fi scrise pe mai multe linii. nceputul comentariului este marcat prin perechea /*, iar sfritul comentariului prin perechea */. n continuare, sunt prezentate diferite stiluri de utilizare a comentariilor bloc.
/* Exemplu de comentariu bloc /* Exemplu de comentariu bloc */

*/

/******************************** * Exemplu de comentariu bloc * ********************************/ /* * */ Exemplu de comentariu bloc

Remarc: comentariile bloc nu pot fi incluse unul n altul.

64

Un alt tip de comentariu implementat este comentariul linie. nceputul comentariului este marcat de perechea // comentnd toat informaia pn la sfritul rndului.
// Exemplu de comentariu linie int i; // variabila de contor a ciclului

Remarc: comentariile linii pot fi incluse n comentarii bloc. Comentarii de documentare Un alt tip de comentarii sunt comentariile de documentare. Ele permit descrierea unei aplicaii la nivel de tipuri de date (clase sau structuri), la nivel de funcii membre, la nivel de cmpuri. Comentariile de documentare ncep cu /// (trei oblice) fiind urmate de o serie de marcaje cu anumit funcionalitate de descriere. Din comentariile de documentare poate fi generat la compilare un document XML care descrie elemente utilizate pentru crearea aplicaiei. Pentru a obine documentul dat, la compilare din linia de comand se aplic opiunea /doc. Iat un exemplu:
csc.exe /doc:DocProg.xml prog.cs

Mediul de dezvoltare Visual Studio, de asemenea, permite fixarea unei opiuni pentru a fi generat i fiierul XML de documentare n baza comentariilor de documentare. Aceast opiune se fixeaz n felul urmtor: - n Solution Explorer click butonul drept al oricelului pe numele proiectului; - din meniul de context este selectat opiunea Properties; - din fereastra proprietilor este selectat pagina Build; - n aceast pagin este bifat opiunea XML documentation file. Iat cum arat fereastra dat dup bifare (cu rou este ncercuit opiunea XML documentation file):

65

n continuare, este prezentat un exemplu de program n care sunt utilizate comentarii de documentare la nivel de clas, la nivel de cmp, la nivel de metode.
using System; namespace Comentarii { /// <summary> /// Tipul abstract de date Student /// </summary> /// <remarks> /// Utilisare /// <example>Student s=new Student("Sirbu","Ion");</example> /// </remarks> class Student { /// <summary> /// Informatia de identitate /// </summary> private string nume; private string prenume; /// <summary>
66

/// /// /// ///

/// Notele studentului /// </summary> private int[] note = new int[30]; /// <summary> /// Constructorul clasei /// </summary> /// <param name="n"></param> /// <param name="p"></param> public Student(string n, string p) { nume = n; prenume = p; } /// <summary> /// Proprietate care returneaza numele /// <value>returneaza numele studentului</value> /// </summary> /// <seealso cref="nume"/> public string Nume { get { return nume; } } /// <summary> /// Functie indexator /// </summary> /// <param name="i"></param> /// <returns>Nota cu indicele i</returns> public int this[int i] { get { return note[i]; } } <summary> Punctul de intrare in program. </summary> <param name="args"></param> static void Main(string[] args) { Student st = new Student("Sirbu", "Ion"); Console.WriteLine(st.Nume); Console.ReadLine(); }

67

} }

Ca rezultat al compilrii programului se obine un document XML, generat ca rezultat al prelucrrii comentariilor de documentare. Documentul respectiv este prezentat in continuare.
<?xml version="1.0"?> <doc> <assembly> <name>Comentarii</name> </assembly> <members> <member name="T:Comentarii.Student"> <summary> Tipul abstract de date Student </summary> <remarks> Utilisare <example>Student s=new Student("Sirbu","Ion");</example> </remarks> </member> <member name="F:Comentarii.Student.nume"> <summary> Informatia de identitate </summary> </member> <member name="F:Comentarii.Student.note"> <summary> Notele studentului </summary> </member> <member name="M:Comentarii.Student.#ctor(System.String,System.String)"> <summary> Constructorul clasei </summary> <param name="n"></param> <param name="p"></param> </member> <member name="M:Comentarii.Student.Main(System.String[])"> <summary> Punctul de intrare in program. </summary> <param name="args"></param> </member> <member name="P:Comentarii.Student.Nume"> <summary>

68

Proprietate care returneaza numele <value>returneaza numele studentului</value> </summary> <seealso cref="F:Comentarii.Student.nume"/> </member> <member name="P:Comentarii.Student.Item(System.Int32)"> <summary> Functie indexator </summary> <param name="i"></param> <returns>Nota cu indicele i</returns> </member> </members> </doc>

Cercetnd acest document se poate observa c numele membrilor ncep cu simboluri descriptive pentru a sugera mai bine ce descriu. Astfel sunt utilizate urmtoarele simboluri: T pentru a sugera c este vorba despre un tip de date. M pentru a sugera c este vorba despre o metod. P pentru a sugera c este vorba despre o proprietate. F pentru a sugera c este vorba despre un cmp. Exist mai multe marcaje care au fiecare funcii de descriere speciale. Marcajele cele mai importante sunt prezentate n tabelul ce urmeaz.
Marcaje
<c> <code>

Descriere succint
Permite inserarea unei linii de cod. Permite inserarea unui fragment de cod.

Exemple de utilizare
///<c>int i;</c>

<exemple>

<list>

<item>

///<code> /// int i; /// i=100; ///</code> Permite ///<exemple> inserarea unui /// int i; exemplu. /// i=100; ///</exemple> Permite crearea ///<list> unei liste, /// <item>a</item> enumerri. /// <item>b</item> ///</list> Permite Vezi <list> descrierea elementelor unei liste.

69

<para> <include>

<param>

<permission>

<remarks>

<returns>

<see>

<seealso>

<summary> <values>

<exception>

Permite inserarea unui alineat. Permite includerea unui fragment dintrun fiier existent. Permite descrierea parametrilor unei metode. Permite documentarea accesuluiu la membru. Permite generarea unei descrieri detaliate. Permite descrierea valorii returnate de metod. Genereaz o referin la un moment deja descris Genereaz o referin la un moment deja descris. Permite generarea unei descrieri. Permite descrierea valorilor returnate de proprieti. Permite descrierea situaiilor excepionale.

///<para> /// Alineat ///</para> ///<include file='C:\Salut.xml' ///path='doc/members/member ///[@name="T:Salutare.Program"]'/> ///<param name="n"> ///</param>

///< permission cref="nume"/>

///<remarks> /// O descriere mai ampla. ///</remarks> ///<returns> /// Este returnat maximum. ///</returns> ///<see cref="nume"/>

///<seealso cref="nume"/>

///<summary> /// O descriere mai ampla. ///</summary> ///<values> /// Este returnat numele /// persoanei. ///</values> ///<exception cref="nume"/>

Mediul de dezvoltare Visual Studio are implementat mecanismul de generare automat a abloanelor pentru comentariile de documentare. Dac sunt culese trei linii oblice ///, atunci automat sunt generate unele marcaje. ns apariia marcajelor depinde de locul de culegere a oblicelor. Locuri sensibile sunt liniile care preced declararea unui tip de date, definirea unei metode, declararea unui cmp.
70

Spaii de nume Platforma .NET conine o mulime bogat de clase i o serie de alte construcii utile n procesul de creare a aplicaiilor. Pe de alt parte, programatorul poate crea clasele i construciile proprii. Acest proces poate duce la generarea unor nume identice cu cele existente. Pentru a micora probabilitatea unor astfel de situaii toate numele sunt grupate n spaii de nume. Exist o ierarhie de spaii de nume a platformei .NET. Totodat, platforma .NET permite descrierea spaiilor de nume necesare n contextul unei aplicaii concrete. Descrierea unui spaiu de nume este facut astfel:
namespace numeSpatiu { . . . }

unde numeSpatiu este un identificator corect care reprezint denumirea spaiului de nume. Un spaiu de nume poate s conin diferite elemente componente, cum ar fi clase, structuri, delegai, interfee i alte spaii de nume. Astfel poate fi obinut o structur ierarhizat de spaii de nume:
namespace numeSp1 { . . . namespace numeSp2 { . . . } namespace numeSp3 { . . . } . . . }

O ierarhie de spaii de nume poate avea i urmtoarea structur: un spaiu de nume este corpul altui spaiu de nume care, la rndul su, este corpul altui spaiu de nume i aa mai departe. O astfel de ierarhie poate fi redus la un spaiu de nume, dar cu un nume compus din numele tuturor spaiilor de nume concatenate prin punct. De exemplu, urmtoarea ierarhie
namespace numeSp1 { namespace numeSp2 { namespace numeSp3
71

{ . . . } } }

se reduce la urmtorul spaiu de nume:


namespace numeSp1.numeSp2.numeSp3 { . . . }

Toate elementele componente ale unui spaiu de nume sunt caracterizate printr-un nume absolut (complet), constnd din numele spaiului concatenat prin punct cu numele elementului:
numeSpatiu.numeElement

De aceea diferite spaii de nume pot conine elemente care au acelai nume, deoarece numele lor complet va fi totui diferit. Numele complet al unui element poate conine concatenate mai multe spaii de nume, dac elementul aparine unui spaiu de nume din cadrul unei ierarhii de spaii de nume:
numeSp1.numeSp2. .numeElement

Pentru a simplifica expresile de adresare la componentele unui spaiu de nume, este utilizat instruciunea using care permite procesul de importare a spaiului de nume necesar:
using numeSpatiu; . . .

Dac un spaiu de nume este importat n aplicaie, adresarea la elementul necesar se face doar prin numele elementului, i nu prin numele lui complet, adic se omite numele spaiului de nume. Numele spaiului importat poate fi unul compus dac spaiul este un element al unei ierarhii de spaii:
using numeSp1.numeSp2. .numeSpatiu; . . .

Instruciunea using poate fi folosit i pentru crearea de aliasuri, adic nite nume mai simple pentru oarecare combinaie de nume:
using numeAlias=nSp1.nSp2. .nSpk;

unde numeAlias este un identificator corect. De exemplu,


using A=R1.R2;

permite o scriere mai simpl a urmtoarei expresii


R1.R2.B ob=new R1.R2.B(...);

n felul urmtor
A.B ob=new A.B(...);

72

Totui, nu totdeauna importarea de spaii de nume permite simplificarea numelor utilizate. De exemplu, spaiile de nume nsp1 i nsp2 conin clasa ex:
namespace nsp1 { . . . class ex { . . . } . . . } namespace nsp2 { . . . class ex { . . . } . . . }

Chiar dac sunt importate spaiile de nume nsp1 i nsp2 la folosirea clasei ex se va face uz de numele complet nsp1.ex sau nsp2.ex, pentru a specifica anume clasa care este utilizat:
using nsp1; using nsp2; . . . nsp1.ex ob=new nsp1.ex();

Directivele preprocesorului Programele scrise n C# pot conine n afara instruciunilor i a expresiilor, care sunt construcii referitoare nemijlocit la limbaj, o serie de construcii care sunt prelucrate nainte de a fi efectuat compilarea codului-surs al programului. Datorit acestei prelucrri, ele sunt denumite directive ale preprocesorului. Directivele se deosebesc de construciile limbajului prin semnul # plasat naintea numelui directivei. Regulile de inserare a directivelor n program sunt urmtoarele: fiecare directiv trebuie scris ntr-un singur rnd; amplasarea directivei n cadrul rndului nu este important; amplasarea numelui directivei fa de # nu este important.
#define XP

73

#define define XP

XP

Aceste trei rnduri reprezint nite definiri echivalente ale constantei cu numele XP. Directivele sunt utilizate pentru: a defini constante simbolice; a permite o compilare condiional a programului; a evidenia un fragment de cod; a schimba contorul liniilor. Pentru a defini constante simbolice, este utilizat directiva #define. Definirea unei constante este realizat n felul urmtor:
#define numeConstSimb

unde numeConstSimb este un identificator care descrie numele constantei simbolice. De exemplu,
#define DIMENSIUNE

O directiv legat de directiva #define este directiva #undef. Forma general de aplicare a acestei directive este
#undef nume

unde parametrul nume reprezint numele unei construcii definite prin #define. Aciunea acestei directive este foarte simpl, ea anulnd din acest punct aciunea construciei definite anterior cu ajutorul directivei #define. n procesul elaborrii de programe, apar uneori situaii cnd programul const dintr-un fragment principal comun pentru toate cazurile i fragmente de cod pentru situaii concrete care se exclud unele pe altele. De exemplu, dac este realizat un program care va avea versiuni pentru diferite sisteme de operare i unele fragmente sunt realizate diferit pentru diferite sisteme, atunci versiunea pentru Windows XP nu are pentru ce s conin fragmentul de cod pentru sistemul Windows 2003 i invers. Pentru a putea selecta simplu fragmentele care se refer la o situaie concret, sunt utilizate directivele condiionale, care au ceva asemnare cu instruciunea de selecie if. Pentru a indica sfritul aciunii unei directive condiionale, este utilizat totdeauna directiva #endif. O alt directiv este #if cu urmtoarea form general
#if condiie bl_instr1 #else bl_instr2 #endif

74

care testeaz parametrul condiie i dac este obinut valoarea de adevr, este inclus spre compilare blocul de instruciuni bl_instr1, excluzndu-se blocul bl_instr2, iar dac este obinut valoarea fals, este inclus blocul bl_instr2 i este exclus blocul bl_instr1. Poate fi utilizat i forma simpl a directivei
#if condiie bl_instr1 #endif

Parametrul condiie poate fi o condiie simpl sau compus, admind n calitate de elemente constitutive constante simbolice, operatorii relaionali == i ! =, operatori logici ai limbajului &&, ||, !. O condiie simpl testeaz dac constanta simbolic este descris sau nu. Dac constanta este definit, condiia are valoare de adevr, n caz contrar, are valoare de fals. De exemplu:
#if TEST Console.WriteLine(t1={0}, t2={1}, t1, t2); #else Console.WriteLine(t3={0}, t4={1}, t3, t4); #endif

Constantele simbolice nu au valori explicite, totui ele pot fi comparate cu dou valori de tip bool true sau false. Exemplul de mai sus poate fi rescris n urmtorul mod echivalent:
#if TEST==true Console.WriteLine(t1={0}, t2={1}, t1, t2); #else Console.WriteLine(t3={0}, t4={1}, t3, t4); #endif

Pentu a obine o condiie compus, vor fi reunite mai multe condiii simple cu ajutorul operatorilor logici. De exemplu:
#if (TEST && !(TRACE==false)) Console.WriteLine(t1={0}, t2={1}, t1, t2); #else Console.WriteLine(t3={0}, t4={1}, t3, t4); #endif

Directivele condiionale pot fi incluse una n alta obinnd astfel directive imbricate. n asemenea caz, sunt folosite tot attea directive #endif cte i directive de tip #if. Exist o schem general de imbricare care se aseamn cu scria if-else, dar n acest caz mai este utilizat o directiv care ar fi combinarea directivelor #else i #if. Aceast directiv este #elif. Cnd este utilizat o astfel de schem, directiva #endif este utilizat o singur dat la sfritul construciei. Iat care ar fi schema general a acestei construcii:
#if cond1 bl_instr1

75

#elif cond2 bl_instr2 . . . #elif condk bl_instrk #else bl_instr0 #endif

Logica execuiei construciei date este simpl: mai nti este testat condiia din prima directiv #if i dac ea este adevrat, atunci este inclus spre compilare blocul de instruciuni bl_instr1, iar blocurile bl_instr2,..., bl_instrk, bl_instr0 vor fi excluse, dac este fals, atunci va fi testat condiia cond2 din directiv #elif i dac ea este adevrat, atunci este inclus spre compilare blocul bl_instr2, iar blocurile bl_instr1, bl_instr3, ..., bl_instrk, bl_instr0 vor fi excluse .a.m.d. Dac condiiile din toate directivele #elif sunt false, atunci va fi inclus spre compilare blocul bl_instr0, dac ramura #else exist, celelalte blocuri de instruciuni fiind excluse. Dac ns ramura #else nu exist, atunci vor fi excluse din compilare toate blocurile de instruciuni participante n construcia dat. O directiv utilizat la dirijarea procesului compilrii este directiva #error, avnd urmtoarea form:
#error mesaj

care dac este ntlnit, oprete aciunea de compilare, afind mesajul din parametrul mesaj.
#if VECTOR int [] vect=new int[100]; #else #error Vector nedefinit #endif

n procesul compilrii unui program, pentru a putea mai uor localiza erorile, sunt contorizate liniile programului. Valoarea iniial a contorului este 1. n mesajele de eroare sau de avertizare apare i numrul liniei n care este depistat problema. Totodat, n mesajele de acest tip poate aprea i numele fiierului n care se afl programul compilat. n unele circumstane, ar fi necesar posibilitatea schimbrii valorii iniiale a contorului liniilor sau i numele fiierului, care apare n mesajele compilatorului. La astfel de necesiti este orientat directiva #line
#line contor_nou [nume_fisier]

unde parametrul contor_nou permite reiniializarea valorii iniiale a contorului liniilor programului, iar nume_fisier este un nume nou care va aprea n mesaje
76

n locul denumirii fizice a fiierului. Pentru a sublinia c al doilea parametru este opional, el este inclus n paranteze ptrate. Rezultatul aciunii directivei se reduce la urmtoarea idee: pn la directiv va fi o numrare a liniilor n baza unui contor, iar din punctul apariiei directivei, contorul este reiniializat la valoare din directiv i n continuare aceast valoare va sta la baza numrrii liniilor. Aceeai idee este legat i de al doilea parametru: din punctul apariiei directivei n mesaje, va aprea numele propus n directiv. De exemplu,
. . . #linie 100 static void Main() { inti; . . . } . . .

la compilarea programului anterior va fi generat mesajul de eroare simbol nedefinit, fcndu-se referin la linia 102, iar programul va fi identificat cu numele fizic al fiierului n care este nscris programul. Dac va fi folosit acelai program trivial, dar cu umtoarea directiv:
. . . static void Main() { #line 10 altnume.c inti; . . . } . . .

n mesajul de eroare va fi menionat linia 10, iar numele de identificare a programului va fi cel descris n directiva #line. n afar de acest mod de utilizare a directivei #line, mai exist posibilitatea de a folosi dou etichete cu aceast directiv: default i hidden. Deci se obin urmtoarele variante ale directivei #line:
#line default

i
#line hidden

Prima variant permite nlocuirea unei linii terse, pstrnd astfel neschimbat numrul de linii al programului i deci fiecare linie i va pstra numrul de ordine iniial. A doua variant permite ascunderea fragmentului de cod de urmeaz directivei pn la urmtoarea directiv de tip #line. Ascundere va nsemna salt peste fragment n procesul execuiei pe pai. Ascunderea nu influeneaz nicicum

77

calcularea numrului de linii ale programului. Calcularea numrului de linii va fi fcut lund n consideraie i liniile din fragmentul ascuns. Directiva #region ncepe delimitarea unui fragment de cod, care trebuie s fie ncheiat cu directiva #endregion. Deci aceste directive acioneaz n pereche. Directiva #region are urmtoarea form:
#region [nume]

unde nume reprezint denumirea fragmentului de cod, fiind opional, adic poate lipsi. Acest element poate consta din orice combinaie de simboluri ale alfabetului limbajului. De exemplu
#region Functia Main static void Main() { . . . } #endregion

Fragmentul delimitat prin perechea de directive #region - #endregion are n cadrul mediului de dezvoltare Visual Studio proprietatea de a putea fi comprimat pentru a obine spaiu de redactare pentru alte fragmente sau a putea fi desfurat pentru a fi vizualizat. La comprimare fragmentul va fi reprezentat printr-o box n care este numele din directiva #region, de exemplu
Functia

sau nsi #region dac directiva este anonim.


#region

Caracterizare operatori
Clasificare operatori Limbajul C# are o gam bogat de oretatori, ceea ce deschide posibiliti largi n prelucrarea datelor. Tabelul ce urmeaz vine cu o clasificare i o caracterizare succint a operatorilor existeni n limbaj.
Nr. d/o
1 2 3 4 5 6 7

Categorie operatori
Aritmetici Logici i relaionali Concatenare Incrementare i decrementare Deplasament Comparare Atribuire + - * / & ^ | ~ + ++ -<< >> == != < = += -=

Operatori
% && || !

> <= >= *= /= %= &= |= ^= ...

78

8 9 10 11 12 13 14 15

Accesare membri Indexare Conversie Condiional Creare obiect Informaie despre tip Depire Adres

. [] () ?: new sizeof is typeof as checked unchecked * -> &

O serie de operatori enumerai mai sus permit operaii cu tipurile de date. Astfel operatorul is permite testarea dac o expresie este de careva tip. Forma general ar fi
exp is tipDeDate

unde exp este expresia testat, iar tipDeDate reprezint tipul de date n conformitate cu care se face testarea. Valoarea returnat de operator este de tip bool i va fi true, dac expresia este de tipul dat, sau va fi false, dac tipul expresie nu va corespunde tipului testat. Iat un exemplu
string s = Student; if(s is string) Console.WriteLine("Expresia este de tip string"); else Console.WriteLine("Expresia nu este de tip string");

care va afia mesajul Expresia este de tip string. Un alt operator important este operatorul as. Acest operator permite conversia unei expresii la un anumit tip referenial. Forma general este
exp as tipDeDate

unde exp este expresia convertit, iar tipDeDate reprezint tipul de date n conformitate cu care se face transformarea. Valoarea returnat de operator este expresia convertit la tipul specificat, dac conversia este posibil i null, dac expresia nu poate fi convertit la tipul de date specificat. Avantajul utilizrii operatorului as este faptul c el totdeauna ofer o valoare i nu lanseaz excepii, atunci cnd apar situaii de necorespundere a tipului. Urmeaz un exemplu
object o = Student; string s = o as string; Console.WriteLine("{0}",s);

Va fi afiat cuvntul Student, fiindc conversia la string a fost posibil. Dac rescriem puin exemplul dup cum urmeaz
object o = 125; string s = o as string; Console.WriteLine("{0}",s);

nu va fi afiat nimic, fiindc conversia la string nu a fost posibil.

79

Un alt operator este sizeof, care ofer mrimea locaiei ocupate de o variabil de un anumit tip de date msurat n octei. Forma de utilizare este urmtoarea
sizeof(tipDeDate)

unde tipDeDate reprezint tipul de date a crui mrime este determinat. Pentru tipurile predefinite orientate la valoarea operatorul dat nu are restricii, ns pentru tipurile refereniale predefinite i pentru tipurile definite de programator acest operator poate fi utilizat doar n regim nesigur (unsafe). Un operator care face controlul unor expresii la depire este checked. Operatorul care anuleaz acest control este unchecked. Acest operator are aciune de bloc i aciune de expresie. Pentru a controla un bloc de instruciuni, este introdus cu ajutorul acoladelor astfel:
checked { . . . //instruciuni i expresii . . . }

n exemplul ce urmeaz va fi semnalat eroare din cauza depirii.


byte b=255; checked { b++; }

Pentru a controla o expresie, ea este introdus cu ajutorul parantezelor astfel:


checked (exp)

Acest fel de utilizare este permis doar n cadrul unor expresii. De exemplu:
byte b=252; int i = checked(b++)+10;

Conversia datelor Limbajul C# este un limbaj construit pe principiul urmririi cu strictee a tipurilor datelor care particip n expresii. Cu toate acestea este totui posibil mixarea unor date de diferite tipuri n expresii. De exemplu, o astfel de situaie ar putea fi normal n contextul limbajului C#:
int a=10, b=15; float f=1.42; double d=57.5; char c=a;

80

. . . // undeva n program ar fi permisa o asa expresie r=a*b+f*c+d/f;

Astfel de situaii sunt reglementate utilizndu-se conversia datelor. Conversia este o transformare a datelor, avnd ca scop trecerea lor de la anumite tipuri de date la altele. Conversia este de dou tipuri: conversie automat considerat implicit; conversie forat considerat explicit. Conversia implicit nu presupune participarea programatorului n procesul de conversie. Conversia implicit are dou forme: conversie implicit la atribuire; conversie implicit n expresii. Conversia implicit la atribuire are loc, atunci cnd tipul valorii din partea dreapt a operatorului de atribuire nu coincide cu tipul descriptorului din partea stng a operatorului. De exemplu:
int a=-101, b=156; float f; f=a+b;

Conversia implicit la atribuire nu are loc pentru orice tipuri ale expresiilor din partea stng i partea dreapt a operatorului de atribuire. Dac conversia la atribuire ar duce la pierdere de informaie, atunci o astfel de conversie nu va fi efectuat implicit. Dac tipul expresiei din partea dreapt a operatorului de atribuire este mai slab dect tipul descriptorului din partea stng a operatorului, atunci conversia va avea loc implicit. Un tip de date este mai slab dect alt tip de date dac domeniul lui de valori este mai restrns dect al celuilalt tip. Urmtorul tabel ofer toate posibilitile de conversie implicit a datelor, considernd tipurile predefinite orientate la valoare:
Tip de date Conversie implicit

sbyte byte short ushort int uint long ulong float char

short, int, long, float, double, decimal short, ushort, int, uint, long, ulong, float, double, decimal int, long, float, double, decimal int, uint, long, ulong, float, double, decimal long, float, double, decimal long, ulong, float, double, decimal float, double, decimal float, double, decimal Double short, ushort, int, uint, long, ulong, float, double, decimal

81

Atunci cnd sunt mixate n expresii date de diferite tipuri, pentru a putea fi obinut valoarea expresiei, se recurge la algoritmul conversiei automate n expresii. Cu referire la tipuri predefinite de date, acest algoritm are urmtoarele prevederi: mai nti, toate datele de tip char, de tip byte, de tip sbyte, de tip short i de tip ushort sunt convertite la int. Mai departe este aplicat urmtorul algoritm:
Dac un operand este de tip double i al doilea operand nu este de tip decimal Atunci i al doilea operand este trecut la double Altfel Dac un operand este de tip float i al doilea operand nu este de tip decimal Atunci i al doilea operand este trecut la float Altfel Dac un operand este de tip decimal i al doilea operand nu este de tip double sau float Atunci i al doilea operand este trecut la decimal Altfel Dac un operand este de tip long i al doilea operand nu este de tip ulong Atunci i al doilea operand este trecut la long Altfel Dac un operand este de tip ulong i al doilea operand nu este de tip int Atunci i al doilea operand este trecut la ulong Altfel Dac un operand este de tip int i al doilea operand este de tip uint Atunci ambii operanzi sunt trecui la long

Algoritmul general mai necesit unele precizri. Dac un operand este de tip decimal, iar cellalt este fie de tip double, fie de tip float sau dac un operand este de tip ulong, iar cellalt este fie de tip long, fie de tip int, conversia automat va fi imposibil i o serie de operaii nu vor putea fi efectuate. Pentru a nelege mai bine modul de aplicare a conversiei automate n expresii, va fi prezentat urmtorul exemplu
char ch=c; int i=102: float f=3.42; double d=38.9, r; r = ( ch / i) + (f * d) - (f + i); i i f d f i i d f d d

Ca rezultat al conversiei automate, valoarea expresiei din partea dreapt a operatorului de atribuire va fi de tip double.

82

Conversia explicit a datelor are loc, atunci cnd programatorul impune datelor o transformare de trecere de la un tip de date la altul. Conversia forat poate fi obinut plasnd numele tipului de date n faa expresiei transformate:
(tip)expresie

De exemplu, dac ar fi necesar a vedea de cte ori este mai mare valoarea unei variabile ntregi fa de valoarea altei variabile ntregi, s-ar efectua operaia de mprire:
int i=10, j=4; float raport; raport=i/j;

Valoarea obinut n variabila raport va fi una aproximativ egal cu 2, fiindc avem o mprire ntreag. Pentru a corecta rezultatul obinut, trebuie aplicat operaia de conversie forat mcar unui operand:
raport=(float)i/j;

n acest caz, este obinut valoarea 2.5 care este cea cutat. Conversia explicit poate duce la pierdere de informaie. De aceea aciunea de conversie explicit poate fi testat cu ajutorul operatorului checked. De exemplu
long l=300000000000; int i; i=checked((int)l);

Poate fi efectuat conversia unei valori de tip orientat la valoare ctre o valoare de tip referenial object. O astfel de transformare poart denumirea de mpachetare (boxing). Poate fi efectuat i conversia invers, adic de la o valoare de tip referenial object ctre o valoare de anumit tip valoric. O astfel de transformare poart denumirea de despachetare (unboxing). n continuare, urmeaz cte un exemplu de mpachetare
int i = 123; object o = i; //impachetare implicita

i despachetare
object o = 123; int i = (int)o; //despachetare explicita

Suprancarcarea operatorilor Un tip de date este caracterizat nu doar prin domeniul de valori admisibile, dar i printr-o serie de operaii care sunt permise asupra lor, numit funcionalitatea tipului. n cadrul tipurilor predefinite, operaiile sunt exprimate prin operatori, ceea

83

ce nu este valabil pentru tipurile abstracte de date create de programator. n cadrul tipurilor abstracte de date, funcionalitatea este exprimat prin funcii membre. Totui, expresiile scrise n baz de operatori sunt mai simple i pot fi mai uor citite, comparativ cu cele scrise n baz de funcii. De exemplu, expresia
C+A*B

exprimat prin operatori este mult mai simpl dect expresia


C.Adunare(A.Produs(B))

exprimat prin funcii. Pentru a extinde posibilitatea de utilizare a unui operator n contextul unor obiecte, este necesar a realiza procesul de suprancrcare a operatorului dat n cadrul clasei n baza creia sunt definite obiectele. O suprancrcare a unui operator n interiorul unei clase nseamn a-i da un anumit sens operatorului vizat n cadrul clasei date, pstrndu-i sensul sau semnificaia pentru celelalte tipuri de date. Suprancrcarea operatorilor este legat de suprancrcarea funciilor, fiindc procedeul de suprancrcare a operatorilor se realizeaz n baza funciilor. n procesul de suprancrcare a operatorilor, sunt respectate o serie de restricii: suprancrcarea nu poate schimba aritatea operatorului, adic dac operatorul este unar sau binar, atunci dup suprancrcare el va fi respectiv tot unar sau binar; suprancrcarea nu poate schimba precedena operatorilor. n continuare, va fi caracterizat procedeul de suprancrcare a operatorilor. Particularitatea principal este faptul c funcia membru, prin intermediul creia va fi efectuat suprancrcarea unui operator, va fi o funcie cu un nume special format prin utilizarea cuvntului-cheie operator. Totodat, este important faptul c aceast funcie va fi una static. n rest este o funcie obinuit i deci va fi definit ca i oricare alt funcie membru a clasei:
class nume_cl // orice operator supraincarcabil { . . . public static tip_r operator (lista_pf) { //instructiuni } . . . }

unde este numele operatorului suprancrcat, iar lista_pf reprezint lista parametrilor fictivi ai funciei. Lista parametrilor fictivi conine, de regul, doi parametri pentru operatorii binari sau un parametru pentru operatori unari.

84

Exemplu. De alctuit un program n care este implementat structura vector, definind operatorii caracteristici vectorilor prin suprancrcarea operatorilor.
using System; namespace SpatiuVector { struct vector { float x; float y; float z; public vector(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public vector(vector v) { this.x = v.x; this.y = v.y; this.z = v.z; } public static vector operator *(vector v, float c) { return new vector(c * v.x, c * v.y, c * v.z); } public static vector operator *(float c, vector v) { return v * c; } public static vector operator +(vector v1, vector v2) { vector vr = v1; vr.x += v2.x; vr.y += v2.y; vr.z += v2.z; return vr; } public static float operator *(vector v1, vector v2) { float produs; produs = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; return produs; } public override string ToString() { return"("+x.ToString()+","+y.ToString()+","+z.ToString()+")"; }

85

} class Program { static void Main(string[] args) { vector v1 = new vector(2.3f, 4.3f, -3.7f); vector v2 = new vector(6.3f, -4.9f, 7.5f); vector suma; float produs; suma = 12.4f * v1 + v2; Console.WriteLine(suma); produs = v1 * v2; Console.WriteLine(produs); Console.Read(); } }

Pentru a suprancrca corect operatorii, trebuie de tinut cont i de o serie de cerine. 1. Operatorul [] (indice) nu este suprancrcat prin metoda obinuit, ci definind funcia indexator n cadrul clasei. 2. Operatorii compui de atribuire (+=, -=, *= .a.m.d.) nu sunt suprancrcai explicit, ci suprancrcnd operatorul n baza cruia este construit (+, -, * .a.m.d.) este obinut automat i suprancrcarea operatorului corespunztor de atribuire. 3. Suprancrcarea operatorilor relaionali < > <= >= == != se face doar n pereche, adic dac este suprancrcat operatorul > neaprat trebuie efectuat i suprancrcarea operatorului <. Perechile corespund gruprii anterioare. Urmeaz o exemplificare care ar putea fi inclus n clasa vector:
. . . public static bool operator ==(vector v1, vector v2) { if(v1.x==v2.x && v1.y==v2.y && v1.z==v2.z) return true; else return false; } public static bool operator !=(vector v1, vector v2) { return !(v1==v2); } . . .

86

Operatorii de incrementare (++) i de decrementare (--) au formele prefix i postfix. Efectund o suprancrcare a operatorului necesar, se obine realizarea ambelor forme.
4.

Conversia datelor de tip clas i structur Despre conversia datelor s-a vorbit deja n unul dintre paragrafele anterioare. Dar ideile legate de conversie au fost axate n principal pe tipurile de date predefinite. Nu trebuie de uitat faptul, c pe lng tipurile predefinite, mai exist o categorie important de tipuri de date, i anume cele create de programator pentru a modela anumite entiti din domeniul de problem. n cadrul acestei categorii de date, de asemenea, poate apare necesitatea de a converti datele de anumit tip la alt tip de date fie predefinit, fie definit de utilizator. Este bine de amintit faptul c n cadrul datelor predefinite sunt conversii implicite i explicite. n cadrul tipurilor predefinite, algoritmul efecturii conversiilor fie implicite, fie explicite este fixat n limbaj. Dac se trece la tipurile de date definite de programator, vor fi posibile aceleai conversii implicite i explicite, att doar c algoritmul trecerii de la un tip de date la alt tip de date va trebui s fie descris algoritmic. De aceea printre multitudinea de operatori ai limbajului va fi evideniat operatorul de conversie care st la baza fie a descrierii, fie a realizrii conversiei datelor. Pentru a realiza conversia datelor de la un anumit tip de date la altul, n cadrul tipului dat va fi efectuat suprancrcarea operatorului de conversie. Prin supcancrcarea operatorului de conversie va fi efectuat descrierea algoritmic a procesului de transformare a unui tip n altul. Suprancrcarea operatorului de conversie se obine n conformitate cu urmtoarea descriere:
class numeTip { . . . public static mod_conversie operator tipRez(numeTip val) { //instructiuni } . . . }

unde val este valoarea convertit de la tipul clasei numeTip la tipul tipRez, iar modConversie fixeaz modul implicit sau explicit de conversie, fiind exprimat respectiv prin cuvintele-cheie implicit sau explicit. La baza selectrii modului de conversie vor sta urmtoarele idei: dac conversia nu duce la pierdere de informaie, este selectat modul implicit de conversie, iar dac este posibil anumit pierdere de informaie, este selectat modul explicit de conversie. Fiindc
87

nu doar clasele sunt tipuri definite de programator, ci i structurile, se va proceda ntr-un mod similar i n cazul structurilor.
using System; namespace Conversie { enum curs { lei=10, bani=60 } struct Lei { private uint lei; private uint bani; public Lei(uint l, uint b) { lei = l; bani = b; } public static implicit operator float(Lei L) { return (L.lei + L.bani / 100.0f); } public static explicit operator Lei(float S) { return new Lei((uint)S, (uint)((S - (uint)S) * 100)); } public static implicit operator Dolari(Lei L) { float c = (uint)curs.lei + (uint)curs.bani / 100f; float s = L; return (Dolari)(s / c); } public static implicit operator Lei(Dolari D) { float c = (uint)curs.lei + (uint)curs.bani / 100f; float s = D; return (Lei)(s * c); } public override string ToString() { return lei.ToString()+"L"+bani.ToString()+"B"; } } struct Dolari { private uint dolari; private uint centi; public Dolari(uint d, uint c) {

88

dolari = d; centi = c; } public static implicit operator float(Dolari D) { return (D.dolari + D.centi / 100.0f); } public static explicit operator Dolari(float S) { return new Dolari((uint)S, (uint)((S - (uint)S) * 100)); } public override string ToString() { return dolari.ToString() + "$" + centi.ToString() + "C"; }

} class Program { static void Main(string[] args) { Lei L1 = new Lei(34, 78); float suma; Lei L2; suma = L1; //conversie implicita L2 = (Lei)suma; //conversie explicita Dolari d = L1; //conversie implicita Console.WriteLine(bani); Console.WriteLine(l2.ToString()); Console.WriteLine(d.ToString()); } }

n cazul tipurilor create de programator, exist un specific atunci cnd tipurile legate prin procedura de conversie sunt legate prin relaia de motenire. n cazul tipurilor de date nrudite, conversia de la tipul derivat la tipul de baz este una implicit, n timp ce conversia de la tipul de baz la tipul derivat poate fi doar explicit. Cnd tipurile legate prin conversie sunt ambele definite de programator suprancrcarea operatorilor de conversie este realizat n oricare dintre tipuri. Totodat, poate fi realizat doar n cadrul unui singur tip sau poate fi mprit ntre tipuri.

Crearea interfeei utilizator

89

Crearea de aplicaii Windows (cu interfa grafic) n cadrul mediului de dezvoltare Visual Studio, pot fi create mai multe tipuri de proiecte. Toate exemplele prezentate pn acum erau aplicaii de tip consol, fiind create deci n baza proiectelor de tip consol. Un alt tip important de aplicaii sunt aplicaiile cu interfa grafic, ele fiind proiecte de tip Windows. Baza acestui tip de aplicaii este una sau mai multe ferestre grafice, care la etapa de design mai sunt numite i forme, ceea ce este reprezentat n imaginea ce urmeaz:

Fiecare fereastr este un obiect generat dintr-o clas nrudit cu clasa Form. Deseori aplicaia creat conine una sau mai multe clase care motenesc de la clasa Form:
public class numeForma : Form { . . . }

Din cte s-a menionat deja, un rol important l joac clasa Form. Clasa Form este un control, fiindc motenete de la clasa Control. De aceea clasa Form este prezentat prin urmtoarea ierarhie de motenire.

90

Deoarece exist o gam larg de controale, n diagrama anterioar mai sunt prezentate dou ramuri de motenire a clasei TextBox i a clasei Button, care, de asemenea, motenesc de la clasa Control. Cea mai simpl aplicaie care, pur i simplu, deschide o fereastr este prezentat de urmtorul exemplu:
using System; using System.Windows.Forms; namespace Fereastra { class program { [STAThread] static void Main() { Form f = new Form(); Application.Run(f); } } }

// atribut // creare obiect de tip form // afisare fereastra

La elaborarea aplicaiilor de tip Windows trebuie de respectat o serie de cerine: simplitate; consisten; poziionare corect a controalelor; estetism. Simplitate ar nsemna ca aplicaia s corespund scopului pentru care a fost elaborat. Totodat, nu trebuie de introdus efecte care ar fi de prisos n procesul de desfurare a operaiilor n cadrul aplicaiei. Firul de dezvoltare a ideii unei aciuni trebuie s fie ct mai transparent, ct mai clar, ct mai ineles, astfel nct chiar i un utilizator cu o pregtire mai lejer s o poat percepe. Consisten ar nsemna urmarea acelorai idei legate de designul aplicaiei pe parcursul dezvoltrii ntregii aplicaii.

91

Poziionarea corect a controalelor este foarte important pentru realizarea unei aplicaii reuite. Ar fi bine de plasat controalele mai importante n partea de sus a formei, iar cele mai puin importante n partea de jos. Totodat, este bine de grupat controalele pe form. Criteriul gruprii i va aduce aportul i la simplitatea aplicaiei, i la consistena ei. Totodat, amplasarea relativ a controalelor depinde de gradul de legtur dintre datele pe care le conin. Estetismul este un factor care poate prea mai puin important, dar totui o aplicaie cu un grad nalt de estetism conduce la o productivitate mai mare n utilizarea ei. Desigur c exist un grup de parametri ce influeneaz aspectele despre care s-a vorbit anterior. Cei mai importani ar fi: culoare; font; transparen. O fereastr cu controale echilibrat din punctul de vedere al culorilor este o baz reuit pentru o aplicaie. Nu sunt utilizate culori stridente, care peste o perioad oarecare de timp au o influen de iritare. Culorile stridente pot fi utilizate doar pentru atenionare. Dac este propus informaie textual, ea trebuie s fie pe un fond contrastant pentru a putea lua cunotin de text fr mare ncordare. Un rol important l are i selectarea familiei de fonturi. Fonturile trebuie s respecte caracterul aplicaiei. Aplicaiile serioase nu trebuie s i permit fonturi jucue. Sunt utilizate fonturi care permit o citire fr dificultate a informaiei propus spre atenie. Pot fi utilizate, de asemenea, unele efecte legate de transparen. n unele cazuri ele au un rol estetic, n altele rol de utilitate. De exemplu, avnd dou forme cu mult informaie fiecare, pentru a vedea informaia de pe fiecare concomitent, sar putea folosi efecte de transparen. Orice aplicaie const din una sau mai multe ferestre. n procesul de generare a proiectului aplicaiei de tip Windows, n el este introdus automat o form care corespunde unei viitoare ferestre. n continuare, proiectul poate fi dezvoltat att n regim de design, ct i n regim de cod. Multe aciuni pot fi efectuate n ambele regimuri i de aceea programatorul selecteaz metoda care i convine cel mai mult. Dac aplicaia conine mai mult dect o fereastr, atunci apare necesitatea de a aduga noi forme (ferestre) la proiect. Adugarea unei forme n regim de disign poate fi efectuat n felul urmtor: o clic opiunea Project a meniului principal; n meniul care apare clic opiunea Add Windows Form...; n fereastra care apare cu opiunea Windows Form selectat se scrie numele fiierului care corespunde numelui clasei formei; este acionat butonul Add.
92

Adugarea unei forme n regim de cod poate fi efectuat dac exist deja clasa formei n baza creia va fi creat un obiect nou (fereastra), dup cum urmeaz:
Form1 f2 = new Form1();

care poate fi activat, de exemplu, astfel:


f2.Show();

Clasa formei poate fi una complex, deoarece poate fi mbogit cu diferit funcionalitate, cu diferite controale, cu aspectul necesar. n baza unei clase de acest tip pot fi generate la necesitate diferite ferestre, nu doar n aplicaia n care a fost creat, ci i n alte aplicaii. Totodat, ea poate fi utilizat n calitate de clas de baz, extinzndu-i funcionalitatea i proprietile. Din acest motiv, o astfel de clas mai poate fi numit form motenit. La proiect pot fi adugate i forme de acest tip. Adugarea unei astfel de forme n regim de disign poate fi efectuat n felul urmtor: o clic opiunea Project a meniului principal; n meniul care apare clic opiunea Add New Item...; n fereastra care apare este selectat opiunea Inherited Form, scriind numele fiierului care corespunde numelui clasei formei; este acionat butonul Add; din fereastra cu numele Inheritance Picker, unde apar toate formele din aplicaia curent, este selectat forma necesar sau este acionat butonul Browse... pentru a selecta forme din alte aplicaii; este acionat butonul OK. La acionarea butonului Browse... pot fi adugate doar forme, care se gsesc n biblioteci dinamice cu extensiunea .dll. Adugarea unei forme motenite n regim de cod este efectuat n presupunerea c exist deja clasa formei, de exemplu FormaMostenita, i se procedeaz dup cum urmeaz:
class Form1 : [SpatiuDeNume.]FormaMostenita { . . . }

unde FormaMostenita este numele clasei existente, iar SpatiuDeNume este numele spaiului de nume, dac clasa este importat din alt spaiu de nume dect spaiul curent. Pentru o interaciune eficient cu ferestrele exist: proprieti, care permit schimbarea aspectului, parametrilor ferestrei; metode, care permit realizarea unor aciuni legate de o fereastr concret; evenimente, care sunt utilizate cnd s-a declanat o situaie n cadrul aplicaiei.

93

Proprietile pot fi fixate n regim de design n fereastra proprietilor (Properties) sau n regim de cod. Dac fereastra proprietilor nu este vizibil, atunci n opiunea View a meniului principal exist opiunea Properties Window acionarea creia duce la apariia ferestrei. La activizarea unui control n fereastra dat va apare lista de proprieti i de valori corespunztoare controlului dat. O serie de proprieti importante caracteristice formei pot fi divizate n urmtoarele categorii ce in de: poziionarea ferestrei; dimensionarea ferestrei; determinarea aspectului ferestrei. Poziionarea ferestrei se face cu ajutorul proprietii Location, care corespunde vrfului stnga-sus al ferestrei i reprezint un punct cu proprietile X i Y, corespunznd coordonatelor punctului. n regim de design, locaia ferestrei n punctul cu coordonatele 200 i 300 se face astfel
Properties . . . Location X Y . . . 200, 300 200 300

iar n regim de cod dup cum urmeaz


this.Location=new Point(200, 300);

O aft modalitate de poziionare a ferestrei se face cu ajutorul proprietilor Top i Left, care pentru fereastr corespund poziionrii vrfului stnga-sus al ferestrei. Poziionarea ferestrei este influenat i de proprietatea StartPosition care impune locul apariiei iniiale a ferestrei. Proprietatea aceasta primete ca valoare una dintre valorile enumerrii de tip FormStartPosition, prezentate n continuare: o Manual plasarea ferestrei n punctul definit de proprietatea
Location. o CenterScreen plasarea ferestrei n centrul ecranului, independent de valoarea proprietii Location. o WindowDefaultLocation plasarea ferestrei n poziia implicit, independent de valoarea proprietii Location. o WindowDefaultBounds plasarea ferestrei n poziia implicit, independent de valoarea proprietii Location, i dimensionarea
o

implicit a ferestrei. CenterParent plasarea ferestrei n centrul ferestrei printe, independent de valoarea proprietii Location.

94

De exemplu, fixarea proprietii StartPosition n regim de cod poate fi fcut astfel:


this.StartPosition=FormStartPosition.CenterScreen;

Dimensionarea ferestrei se face cu ajutorul proprietii Size, care const din proprietile Height i Width de fixare corespunztor a nlimii i limii ferestrei. Aceste proprieti pot fi fixate att n regim de design, ct i n regim de cod. De exemplu, fixarea limii la 300 i a nlimii la 200 n regim de design se face astfel:
Properties . . . Size Width Height . . . 300, 200 300 200

Acelai lucru n regim de cod se face astfel


this.Height = 200; this.Width = 300;

sau ntr-un mod echivalent crend un obiect de tip Size:


this.Size=new Size(300, 200);

Tot

dimensionarea ferestrei in i proprietile MaximumSize i MinimumSize, care pun restricii la dimensiunile minimale i maximale ale ferestrei. Ambele proprieti sunt de tip Size i pot fi manipulate dup modelul prorpietii Size descrise anterior, att n regim de design, ct n regim de cod. De exemplu, fixarea dimensiunilor maximale ale ferestrei la valoarea 400 n regim de design se face astfel:
Properties . . . MaximumSize Width Height . . . 400, 400 400 400

de

n regim de cod se procedeaz astfel


this.MaximumSize.Height = 400; this.MaximumSize.Width = 400;

sau ntr-un mod echivalent crend un obiect de tip Size:


this.MaximumSize=new Size(400, 400);

Aspectul ferestrei const din mai multe elemente, cum ar fi culoare fond, culoare text (linie), bara de titlu, stilul chenarului, stilul de vizualizare a ferestrei. Culoarea formei (fundalul) este fixat prin intermediul proprietii BackColor, care este o proprietate de tip Color. Similar culoarea textului sau a liniilor poate fi fixat cu ajutorul proprietii ForeColor, care este tot de tip Color. Ele pot fi
95

manipulate i n regim de design, i n regim de cod. Iat nite atribuiri valabile n regim cod:
this.BackColor = Color.Red; // culoare fon this.ForeColor = Color.Green; // culoare text

Exist o serie de proprieti, care se refer la bara de titlu, i i pot schimba aspectul. Proprietatea ShowIcon ine de pictograma meniului de sistem i dac are valoarea true, pictograma va fi vizibil, iar dac are valoarea false pictograma nu va fi vizibil, nefiind accesibil astfel nici meniul de sistem. O alt proprietate este Icon i se refer, de asemenea, la pictograma de sistem, dar prin intermediul ei se poate determina imaginea ei. Proprietile MaximizeBox i MinimizeBox se refer la butoanele de maximizare i minimizare a ferestrei. Ele sunt de tip boolean i pot lua valorile true sau false. Dac proprietatea respectiv are valoarea true, atunci butonul respectiv este activ i i poate ndeplini funcia. Dac ns proprietatea respectiv are valoarea false, atunci butonul respectiv este inactiv, avnd o nuan mai pal, i nu i poate ndeplini funcia corespunztoare. Dac ambele proprieti au valoarea false, atunci butoanele de minimizare i maximizare nu sunt nici vizibile. Dac se dorete o fereastr fr butoanele de comand, de pe bara de titlu este utilizat proprietatea ControlBox. Fixndu-i proprietii valoarea false, vor dispare att toate butoanele de comand, ct i pictograma meniului de sistem. Valoarea true le face din nou vizibile. O alt proprietate este proprietatea Text, cu ajutorul creia se fixeaz textul de pe bara de titlu sau titlul ferestrei. Mai jos urmeaz un exemplu de fixare a titlului:
this.Text = Lucrare de laborator;

Pentru

stilul chenarului ferestrei este aplicat proprietatea FormBorderStyle. Pentru a fixa valoarea proprietii este utilizat enumerarea cu acelai nume FormBorderStyle. Aceast enumerare are urmtoarele etichete:
Nr. Denumiri etichete d/o 1 None 2 3 4 5 FixedSingle Fixed3D FixedDialog Sizable Descriere succint a efectului utilizrii Fereastra este afiat fr chenar i fr bara de titlu Fereastra este afiat cu un chenar simplu i cu o bar de titlu obinuit Fereastra este afiat cu un chenar cu efecte 3D i cu o bar de titlu obinuit Fereastra este afiat cu un chenar simplu i cu o bar de titlu obinuit Fereastra este afiat cu un chenar mai evideniat i cu o

modela

96

FixedToolWindow

SizableToolWindows

bar de titlu obinuit Fereastra este afiat cu un chenar simplu i cu o bar de titlu mai ngust de pe care lipsesc butoanele de comand Fereastra este afiat cu un chenar mai evideniat i cu o bar de titlu mai ngust de pe care lipsesc butoanele de comand

n continuare, este prezentat un exemplu de fixare a stilului chenarului ferestrei: n regim de design
Properties . . . FormBorderStyle Fixed3D . . .

n regim de cod

this.FormBorderStyle = FormBorderStyle.Fixed3D;

Un alt mod de schimbare a aspectului ferestrei este proprietatea BackgroundImage, care permite fixarea unei imagini n calitate de fundal al ferestrei. De exemplu, pentru a fixa n regim de cod imaginea Zapotec.bmp drept fundal al ferestrei, se va proceda astfel:
this.BackgroundImage = Image.FromFile(@C:\Zapotec.bmp);

utiliznd funcia static FromFile() a clasei Image pentru a crea obiectul de tip Image (imagine) n baza datelor din fiier. Pentru a putea dirija cu transparena ferestrei, exist proprietatea Opacity. n regim de design, aceast proprietate este masurat n procente i ia valori de la 0 la 100. Valoarea 0 nseamn transparen total, iar 100 nseamn opacitate total. n regim de cod, nivelul de transparen este msurat n valori de la 0 la 1, lund valori zecimale. n continuare, este propus un exemplu de fereastr cu gradul de transparent egal cu 60%: n regim de design
Properties Opacity . . . . . . 60%

n regim de cod

this.Opacity = 0.6;

Pentru a putea dirija transparena unei culori, este utilizat proprietatea TransparencyKey. Aceast proprietate ia valori de tip Color (culoare).

97

Culoarea fixat devine total transparent. n continuare va fi fixat transparent culoarea roie (Red): n regim de design
Properties . . . TransparencyKey . . . Red

n regim de cod

this.TransparencyKey = System.Drawing.Color.Red;

Urmtoarele dou proprieti determin modul de deschidere a ferestrei. Proprietatea WindowState arat dac fereastra va fi deschis n mod normal, n mod maximizat sau n mod minimizat. Pentru a fixa valoarea proprietii, este utilizat enumerarea FormWindowState, avnd urmtoarele etichete:
Nr. Denumiri etichete d/o 1 Maximized 2 3 Minimized Normal Descrierea succint a efectului utilizrii Fereastra este afiat n mod maximizat Fereastra este afiat n mod minimizat Fereastra este afiat n mod normal

Proprietatea ShowOnTaskBar poate lua una dintre valorile true sau false, determinnd astfel dac va aprea sau nu butonul aplicaie pe bara aplicaiei. Urmeaz un exemplu, n care parametrii sunt fixai n regim de cod, astfel nct fereastra s apar maximizat i, totodat, fr a vedea n bara aplicaiilor butonul aplicaiei:
this.WindowState = FormWindowState.Maximized; this.ShowOnTaskBar = false;

Pe lng proprieti sunt, de asemenea, i o serie de metode importante. Exist dou metode de afiare a ferestrelor Show() i ShowDialog(). Specificul metodei a doua const n faptul c fereastra este afiat ca o fereastr modal de dialog. Aceasta nsemn c oricare alt fereastr a aplicaiei va putea fi activizat doar dup nchiderea ferestrei modale. n schimb, la afiarea ferestrei cu metoda Show() va fi posibil trecerea de la ea, activiznd oricare alt fereastr a aplicaiei. Pentru fereastra modal de dialog, este important proprietatea DialogResult. Proprietatea aceasta poate lua valori de tipul enumerrii DialogResult. Enumerarea DialogResult are urmtoarele etichete:
Nr. Denumiri etichete d/o 1 None Descrierea succint a efectului utilizrii Fereastra continu s fie vizibil, nereturnndu-se nimic

98

2 3 4 5 6 7 8

OK Cancel Abort Retry Ignore Yes No

Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului OK Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului Cancel Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului Abort Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului Retry Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului Ignore Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului Yes Fereastra e nchis, returnnd valoare generat, de regul, la acionarea butonului No

nchiderea unei ferestre modale face ca valoarea proprietii DialogResult s devin egal cu DialogResult.Cancel. Pentru a cunoate informaii despre nchiderea ferestrei modale de dialog este testat proprietatea DialogResult. De exemplu:
. . . Form f2 = new Form(); . . . f2.ShowDialog(); if(f2.DialogResult == DialogResult.OK) { MessageBox.Show(Butonul OK); } . . .

O metod important este i Activate(), care permite activarea n regim de cod a unei ferestre. Pe ecran pot fi deschise mai multe ferestre, dar activ este doar una. Aceast metod fixeaz focusul pe o fereastr concret. O alt metod este Hide(), care permite ascunderea ferestrei. n acest caz, resursele ferestrei sunt active n memorie, att doar c fereastra nu este vizibil. Acelai efect s-ar obine dac ar fi utilizat proprietatea Visible, fixndu-i valoarea false. Facerea ferestrei vizibile din nou se realizeaz cu ajutorul metodei Show() caracterizat anterior. Similar poate fi utilizat proprietatea Visible, fixndu-i valoarea true.

99

Metoda Close() permite nchiderea ferestrei. n acest caz, toate resursele ferestrei sunt eliberate din memorie. Orice ncercare de a activiza fereastra nu poate da rezultate pozitive. Un alt aspect important n cadrul controalelor sunt evenimentele. Evenimentele pot fi privite ca nite ntmplri specifice n firul de existen a componentelor i a controalelor. Forma, fiind i ea un reprezentant al controalelor, are o mulime numeroas de evenimente caracteristice. Printre evenimente exist o serie de evenimente care in de ciclul de via a ferestrei. Ciclul de via a ferestrei ncepe prin crearea resurselor la apelarea constructorului. Apoi este declanat evenimentul Load, care are loc la ncrcarea n memorie a resurselor ferestrei. Urmtorul eveniment declanat este Activated, care se produce la activarea ferestrei. La prima apariie a ferestrei se produce evenimentul Shown i se declaneaz evenimentul Paint, care realizeaz desenarea ferestrei. Orice schimbare a coninutului ferestrei va genera evenimentul Paint. Ambele evenimente sunt din ciclul de creare a ferestrei. La nchiderea ferestrei, primul eveniment declanat este FormClosing, care precede procedura nemijlocit de nchidere i la aceast etap nchiderea poate fi anulat. Ultumul eveniment din ciclul de via a ferestrei este FormClosed, care subliniaz faptul c fereastra se nchide i procesul este ireversibil la aceast etap. Un eveniment care ine tot de ciclul de via a ferestrei este Deactivate, care se produce atunci cnd este activat o alt fereastr a aceleiai aplicaii. Orice eveniment poate fi prelucrat la declanarea lui. A prelucra un eveniment nseamn a lega de acest eveniment o serie de aciuni specifice. Prelucrarea unui eveniment se realizeaz prin intermediul unei funcii speciale, manipulator de eveniment (event handler). De regul, funciile date au modificatorul de acces private, sunt de tip void, iar numele este compus din denumirea obiectului i denumirea evenimentului, concatenate prin simbolul _, avnd, de regul, urmtoarea form:
private void numeControl_numeEven(object sender, clasaEven e) { . . . }

Orice funcie de aa tip are doi parametri fictivi. Primul parametru cu nume sender corespunde obiectului, care a declanat evenimentul i este de tip object. Pentru a fi utilizat, el, de regul, trebuie convertit la tipul obiectului care a declanat evenimentul. Al doilea parametru conine informaie despre eveniment i tipul acestui parametru clasaEven este unul derivat de la tipul EventArgs, cel mai simplu tip care permite descrierea evenimentelor. De exemplu, manipulatorul evenimentului Load ar putea fi descris astfel:

100

private void Form1_Load(object sender, EventArgs e) { Form f2 = new Form(); f2.Show(); }

Componente i controale Din cte s-a accentuat anterior, baza unei aplicaii Windows o reprezint forma. Dar pentru a obine o aplicaie cu adevrat funcional, este nevoie de mai multe elemente ale cror interaciune ofer rezultatul dorit. Alturi cu clasa Form, n cadrul platformei .NET este realizat o gam larg de clase, care uureaz enorm lucrul de creare a aplicaiilor. Obiectele unor clase au reprezentare vizual. Astfel de clase formeaz mulimea controalelor. Obiectele altor clase ns nu au reprezentare vizual. Astfel de clase formeaz mulimea componentelor. Toate controalele sunt derivate din clasa Control, iar toate componentele sunt derivate din clasa Component. Adugarea elementelor n aplicaie poate fi fcut n regim de design i n regim de cod. Foarte simplu acest lucru se face n regim de design, fapt care va fi utilizat deseori. Componentele i controalele se adaug n mod similar. Operaia se realizeaz prin intermediul ferestrei ToolBox. Dac ea nu este vizibil, atunci este fcut vizibil: o Clic opiunea View a meniului principal; n meniul care apare clic opiunea ToolBox. Controalele i componentele sunt grupate n compartimente. De aceea, mai nti, este deschis compartimentul necesar. Adugarea componentei sau a controlului pe form poate fi efectuat prin mai multe metode: 1) clic elementul necesar; prin tragere se face desenarea elementului pe form n locul potrivit i de dimensiunea dorit; 2) tragerea elementului necesar pe form n poziia potrivit; 3) dublu clic pe elementul necesar i el va aprea pe form. Este de subliniat c pe form de fapt apar doar controalele, care, dup cum a fost spus deja, au reprezentare vizual. Componentele, fiind adugate n contextul aplicaiei, nu apar pe form, ci pe o band poziionat mai jos de form i pentru ele nu au sens noiunile de poziie i de dimensiune. Orice element adugat n contextul aplicaiei poate trece prin procesul de ajustare a proprietilor. n regim de design, ajustarea proprietilor se face prin fereastra proprietilor: o clic elementul necesar;

101

clic n fereastra Properties proprietatea necesar; este fixat valoarea dorit. Exist, de asemenea, posibilitatea de redactare colectiv a proprietilor elementelor, ceea ce se face astfel: o sunt selectate elementele necesare prin Ctrl+Clic sau prin tragere; clic n fereastra Properties proprietatea necesar; este fixat valoarea dorit. n fereastra proprietilor vor aprea doar proprietile comune ale elementelor selectate. Adugarea elementelor poate fi efectuat nu doar n regim de design, ci i n regim de cod. Pentru aceasta n locul necesar este creat un obiect de anumit tip, fixndu-i proprietile potrivite. Dac elementul adugat este un control, ca el s fie vizibil i se fixeaz proprietatea Parent egal ca valoare cu controlul care l conine. Sau este adugat la controlul-printe cu ajutorul metodei Add() a proprietii Controls. De exemplu, n lumina celor spuse, adugarea unui buton pe forma n regim de cod poate fi fcut aa

. . . Button bt = new Button(); bt.Text = Apasa; bt.Parent = this; . . .

sau aa
. . . Button bt = new Button(); bt.Text = Apasa; this.Controls.Add(bt); . . .

Fiindc din punctul de vedere al aspectului vizual i al interaciunii cu utilizatorul sunt importante controalele, vor fi evideniate o serie de proprieti, metode i evenimente importante. Fiindc toate controalele sunt derivate de la clasa Control, ele motenesc o serie de proprieti de la clasa de baz. Iat o serie de proprieti motenite din clasa Control:
Anchor BackColor Bottom Cursor Controls Dock Enabled Font ForeColor Height Left Location Margin MaximumSize MinimumSize Name Padding Parent Right Size TabIndex Text Top Visible Width

Proprietile BackColor, ForeColor, Left, Location, Size, Height, Width, MaximumSize, MinimumSize, Top nu se deosebesc de aceleai
102

proprieti ale clasei Form, nsemnnd corespunztor culoarea fundalului, culoarea literelor, poziionarea marginii de stnga, poziia, mrimea, nlimea, limea, mrimea maximal, mrimea minimal, poziionarea marginii de sus. Manipularea lor se face tot aa ca i n cazul obiectelor de tip Form. Proprietile Bottom i Right sunt foarte asemntoare prin modul de operare cu proprietile Top i Left i nseamn respectiv poziionarea marginii de jos i a marginii de dreapta. Proprietile legate de poziionare sunt toate calculate n raport cu vrful stnga-sus a controlului-printe care conine controlul dat. O alt proprietate ce determin cu care margine controlul dat se va uni cu controlul-container este proprietatea Dock. La unire controlul se leag cu o latur sau cu toate patru laturi n dependen de valoarea selectat. Legtura se face pe ntreaga lungime a laturii, dac i permite valoarea proprietii MaximumSize. Pentru a fixa valoarea proprietii, este utilizat enumerarea DockStyle. Aceast enumerare are urmtoarele etichete:
Nr. Denumiri etichete d/o 1 Bottom 2 3 4 5 6 Fill Left None Right Top Descrierea succint a efectului utilizrii Controlul se leag de container cu marginea de jos Controlul se leag de container pe toate patru margini Controlul se leag de container cu marginea din stnga Controlul nu se leag de container cu nici o margine Controlul se leag de container cu marginea din dreapta Controlul se leag de container cu marginea de sus

n regim de design, valoarea proprietii este fixat prin intermediul ferestrei proprietilor, scriind valoarea etichetei sau acionnd butonul care corespunde marginii, ca n imaginea urmtoare:

103

n regim de cod, fixarea valorii se face prin atribuire ca n fragmentul ce urmeaz:


Button btn = new Button(); . . . btn.Dock = DockStyle.Bottom;

Proprietatea Anchor, de asemenea, leag controlul de o oarecare margine a controlului-container, ns legarea se face prin conservarea distanei dintre marginea controlului i marginea corespuztoare a controlului-container. Se face un fel de ancorare a controlului. Ancorarea controlului se permite pe fiecare dintre patru margini. Pentru fixarea valorii proprietii, este utilizat enumerarea AnchorStyles. Aceast enumerare are urmtoarele etichete:
Nr. Denumiri etichete d/o 1 Bottom 2 3 4 5 Left None Right Top Descrierea succint a efectului utilizrii Controlul se leag de container cu marginea de jos Controlul se leag de container cu marginea din stnga Controlul nu se leag de container cu nici o margine Controlul se leag de container cu marginea din dreapta Controlul se leag de container cu marginea de sus

Fixarea valorii proprietii se face n regim de design prin fereastra proprietilor, scriind valorile delimitate prin virgul sau acionnd butonul ce simbolizeaz legtura dintre margini ca n imaginea ce urmeaz.

104

Fiindc ancorarea se poate face pe mai multe margini, pentru a combina n regim de cod valorile corespunztoare, se aplic operatorul | (sau). Iat un exemplu:
Button btn = new Button(); . . . btn.Anchor = AnchorStyles.Top | AnchorStyles.Left;

Tasta Tab are o semnificaie special n cadrul fiecrei aplicaii. Acionarea tastei Tab duce la schimbarea controlului activ. Deci acionnd tasta Tab pot fi parcurse controalele de pe form. Combinaia Shift+Tab, de asemenea, permite o parcurgere, dar n sens invers. Fiecare control de pe form are un indice special, care determin ordinea de parcurgere a controalelor cu ajutorul tastei Tab. Proprietatea responsabil de acest indice este TabIndex. Cu ct acest indice este mai mic, cu att el are o preceden mai mare n irul parcurgerii. Urmeaz un exemplu de fixare a indicelui egal cu 0, fcnd controlul button1 cel mai prioritar: n regim de design
Properties . . . TabIndex . . .

n regim de cod

button1.TabIndex = 0;

Indicele dat poate fi fixat n ansamblu, avnd vizibili indicii tuturor controalelor. Pentru aceasta n opiunea View a meniului principal este selectat opiunea Tab Order. Drept rezultat toate controalele vor avea specificat indicele Tab. Orice clic n caseta cu indicele dat i va schimba valoarea cu o unitate n crestere pn la

105

valoarea maximal revenind apoi la zero. Selectarea din nou a opiunii Tab Order va duce la fixarea valorilor determinate, casetele cu indici devenind invizibile. O proprietate simpl a controalelor este proprietatea Text. Ea permite introducerea unui fragment de text care va fi asociat ntr-un anumit fel cu controlul. Modul de asociere este diferit pentru diferite controale. De exemplu, pentru un control de tip Button, va fi textul care apare pe buton, iar pentru un control de tip TextBox, va fi textul care apare n box putnd fi ulterior redactat. Putnd fi fixat att n regim de design, ct i n regim de cod, n continuare este prezentat un exemplu de fixare n regim de cod a proprietii Text pentru un obiect de tip CheckBox:
CheckBox chb = new CheckBox(); . . . chb.Text = Student;

Proprietatea Enabled permite fixarea controlului n stare activ sau pasiv. Un control pasiv este vizibil, avnd o vizibilitate mai splcit, dar este imposibil interaciunea cu el. Proprietatea Visible permite fixarea vizibilitii controlului. Ambele proprieti pot lua valoarea true, artnd c controlul este fie activ, fie vizibil, sau valoarea false, artnd c controlul este fie pasiv, fie invizibil. Ele pot fi fixate att n regim de design, ct i n regim de cod. n continuare este prezentat un exemplu de fixare n regim de cod a proprietii Enabled pentru un buton, fcndu-l pasiv:
Button buton = new Button(); . . . buton.Enabled = false;

Proprietile Margin i Padding sunt asemntoare prin faptul c definesc spaii n vecintatea contului controlului, respectiv n exteriorul i n interiorul lui. Se

106

formeaz astfel nite chenare libere, neocupate de informaie. Pot fi fixate i n regim de design, i n regim de cod. Proprietile Margin i Padding returneaz ambele valori de tip Padding. Ca ilustrare vine un exemplu de fixare a acestor proprieti n regim de cod:
TextBox txtb = new TextBox(); . . . txtb.Margin = new Padding(30); txtb.Padding = new Padding(10, 5, 10, 5);

Spaiul exterior va fi de 30 pe toate laturile, iar spaiul intern va fi de 10 pe laturile stnga i dreapta i de 5 pe laturile sus i jos. Proprietatea Name permite identificarea unui control cu ajutorul unui nume. Fixarea poate fi fcut fie n regim de design fie n regim de cod. Urmeaz un exemplu de fixare a numelui controlul button1: n regim de design
Properties (Name) . . . . . . Buton

n regim de cod

button1.Name = Buton;

O proprietate important este proprietarea Parent care permite pentru un control dat citirea sau fixarea controlului container. Dac un control este creat n regim de cod el nu va fi vizibil n fereastra aplicaiei pn nu i va fi fixat proprietatea Parent pe o anumit valoare, adic pn nu va fi determinat controlul-container. Urmeaz un exemplu de creare a unei boxe textuale, containerul creia va fi nsi fereastra aplicaiei:
TextBox tboxa = new TextBox(); tboxa.Parent = this;

Un control poate fi container pentru alte controale. De aceea orice control are o proprietate care reprezint colecia controalelor coninute n controlul dat. Aceast proprietate este Controls, care are o serie de metode i proprieti utile n procesul de prelucrare a datelor. Tabelul ce urmeaz conine informaii succinte despre cele mai importante metode i proprieti.
Nr Denumiri elemente d/o 1 Count 2 3 Item Owner Descriere succint a efectului utilizrii Ofer numrul de controale coninute n colecie. Ofer un control din colecie. Returneaz controlul care

107

4 5 6 7 8 9 10 11 12

Add(Control c) AddRange(Control[] tc) Clear() Contains(Control c) Find(string nume, bool tot) GetChildIndex(Control c) IndexOf(Control c) Remove(Control c) RemoveAt(int indice)

conine colecia de controale. Permite adugarea unui control la colecia de controale. Permite adugarea unui tablou de controale la colecia de controale. Permite golirea coleciei de controale. Controlul se leag de container cu marginea de sus. Permite cutarea dup nume a controalelor n colecia de controale. Returneaz indicele controlului n colecie. Returneaz indicele controlului n colecie. terge controlul dat din colecia de controale. terge controlul avnd indicele dat din colecia de controale.

Urmeaz un exemplu de creare i adugare a unei boxe textuale la colecia de controale a nsi ferestrei aplicaiei:
TextBox tboxa = new TextBox(); this.Controls.Add(tboxa);

Cursorul poate avea aspecte diferite n dependen de aciunile n care este implicat. De aceea proprietatea Cursor poate dirija aspectul cursorului. Pentru a putea dirija aspectul cursorului este proiectat clasa Cursors, care conine proprieti statice destinate determinrii aspectului cursorului. Tabelul ce urmeaz conine denumirile proprietilor i pictogramele corespunztoare cursorului, care sunt foarte sugestive i nu necesit explicaii.
Nr Denumiri d/o elemente 1 AppStarting 2 3 4 Arrow Cross Default Aspectul pictogramei cursorului

108

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

Hand Help HSplit IBeam No NoMove2D NoMoveHoriz NoMoveVert PanEast PanNE PanNorth PanNW PanSE PanSouth PanSW PanWest SizeAll SizeNESW SizeNS SizeNWSE SizeWE UpArrow VSplit

109

28

WaitCursor

Fixarea aspectului poate fi fcut fie n regim de design fie n regim de cod. Urmeaz un exemplu de fixare a unui aspect al cursorului sub form de mn: n regim de design
Properties Cursor . . . . . . Hand

n regim de cod

this.Cursor = Cursors.Hand;

Pentru a dirija stilul inscripiilor textuale este utilizat proprietatea Font. Aceast proprietate este caracterizat de o serie de proprieti, descriind diferite aspecte. Pot fi enumerate urmtoarele proprieti mai importante:
Nr Denumiri elemente d/o 1 Bold 2 3 4 5 6 7 8 9 10 Descriere succint a efectului utilizrii Indic dac caracterele sunt aldine. Indic familia setului de caractere. Indic spaierea ntre linii a setului de caractere. Indic dac caracterele sunt italice. Indic numele setului de caractere. Indic mrimea setului de caractere. Indic dac caracterele sunt tiate. Indic stilul setului de caractere. Indic dac caracterele sunt subliniate. Indic unitatea de msur a mrimii caracterelor.

FontFamily Height Italic Name Size Strikeout Style Underline Unit

n regim de design prin intermediul lor pot fi fixate valorile necesare. Dar n regim de cod majoritatea lor sunt doar pentru citire i nu permit schimbarea valorii. De aceea dac n regim de cod este necesar de a schimba proprieti legate de setul de caractere se creaz un obiect cu proprietile necesare n baza clasei Font i acest obiect este atribuit proprietii Font a controlului necesar. Urmeaz un exemplu de

110

fixare pentru aplicaie a setului de caractere Arial, cu mrimea 10 n uniti Point, cu caractere aldine: n regim de design
Properties Font Name Size Unit Bold . . . . . . Arial,10pt Arial 10 Point True

n regim de cod

this.Font = new Font(Arial,10, FontStyle.Bold,GraphicsUnit.Point);

Aici a fost utilizat enumerarea FontStyle cu urmtoarele etichete:


Nr Denumiri etichete d/o 1 Bold 2 3 4 5 Descriere succint a efectului utilizrii Indic dac caracterele sunt aldine. Indic dac caracterele sunt italice. Indic dac caracterele sunt normale. Indic dac caracterele sunt tiate. Indic dac caracterele sunt subliniate.

Italic Regular Strikeout Underline

De asemenea a fost utilizat enumerarea GraphicsUnit, avnd urmtoarele etichete:


Nr Denumiri etichete d/o 1 Display 2 3 4 5 6 Document Inch Millimeter Pixel Point Descriere succint a efectului utilizrii Indic unitatea dispozitivului de afiare ca unitate de msur. Indic unitatea documentului (1/300 inch)ca unitate de msur. Indic olul (inch) ca unitate de msur. Indic milimetru ca unitate de msur. Indic pixelul dispoitivului ca unitate de msur. Indic pixelul imprimantei (1/72 inch) ca unitate de msur.

111

World

Indic unitatea sistemului de coordonate mondial ca unitate de msur.

Controalele au o gam larg de evenimente, fapt ce permite o prelucrare specific dependent de unele condiii. Din cte s-a subliniat anterior, evenimentul de interes este prelucrat n cadrul unei funcii speciale, avnd, de regul, urmtoarea form:
private void numeControl_numeEven(object sender, clasaEven e) { . . . }

Pentru a defini o funcie de prelucrare a unui eveniment legat de un control, este selectat controlul dat, n fereastra proprietilor se trece la categoria evenimentelor, dup care este realizat un dublu clic pe evenimentul dorit. Va fi generat antetul funciei de prelucrare a evenimentului dat, urmat de corpul funciei, constnd doar din acolade. Fiecare control are un eveniment implicit, caracteristic lui. Generarea funciei de prelucrare pentru acest eveniment se face mai simplu, efectund un dublu clic pe controlul respectiv. Foarte multe evenimente sunt legate de schimbarea unei caracteristici anume a unui obiect de anumit tip. Alte evenimente sunt declanate de unele stri specifice ale controlului. O categorie important de evenimente o reprezint evenimentele legate de oricel i de tastatur. n continuare, vor fi prezentate o serie de evenimente din aceast categorie. Cele mai simple sunt Click i DoubleClick:
Click DoubleClick Cnd este efectuat un clic pe control Cnd este efectuat dublu clic pe control

Parametrul fictiv al funciei de prelucrare care descrie evenimentul este de tipul clasei EventArgs. Fiind o clas, care este printele tuturor evenimentelor, este una foarte general i de aceea nu conine informaie foarte specific acestui eveniment. Urmtorul grup de evenimente se caracterizeaz prin rdcina Mouse prezent n toate denumirile.
MouseClick MouseDoubleClick MouseDown MouseEnter Cnd este efectuat un clic al oricelului, cursorul fiind deasupra controlului Cnd este efectuat un dublu clic al oricelului, cursorul fiind deasupra controlului Cnd este apsat un buton al oricelului, cursorul fiind deasupra controlului Cnd cursorul oricelului intr n domeniul controlului

112

MouseHover MouseLeave MouseMove MouseUp MouseWheel

Cnd cursorul oricelului este deasupra controlului Cnd cursorul oricelului prsete domeniul controlului Cnd cursorul oricelului se mic deasupra controlului Cnd este eliberat butonul apsat al oricelului cursorul fiind deasupra controlului Cnd este rotit rotia oricelului, cursorul fiind deasupra controlului

Pentru evenimentele MouseEnter, MouseHover, MouseLeave, parametrul fictiv al funciei de prelucrare, care descrie evenimentul este tot de tipul clasei EventArgs. Pentru toate celelalte evenimente, parametrul fictiv al funciei de prelucrare, care descrie evenimentul, este de tipul clasei MouseEventArgs. Aceast clas conine o serie de proprieti care descriu unele caracteristici legate de dispozitivul care a declanat evenimentul, i anume de oricel. Obiectul de tip MouseEventArgs are o serie de proprieti utile, care sunt descrise n tabelul ce urmeaz.
Button Clicks Delta Location X Y Ofer informaie despre butonul acionat Ofer informaie despre numrul de clickuri efectuate Ofer informaie despre numrul de rotiri efectuate de rotia de scrol Ofer informaie despre locul de poziionare a cursorului oricelului Ofer informaie despre coordonata X a cursorului Ofer informaie despre coordonata Y a cursorului

Pentru

valorii proprietii Button este utilizat MouseButtons. Aceast enumerare are urmtoarele etichete:
Nr. Denumiri etichete d/o 1 Left 2 3 4 Middle None Right Descrierea succint a utilizrii Este acionat butonul oricelului Este acionat butonul oricelului Nu este acionat nici al oricelului Este acionat butonul oricelului

fixarea

enumerarea
efectului stng al centru al un buton

drept al

113

n exemplul ce urmeaz, textul din boxa textual textBox1 se va schimba n dependen de butonul acionat al oricelului.
private void button1_MouseDown(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Left) textBox1.Text = Left; if(e.Button == MouseButtons.Middle) textBox1.Text = Middle; if(e.Button == MouseButtons.Right) textBox1.Text = Right; }

Pentru a mri posibilitile de interaciune cu aplicaia elaborat, sunt utilizate i evenimente ce in de tastatur. Sunt doar trei evenimente, legate de tastatur KeyDown, KeyUp i KeyPress. Primele dou se produc atunci cnd este apsat i, respectiv, este relaxat o anumit tast. Cel de al treilea eveniment se produce la acionarea unei taste. Este important ca controlul cu care este legat evenimentul s fie activ. Funciile de prelucrare a evenimentelor sunt asemntoare dup tipul parametrilor pentru primele dou evenimente. Parametrul fictiv al funciei de prelucrare, care descrie evenimentul, este de tipul clasei KeyEventArgs. Ultimul eveniment are un alt tip pentru descrierea acestui parametru, i anume clasa KeyPressEventArgs. Clasa KeyEventArgs este mai bogat dect KeyPressEventArgs n ceea ce privete informaia ce vine de la tastatur. Clasa KeyPressEventArgs conine proprietatea KeyChar, ce descrie tasta care a fost apsat. Returneaz o valoare de tip char i deci corespunde valorii alfanumerice a tastei, dac tasta este alfanumeric. Tastele nealfanumerice nu genereaz evenimentul KeyPress. O alt proprietate a acestei clase este Handled de tip bool cu valorile posibile true sau false, care arat c evenimentul a fost sau nu prelucrat. Clasa KeyEventArgs are i ea o serie de proprieti utile, care sunt descrise n tabelul ce urmeaz.
Alt Control Shift KeyCode KeyData KeyValue Modifiers Ofer informaie dac este sau nu apsat tasta Alt Ofer informaie dac este sau nu apsat tasta Control Ofer informaie dac este sau nu apsat tasta Shift Ofer informaie despre codul tastei apsate Ofer informaie despre tastele apsate Ofer informaie despre valoarea numeric a tastei apsate Ofer informaie dac sunt sau nu

114

Handled

apsate tastele Alt, Control, Shift Ofer informaie dac a fost sau nu prelucrat evenimentul dat

Proprietile KeyCode, KeyData, Modifiers returneaz valori de tipul enumerrii Keys. Este o enumerare care are descrise toate tastele tastaturii. Fiindc conine foarte multe etichete, vor fi prezentate doar selectiv o mic parte din ele:
Keys.A Keys.H Keys.R Keys.Z Keys.F1 Keys.F12 Keys.Control Keys.Shift Keys.Alt Keys.Escape Keys.DownArow Tasta Tasta Tasta Tasta Tasta Tasta Tasta Tasta Tasta Tasta Tasta A H R Z funcional F1 funcional F12 Ctrl Shift Alt Esc sgeat jos

Proprietile prezentate permit descrierea diferitelor situaii. Fiind multe proprieti, aceeai situaie poate fi descris n mai multe moduri. Trebuie de subliniat faptul c KeyData conine concomitent informaia despre mai multe taste. Pentru a combina mai multe taste ntr-o singur valoare, este utilizat operatorul |, de exemplu:
Keys.Control | Keys.B

De aceea testarea condiiei de apsare a combinaiei de taste Ctrl+B are urmtoarele variante: - cu KeyCode
if (e.KeyCode == Keys.B && e.Control) { . . . }
-

cu KeyData

if (e.KeyData == (Keys.Control | Keys.B)) { . . . }


-

cu KeyValue

if (e.KeyVlue == 66 && e.Modifiers == Keys.Control) { . . . }

115

Pot fi combinate, de asemenea, mai multe taste de modificare, utiliznd operatorul cunoscut |. De exemplu, testarea combinaiei de taste Ctrl+Alt+A poate fi realizat astfel:
if(e.KeyCode==Keys.A && e.Modifier==(Keys.Control | Keys.Alt)) { . . . }

O categorie important de evenimente sunt cele legate de activarea sau dezactivarea controlului. n tabelul ce urmeaz sunt enumerate i caracterizate succint evenimentele care se declaneaz la activare i dezactivarea unui control.
GotFocus Enter Leave LostFocus Validating Validated Cnd controlul devine control activ. Este un eveniment de nivel jos Cnd controlul devine control activ Cnd controlul devine un control inactiv Cnd controlul devine un control inactiv. Este un eveniment de nivel jos Cnd informaia controlului este n proces de validare Cnd informaia controlului este validat

Ordinea de declanare a evenimentelor depinde de metoda de activare i dezactivare.


Ordinea Metoda de activare declanrii Tasta TAB, metodele Select(), Cu ajutorul SelectNextControl(), oricelului, proprietatea ActiveControl metoda Focus() 1 Enter Enter 2 GotFocus GotFocus 3 Leave LostFocus 4 Validating Leave 5 Validated Validating 6 LostFocus Validated

Evenimentele Validating i Validated se declaneaz doar dac proprietatea CausesValidation are valoarea true. n caz contrar, aceste evenimente nu se vor declana i de aceea pentru aceast situaie ele trebuie excluse din listele anterioare.

Controale de tip container O categorie important de controale sunt controalele de tip container. Ele permit gruparea unor elemente dup anumite principii logice. n continuare vor fi
116

caracterizate succint cele mai des utilizate controale de acest tip. Un control de tip container, care a fost deja caracterizat este controlul reprezentat de clasa Form, care este un container autonom ce st la baza crerii aplicaiilor. Celelalte controale de tip container nu sunt autonome ci sunt pari componente ale altor containere. Un container care pe permite gruparea controalelor, trasnd un chenar n jurul lor este controlul GroupBox. Pe chenar mai poate fi plasat i un nume de identificare. Foarte des controalel de tip GroupBox sunt utilizate pentru gruparea butoanelor de tip RadioButton. Avantajul unei astfel de gupri este faptul c butoanele vor fi dependente: cnd unul este activat celelalte devin neactive. Pentru a aduga controale la un control de tip GroupBox este utilizat proprietatea deja cunoscut Controls i metodele ei. Printre ele va fi evideniat metoda deja amintit Add(), care are mai multe forme. n continuare este prezentat un exemplu de utilizare a controlului de tip GroupBox, fiind organuzat seciunea de selectare a vrstei unei persoane (tnar, mediu, btrn):
using System.Drawing; using System.Windows.Forms; namespace Grupcontrol { public partial class Form1 : Form { public Form1() { InitializeComponent(); string[] denumiri ={ "tinar", "mediu", "batrin" }; Control[] controale=new Control[6]; GroupBox gb = new GroupBox(); gb.Text = "Virsta"; gb.Location=new Point(5,5); gb.Width = 125; for (int i = 0; i < 3; i++) { controale[2*i] = new Label(); controale[2*i].Text = denumiri[i]; controale[2*i].Location = new Point(10, 15 + i * 30); controale[2*i].Width = 50; controale[2*i+1] = new RadioButton(); controale[2*i+1].Location = new Point(75, 15 + i * 30); controale[2*i+1].Width = 30; } (controale[1] as RadioButton).Checked = true; gb.Controls.AddRange(controale);

117

gb.Parent = this; } } }

Un alt container este controlul reprezentat de clasa Panel. Grupnd o serie de controale n cadrul controlului de tip Panel se obin o serie de efecte utile: - toate controalele pot fi ascunse prin ascunderea containerului; - poate fi fixat o valoare a unei proprieti ale tuturor controalelor prin motenire de la proprietatea similar a containerului care le conine. Pentru a aduga controale la un control de tip Panel este utilizat proprietatea deja cunoscut Controls i metodele ei. n exemplul ce urmeaz sunt utilizate dou paneluri pe unul fiind plasate datele personale, iar pe cellalt informaia ce ine de adresa unei persoane:
using System.Drawing; using System.Windows.Forms; namespace Panelcontrol { public partial class Form1 : Form { public Form1() { TextBox tb; Label lb; InitializeComponent(); Panel[] panele = new Panel[2]; TextBox tb; Label lb; string[] denumiri ={"Nume", "Prenume", "Str.", "Bloc"}; for (int i = 0; i < 2; i++) { panele[i] = new Panel(); panele[i].Location = new Point(i * this.ClientSize.Width / 2, 0); panele[i].Width = this.ClientSize.Width / 2; panele[i].Height = this.ClientSize.Height; panele[i].BorderStyle = BorderStyle.Fixed3D; panele[i].Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Top; for (int j = 0; j < 2; j++) { lb = new Label();

118

lb.Text = denumiri[2*i+j]; lb.Location = new Point(2, 10 + j*25); lb.Width = 50; panele[i].Controls.Add(lb); tb = new TextBox(); tb.Location = new Point(55, 5 + j*25); tb.Width = 85; panele[i].Controls.Add(tb); } panele[i].ForeColor = Color.Blue; this.Controls.Add(panele[i]); } panele[1].Anchor = panele[1].Anchor | AnchorStyles.Right; } } }

Un container util este controlul reprezentat de clasa TabControl. Acest control are proprietatea important de a avea mai multe pagini. Cnd o pagin este activ se vd doar controalele de pe aceast pagin, controalel de pe celelalte pagini fiind nevizibile. Deci este activ doar logica paginii active. Un container de tip TabControl are proprietatea TabPages, care este o colecie de controale de tip TabPage. Paginile pot avea nume sau pot fi anonime, dac este necesar. Controalele sunt adugate n pagini, utiliznd proprietatea deja cunoscut Controls i metodele ei. Exemplul ce urmeaz realizeaz acelai lucru ca i exemplul precedent att doar c n locul a dou paneluri este utilizat un control de tip TabControl cu dou pagini:
using System.Drawing; using System.Windows.Forms; namespace Tabcontrol { public partial class Form1 : Form { public Form1() { InitializeComponent(); TabControl tc = new TabControl(); tc.Dock = DockStyle.Fill; TextBox tb; Label lb; string[] numePagini ={ "Persoana", "Adresa" }; string[] denumiri ={ "Nume", "Prenume", "Str.", "Bloc" };

119

for (int i = 0; i < 2; i++) { TabPage tp = new TabPage(numePagini[i]); for (int j = 0; j < 2; j++) { lb = new Label(); lb.Text = denumiri[2 * i + j]; lb.Location = new Point(2, 10 + j * 25); lb.Width = 50; tp.Controls.Add(lb); tb = new TextBox(); tb.Location = new Point(55, 5 + j * 25); tb.Width = 85; tp.Controls.Add(tb); tp.ForeColor = Color.Blue; } tc.TabPages.Add(tp); } tc.Parent = this; } } }

Din materialul prezentat poate fi tras concluzia, c controalele de tip container au particulariti individuale, dar au i o serie de trsturi asemntoare.

Validarea informaiei introduse Un moment important n procesul de interaciune a utilizatorului cu aplicaia este legat de validarea informaiei introduse de utilizator. Nite date introduse greit pot genera erori, unele dintre ele putnd fi grave. Exist dou strategii de validare: la nivel de cmpuri, cnd validarea se face la introducerea informaiei ntrun anumit cmp; la nivel de form, cnd validarea se face dup introducerea informaiei n toate cmpurile. Pentru validarea informaiei la nivel de cmp, sunt utilizate, n primul rnd, o serie de proprieti, care in de diferite controale, dar care permit controlul informaiei introduse. Astfel proprietatea MaxLenght fixeaz lungimea maximal a textului, exprimat n numr de simboluri, care poate fi introdus n cmpul dat. Deci este realizat o validare de lungime a textului introdus. O alt proprietate este ReadOnly, care interzice scrierea informaiei n cmp i permite doar citirea
120

informaiei din cmp. n situaiile cnd informaia din cmp este static i nu este dorit schimbarea ei, o siguran n aceast privin ofer proprietatea ReadOnly. Deci este realizat o validare prin interzicerea schimbrii informaiei. Un alt tip de validare se produce la introducerea parolelor. n primul rnd, parolele se introduc secretizat utiliznd simboluri de mascare, de regul stelue (*). Pentru a fixa simbolul de reproducere a parolei este utilizat proprietatea PasswordChar. Dac aceast proprietate are o valoare diferit de null, textul n caseta textual va fi reprodus ca parol, n caz contrar, textul este afiat normal. Ca moment de validare a parolelor la definirea parolei pot fi create dou casete pentru parol, n scopul comparrii textului lor. Totodat, pot fi puse unele restricii dependente de lungime. Pentru validare mai sunt utilizate i o serie de evenimente. Evenimentele nemijlocit utilizate pentru validarea informaiei introduse sunt Validating i Validated. Aceste validri se produc la nivel de cmp. Evenimentul Validating se produce naintea evenimentului Validated. Legat de aceste evenimente este proprietatea CausesValidation, care permite sau interzice declanarea acestor evenimente. Cnd aceat proprietate are valoarea true, evenimentele au dreptul la declanare, ceea ce nu se ntmpl, n caz c valoarea proprietii este false. Aceste evenimente sunt declanate atunci cnd controlul trece din stare activ n stare neactiv. Funcia de prelucrare a evenimentului Validating are n calitate de al doilea parametru fictiv un obiect de tipul CancelEventArgs. De exemplu:
private void textBox1_Validating(object sender, CancelEventArgs e) { . . . }

Un obiect de tipul CancelEventArgs are proprietatea Cancel de tip bool. Fixndu-i valoarea true este anulat dezactivarea controlului curent i activarea altui control. n procesul de validare pot fi utile o serie de metode statice a tipului de date Char. Tabelul urmtor conine o descriere succint a aciunii realizate de unele dintre aceste funcii:
IsControl IsDigit IsLetter IsLetterOrDigit Indic dac caracterul un caracter de control Indic dac caracterul o cifr Indic dac caracterul o liter Indic dac caracterul liter sau cifr
121

Unicode specificat este Unicode specificat este Unicode specificat este Unicode specificat este

IsLower IsNumber IsPunctuation IsSeparator IsSymbol IsUpper IsWhiteSpace

Indic dac caracterul o liter minuscul Indic dac caracterul un numr Indic dac caracterul un semn de punctuaie Indic dac caracterul un caracter de separare Indic dac caracterul un simbol Indic dac caracterul o liter majuscul Indic dac caracterul un spaiu alb

Unicode specificat este Unicode specificat este Unicode specificat este Unicode specificat este Unicode specificat este Unicode specificat este Unicode specificat este

Fiecare dintre aceste funcii ia n calitate de parametru un caracter i returneaz valoarea true sau false, n dependen de satisfacerea sau nesatisfacerea de ctre caracter a unei condiii. De exemplu, se testeaz dac variabila conine ca valoare o cifr:
char c = 1; if(Char.IsDigit(c) == true) MessageBox.Show(Valoarea este o cifra.);

n continuare, este prezentat un exemplu de validare utiliznd aceste metode. Fie exist o caset textual n care se introduce numele unei persoane. Numele unei persoane nu poate conine, de exemplu, cifre. De aceea n procesul validrii se va testa neconinerea de cifre. Dac textul va conine cifre atunci boxa textual va rmne activ, nefiind permis activizarea altui control, textul colorndu-se n rou.
private void textBox1_Validating(object sender, CancelEventArgs e) { int lung = (sender as TextBox).Text.Length; for(int i = 0; i < lung; i++) { if(Char.IsDigit((sender as TextBox).Text[i]) == true) { e.Cancel = true; textBox1.ForeColor = System.Drawing.Color.Red; return; } } textBox1.ForeColor = System.Drawing.Color.Black; }

O alt modalitate de validare a informaiei la nivel de cmpuri poate fi realizat prin intermediul evenimentelor KeyPress, KeyUp, KeyDown. Cu
122

ajutorul acestor evenimente se poate urmri informaia introdus caracter cu caracter. La introducerea unui caracter care nu corespunde cerinelor specificate se pot realiza anumite contraaciuni. n exemplul ce urmeaz este testat introducerea unui numr, spre exemplu, anul naterii, i dac apar alte simboluri, se atenioneaz prin culoarea roie a textului:
private void textBox1_KeyPress (object sender, KeyPressEventArgs e) { if (Char.IsDigit(e.KeyChar) == false) this.ForeColor = System.Drawing.Color.Red; }

Evenimentele KeyPress, KeyUp, KeyDown pot fi utilizate i pentru validarea informaiei la nivel de form. Dar aceste evenimente nu sunt totdeauna active la nivel de form. Dac forma nu conine controale, atunci aceste evenimente sunt active la nivel de form. Dac ns forma conine mcar un control, atunci activarea evenimentelor la nivel de form depinde de proprietatea KeyPreview. Valoarea true a acestei proprieti duce la activarea evenimentelor date. n exemplul ce urmeaz, butonul button1 devine activat doar atunci cnd toate boxele textuale au informaia textual introdus. Este utilizat proprietatea Enabled a butonului atribuindu-i-se valoarea true.
private void form1_KeyPress(object sender, KeyPressEventArgs e) { foreach(Control c in this.Controls) { if (c is TextBox == true && c.Text == "") { button1.Enabled=false; return; } } button1.Enabled = true; }

n funciile de prelucrare a evenimentelor KeyPress, KeyUp, KeyDown este utilizat un parametru fictiv referitor la eveniment fie de tipul KeyPressEventArgs, fie de tipul KeyEventArgs. Ambele clase au proprietatea Handled de tip bool care poate fi utilizat n procesul validrii informaiei introduse. Dac valoarea proprietii este fixat egal cu true, acest fapt nseamn c evenimentul a fost prelucrat deja. Procednd astfel, se poate evita introducerea unor caractere care nu corespund tipului de informaie. n exemplul ce urmeaz va fi evitat introducerea de cifre ntr-o caset textual destinat pentru nume de persoane:
private void textBox1_KeyPress (object sender,
123

KeyPressEventArgs e) { if (Char.IsDigit(e.KeyChar) == true) e.Handled=true; }

Pentru atenionare la producerea unei erori poate fi utilizat componenta ErrorProvider. Un obiect de tipul ErrorProvider se leag cu controale. Pentru fiecare control el are un text succint ce descrie eroarea. Dac textul nu este un ir nul, atunci lng controlul respectiv apare un cerc rou cu semnul exclamrii (aceast pictogram la dorin poate fi schimbat prin proprietatea Icon), n caz contrar, semnul respectiv nu este afiat. La fixarea cursorului oricelului pe pictograma respectiv va apare textul care descrie eroarea din control. Pentru atenionare mai este folosit i efectul de clipire a pictogramei obiectului de tip ErrorProvider, viteza de clipire putnd fi controlat cu ajutorul proprietii BlinkRate. O metod foarte important este funcia SetError():
ErrorProv.SetError(numeControl, Text);

unde numeControl este numele controlului asociat cu obiectul ErrorProvider, iar Text este textul ce reprezint descrierea erorii. n exemplul ce urmeaz obiectul eroare de tip ErrorProvider este asociat cu caseta parola pentru introducerea parolei. ntruct de regul o parol nu trebuie s fie nul, utilizatorul este atenionat:
private void form1_KeyPress(object sender, KeyPressEventArgs e) { if(parola.Text == ) eroare.SetError(parola, Parola nu poate fi nul); else eroare.SetError(par, ); }

Obiectele de tipul ErrorProvider pot fi create la nivel de cod i la nivel de designer. La nivel de designer componenta ErrorProvider este adugat din seciunea Components a boxei cu instrumente (ToolBox). Deoarece un obiect de acest tip poate fi asociat cu mai multe controale, fixarea textului ce descrie eroarea din control se face n cadrul proprietilor controlului. Printre proprietile controlului este gsit proprietatea Error on numeObiect, unde numeObiect este numele obiectului de tip ErrorProvider. Valoarea acestei proprieti este de tip string i reprezint un text care descrie eroarea produs n cadrul controlului. La nivel de cod este creat obiectul, de exemplu:
ErrorProvider eroare = new ErrorProvider();

124

Apoi utiliznd metoda SetError(), se face asociarea cu controlul necesar i fixarea textului care descrie eroarea. Utilizarea metodei SetError() se face de numrul necesar de ori. Ca notificare a erorii poate fi utilizat i o box cu mesaj. n cadrul clasei MessageBox este metoda static Show() care deschide o box cu mesaj. Mesajul este transmis n calitate de parametru a metodei Show(). De exemplu:
MessageBox.Show(Parola nu poate fi nul);

Utilizarea meniurilor Un element important al multor aplicaii este meniul, care ofer o descriere succint a posibilitilor ncorporate n aplicaia concret. De aceea exist i o serie de clase care permit crearea unei interactiviti n baz de meniuri. Lucrul cu meniurile este posibil att la nivel de designer, ct i la nivel de cod. La nivel de designer exist toat gama de posibilti, pornind de la creare i terminnd cu realizarea funcionalitii legat de opiunile meniului. Pentru crearea unui meniu la nivel de designer n cadrul unei aplicaii, este utilizat componenta MenuStrip din boxa cu scule (ToolBox). Dup ce aceast component este tras pe form, mai jos de form apare numele meniului creat, iar n form apar locuri sensibile care pot fi redactate, extinse sau restrnse, permind astfel crearea opiunilor unui meniu ramificat. Imaginea de mai jos prezint o variant de meniu n proces de creare. Dup cte se observ din figur, opiunile meniului por fi de patru tipuri: opiune textual, opiune de tip ComboBox, opiune de tip TextBox i opiune care modeleaz o linie de separare ntre opiunile meniului.

125

O opiune textual de meniu const din trei zone: locul pentru nserarea unei imagini; locul pentru textul propriu - zis al opiunii; locul pentru scurttur sau pentru sgeata care indic faptul c opiunea are o deschidere ntr-un submeniu. n continuare este prezentat un fragment de meniu care exemplific diferite tipuri de opiuni textuale:

n regim de design aceste zone pot fi uor definite prin intermediul ferestrei proprietilor, fixnd valorile necesare pentru proprietile Image, ShortcutKeys i Text. n continuare este prezentat un model de fixare a acestor proprieti:
Properties . . . Image C:\file.jpg . . . ShortcutKeys Ctrl+F . . . Text File . . .

De fiecare opiune pot fi legate o serie de evenimente. Pentru a defini evenimentul necesar, este selectat opiunea, se trece n fereastra proprietilor la seciunea evenimentelor i fcnd dublu clic pe evenimentul dorit, va fi creat schema metodei de prelucrare a acestui eveniment. De exemplu, evenimentul EnabledChanged pentru opiunea nou:

126

private void nou_EnabledChanged(object sender, EventArgs e) { }

Pentru evenimentul Click al opiunii, exist o metod mai simpl de creare a schemei metodei, care const n efectuarea unui dublu clic pe opiunea necesar. De exemplu, pentru opiunea edit:
private void edit_Click(object sender, EventArgs e) { }

Din cte s-a menionat anterior, lucrul cu meniurile poate fi realizat i la nivel de cod. Exist mai multe clase orientate spre lucrul cu meniurile. n continuare vor fi caracterizate cteva dintre cele mai utilizate clase. Bara-meniu este reprezentat prin clasa MainMenu. Aceast clas realizeaz o versiune mai veche de meniu, fiind pstrat pentru compatibilitate cu aplicaiile mai vechi. Pentru crearea opiunilor acestui tip de meniu, este utilizat clasa MenuItem. Unul dintre constructorii des utilizai ai acestei clase are n calitatea de parametru textul opiunii. De exemplu:
MenuItem file = new MenuItem(File);

O serie de proprieti importante ale clasei MenuItem sunt prezentate n tabelul de mai jos:
Nr. Denumiri d/o proprieti 1 Checked 2 3 4 5 6 7 8 Enabled IsParent MenuItems RadioCheck Shortcut Text Visible Descrierea succint a efectului utilizrii Este de tip bool i arat dac opiunea este bifat sau nu Este de tip bool i arat dac opiunea este activ sau pasiv Este de tip bool i arat dac opiunea are submeniu Reprezint colecia de opiuni subordonat opiunii date Este de tip bool i arat dac opiunea este bifat sau nu Definete scurttur pentru lansarea procesului legat de opiunea dat Reprezint textul opiunii date Este de tip bool i arat dac opiunea este vizibil sau nu

Pentru clasa MainMenu, de asemenea, vor fi prezentate n tabelul ce urmeaz o serie de proprieti importante, care n mare parte coincid cu proprietile clasei MenuItem:
127

Nr. Denumiri d/o proprieti 1 Enabled 2 3 4 5 IsParent MenuItems Text Visible

Descriere succint a efectului utilizrii Este de tip bool i arat dac opiunea este activ sau pasiv Este de tip bool i arat dac opiunea are submeniu Reprezint colecia de opiuni subordonat opiunii date Reprezint textul opiunii date Este de tip bool i arat dac opiunea este vizibil sau nu

Vor fi caracterizate proprietile clasei MenuItem, cu toate c proprietile clasei MainMenu sunt n mare parte similare. Opunile de tip MenuItem pot fi nsemnate n partea stng cu bif sau cu un buton-radio. Acest fapt este exprimat respectiv prin proprietile Checked i RadioCheck. Dac opiunea va fi pasiv, efect obinut cnd proprietatea Enabled are valoarea false, ea va fi vizibil, dar procesele legate de dnsa nu vor putea fi lansate. Cu ajutorul proprietii Text poate fi fixat sau eventual schimbat textul opiunii. Pentru a aduga opiuni la o anumit opiune, este utilizat proprietatea MenuItems, care reprezint colecia opiunilor. Fiind o colecie, are metodele Add() i AddRange(), care permit adugarea unei sau a mai multor opiuni. De exemplu:
MainMenu meniu = new MainMenu(); MenuItem file = new MenuItem(File); MenuItem edit = new MenuItem(Edit); meniu.MenuItems.Add(file); meniu.MenuItems.Add(edit);

sau un rezultat echivalent, utiliznd AddRange()


MainMenu meniu = new MainMenu(); MenuItem file = new MenuItem(File); MenuItem edit = new MenuItem(Edit); meniu.MenuItems.AddRange(new MenuItem []{file, edit});

Pentru ca un meniu de tipul MainMenu s devin meniul unei forme, este utilizat proprietatea formei Menu. De exemplu:
MainMenu meniu = new MainMenu(); . . . this.Menu = meniu;

Opiunea meniului poate avea o liter evideniat pentru a crea o scurttur n baza tastei Alt, o scurttur de lansare a proceselor legate de opiune. Litera evideniat va fi subliniat la acionarea tastei Alt. Pentru a evidenia o liter a opiunii, n faa literei este plasat simbolul &. De exemplu:
MenuItem file = new MenuItem(&File);
128

Totodat, opiunile meniului pot fi separate n grupuri logice prin plasarea liniilor de separare. O linie de separare se obine la inserarea unei opiuni a crui text este cratima (-). De exemplu:
MenuItem separator = new MenuItem(-);

n procesul lucrului cu meniurile apare uneori necesitatea crerii unor meniuri dinamice, care se schimb de la situaie la situaie. Pot exista mai multe procedee de creare a meniurilor dinamice. Vor fi evideniate dou posibiliti simple n realizare: crearea unui meniu general, din care prin excluderea unor opiuni vor fi generate meniurile necesare; crearea unor meniuri elementare, din care prin combinare vor fi generate meniurile necesare. Prima variant este realizat utiliznd proprietatea Visible. Opiunile ascunse vor avea valoarea proprietii egal cu false, iar cele vizibile egal cu true. A doua variant este realizat utiliznd metoda MergeMenu(). Metoda dat permite combinarea a dou meniuri, adugnd la un meniu opiunile altui meniu. De exemplu, la meniul m1 se adaug opiunile meniului m2:
m1.MergeMenu(m2);

Versiunea nou de meniu se aseamn n unele privine cu versiunea caracterizat anterior, dar este mai complex. Bara-meniu este reprezentat, din cte s-a comunicat anterior, prin clasa MenuStrip. Pentru crearea opiunilor acestui tip de meniu, sunt utilizate o serie ntreag de clase, cum ar fi:
Nr. Denumiri clase d/o 1 ToolStripMenuItem 2 3 4 5 ToolStripTextBox ToolStripButton ToolStripSeparator ToolStripComboBox Descrierea succint a efectului utilizrii Permite crearea unei opiuni textuale de meniu Permite crearea unei opiuni de meniu de tip caset textual Permite crearea unei opiuni de meniu de tip buton Permite crearea unei linii de separare ntre opiunile meniului Permite crearea unei opiuni de meniu de tip Combobox

Fcnd unele comparaii,se poate deduce c proprietile opiunilor sunt n mare parte asemntoare cu proprietile opiunilor din meniurile de tip vechi. Vor fi prezentate doar o serie de proprieti importante:
Nr. Denumiri d/o proprieti 1 Checked Descrierea succint a efectului utilizrii Este de tip bool i arat dac opiunea este bifat sau nu
129

2 3 4 5 6 7 8

Enabled DropDownItems Image Owner Shortcuts Text Visible

Este de tip bool i arat dac opiunea este activ sau pasiv Reprezint colecia de opiuni subordonat opiunii date Permite fixarea imaginii unei opiuni Fixeaz sau arat proprietarul opiunii Definete scurttur pentru lansarea procesului legat de opiunea dat Reprezint textul legat cu opiunea dat Este de tip bool i arat dac opiunea este vizibil sau nu

Pentru a aduga opiuni la un anumit meniu, este utilizat proprietatea Items, care reprezint colecia opiunilor. Cu aceast colecie sunt utilizate metodele Add() i AddRange(), care permit adugarea unei sau a mai multor opiuni. De exemplu:
MenuStrip meniu = new MenuStrip(); ToolStripMenuItem file = new ToolStripMenuItem(File); ToolStripMenuItem edit = new ToolStripMenuItem(Edit); meniu.Items.AddRange(new ToolStripMenuItem [] {file, edit});

Trebuie de menioat c pentru a aduga opiuni la o anumit opiune este utilizat puin alt proprietate, i anume DropDownItems, reprezentnd colecia opiunilor. Cu aceast colecie de asemenea vor fi utilizate metodele Add() i AddRange(). De exemplu:
ToolStripMenuItem file = new ToolStripMenuItem(File); ToolStripMenuItem nou = new ToolStripMenuItem(Nou); ToolStripMenuItem salvare = new ToolStripMenuItem(Salvare); file.DropDownItems.AddRange(new ToolStripMenuItem [] {nou, salvare});

Dac opiunile adugate sunt de tipuri diferite, la organizarea lor n colecii va fi utilizat clasa mai general ToolStripItems. De exemplu:
ToolStripMenuItem edit = new ToolStripMenuItem(Edit); ToolStripMenuItem copy = new ToolStripMenuItem(Copy); ToolStripTextBox tb = new ToolStripTextBox(); edit.DropDownItems.AddRange(new ToolStripItems[] {copy, tb});

S-a menionat deja c o opiune textual poate avea asociate scurttur i imagine. Pentru a le defini la nivel de cod, sunt utilizate respectiv proprietile ShortcutKeys i Image. Proprietatea ShortcutKeys este de tipul Keys, ns pentru a-i fixa valoarea pot fi utilizate valori ale enumerrii Shortcut, dar fcnd n prealabil conversia la tipul Keys. Urmeaz un exemplu n baza acestor idei:
file.Image = Image.FromFile(@C:\file.jpg);

130

file.ShortcutKeys = (Keys)Shortcut.CtrlA;

Pot fi, de asemenea, definite i metode de prelucrare a diferitelor evenimente ale unei opiuni a meniului. Printre acestea mai frecvent este definit metoda de prelucrare a evenimentului de acionare a unei opiuni a meniului. n acest caz, este aplicat evenimentul Click al opiunii, care permite ataarea la opiune a metodei de prelucrare a evenimentului respectiv. n continuare este prezentat un exemplu de pregtire a opiunii exit pentru ca la acionarea ei forma s se nchid:
. . . ToolStripMenuItem file = new ToolStripMenuItem(File); ToolStripMenuItem exit = new ToolStripMenuItem(Exit); exit.Click += new EventHandler(exit_Click); file.DropDownItems.Add(exit); . . . private void exit_Click(object sender, EventArgs e) { this.Close(); }

O component important, dar care nu va fi caracterizat este componenta ToolStrip, care permite crearea barelor cu instrumente (butoane cu imagini sugestive). n multe privine ea se aseamn cu componentele care permit crearea meniurilor. Crearea de componente i controale Exist o gam bogat de componente i controale care permit realizarea celor mai sofisticate sarcini. Dar n unele situaii apare totui necesitatea de creare a unor componente sau controale proprii. Ar putea aprea o astfel de necesitate de exemplu cnd n procesul elaborrii de aplicaii apar situaii tipice legate de componente i controale, situaii care se repet n mai multe aplicaii. Elaborarea componentelor i controalelor proprii se va realiza prin derivare de la o clas de baz, extinzndu-i funcionalitatea n direcia necesar. n ierarhia de derivare exist clasa Component din care sunt obinite toate componentele i clasa Control din care sunt obinute toate controalele. Componenta proprie ar putea fi derivat din clasa Component, iar controlul propriu ar putea fi derivat de clasa Control. ns o astfel de elaborare ar necesita foarte mult lucru. Una dintre reguli la elaborarea componentelor i controalelor proprii este de a elabora elementul necesar n baza unei clase care este foarte apropiat ca funcionalitate i proprieti de elementul elaborat. Adic dac este posibil de utilizat componente i controale de nivel mai nalt. n acest caz vor fi dou posibiliti: fie trebuie de adugat noi caracteristici la clasa de baz, fie trebuie de schimbat unele caracteristici existente.
131

n cazul al doilea dac metodele schimbate sunt virtuale ele vor fi simplu suprancrcate, iar dac nu sunt virtuale este posibilitatea de a utiliza modificatorul new. De exemplu, este necesar de elaborat un control care reprezint o box textual, a crui fundal va fi rou dac boxa este doar pentru citire. Este natural de a obine controlul dat prin derivare de la clasa TextBox. Acest control va fi numit TextBoxColor. Pentru a obine ce se dorete este necesar doar de a schimba proprietatea ReadOnly. Fiindc aceast proprietate nu este de tip virtual ea nu poate fi suprancrcat. Dar se va merge pe calea utilizrii modificatorului new pentru a obine n clasa dat o nou versiune pentru proprietatea ReadOnly. Exemplul este desfurat n cele ce urmeaz:
class TextBoxColor : System.Windows.Forms.TextBox { public new bool ReadOnly { get { return base.ReadOnly; } set { if(value == true) this.BackColor = System.Drawing.Color.Red; else this.BackColor = System.Drawing.Color.White; base.ReadOnly = value; } } }

Clasa elaborat va aprea i n boxa cu scule (ToolBox). De aceea poate fi liber utilizat nu doar n regim de cod ci i n regim de design. Deci controlul poate fi plasat pe form. Iar valorile proprietilor i pot fi fixate n fereastra proprietilor. Controlul elaborat poate fi utilizat i n alte proiecte. Pentru aceasta n boxa cu scule trebuie adugat acest control, ceea ce se obine procednd astfel: o Clic opiunea Tools a meniului principal; n meniul care apare clic opiunea Choose Toolbox Items...; o n caseta de dialog Choose Toolbox Items clic butonul Browse; o este selectat fiierul cu extensiunea .exe sau .dll care face parte din proiectul n care a fost elaborat controlul; n boxa cu scule apare controlul elaborat.

132

Pentru a obine componente i controale proprii totui nu n toate cazurile este comod de a utiliza componente sau controale de nivel nalt. Mediul de dezvoltare Visual Studio conine scenarii de creare a componentlor i a controalelor proprii. Astfel se poate de mers pe urmtoarea cale: o clic opiunea Project a meniului principal; n meniul care apare clic opiunea Add User Control...; n fereastra care apare cu opiunea User Control selectat se scrie numele fiierului care corespunde numelui controlului creat; este acionat butonul Add. n felul acesta se intr n regimul de creare a unui control propriu. Procesul de creare permite aciuni i n regim de design, fiindc este posibil tragerea controalelor necesare din boxa cu scule pe forma care reprezint controlul. Controlul creat este reprezentat de o clas, motenit de la clasa de baz UserControl, care este obinut din clasa ContainerControl. La adugarea controalelor ele de regul sunt introduse cu modificatorul de acces private. Cnd sunt utilizate controale eventual pot fi utilizate funcionaliti ale acestora. Dar datorit modificatorului de acees ele nu vor fi accesibile direct. De aceea va trebui s fie prelucrate momentele necesare: proprieti, metode, evenimente. Va fi utilizat scenariul anterior pentru a crea urmtorul control: fiindc pe lng o box textual deseori se plaseaz o etichet pentru ai da o denumire, va fi creat un control care va reprezenta o box textual cu nume (NamedTextBox). Controlul va fi creat n baza unei boxe textuale cu numele boxa i a unei etichete, cu numele eticheta. Proprietatea Text a etichetei va constitui propiretatea Nume a controlului, iar proprietate Text a boxei textuale, va reprezenta proprietatea Textul a controlului. O box textual are o serie de evenimente legate de textul pe care l conine. De aceea ca exemplu va fi realizat evenimentul care se declaneaz cnd textul este schimbat. Acest eveniment al controlului va fi numit TextSchimbat. Va fi necesar o declarare a acestui eveniment, insernd n cadrul clasei controlului urmtoarea descriere:
public event System.EventHandler TextSchimbat;

Boxa textual care intr n componena controlului are evenimentul TextChange. Lucrul va fi organizat astfel nct declanarea acestui eveniment al boxei va duce la declanarea evenimentului TextSchimbat al controlului elaborat. n cele ce urmeaz este prezentat parial clasa care corespunde controlului elaborat, coninnd proprietile i evenimentul despre care s-a vorbit anterior:
public partial class NamedTextBox : UserControl { public event System.EventHandler TextSchimbat; public NamedTextBox() {

133

InitializeComponent(); } public string Nume { get { return eticheta.Text; } set { eticheta.Text = value; } } public string Textul { get { return boxa.Text; } set { boxa.Text = value; } } private void boxa_TextChange(object sender, EventArgs e) { if(TextSchimbat != null) TextSchimbat(this, new EventArgs()); } }

Controlul elaborat este vizibil n boxa cu scule i deci se poate lucra cu el i n regim de design. Totodat proprietile i evenimentele lui pot fi vzute n fereastra proprietilor. Deci pot fi fixate valorile necesare. Pentru a-l testa este creat o aplicaie coninnd un asfel de control. Sunt fixate valorile proprietilor necesare i este definit o funcie simpl de prelucrare a evenimentului TextSchimbat dup cum urmeaz:
private void namedTextBox1_TextSchimbat(object s, EventArgs e) { Message Box.Show (Textul Schimbat); // procesare necesara la evenimentul respectiv }

Not. Modul de organizare a evenimentelor va fi mai clar dup studierea temei Delegai i evenimente.

134

Creare de aplicaii MDI Aplicaiile Windows sunt diferite dup tipul interfeei. O form de interfa este de tip document singular (n englez SDI abreviere de la Single Document Interface). n acest caz, documentul este reprezentat printr-o fereastr care nu este subordonat direct altei ferestre. O alt form de interfa este de tip document multiplu (n englez MDI abreviere de la Multiple Document Interface). n acest caz, documentul este reprezentat printr-o fereastr care este subordonat direct unei forme printe i n cadrul ei pot fi deschise mai multe forme copii, care nu pot prsi niciodat cadrul formei printe. Iat cum ar arta cea mai simpl aplicaie de aa tip

Aplicaiile de tip MDI pot fi clasificate n felul urmtor: aplicaii MDI omogene, cnd formele copil sunt tratate n mod similar (un exemplu de aa aplicaie ar fi editoarele cu posibilitate de prelucrare a mai multor documente textuale); aplicaii MDI neomogene, cnd formele copil ndeplinesc funcii diferite (exemplu de o astfel de aplicaie este MSAccess, care are forme pentru crearea tabelelor, pentru crearea rapoartelor, pentru efecuare de interpelri .a.). Aplicaiile de tip MDI neomogene sunt mai complexe din punctul de vedere al realizrii. La activarea diferitelor forme copil va trebui s se schimbe meniul aplicaiei, ascunznd opiunile meniului care nu au sens n contextul formei activate i activnd opiuni necesare n acest context. Mediul de dezvoltare ofer o serie de posibiliti de creare a unor aplicaii de tip MDI. Procesul ncepe cu crearea unei aplicaii de tip Windows. n lista de proprieti a oricrei forme exist o proprietate responsabil de transformarea ei dintr-o aplicaie de tip SDI n una de tip MDI. Aceast proprietate este IsMdiContainer de tip bool. Cnd valoarea ei este false, forma va da natere la o aplicaie de tip SDI, iar cnd aceast valoare este true, forma va da natere unei aplicaii de tip MDI. Dac forma are proprietatea IsMdiContainer egal cu true, atunci forma i schimb aspectul, fundalul devenind mai ntunecat.
135

O astfel de form poate primi n calitate de controale butoane, casete textual, dar adugarea unor astfel de controale nu are prea mare sens pentru o aplicaie de tip MDI. De mai mare folos pentru astfel de aplicaii sunt meniurile i boxele cu instrumente, ele fiind create n conformitate cu specificul aplicaiei. Pe lng forma printe, o aplicaie de tip MDI conine i forme copil. Dac formele copil vor fi cu funcii similare, atunci este creat o form, definindu-i funcionalitatea necesar (prin controale, componente, proprieti, metode, evenimente). Dac formele copil vor fi cu funcii diferite, atunci este creat un numr necesar de forme, fiecare cu funcionalitatea sa. Fiecare form corespunde unei clase n baza creia vor fi generate obiectele necesare (ferestre). Pentru ca fereastr creat s devin fereastr copil a unei ferestre printe este necesar a-i fixa proprietatea MdiParent, indicnd n calitate de valoare fereastra, care va juca rolul de printe. Ferestrele copil vor fi generate la nivel de executare a aplicaiei. Pentru a exemplifica, se va presupune c aplicaia conine un meniu avnd opiunea Nou care se deschide n opiunile Document i Grafic. La acionarea opiunii Document se va deschide o fereastr copil de tip Document, iar la acionarea opiunii Grafic se va deschide o fereastr copil de tip Grafic. De aceea se mai presupune c exist clase Document i Grafic, care motenesc direct sau indirect de la clasa Form i realizeaz funcionalitatea legat respectiv de conceptele document i grafic. n continuare sunt prezentate funciile de prelucrare a evenimentului de acionare a opiunilor Document:
private void document_Click (object sender, EventArgs e) { Document fc = new Document(); fc.Name = Document- + Copil + this.MdiChildren.Length.ToString(); fc.Text = fc.Name; // se arat c forma printe este // intr-adevr printe a feerstrei create fc.MDIParent = this; // fc devine fereastr copil fc.Show(); // se va vizualiza n interiorul // formei printe }

i Grafic:
private void grafic_Click(object sender, EventArgs e) { Grafic fc = new Grafic(); fc.Name = Grafic- + Copil+ this.MdiChildren,Length.ToString(); fc.Text = fc.Name; fc.MdiParent = this;

136

fc.Show ( ); }

n aceste fragmente este utilizat proprietatea MdiChildren, care reprezint o colecie coninnd toate formele copil. De aceea MdiChildren[0] corespunde primului copil .a.m.d. O alt proprietate util n contextul aplicaiilor de tip MDI este ActiveMdiChild, care returneaz fereastra copil care este activ (returneaz un obiect de tip Form). Formele copil pot conine i meniuri specifice fiecrei forme. Dac este activat o fereastr creat n baza unei forme cu meniu, atunci meniul dat este ataat la meniul ferestrei printe. n felul acesta, are loc o acomodare a meniului la situaia specific legat de fereastra copil activ. De aceea n forma copil pot fi create fie meniuri diferite, fie meniuri care completeaz.

Delegai
Delegai simpli Este un lucru obiniuit de a transmite variabile, create n baza unor tipuri de date predefinite, i obiecte, create n baza unor clase, n calitate de parametri reali n procesul de apelare a unor metode. Drept exemplu ar putea fi metode ale unor clase din biblioteca fundamental de clase (de exemplu, metoda WriteLine() a clasei Console). Dar nu toate situaiile se rezolv prin apeluri, utiliznd doar astfel de parametri. O categorie de posibili parametri ar putea fi chiar nsei funciile. Ar fi bine de enumerat n ce situaii ar putea fi necesari astfel de parametri: Tratarea evenimentelor. Crearea mai multor fire de execuie. Crearea unor biblioteci de algoritmi (algoritmii de sortare snt asemntori). Funciile nu sunt transmise direct n calitate de parametri ai altor metode (funcii). Pentru a realiza astfel de aciuni, este utilizat noiunea de delegat. Un delegat face o mpachetare specific a informaiei despre funcie pentru a putea fi transmis apoi n calitate de parametru altei funcii. Un delegat este o noiune foarte specific. El are dou faete. Pe de-o parte, un delegat este o clas n baza creia pot fi create obiecte, ns pe de alt parte, fiecare obiect creat n baza unui delegat poart tot numele de delegat.

137

Descrierea unui delegat este foarte asemntoare cu descrierea unui prototip (signatur) de funcie:
tipr numeFunctie ([tip1 p1[,,tipn pn]]);

Declararea unui delegat la nivel de clas este fcut n caz general astfel:
[modificatori] delegate tipr numeDelegat([tip1 p1[,, tipn pn]]);

Aceast asemnare se datoreaz faptului c un delegat corespunde unei clase de funcii asemntoare dup prototip. Delegaii ce corespund funciilor cu prototipuri diferite vor fi diferii. A fost menionat deja faptul c n baza unei clase-delegat pot fi create obiecte care se vor numi, de asemenea, delegai. Crearea unui obiect de tip delegat este realizat astfel:
numeDelegat obiectDelegat = new numeDelegat(numeFunctie);

unde numeFunctie este funcia n baza creia este creat delegatul. Aceast funcie are tipul valorii returnate ca i cel al delegatului, iar parametrii si au acelai tip ca i parametrii delegatului. n continuare este prezentat un exemplu cu utilizare de delegai:
struct valuta { private uint Dolari; private uint Centi; public valuta (uint D, uint C) { Dolari = D; Centi = C; } public static string Unitate () { return Dolari; } public override string ToString () { return Dolari.ToString() + $ + Centi.ToString(); } } public delegate string UnSir(); static void Main() { int i=10; Valuta v = new Valuta(100, 49); UnSir sirulMeu = new UnSir(i.ToString); Console.WriteLine(sirulMeu()); UnSir sirulMeu = new UnSir(v.ToString);

138

Console.WriteLine(sirulMeu()); UnSir sirulMeu = new UnSir(Valuta.Unitate); Console.WriteLine(sirulMeu()); }

n acest exemplu, urmrind utilizarea delegailor, pot fi trase o serie de concluzii importante: n baza unuia i aceluiai delegat pot fi create diferite obiecte (delegai) avnd ca baz diferite metode. Metodele pot fi att statice, ct i nestatice. Metodele nestatice sunt introduse printr-un obiect, iar cele statice printr-o clas. Metodele pot fi ale diferitor clase. Pentru crearea unui delegat, este important signatura metodei. Delegaii pot fi apelai ca nite funcii libere, nelegate de anumite obiecte. Delegaii nu sunt foarte necesari la nivel de apelare simpl a unor funcii. Ei sunt importani prin calitatea lor de a putea fi transmii ca parametri ai altor metode. Astfel ei realizeaz transmiterea funciilor (metodelor) ca parametri altor metode. n exemplul ce urmeaz n baza funciilor Dublare() i Patrat() este creat tabloul de delegai op, iar elementele acestui tablou sunt transmise pe rnd metodei ProcesareSiAfisare(), realizndu-se calculul necesar i afiarea rezultatului:
class Operatii { public static double Dublare (double val) { return 2*val; } public static double Patrat (double val) { return val*val; } } public delegat double OpMat(double v); public static void ProcesareSiAfisare (OpMat op, double v) { double rez = op(v); Console.WriteLine(Pentru {0} valoarea este {1}, v, rez); } static void Main () { OpMat [] op = {new OpMat (Operatii.Dublare),

139

new OpMat (Operatii.Patrat)}; for (int i=0; i<op.Lenght; i++) { ProcesareSiAfisare(op[i], 1.2); ProcesareSiAfisare(op[i], -143.12); } }

Drept rezultat va fi afiat:


Pentru Pentru Pentru Pentru 1.2 valoarea este 2.4 -143.12 valoarea este -286.24 1.2 valoarea este 1.44 -143.12 valoarea este 20483.3344

O importan deosebit o au delegaii n procesul crerii unor biblioteci de algoritmi. Pentru a acoperi un domeniu mai vast de date i deci a obine o aplicabilitate mai mare a algoritmilor, algoritmii sunt cercetai pentru a gsi prile lor asemntoare i neasemntoare n raport cu diferite tipuri de date. Pentru exemplificare va fi luat sortarea prin metoda bulelor.
. . . void Bule (int [] tab) { for (int i = 0; i< tab.Lenght; i++) for (int j=0; j< tab.Lenght; j++) if (t[i] < t[i+1]) { int temp = t[i]; t[i] = t[i+1]; t[i+1]=temp; } } . . .

Fragmentul anterior prezint realizarea algoritmului pentru un tablou de tip int. Schimbnd tipul de date al tabloului, algoritmul ar putea s nu mai lucreze din motiv c compararea a dou elemente nu se mai realizeaz tot aa:
if (t[i] < t[i+1]) { . . . }

Deci, pentru alte tipuri de date, trebuie acomodat algoritmul de comparare a dou elemente. Toate celelalte aciuni ale algoritmului de sortare prin metoda bulelor vor fi similare pentru toate tipurile de date. n lumina acestor idei, metoda de comparare va fi transmis n calitate de parametru n metoda ce realizeaz metoda bulelor i n felul acesta metoda bulelor va putea fi aplicat unui tablou de orice tip cu singura condiie ca tipul s conin metoda de comparare a dou elemente.

140

O metod de comparare trebuie s satisfac unele cerine: Returneaz o valoare de tip bool. Are doi parametri fictivi, corespunznd elementelor comparate, ambii fiind de tip object. Se datoreaz faptului c metoda de sortare trebuie realizat pentru cazul cel mai general. n corpul funciei ambii parametri fictivi sunt convertii la tipul clasei concrete, n cadrul creia se realizeaz funcia de comparare. n exemplul ce urmeaz, clasa Angajat conine i o metod de comparare a dou obiecte de tip Angajat. n cazul a doi angajai, o posibil metod de comparare ar putea fi compararea n raport cu salariul, idee realizat n metoda de comparare, aparinnd clasei Angajat. Acest fapt permite aplicarea metodei bulelor la ordonarea unui tablou de tip Angajat.
class Angajat { private string nume; private decimal salariu; public Angajat (string n, decimal s) { nume = n; salariu = s; } public override string ToString() { return nume + are salariul + s.ToString(); } public static bool ComparareAngajati(object St, object Dr) { Angajat a1 = (Angajat) St; Angajat a2 = (Angajat) Dr; if (a1.salariu > a2.salariu) return true; else return false; } } public delegate bool Comparare(Object St, Object Dr); class Sortare { public static void Bule (object [ ] tab, Comparare comp) { for (int i = 0; i< tab.Lenght; i++) for (int j=0; j< tab.Lenght; j++) if (comp (tab[j], tab[j+1]))

141

{ object temp = tab[j]; tab[j]=tab[j+1]; tab[j+1]=temp; } } } static void Main ( ) { Angajat [] angajati = {new Angajat(Srbu, 200); new Angajat(Dobo, 150); new Angajat(Negru, 400); new Angajat(Ciubotari, 300);} Comparare compAng = new Comparare(Angajat.ComparareAngajat); Sortare.Bule(angajati, compAng); for (int i=0; i<angajati.Lenght; i++) Console.WriteLine(angajati[i].ToString()); }

Delegai multipli n mulimea delegailor exist o categorie important de delegai, i anume delegaii multipli. Dac un delegat simplu este legat de o singur metod pe care o reprezint, atunci un delegat multiplu poate reprezenta concomitent mai multe metode. Declararea unui delegat multiplu la nivel de clas este fcut n caz general astfel:
[modificatori] delegate void numeDelegat([tip1 p1[,, tipn pn]]);

Dup cum se observ, un delegat multiplu are tipul returnat void. Pe lng aceast mic deosebire, un delegat multiplu mai are definii o serie de operatori: 1) += pentru a alipi la un delegat metodele asociate cu delegatul adunat; 2) -= pentru a extrage dintr-un delegat metodele asociate cu delegatul sczut; 3) + pentru a aduna doi delegai, obinnd ca rezultat un delegat ce conine metodele ambelor delegai (delr = del1 + del2); 4) - pentru a scadea doi delegai, obinnd ca rezultat un delegat ce conine metodele, aparinnd primului delegat i neapainnd celui de al doilea delegat (delr = del1 - del2).

142

n continuare exemplul prezentat anterior, coninnd clasa Operatii, este rescris pentru a putea utiliza un delegat multiplu:
class Operatii { public static void Dublare(double v) { Console.WriteLine({0} - valoare dubla {1}, v, 2*v); } public static void Patrat(double v) { Console.WriteLine({0} - patrat {1}, v, v*v); } } public delegat void OpMat(double v); public static void ProcesareSiAfisare (OpMat op, double v) { op(v); } static void Main () { OpMat op = new OpMat (Operatii.Dublare); op += new OpMat (Operatii.Patrat); ProcesareSiAfisare(op, 1.2); ProcesareSiAfisare(op, -143.12); }

Drept rezultat va fi afiat:


1.2 - valoare dubla 2.4 1.2 - patrat 1.44 -143.12 - valoare dubla -286.24 -143.12 - patrat 20483.3344

Delegai i evenimente Ideologia acceptat n prezent, pentru a obine o interactivitate ntre utlizator i calculator, se bazeaz pe prelucrarea evenimentelor. Apariia unei situaii specifice care trebuie s se termine cu o prelucrare specific poate fi numit eveniment. Fiindc evenimentele sunt legate de managementul unui anume proces, ele pot fi diverse. Totui, sunt o serie de evenimente standarde legate de anumite dispozitive periferice, cum ar fi oricelul, tastatura. Ar putea fi numite, de exemplu, Click, DoubleClick .a. Dar diferite procese vor reaciona diferit la situaia de Click, de aceea aceast reacie va fi materializat prin crearea unei funcii
5)
143

concrete care va exprima algoritmul concret de prelucrare pentru situaia dat. De aceea, pentru a activa funciile legate de procesele de prelucrare a evenimentelor, sunt utilizai delegaii. Deci delegaii sunt importani i n cadrul procesrii evenimentelor. Orice eveniment are dou faete. n primul rnd, exist un obiect care lanseaz evenimentul (lansator engl. sender). n al doilea, rnd exist o serie de obiecte care recepioneaz evenimentul (recepioner engl. receiver). Limbajele platformei .NET permit nu doar aplicarea evenimentelor standarde implementate n cadrul platformei, ci i implementarea de noi evenimente necesare n contextul problemei soluionate. Implementarea unui eveniment nou cere ndeplinirea mai multor cerine. Pentru a nelege toate momentele necesare la realizarea unui eveniment nou, ar fi bine de urmrit o serie de caracteristici ale evenimentelor standarde existente n limbajul C#. Fiecare eveniment i are un delegat corespunztor, fiindc este necesar activarea unei funcii care va procesa situaia legat de eveniment. Delegatul corespunztor unui eveniment este un delegat multiplu i are urmtoarea form general:
modif_acces delegate void NumeDelegat(object l, ClasaEv e);

unde modif _acces reprezint modificatorul de acces, NumeDelegat este denumirea delegatului, l este parametrul ce corespunde i descrie obiectul ce a lansat evenimentul, e este parametrul de tipul clasei ClasaEv ce descrie evenimentul. De exemplu, pentru evenimentul Click este descris urmtorul delegat:
public delegate void EventHandler(object s, EventArgs e);

n baza delegatului corespunztor evenimentului se descrie numele evenimentului. Poate fi aplicat urmtoarea form general:
modif_acces static event NumeDelegat NumeEv;

unde modif_acces reprezint modificatorul de acces, NumeDelegat este denumirea delegatului, NumeEv este numele evenimentului. De exemplu, eveniment Click este descris n felul urmtor:
public static event EventHandler Click;

Orice eveniment lansat poate avea un set de funcii specializate de prelucrare a evenimentului dat. Prototipul (signatura) funciei trebuie sa corespund delegatului asociat cu evenimentul, avnd n caz general urmtoarea form
modif_acces void NumeFunctie(object l, ClasaEv e) { . . . }

unde modif_acces reprezint modificatorul de acces, NumeFunctie este denumirea funciei, l este parametrul ce corespunde obiectului ce a lansat evenimentul, e este parametrul de tipul clasei ClasaEv ce descrie evenimentul.
144

Funcia necesar este anexat la setul de funcii ce prelucreaz evenimentul prin intermediul delegatului corespunztor astfel:
numeObiect.NumeEv += NumeDelegat(NumeFunctie);

unde numeObiect reprezint obiectul ce are aa un eveniment, NumeEv este numele evenimentului, iar NumeFunctie este funcia de prelucrare a evenimentului corespunztor. Pentru exemplificare se va presupune existena pe o form a unei etichete i a dou butoane btnUnu, btnDoi, pentru care va fi activat evenimentul Click. btnUnu btnDoi n corespundere cu schema descris anterior, va fi creat urmtoarea funcie de prelucrare:
private void Button_Click { if (((Button)sender).Name label.Text=Actionat if (((Button)sender).Name label.Text=Actionat } (object sender, EventArgs e) == btnUnu) butonul 1; == btnDoi) butonul 2;

Funcia este inclus n setul de prelucrare a evenimentului pentru ambele butoane:


btnUnu.Click += new EventHandler (Button_Click) btnDoi.Click += new EventHandler (Button_Click)

A fost menionat c evenimentul poate fi prelucrat de un set de funcii. De aceea ca exemplu mai este creat urmtoarea funcie
private void Button2_Click (object s, EventArgs e) { MessageBox.Show(Butonul 2); }

care va fi lansat doar la acionarea butonului btnDoi:


btnDoi.Click += new EventHandler (Button2_Click)

Deci la acionarea butonului btnUnu va fi activat funcia Button_Click() pe cnd la acionarea butonului btnDoi vor fi activate dou funcii Button_Click() i Buttion2_Click(). Delegai i fire de execuie Ideea firelor de execuie este de a rula mai multe procese n paralel. Un paralelism adevrat se obine doar atunci cnd sistemul de execuie (calculatorul) are mai multe procesoare i unui fir de execuie i revine un procesor. Dar n practic nu totdeauna este posibil punerea la dispoziia unui proces a unui
145

procesor. De aceea, n asemenea situaii, are loc partajarea timpului de execuie a procesorului pentru mai multe procese. Este creat o coad a proceselor executate. Este executat primul proces un fragment de timp, apoi el este abandonat i se trece la executarea urmtorului proces un fragment de timp, continund astfel pn la epuizarea cozii proceselor, dup care procedeul se repet de numrul necesar de ori. Astfel se creeaz impresia unei execuii paralele a proceselor, care de fapt nu este altceva dect o simulare a paralelismului. Firele de execuie sunt organizate sub form de funcii, de aceea pentru lansarea firelor de execuie n C# se recurge la delegai. Exist o serie de clase i delegai orientai la crearea i gestionarea firelor de execuie. Pentru a avea acces la componente ce in de firele de execuie, trebuie ataat spaiul de nume System.Threading. O clas important ce permite lucrul cu firele de execuie este clasa Thread. Firul se creeaz n baza unui obiect de tip delegat. Delegatul pentru crearea unui fir are urmtoarea descriere:
public delegate void ThreadStart();

Deci crearea unui obiect de tip delegat, care va desemna punctul de nceput al firului de execuie, se efectueaz astfel:
ThreadStart delegatFir=new ThreadStart(NumeFunctie);

unde NumeFunctie este funcia, care realizeaz firul de execuie. Pentru a crea firul de execuie, poate fi utilizat constructorul clasei Thread:
Thread fir=new Thread(delegatFir);

avnd ca parametru un obiect de tipul delegatului ThreadStart. Clasa Tread are o serie de metode i proprieti utile n procesul de dirijare a firelor de execuie. Iat cteva metode i proprieti:
Nr. Nume d/o 1 Start() 2 3 4 5 6 7 Sleep() Suspend() Resume() Abort() CurrentThread Name Descrierea succint a efectului utilizrii Permite pornirea unui fir de execuie Permite trimitrea firului de execuie n stare de somn Permite suspendarea unui fir de execuie Permite repornirea unui fir de execuie Permite oprirea unui fir de execuie Proprietate static, care permite identificarea firului de execuie curent Permite fixarea unui nume pentru firul de execuie
146

Exemplu. De alctuit un program n care sunt create dou fire de execuie, fiecare fcnd afiarea numerelor pe un interval din 100 n 100.
using System; using System.Threading; namespace fireConsola { class Program { private static int Interval; static void AfisareNumere() { Thread firC=Thread.CurrentThread; for(int i=0; i<Interval; i++) if(i%100==0) Console.WriteLine(firC.Name+i.ToString()); } static void StartFir() { AfisareNumere(); Console.WriteLine("Fir terminat."); } static void Main(string[] args) { Interval = int.Parse(Console.ReadLine()); Thread m = Thread.CurrentThread; m.Name = "Main"; ThreadStart fd = new ThreadStart(StartFir); Thread fir = new Thread(fd); fir.Name = "Fir"; AfisareNumere(); fir.Start(); Console.WriteLine("Fir Main terminat."); Console.ReadLine(); } } }

Pentru o dirijare mai fin a firelor de execuie, este activat i mecanismul prioritilor prin intermediul cruia poate fi dictat gradul de importan a proceselor executate. Clasa Thread are proprietatea Priority, care poate lua valori de tipul enumerrii ThreadPriority. Urmtorul tabel descrie valorile enumerrii ThreadPriority:
Nr. d/o Nume Descriere succint a efectului utilizrii

147

1 2 3 4 5

Highest AboveNormal Normal BelowNormal Lowest

Fixeaz cea mai nalt prioritate a firului Fixeaz o prioritate a firului mai mare ca cea normal Fixeaz o prioritate normal a firului Fixeaz o prioritate a firului mai mic ca cea normal Fixeaz cea mai joas prioritate a firului

Un exemplu de fixare a prioritii ar fi urmtorul:


fir.Priority = ThreadPriority.Highest;

Un alt moment referitor la firele de execuie este legat de interaciunea lor. Exist date, care pot fi folosite de diferite fire de execuie. Se poate ntmpla ca datele s fie utilizate de un fir de execuie i intervenia altui fir ar putea s le compromit. n asemenea situaii, este necesar sincronizarea firelor de execuie. Sincronizarea ar nsemna o conlucrare a firelor de execuie, cnd toate datele prelucrate de firele de execuie sunt procesate corect. Un mecanism de sincronizare este realizat prin blocarea unor date pentru toate firele de execuie, cu excepia unuia, care face o procesare a acestor date. Dac anumite fire de execuie au necesitatea de a procesa datele blocate, ele trec n regim de ateptare pn la deblocarea lor. Schema general de blocare este urmtoarea
lock(r) { Procesare(); }

unde r este resursa blocat, fiind exprimat printr-un obiect sau printr-o variabil de anumit tip, iar Procesare() este expresia procesrii necesare. Aplicarea blocrii trebuie fcut cu mult atenie, fiindc poate genera ineficien de prelucrare sau erori, care uneori sunt greu de depistat. Ineficiena apare atunci cnd procedeul de blocare este folosit foarte des. O utilizare abundent a blocrii poate duce la o executare a firelor nu paralel, ci mai degrab secvenial. O eroare posibil este aa-numitul blocaj total (deadlock). El apare cnd un fir de execuie are urmtoarea schem de blocare
lock(a) //firul 1 { . . . lock(b) { . . . }

148

. . . }

iar al doilea fir de execuie are schema de blocare ce urmeaz:


lock(b) //firul 2 { . . . lock(a) { . . . } . . . }

Dac ambele fire vor ajunge la blocajele corespunztoare aproximativ n acelai timp, atunci firul nti va bloca resursa a, iar firul al doilea va bloca resursa b. naintnd n procesare, firul nti va necesita resursa b, care este blocat i de aceea firul nti va trece n regim de ateptare. n mod similar, naintnd n procesare firul al doilea, va necesita resursa a, care este blocat i de aceea firul al doilea va trece de asemenea n regim de ateptare. Deci ambele fire au czut n regim de ateptare, care nu poate fi rezolvat nicicum. E clar c pentru a evita blocajul total, trebuie de evitat schemele de blocaj prezentate anterior. Dar uneori este greu de urmrit tipul schemei de blocaj din motiv c schema poate fi ascuns de o ierarhie de apeluri ale unor funcii implicate n prelucrarea datelor.

Grafic
Folosirea GDI+ Sistemul grafic este reprezentat prin mai multe dispozitive periferice. Exemple de dispozitive periferice grafice pot fi monitoarele, imprimantele, ploterele. O caracteristic important a procesrii informaiei grafice l reprezint faptul c ea este independent de dispozitive. Acest fapt se obine prin intermediul standardului GDI (engl. Graphic Device Interface), care este o parte a sistemului de operare Windows, realizat prin biblioteca dinamic GDI.DLL. Pentru interaciune cu un dispozitiv concret, mai este activat programul pilot (engl. driver) al dispozitivului, realizat tot sub forma unei biblioteci dinamice. n procesul operaiilor grafice, este creat i meninut o structur specializat, numit contextul dispozitivului (engl. device context). Contextul dispozitivului conine informaie referitoare la dispozitiv. Informaia coninut n contextul dispozitivului poate fi schimbat doar indirect prin apelarea unor funcii specializate.

149

Desigur c platforma .NET utilizeaz aceleai idei de baz, dar le mbrac n ideile orientrii spre obiecte. Platforma .NET vine cu o mbuntire a standardului GDI obinndu-se GDI+, care este mai optimizat i deci mai eficient. n cadrul platformei sunt implementate o serie de clase care permit procesrile necesare la nivelul operaiilor grafice. Platforma .NET conine o serie de spaii de nume responsabile pentru unele trsturi ale prelucrrilor grafice. De aceea la efectuarea unor operaii grafice n proiect vor fi incluse spaiile de nume necesare. Astfel spaiul System.Drawing conine toate clasele generale care fac posibil lucrul cu grafica. Spaiul System.Drawing.Drawing2D conine clasele care permit realizarea graficii 2D. Spaiul System.Drawing.Imaging conine clasele care permit lucrul cu imagini. Spaiul System.Drawing.Printing conine clasele care realizeaz imprimarea. Spaiul System.Drawing.Text conine clasele care permit utilizarea textului n cadrul graficii. Pentru a efectua operaii grafice de desenare n primul rnd este necesar a crea canavaua pe care se va desena. Orice control are metoda CreateGraphics(), care permite a crea canavaua grafic. n continuare este prezentat un exemplu foarte simplu de desenare a unui dreptunghi pe canavaua unei forme:
using System.Drawing; using System.Windows.Forms; namespace Grafica { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Show(); Graphics canava = this.CreateGraphics(); Pen p = new Pen(Color.Red, 3); canava.DrawRectangle(p, 0, 0, 100, 200); } } }

Dreptunghiul dat apare n colul stnga sus a formei. Totui acest desen are un neajuns: la minimizarea i restabilirea ferestrei sau la trecere unei ferestre pe deasupra desenului, desenul va disprea i nu va mai apare dect la o nou desenare. Pentru a evita un astfel de neajuns este bine de efectuat desenarea din funcii care sunt legat de evenimentul Paint. Ca exemplu al unor astfel de funcii este metoda virtual OnPaint() a formei i funcia care proceseaz nemijlocit
150

evenimntul

Paint. Funciile menionate au un parametru de tip PaintEventArgs pentru descrierea evenimentului Paint. Obiectele de tip PaintEventArgs au proprietatea Graphics, care returneaz canavaua grafic a

controlului. Pentru exemplificare va fi rescris exemplul anterior, utiliznd funciile menionate:


protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics canava = e.Graphics; Pen p = new Pen(Color.Red, 3); canava.DrawRectangle(p, 0, 0, 100, 200); }

sau
private void Form_Paint(object s, PaintEventArgs e) { Graphics canava = e.Graphics; Pen p = new Pen(Color.Red, 3); canava.DrawRectangle(p, 0, 0, 100, 200); }

Din motiv c aceste funcii sunt apelate la declanarea evenimentului de redesenare, figura se va pstra pe suprafaa ferestrei. Pe de alt parte operaiile de desenare sunt critice ca timp de efectuare i de aceea dac se face abuz de operaii neoptimizate se poate ajunge la efecte grafice nedorite. Pentru a realiza operaii de desenare mai optimizate trebuie de redesenat doar fragmentele care au suferit schimbri. Din acest motiv parametrul e de tip PaintEventArgs are proprietatea ClipRectangle, care permite fixarea poriunii dreptunghiulare care va fi redesenat. Aceast proprietate are o serie de proprieti, legate de poziie i poziionare: X, Y, Top, Bottom, Left, Right. Revenind la exemplul ce deseneaz un dreptunghi, se poate spune c redesenarea lui n cadrul funcie ce prelucreaz evenimentul Paint nu are sens dac X>100 sau Y>200. n acest caz dreptunghiul nu este vizibil i degeaba este redesenat pierznd timp i resurse. De aceea se va face urmtoarea optimizare a funcie respective:
private void Form_Paint(object s, PaintEventArgs e) { Graphics canava = e.Graphics; Pen p = new Pen(Color.Red, 3); if(e.ClipRectangle.X<=100 && e.ClipRectangle.Y<=200) canava.DrawRectangle(p, 0, 0, 100, 200); }

151

n procesul prelucrrii informaiei grafice sunt necesare o serie de clase de poziionare i de dimensionare. Astfel exist clasa Point, orientat la lucru cu coordonate ale punctului. Aceast clas are constructor fr parametri i constructor cu parametri. Desigur c clasa Point are parametrii X i Y, pentru a putea accesa coordonatele punctului. Coordonatele punctelor de tip Point sunt ntregi de tip int. O alt clas similar este PointF, care se deosebete de Point doar prin faptul c se refer la puncte cu coordonate de tip float. O alt clas important este clasa Size, orientat la lucru cu dimensiuni. Similar cu clasa Point aceast clas are constructor fr parametri i constructor cu parametri. Pentru clasa Size sunt caracteristici parametrii Width i Height, pentru a putea accesa respectiv limea i nlimea obiectului. Aceti parametri sunt ntregi de tip int. O alt clas similar este SizeF, care se deosebete de Size doar prin faptul c se refer la dimensiuni cu componente de tip float. Cu toate c structural att clasa Point (PointF) ct i clasa Size (SizeF) reprezint perechi de numere ntregi (cu virgul mobil), totui nu este posibil trecerea direct de la date de tip Point (PointF) la date de tip Size (SizeF) i invers. Acest lucru poate fi fcut doar prin conversie. n continuare sunt prezentate cteva operaii admise:
Point p=new Point(100,50); Size s; s=(Size)p; s.Width=200; p=(Point)s;

Clasa Size are suprancrcai i o serie de operatori: 1. operatorul +, avnd dou forme, prezentate convenional astfel; a) Point = Point + Size; (operaie necomutativ) b) Size = Size + Size; 2. operatorul -, avnd dou forme, prezentate convenional astfel; c) Point = Point Size; (operaie necomutativ) d) Size = Size Size. Iat un exemplu:
Point p=new Point(100,50); Point p1=new Point(); Size s=new Size(30,50); P1 = p + s;

Pentru a delimita o zon dreptunghiular este utilizat clasa Rectangle. Clasa Rectangle are suprancrcai mai muli constructori pentru a acoperi mai multe situaii. Parametrii cu care opereaz clasa Rectangle sunt de tip int. O alt clas
152

similar este RectangleF, care se deosebete de Rectangle doar prin faptul c opereaz cu parametri de tip float. Pentru clasele descrise anterior este important noiunea de sistem de coordonate i deci originea poate avea urmtoarele poziii: - colul stnga sus al ecranului; - colul stnga sus al ferestrei; - colul stnga sus al zonei client. n procesul de creare a aplicaie sunt utilizate mecanisme de depanare, printre care ar fi i executarea pe pai sau fixarea punctelor de ntrerupere. Dar aplicaiile care utilizeaz grafica au un specific de depanare din motiv c este utilizat evenimentul Paint, legat de redesenarea ferestrei. Orice acoperire sau descoperire a ferestrei duce la declanarea evenimentului Paint. De aceea la executarea pe pai poate fi declanat evenimentul Paint la orice pas ceea ce va duce la o denaturare a unor fragmente ale algoritmilor legai de grafic. Pentru a putea totui aplica unele mecanisme de depanare se va folosi proprietatea TopMost a formei. Aceast proprietate este de tip bool, pitnd primi valoarile true sau false. Atunci cnd proprietatea ia valoarea true fereastra nu va putea fi acoperit de fereastra altei aplicaii. Se va proceda astfel:
this.TopMost = true;

O astfel de atribuire poate fi plasat n funcia InitializeComponents(). La crearea unor imagini mari care nu ncap pe suprafaa de lucru a ferestrei apare necesitatea de creare a aplicaiilor grafice scrolabile. n procesul de creare a aplicaiilor scrolabile vor fi utilizate cteva proprieti referitoare la aciunea de scrolare. Proprietatea care fixeaz dimensiunea minim a dreptunghiului supus operaie de autoscrolare este AutoScrollMinSize, fiind de tipul clasei Size. De exemplu
this.AutoScrollMinSize = new Size(50, 500);

O alt proprietate este AutoScrollPosition, care ofer poziia curent a scrolului, fiind de tipul clasei Point. Pot fi evideniate dou metode de creare a aplicaiilor scrolabile. Prima este mai complicat i necesit unele operaii de recalculare a coordonatelor n dependen de poziia scrolului. n cele ce urmeaz va fi rescris codul ce deseneaz un dreptunghi, dar va fi adugat i operaia de scrolare:
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Point sp = AutoScrollPosition; if(e.ClipRectangle.X+sp.X<=50 &&
153

e.ClipRectangle.Y+sp.Y<=500) { Graphics canava = e.Graphics; Pen p = new Pen(Color.Red, 3); canava.DrawRectangle(p, sp.X, sp.Y, 100, 200); } }

O alt metod de scrolare se bazeaz pe metoda TranslateTransform(x, y) a clasei Graphics, care permite translarea unui obiect grafic n coresponden cu valorile parametrilor x i y. Utiliznd aceast metoda se va obine urmtoarea variant a exemplului anterior:
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics canava = e.Graphics; Point sp = AutoScrollPosition; canava.TranslateTransform(sp.X, sp.Y); if(e.ClipRectangle.X<=50 && e.ClipRectangle.Y<=500) { Pen p = new Pen(Color.Red, 3); canava.DrawRectangle(p, 0, 0, 100, 200); } }

Un aspect important referitor la grafic este legat de manipularea culorilor. Exist o serie de tipuri referitoare la noiunea de culoare, coninnd operaiile corespunztoare acestei noiuni. O structur important este structura Color. Formarea multitudinii de culori este realizat n baza modelului RGB (engl. RedGreen-Blue, rom. Rou-Verde-Albastru). Mixnd aceste trei culori n proporii diferite pot fi obinute toate culorile spectrului vizibil. Cantitatea fiecrei culori de baz este esprimat printr-o valoare cuprins ntre 0 i 255. De exemplu, combinaiei (0, 0, 0) i corespunde coloarea neagr. Deci fiecare component poate fi reprezentat pe un octet. Reieind din acest considerent, un calcul necomplicat permite evaluarea numrului de culori posibile, care este egal cu: (28)3 = 224 16000000 Pentru a obine culoare din modelul RGB este utilizat funcia static FormArgb(), care are mai multe forme. De exemplu:
Color Negru = Color.FromArgb(0, 0, 0);

n afar de parametrii ce corespund culorilor funcia mai poate avea un parametru care reprezint coeficientul de amestec (engl. Alfa Blend). i acest parametru poate lua valori de la 0 la 255. Dac este 0 atunci culoarea va fi complet absorbit
154

de fundal, iar dac valoarea este 255 atunci culoarea va fi complet vizibil. Formarea culorilor din modelul RGB este un mecanism puternic, dar nu tocmai comod. De aceea sunt predefinite o serie de denumiri pentru culorile des utilizate. Tabelul de mai jos propune o serie de culori predefinite:
Nr d/o 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Nume Azure Black Blue Cyan DarkGreen ForestGreen Gray Green Ivory Khaki LightBlue Maroon Magenta Orange Pink Red Silver Turquoise Violet White Yellow Descriere succint a efectului utilizrii Azuriu Negru Albastru Cyan Verde nchis Verdele pdurii Gri Verde Fildeiu Kaki Albastru deschis Maro Magenta Oranj Roz Rou Argintiu Turcuaz Violet Alb Galben

Pe lng schema creat n baz de 3 octei exist o schem simplificat creat n baz de 2 octei. n acest caz fiecrei culori a modelului RGB i corespunde 5 binari. Deci numrul total de nuae coloristice pentru aceast schem este de 65536. Pentru unele situaii este mai bine de a folosi scheme coloristice formate din mai puine culori. Astfel este folosit noiunea de palet special care este format din 216 culori. Pentru a forma culorile paletei speciale valorile fiecrei culori a modelului RGB sunt luate dintr-o progresie cu raia 51, avnd urmtorii termeni: 0, 51, 102, 153, 204, 255. De aceea i numrul de culori obinute este de 6*6*6=216. Windows utilizeaz un set de 256 culori, care estinde paleta special. Pe lng 216 culori ale paletei speciale, mai sunt utilizate 20 de culori standard i 20 de culori adiionale. Pentru procesul de trasare a liniilor i colorare a suprafeelor sunt proiectate o serie de clase, care realizeaz conceptele de pensul i peni. Clasele referitoare la pensule sunt derivate din clasa abstract Brush. Fiind abstract, n baza ei nu pot
155

fi create obiecte. n schimb sunt alte clase care permit crearea unei game bogate de pensuli. Cel mai simplu mod de colorare este completarea suprafeei cu o culoare. O astfel de pensul se obine n baza clase SolidBrush. Constructorul are ca parametru culoarea de completare:
Brush numePensula = new SolidBrush(culoare);

De exemplu, pentru a obine o pensul albastr


Brush b = new SolidBrush(Color.Blue);

O colorare mai complex se obine prin haurare. Pentru a obine astfel de pensule este utilizat clasa HatchBrush. Unul dintre constructori are ca parametru pe ling culoarea de haurare i stilul de haurare:
HatchBrush numePensula = new HatchBrush(stilHasurare,culoare);

Stilul de haurare poate fi determinat prin constructor sau prin intermediul propritii HatchStyle. Pentru determinarea stilului este proiectat enumerarea HatchStyle. Tabelul ce urmeaz prezint etichetele acestei enumerri:
Nr Nume d/o 1 BackwardDiagonal 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Cross DarkDownwardDiagonal DarkHorizontal DarkUpwardDiagonal DarkVertical DashedDownwardDiagonal DashedHorizontal DashedUpwardDiagonal DashedVertical DiagonalBrick DiagonalCross Divot DottedDiamond DottedGrid ForwardDiagonal Horizontal HorizontalBrick LargeCheckerBoard LargeConfetti Mostr care descrie efectul utilizrii vezi LargeGrid

156

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

LargeGrid LightDownwardDiagonal LightHorizontal LightUpwardDiagonal LightVertical Max Min NarrowHorizontal NarrowVertical OutlinedDiamond Percent05 Percent10 Percent20 Percent25 Percent30 Percent40 Percent50 Percent60 Percent70 Percent75 Percent80 Percent90 Plaid Shingle SmallCheckerBoard SmallConfetti SmallGrid SolidDiamond Sphere Trellis Vertical Wave Weave WideDownwardDiagonal vezi LargeGrid vezi Horizontal

157

55 56

WideUpwardDiagonal ZigZag

De exemplu, pentru a obine un stil de haurare orizontal este creat o pensul n felul urmtor:
Brush bh = new HatchBrush(HatchStyle.Horizontal, Color.Blue);

Pentru a uura lucrul de creare a pensulelor monochrome este proiectat clasa Brushes, care conine proprieti pentru toate culorile standarde ale sistemului Windows. De exemplu, pentru crearea unei pensule verzi se procedeaz astfel:
Brush pv = Brushes.Green;

Este de subliniat faptul c n baza clasei Brushes nu pot fi create obiecte. Pentru crearea obiectelor de tip peni este utilizat clasa Pen. Peniele pot fi create n baza unei culori sau n baza unei pensule. Poate fi de asemenea fixat grosimea penuei. Dac grosimea nu este specificat se consider a fi egal cu unu. De aceea clasa Pen are 4 constructori dup cum urmeaz:
Pen(culoare) Pen(pensula) Pen(culoare, grosime) Pen(pensula, grosime)

n continuare sunt prezentate nite exemple de creare a diferitor tipuri de penie:


Pen penita1 = Pen(Color.Green); Brush bp=new HatchBrush(HatchStyle.SmallGrid, Color.Cyan); Pen penita2 = Pen(bp); Pen penita3 = Pen(Color.FromArgb(23, 156, 98), 4); Pen penita2 = Pen(bp, 3);

Obiectele de tip peni sunt utilizate la trasarea diferitor contururi. Exist o gam bogat de metode care permit desenarea diferitor tipuri de linii i contururi. Aceste metode sunt parte component ale clasei Graphics. n tabelul ce urmeaz sunt descrise succint unele metode ce permit desenarea diferitior linii:
Nr Nume d/o 1 DrawArc 2 DrawBezier 3 4 5 6 7 DrawBeziers DrawClosedCurve DrawCurve DrawElipse DrawIcon Descriere succint a efectului utilizrii Desenarea unui arc Desenarea unei curbe Bezier Desenarea unor curbe Bezier Desenarea unei curbe nchise Desenarea unei curbe Desenarea unei elipse Reprezentarea unei pictograme
158

8 9 10 11 12 13 14 15 16

DrawImage DrawLine DrawLines DrawPath DrawPie DrawPolygon DrawRectangle DrawRectangles DrawString

Reprezentarea unei imagini Desenarea unei linii Desenarea unor linii Desenarea unor linii i curbe definite ntr-un obiect de tip GraphicsPath Desenarea unei figuri asemntoare cu o felie de tort Desenarea unui poligon Desenarea unui dreptunghi Desenarea unor dreptunghiuri Reprezentarea unui text

Pe lng posibilitatea de trasare a contururilor exist posibilitatea de desenare a unor figuri completate (haurate). Pentru astfel de necesiti sunt proiectate o serie de metode a clasei Graphics. Desigur c la desenarea acestor astfel de figuri particip obiecte de tip pensul. Tabelul ce urmeaz prezint citeva metode de acest tip:
Nr Nume d/o 1 FillElipse 2 3 4 FillPie FillPolygon FillRectangle Descriere succint a efectului utilizrii Desenarea unei elipse haurate Desenarea unei felii haurate Desenarea unui poligon haurat Desenarea unui dreptunghi haurat

Un alt aspect referitor la grafic este legat de prelucrarea imaginilor. Exist posibilitatea de ncrcare a imaginilor de tip .bmp, .jpg, .gif, .png. O clas important orientat la imagini este clasa Image. Aceast clas are cteva metode statice ce permit incrcarea unei imagini:
Nr d/o 1 FromFile 2 3 Nume Descriere succint a efectului utilizrii ncrcarea imaginii dintrun fiier ncrcarea imaginii dintro resurs de tip bitmap ncrcarea imaginii din flux

FromHbitmap FromStream

159

O metod des utilizat este metoda FromFile(), care are n calitate de parametru calea spre imaginea ncrcat. Iat un exemplu de ncrcare, de vizualizarea a unei imagini i de organizare a operaiei de scrolare:
private Image pic; private void Form_Load(object sender, EventArgs e) { pic = Image.FromFile(@C:\foto.bmp); this.AutoScrollMinSize = new Size(pic.Width, pic,Height); } private void Form_Paint(object sender, PaintEventArgs e) { Graphics dc = e.Graphics; dc.TranslateTransform(AutoScrollPosition.X, AutoScrollPosition.Y); dc.DrawImage(pic, 0, 0); }

Pentru vizualizarea imaginii este utilizat metoda DrawImage() a obiectului de tip Graphics. Aceast metod are foarte multe variante de suprancrcare (30). Aici este utilizat metoda n care este transmis imaginea i coordonatele punctului de vizualizare, care corespunde vrfului stngasus a imaginii. O astfel de metod permite vizualizarea n dimensiuni naturale ale imaginii. Utiliznd unele variante a metodei pot fi efectuate distorsionri a imaginii: comprimare, ntindere, turtire. Astfel de efecte pot fi obinute utiliznd metode, care au n calitate de patametru un tablou de trei puncte, care determin amplasarea imaginii, corespunznd vrfurilor imaginii: stngasus, dreaptasus, stngajos. De exemplu:
Point [] puncte = { new Point(0, 0), new Point(this.Width/2, 0), new Point(0, this.Height/2); } dc.DrawImage(pic, puncte);

Resursele ocupate de o imagine sunt foarte mari, de aceea atunci cnd imaginea nu mai este necesar trebuiesc eliberate resursele care i corespund. Pentru aceasta este suprancrcat funcia Dispose():
protected override void Dispose(bool disposing) { pic.Dispose(); base.Dispose(disposing); }

O alt operaie important este afiarea unui text n contextul graficii. Pentru a afia un text pe canavaua grafic este utilizat metoda DrawString(). O form general de utilizare este:
numeCanava.DrawString(textAfisat, font, perie, locAfisare);

160

unde textAfisat este un obiect de tip string, afiat cu litere din setul font, utiliznd obiectul perie de tip Brush, amplasarea rezultatului fiind determinat de locAfisare. Parametrul locAfisare are mai multe forme: fie un obiect de tip Point, fie coordonatele unui punct, fie un obiect de tip RectangleF. Deci se obin i mai multe forme a metodei DrawString(). Iat un exemplu
private void Form_Paint(object sender, PaintEventArgs e) { Graphics dc = e.Graphics; Font f = new Font(Times New Roman, 14, FontStyle.Bold); Brush b=new SolidBrush(Color.Blue); dc.DrawString(Exemplu , f, b, 0, 0); }

Fiindc exist mai multe familii de fonturi n cele ce urmeaz va fi prezentat un exemplu care permite listarea numelor lor:
private void Form_Load(object sender, EventArgs e) { this.AutoScrollMinSize = new Size(20, FontFamily.Families.Length*15); } void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; FontFamily[] ff = FontFamily.Families; Brush b = Brushes.Black; Font f = new Font("Times New Roman", 12); Point sp = AutoScrollPosition; g.TranslateTransform(sp.X, sp.Y); for (int i = 0; i < ff.Length; i++) g.DrawString(ff[i].Name, f, b, 80, i * (15)); }

Pentru a obine informaii despre familiile de fonturi a fost utilizat proprietatea static Families a clasei FontFamily, care returneaz o colecie de tip FontFamily ce conine toate familiile de fonturi. Un rezultat similar ar putea fi obinut utiliznd un obiect de tip InstalledFontCollection, care, de asemenea, are proprietatea Families caracterizat similar ca n cazul precedent. Pentru a utiliza mai simplu clasa InstalledFontCollection e bine de activat spaiul de nume System.Drawing.Text. Uneori este necesar de a opera cu dimensiunea textului afiat. Dimensiunea textului este determinat de dou caracteristici: nlimea textului i limea lui. Aceste caracteristici sunt desigur determinate de fontul utilizat pentru afiare. Pentru a determina dimensiunea textului va fi utilizat metoda

161

MeasureString() care ine de clasa Graphics. Ea este suprancrcat, avnd

mai multe variante. Varianta cea mai simpl are doi parametri textul afiat i fontul utilizat la afiare:
SizeF MeasureString(string text, Font F)

Metoda returneaz o valoare de tip SizeF, avnd proprietile Height i Width. Proprietatea Height va corespunde nlimii, iar proprietatea Width va oferi limea textului. Totui metoda MeasureString() nu ofer totdeauna exactitatea necesar n prelucrare, cum ar fi, de exemplu, pentru afiarea formatat a textului. Pentru situaii de exactitate sporit este utilizat metoda MeasureCharacterRanges(), care permite colectarea de informaii despre o serie de secvene de caractere. Printre aceste informaii poate fi gsit i informaia despre mrimea dreptunghiului, care conine secvena de caractere. De aici sunt obinute limea i nlimea secvenei de caractere. Informaiile despre secvenele de caractere sunt stocate ntr-o colecie de elemente de tip Region. De aceea signatura acestei metode este:
Region [] MeasureCharacterRanges(string text, Font F, RectangleF dr, StringFormat sf)

Pe lng parametrii ce reprezint textul cercetat i fontul mai apar parametri dr de tip RectangleF, care determin zona dreptunghiular n raport cu care se cerceteaz textul i sf de tip StringFormat, care determin o serie de elemente ce in de formatare. n primul rnd trebuie determinate secvenele de caractere din cadrul textului cercetat. Pentru aceasta este utilizat clasa CharacterRange, unul dintre constructorii ei fiind
CharacterRange(int primul, int lungime)

unde parametrul primul determin indicele primului caracter al secvenei, iar parametrul lungime determin lungimea secvenei de caractere. Secvenele de caractere sunt organizate sub forma unui tablou de tip CharacterRange. Acest tablou este transmis ca parametru metodei SetMeasurableCharacterRanges(), care ine de clasa StringFormat. Fiind organizate secvenele de caractere poate fi apelat metoda MeasureCharacterRanges(), de unde vor putea fi determinate i dimensiunile secvenelor. Dimensiunile secvenelor vor fi determinate utiliznd metoda GetBounds(), care se refer la clasa Region. n calitate de parametru metodei i se transmite un obiect de tip Graphics, iar ea returneaz dreptunghiul care conine secvena de caractere:
RectangleF GetBounds(Graphics g)

n continuare este prezentat un exemplu n care vor lucra aceste idei. Exemplu. La acionarea butonului din form de realizat afiarea alineat stnga-dreapta a textului din boxa textual pe canavaua grafic a formei sub boxa
162

textual pe toat limea ferestrei. La redimensionarea ferestrei este actualizat i afiarea textului.
using System; using System.Drawing; using System.Windows.Forms; namespace AliniereText { public partial class Form1 : Form { Font fn = new Font("Times New Roman", 14); Graphics g; float rind = 0; public Form1() { InitializeComponent(); g = this.CreateGraphics(); } private void button1_Click(object sender, EventArgs e) { if(textBox1.Text!="") ScriereAlineata(); } private void ScriereAlineata() { float lat, lr = 0, lc = 0, sp = 5.0f; int ii = 0, it; char[] d ={ ' ' }; string[] cuvinte = textBox1.Text.Split(d); CharacterRange[] cr = new CharacterRange[1]; StringFormat sf = new StringFormat(); Rectangle client = this.ClientRectangle; rind = textBox1.Height; g.Clear(this.BackColor); Brush b = Brushes.Black; lat = ClientRectangle.Width - 5; for (int i = 0; i <= cuvinte.Length; i++) { if (i != cuvinte.Length) { cr[0] = new CharacterRange(0, (cuvinte[i]).Length); sf.SetMeasurableCharacterRanges(cr); lc = g.MeasureCharacterRanges(cuvinte[i], fn, client, sf)[0].GetBounds(g).Width;
163

} if (!(i == cuvinte.Length || lr + lc + sp > lat)) { lr += lc + sp; } else { it = i; float ms = (lat - lr + sp) / (it - ii - 1); lr = 0; for (int j = ii; j < it; j++) { g.DrawString(cuvinte[j], fn, b, lr, rind); cr[0] = new CharacterRange(0, (cuvinte[j]).Length); sf.SetMeasurableCharacterRanges(cr); lc = g.MeasureCharacterRanges(cuvinte[j], fn, client, sf)[0].GetBounds(g).Width; lr += lc + sp + ms; } ii = i; if (i != cuvinte.Length) i--; rind += fn.Height; lr = 0; } } } private void Form1_Resize(object sender, EventArgs e) { g = this.CreateGraphics(); if (textBox1.Text != "") ScriereAlineata(); } } }

Rezultatul lucrului programului realizat este prezentat n figura ce urmeaz. n procesul programrii a fost creat metoda ScriereAlineata(), care efecrueaz afiarea alineat a textului. S-a folosit metoda Split(), care face descompunerea unui text n fragmente dup un set de delimitatori. n calitate de parametru este transmis un tablou de tip char, coninnd delimitatorii. n cazul nostru e folosit spaiul ca delimitator. Fiecare cuvnt este declarat ca secven de caractere i i este determinat limea. n calitate de dreptunghi n raport cu care se cerceteaz textul este folosit dreptunghiul care reprezint partea client a formei, utiliznd proprietatea ClientRectangle a formei.

164

165