Explorați Cărți electronice
Categorii
Explorați Cărți audio
Categorii
Explorați Reviste
Categorii
Explorați Documente
Categorii
NET
Cursul de Medii i Tehnologii de Programare i propune s prezinte aspecte ale programrii
.NET utiliznd limbajul C#, ns este conceput astfel nct s nu prezinte exclusiv acest limbaj
de programare, ci face, pe alocuri, comparaii cu limbajul Visual Basic, punctnd diferenele
dintre cele dou limbaje, avantajele i dezavantajele specifice lor. Astfel, vei putea s v
facei o idee mai bun asupra celor dou limbaje de programare i s l alegei, n aplicaiile
pe care le vei dezvolta ulterior, pe acela care se va potrivi cel mai bine stilului dumneavoastr de programare.
vechi, scrise Visual Basic, pot fi importate n .NET folosind un wizard dedicat acestui scop,
excepie fcnd, printre altele, secvenele de cod care acceseaz baze de date prin alte
tehnologii dect Microsoft ADO (ActiveX Data Objects).
Limbajul C# (C Sharp) este un limbaj relativ nou, prima versiune a acestuia fiind introdus
de abia n anul 2002 odat cu apariia Visual Studio .NET 2002. n anul 2012, odat cu Visual
Studio 2012 limbajul C# a ajuns la versiunea 5.0 i la 10 ani de existen. Numele limbajului
provine de la limbajul de programare C. Sharp este un semn care, n notaia muzical,
indic faptul c nota la care este ataat se interpreteaz cu un semiton mai sus (diez). Pe de
alt parte, n muzic C, este echivalentul notei Do.
C# este un limbaj avnd o sintax orientat pe obiecte, bazat pe C++, care include de
asemenea aspecte specifice ctorva limbaje de programare, n special Delphi i Java
(proiectul C# a fost condus de ctre Anders Hejlsberg, cel care anterior a condus proiectul
Delphi), fiind puse accente speciale pe simplificare, flexibilitate i robustee. Prin acestea s-a
urmrit ca programatorii familiarizai deja cu limbajele C i C++ s poat face uor trecerea
la C#.
Pentru a nelege un limbaj, este util compararea acestuia cu celelalte alternative existente,
apoi se poate opta pentru varianta potrivit n cunotin de cauz. Cel mai apropiat
competitor pentru C# este Visual Basic, un alt limbaj nativ .NET care ofer aproximativ
aceleai beneficii ca C#. n acest caz diferena dintre cele dou ine mai mult de sintax. C#
face parte din familia de limbaje C, iar dac suntei familiarizai cu cel puin un limbaj dintre
acestea (C, C++, Java, JavaScript) v vei simi ca acas cu sintaxa C#. Dac, ns, nu
cunoatei niciunul dintre aceste limbaje sau dac suntei familiarizat cu versiunile mai vechi
de Visual Basic sau cu variantele sale de scripting (VBA) atunci versiunea .NET a Visual Basic
va fi cu siguran mai uor de nvat.
O mare parte a puterii limbajelor .NET (ne referim aici n principal la C# i Visual Basic)
provine din gama larg de tehnici de programare pe care le suport. De exemplu, ofer
caracteristici de programare orientat pe obiecte, generice (generics) i programare funcional. Ofer caracteristici de lucru cu liste i mulimi prin LINQ, iar cele mai recente versiuni a
limbajelor adaug suport intrinsec pentru programare asincron. Unele dintre cele mai importante beneficii ale utilizrii limbajelor .NET sunt date de motorul de execuie a programelor care ofer servicii precum verificarea tipurilor la rulare, tratarea excepiilor, gestiunea
firelor de execuie i managementul automat al memoriei. La rularea aplicaiilor intr n
funcie un colector de gunoaie (garbage collector) care elibereaz dezvoltatorii de o mare
parte a muncii asociate cu recuperarea resurselor de memorie pe care programul nu le mai
folosete.
Desigur, limbajele nu exist ntr-un vacuum sunt eseniale bibliotecile care s ofere o gam
larg de caracteristici (trebuie s ofere acces deplin i convenabil la serviciile care stau la
baza funcionrii platformei). n acest sens limbajele .NET sunt foarte puternice, iar aceasta
datorit .NET Framework. Toate limbajele de programare .NET au acces la aceleai biblioteci
i ierarhii de clase, astfel c, practic, nu exist diferene ntre aplicaii similare scrise n
limbaje .NET diferite.
n ciuda complexitii i limitrilor COM, au fost create cu succes nenumrate aplicaii bazate
pe aceast arhitectur. Totui, n prezent majoritatea aplicaiilor Windows nu mai sunt
create cu modelul COM ci, mai degrab, aplicaiile desktop, serviciile sistemului de operare i
bibliotecile de componente reutilizabile sunt create utiliznd platforma .NET.
.NET Framework reprezint o platform software pentru dezvoltarea de sisteme att pentru
familia de sisteme de operare Windows ct i pentru Mac OS X i diferite distribuii de
Unix/Linux. Iat o trecere n revist a ctorva dintre caracteristicile principale ale .NET:
Un motor de execuie comun pentru toate limbajele .NET: un aspect al acestui motor
l reprezint un set bine definit de tipuri de date pe care toate limbajele .NET le
neleg.
Microsoft a creat .NET Framework pentru a face mai uoar dezvoltarea de aplicaii pentru
diversele versiuni ale sistemului de operare Windows. Din acest motiv dezvoltarea aplicaiilor n .NET ar trebui s fie mai facil dect n versiunile anterioare. .NET Framework
mpreun cu limbajele .NET pot fi instrumente puternice, iar tot ce avei de fcut este s
descoperii modului n care acestea conlucreaz prin intermediul mediului de dezvoltare
integrat Visual Studio.
.NET Framework este plasat ntre limbajul de programare (Visual Basic, C# etc.) i sistemul
de operare (de la Windows 98 i Windows NT pn la Windows 8 i Windows Server 2012,
sau orice alte sub-versiuni ale acestora, cum ar fi cele destinate dispozitivelor mobile). .NET
Framework ofer funcionaliti specifice sistemului de operare Windows, furniznd ns i
biblioteci care extind aceste funcionaliti (calcule matematice, criptografie, accesarea bazelor de date etc.). Figura 2.1 ilustreaz vizual relaia ntre toate nivelele din .NET Framework.
O bun parte din dificultile pe care le ntmpin programatorii cnd migreaz la .NET
Framework provin deruta provocat de terminologia specific. Pentru eliminarea oricror
confuzii vom expune n continuare principalii termenii utilizai n dezvoltarea aplicaiilor
.NET. Din punctul de vedere al programatorului, n linii mari, .NET poate fi neles ca un
mediu de execuie i o bibliotec cuprinztoare de clase.
Figura 2.1. Ierarhia .NET Framework (.NET Framework 2.0 anul 2005).
Base Class Library (BCL). Platforma .NET furnizeaz o bibliotec de clase care este disponibil tuturor limbajelor de programare .NET. Aceast bibliotec de clase ncapsuleaz nu
numai primitive precum fire de execuie, operaii cu fiiere, sisteme de redare grafic i
interaciunea cu diverse dispozitive hardware externe, ci furnizeaz i suport pentru anumite servicii solicitate de majoritatea aplicaiilor din lumea real.
Bibliotecile de clase definesc tot ceea ce avei nevoie pentru dezvoltarea oricrui tip de
aplicaie. De exemplu, putei utiliza ASP.NET pentru construirea site-urilor web, WCF pentru
dezvoltarea de servicii n reea, WPF pentru aplicaii desktop cu interfee grafice .a.m.d. De
asemenea, BCL permit interaciunea cu documente XML, sistemul local de fiiere directoare,
comunicarea cu baze de date relaionale (ADO.NET) etc. Pentru scrierea aplicaiilor programatorii pot utiliza biblioteca de clase mpreun cu codul scris de ei nii.
Programele scrise pentru .NET Framework sunt executate ntr-o main virtual care gestioneaz programele n timpul rulrii din punct de vedere al necesitilor acestora. Aceast
main virtual este cunoscut sub denumirea de Common Language Runtime (CLR) i
face parte din .NET Framework. Datorit faptului c programul va rula pe maina virtual
(CLR) programatorul nu are nevoie s ia n considerare tipul procesorului de pe calculatorul
care va rula aplicaia. Toate programele .NET ruleaz sub supervizarea CLR, fiind garantate
anumite proprieti i comportamente n ceea ce privete gestiunea memoriei, securitatea i
tratarea excepiilor.
.NET Framework utilizeaz o arhitectur pe nivele, la baza creia se situeaz Windows API
(vezi Figura 2.1). .NET Framework ofer o vedere orientat pe obiecte a funciilor sistemului
de operare, fr ns a le nlocui, astfel c majoritatea apelurilor din .NET Framework sunt
rezolvate n final ca apeluri ntr-unul din DLL-urile nucleului Windows. CLR reprezint primul nivel care aparine lui .NET Framework, fiind responsabil pentru serviciile de baz .NET,
precum gestiunea memoriei, colectarea de gunoaie, tratarea structurat a excepiilor, coordonarea firelor de execuie i efectuarea verificrilor de securitate. Ar trebui s imaginai
CLR ca fiind supervizorul tuturor aplicaiilor .NET: nu interacionai niciodat direct cu CLR,
ns toate aplicaiile voastre sunt controlate de acesta.
CLR ofer suport pentru mai multe limbaje de programare, asigurnd un mediu de execuie
robust i sigur. De asemenea acesta simplific procesul de distribuire i de management al
aplicaiei (scpai de comarul DLL).
CLR ruleaz o form de cod octet numit Common Intermediate Language (CIL), cunoscut i sub denumirea de Microsoft Intermediate Language (MSIL). Procesul de rulare a
unui program scris ntr-unul din limbajele .NET este ilustrat n Figura 2.2.
excepie care apare ca urmare a unei erori. Dac vei construi tipuri .NET care expun numai
caracteristici conforme cu CLS, putei s fii siguri c toate limbajele .NET le pot consuma.
Dimpotriv, dac utilizai un tip de dat sau structur de programare din afara CLS, nu putei
avea garania c toate limbajele .NET vor putea interaciona cu biblioteca dumneavoastr
.NET.
Cod gestionat i cod negestionat. Se spune c aplicaiile .NET execut cod gestionat
deoarece ruleaz sub controlul CLR i sunt mpiedecate s ruleze cod nesigur care poate
afecta rularea sistemului sau poate compromite datele utilizatorilor. Spre exemplu, CLR
trebuie s previn accesarea fiierelor sistem sau a regitrilor sistem n cazul n care
aplicaia a fost lansat dintr-o locaie Internet care nu este de ncredere. Prin contrast,
aplicaiile non-.NET, cum ar fi cele scrise n Visual Basic 6, execut cod negestionat, sau cod
nativ. Dintre toate limbajele de programare de la Microsoft, numai C++ poate produce att
cod gestionat ct i cod negestionat, ns chiar i dezvoltatorii C++ ar trebui s recurg la
scrierea de cod negestionat numai dac este strict nevoie spre exemplu, pentru efectuarea
operaiilor low-level deoarece numai codul gestionat beneficiaz de avantajele platformei
.NET.
n lumea .NET, limbajele de programare devin doar o modalitate de a interaciona cu .NET
Framework i, n consecin, cu sistemul de operare Windows. Toate programele au nevoie
de un set de reguli stabilite pentru interpretarea fluxului de instruciuni din cadrul programelor. Limbajul de programare .NET furnizeaz un set de reguli, iar .NET Framework pune la
dispoziie obiectele i evenimentele cu care se poate interaciona.
n esen, orice lucru pe care Windows l poate face este cuprins n cadrul .NET Framework.
n mod specific, .NET Framework d un nume programatic fiecrui obiect i eveniment pe
care Windows l poate controla. Orice programator poate utiliza acel nume pentru a se referi
n cod la orice are legtur cu sistemul de operare. De exemplu, dorii s i transmitei
imprimatei s efectueze dou copii ale documentului vostru? ncercai:
My.Computer.Printers.DefaultPrinter.PrinterSettings.Copies = 2
Dei Microsoft a inventat limbajul C#, acesta este documentat de organismul de standardizare ECMA (European Computer Manufacturers Association), permind oricui s implementeze limbajul de programare C#. Acest lucru nu este ceva ipotetic. Proiectul open source
Mono (http://www.mono-project.com) pune la dispoziie instrumente pentru construirea
de aplicaii C# care pot rula pe Linux, iOS i Android. Astfel, Mono poate rula fiierele binare
obinute pe platforme Windows cu Visual Studio .NET fr a mai fi nevoie de recompilarea
acestora. n Figura 2.3 este prezentat mediul MonoDevelop, un IDE GNOME gratuit, proiectat
special pentru programarea n C# i alte limbaje .NET.
n continuare putei alege fie s creai un proiect nou, fie s deschidei unul dintre ultimele
proiecte deschise n Visual Studio.
n funcie de componentele selectate la instalare, vei putea crea proiecte Visual Studio
dintre cele mai diverse, n funcie de limbajul de programare ales (Visual Basic, C#, Visual
C++, Visual F# sau JavaScript). Visual Basic i C# sunt singurele dou limbaje care permit
crearea acelorai tipuri de proiecte (i a celor mai multe dintre limbajele .NET).
Astfel, pentru proiecte Visual Basic sau C# putei crea de la aplicaii Windows uzuale
(Windows Forms Application, Console Application, biblioteci de clase, servicii Windows,
controale personalizate etc.), pn la aplicaii web Windows Store, aplicaii web, aplicaii
WCF (Windows Communication Foundation), aplicaii Silverlight, aplicaii Windows Phone
.a.m.d. (vezi Figura 3.2).
De asemenea, putei s creai aplicaii Visual Studio 2012 pornind de la o serie de modele
predefinite sau exemple online.
Figura 3.2. Dialogul pentru crearea unui proiect nou n Visual Studio 2012.
Figura 3.3 prezint interfaa mediului de dezvoltare integrat Visual Studio 2012 imediat
dup crearea unui proiect nou C# de tip Windows Forms Application.
3
5
4
7
Figura 3.3. Configuraia iniial a interfeei mediului de dezvoltare Visual Studio 2012.
Interfaa este extrem de configurabil, astfel nct poate s arate complet diferit dac
rearanjai dup bunul plac elementele de interfa. Orice s-ar ntmpla, putei reveni la
10
multe persoane pentru interogarea aplicaiei server. Datorit strnsei legturi dintre aceste
dou proiecte, este firesc ca ele s fie gestionate ntr-o singur soluie. Atunci cnd soluia va
fi deschis, vei avea acces la toate fiierele din cadrul celor dou proiecte.
Att proiectele ct i soluiile pot include fiiere asociate, care pot fi utile pentru dezvoltarea
aplicaiilor, dar care nu vor face parte din produsul final compilat. La dublu click pe numele
fiierelor, acestea vor fi deschise cu aplicaia potrivit. Spre exemplu, un fiier cu extensia
.docx va fi deschis de ctre Visual Studio cu Microsoft Word.
Pentru asocierea unuia din aceste fiiere cu proiectul sau cu soluia dai click dreapta pe
numele proiectului/soluiei n Solution Explorer, selectai comanda Add -> Existing Item
(sau din meniul Project -> New Item). Se va deschide o fereastr dialog pentru selectarea
fiierului dorit.
Pentru a aduga o nou component software la proiect dai click dreapta pe numele
proiectului n Solution Explorer, selectai comanda Add -> New Item (sau din meniul Project
-> New Item) i utilizai fereastra dialog pentru selectarea noii componente pe care dorii s
o adugai la proiect (vezi Figura 3.4).
Un scenariu frecvent utilizat este acela n care codul surs este plasat ntr-un proiect,
iar documentaia n alt proiect din aceeai soluie.
11
Meniul FILE
Meniul File conine comenzi pentru creare, deschidere, salvare i nchidere a proiectelor i a
fiierelor asociate acestora (Figura 3.5).
Figura 3.5. Meniul File cuprinde comenzi asociate cu soluia i cu fiierele acesteia
New submeniul New permite crearea unui proiect nou, site Web (ASP.NET sau Serviciu
WCF), proiect Team Foundation Server (TFS) sau fiier (text, bitmap, icon, html, clas etc.).
Comanda Project From Existing Code creeaz un proiect nou i permite alegerea fiierelor
care s fie incluse n noul proiect.
Open submeniul Open conine comenzi pentru deschiderea unei soluii, a unui proiect, site
Web, proiect TFS sau fiier. Comanda Convert permite prin intermediul unui wizard conversia proiectelor scrise n versiuni mai vechi de C# sau VB.
Save All salveaz toate fiierele modificate de la deschiderea acestora sau de la ultima
operaie de salvare.
Export Template acest wizard permite crearea unui ablon de proiect sau alte elemente
care pot fi utilizate mai trziu.
Page Setup i Print permit configurarea imprimantei, respectiv tiprirea documentului
curent. Aceste comenzi sunt activate numai cnd are sens tiprirea documentului curent. De
exemplu, sunt activate atunci cnd vizualizai un fiier text (fiier surs sau XML), ns sunt
dezactivate atunci cnd vizualizai un bitmap sau suntei n Form Designer.
Meniul EDIT
Meniul Edit (Figura 3.6) conine comenzi care manipuleaz text sau alte tipuri obiecte.
Printre acestea clasicele comenzi Undo, Redo, Copy, Cut i Paste pe care le putei gsi n
majoritatea aplicaiilor Windows.
12
Figura 3.6. Meniul Edit conine comenzi pentru manipularea textului i a altor tipuri de obiecte
Cycle Clipboard Ring Clipboard ring conine ultimele elemente copiate n clipboard.
Aceast comand copiaz n locaia curent elementul anterior din clipboard ring. Prin
utilizarea repetat a acestei comenzi, putei parcurge elementele din clipboard ring pn la
gsirea celui pe care l dorii.
Go To permite poziionarea rapid pe o anume linie n fiierul surs curent.
Advanced submeniul Advanced conine comenzi pentru efectuarea unor formatri mai
complexe ale documentului, cum ar fi convertirea textului la majuscule sau minuscule, word
wrap, indentri, comentarea i decomentarea codului.
Bookmarks submeniul Bookmarks conine comenzi pentru adugarea i tergerea
marcajelor, precum i poziionarea pe marcajele deja memorate.
Outlining submeniul Outlining permite expandarea sau comprimarea seciunilor de cod,
precum i activarea sau dezactivarea acestei opiuni.
IntelliSense ofer acces la facilitile IntelliSense. Spre exemplu, comanda List Members
afieaz lista cu proprietile, metodele i evenimentele obiectului curent.
Refactor conine cteva opiuni utile pentru code refactoring. Vom expune n continuare
funciile de refactoring oferite de Visual Studio.
1) Comanda Rename este util atunci cnd dorii s redenumii o variabil sau o metod,
deoarece o redenumete n toate locurile n care aceasta este referit n codul proiectului.
2) Comanda Extract Method ofer o modalitate simpl de creare a unei metode noi pornind de la un fragment de cod.
3) Comanda Encapsulate Field ajut programatorii la crearea unei proprieti pornind de
la un cmp privat al unei clase.
4) Comada Extract Interface ofer o modalitate de creare rapid a unei interfee pornind
de la membrii unei clase, structuri sau interfee.
5) Comanda Remove Parameters ofer o modalitate mai simpl de ordonare a parametrilor unei metode, att n definiia acesteia ct i n apelurile ei.
13
6) Comanda Reorder Parameters ofer o modalitate mai simpl de tergere a unuia sau a
mai multor parametri ai unei metode, att n definiia acesteia ct i n apelurile ei.
Meniul VIEW
Meniul View (Figura 3.7) conine comenzi care permit ascunderea sau afiarea diverselor
ferestre sau bare de instrumente din cadrul mediului de dezvoltare integrat Visual Studio
2012.
Figura 3.7. Meniul View permite afiarea i ascunderea ferestrelor i a barelor cu instrumente Visual Studio
Code comanda Code deschide fiierul selectat ntr-un editor de cod. Spre exemplu, pentru
editarea codului asociat unui formular (de exemplu, Form1), dai click pe formular
(Form1.cs pentru proiect C#, sau Form1.vb pentru proiect Visual Basic) n Solution
Explorer i selectai View Code.
Designer comanda Designer deschide fiierul selectat ntr-un editor grafic, dac exist
vreunul definit pentru acel tip de fiier. Spre exemplu, dac fiierul este un formular, Visual
Studio l deschide ntr-un editor grafic de formulare. Dac fiierul este o clas sau o alt
component de cod, meniul View ascunde aceast comand deoarece Visual Studio nu
dispune de un editor grafic pentru acest tip de fiiere.
Open deschide elementul selectat cu editorul implicit pentru acesta.
Open With deschide elementul selectat cu un editor la alegere. Spre exemplu, putei
deschide codul asociat unui formular cu un editor de text.
14
Tab Order n mod implicit, n Visual Studio 2012, aceast comand nu apare n meniul
View, ns poate fi adugat prin dialogul deschis de comanda Tools -> Customize. Dac un
formular conine controale, comanda Tab Order afieaz deasupra fiecrui control o cifr
care indic ordinea n care vor fi selectate controalele la apsarea tastei TAB (n timpul
rulrii). Pentru a defini aceast ordine trebuie s dai click pe controale n ordinea dorit.
Meniul PROJECT
Meniul Project (Figura 3.9) conine comenzi care v permit adugarea, respectiv tergerea
de elemente n/din proiectul curent. n funcie de elementul selectat n Solution Explorer
difer i comenzile disponibile n acest meniu.
Figura 3.9. Meniul Project v permite adugarea de fiiere i referine n proiectul curent
15
Dup ce ai adugat o referin la un proiect, putei s accesai prin cod obiectele publice ale
referinei. Spre exemplu, dac fiierul MyMathLibrary.dll definete clasa MathTools, iar acea
clas definete funcia public Fibonacci, un proiect care are o referin la acest DLL poate
utiliza urmtoarea secven de cod:
MyMathLibrary.MathTools math_tools = new MyMathLibrary.MathTools();
MessageBox.Show("Fib(5) = " + math_tools.Fibonacci(5));
Add Service Reference afieaz un dialog prin care putei s adugai referine la servicii
Windows Communication Foundation (WCF) i ADO.NET Data.
Set as StartUp Project seteaz unul dintre proiectele unei soluii (cel curent selectat) ca
fiind proiectul care va rula implicit la rularea soluiei curente.
[Nume proiect] Properties aceast comand afieaz paginile cu proprieti ale proiectului. Proprietile vor diferi de la un tip de proiect la altul. Astfel, pentru un proiect C# de
tip Windows Forms Application pagina Application a proprietilor va fi cea din Figura 3.10.
n cazul unui proiect Visual Basic de tip Windows Forms Application pagina Application a
proprietilor acestuia va conine unele diferene, majoritatea provenind din caracteristicile
diferite pe care le pune la dispoziie Visual Basic fa de C# (vezi Figura 3.11).
Una dintre diferenele proprietilor unui proiect Visual Basic i ale unuia C# provine din
modul diferit prin care se indic punctul de intrare n aplicaie. n C# punctul de intrare ntro aplicaie este funcia Main. Implicit, proiectele C# conin fiierul Program.cs care o implementeaz. Pentru proiectele C# de tip Windows Forms Application funcia Main creeaz o
instan a clasei Form1 pe care o afieaz pe ecran:
16
n C# pot exista mai multe fiiere surs care s implementeze funcia Main. Din pagina de
proprieti putei indica fiierul surs care implementeaz varianta dorit de dumneavoastr (Figura 3.12).
n cazul proiectelor Visual Basic de tip Windows Forms Application punctul de intrare n
aplicaie se definete mai simplu, selectnd n caseta Startup object fie numele ferestrei de
start (vezi Figura 3.13), fie numele unui modul de program care implementeaz funcia Main
(dac n proiect exist aa ceva).
17
Diferenele dintre proiectele C# i Visual Basic nu se opresc ns aici. n fereastra de proprieti a proiectelor Visual Basic se poate alege condiia de terminare a execuiei aplicaiei (vezi
Figura 3.14). Implicit, execuia aplicaiilor Visual Basic de tip Windows Forms Application se
termin atunci cnd se nchide fereastra de start (lucru valabil i pentru proiectele C#, dei
aceast opiune lipsete de pe fereastra de proprieti a acestora).
Aceast setare implicit nu este potrivit pentru aplicaiile Visual Basic care conin mai
multe ferestre, deoarece nu este permis nchiderea ferestrei de start fr a se ncheia
execuia aplicaiei, chiar dac pe ecran se afieaz n loc alte ferestre. n acest caz se va
selecta opiunea de nchidere a aplicaiei numai atunci cnd se nchide i ultima fereastr de
pe ecran. Acest lucru se poate implementa i n C#, ns cu ceva mai mult efort din partea
programatorului.
Pe pagina Compile a proiectelor Visual Basic exist patru proprieti care merit o atenie
special (vezi Figura 3.13). Prima dintre ele, Option Explicit determin din partea Visual
Basic cerina ca toate variabilele s fie declarate nainte de a fi utilizate. Inactivarea acestei
opiuni poate conduce uneori la bug-uri subtile. Spre exemplu, urmtoarea secven de cod
intenioneaz s afieze o list a numerelor pare ntre 0 i 10. Din nefericire, o greeal de
tastare face ca funcia Debug.WriteLine s tipreasc valoarea variabilei j, nu a lui i. Deoarece
j nu este iniializat, codul produce la ieire o list de valori nule. Dac setai Option Explicit la
valoarea On, compilatorul va semnala faptul c variabila j nu este declarat, iar problema
devine uor de rezolvat.
For i = 1 To 10
If i Mod 2 = 0 Then Debug.WriteLine(j)
Next i
Figura 3.13. Pagina Compile a proprietilor unui proiect Visual Basic conine opiuni importante pentru
controlul generrii codului
A doua opiune de compilare este Option Strict. Atunci cnd aceast opiune este inactiv,
Visual Studio permite n cod conversii implicite de la un tip de dat la altul, chiar dac
tipurile de date nu sunt ntotdeauna compatibile. Spre exemplu, Visual Basic va permite
urmtoarei secvene de cod s ncerce copierea stringului s n ntregul i. Dac textul din
string se ntmpl s reprezinte o valoare numeric, precum n primul caz, va funciona. n
cazul contrar, precum se vede n cel de al doilea caz, va aprea o eroare la rulare.
18
Dim
Dim
s =
i =
s =
i =
i As Integer
s As String
10
s
Corect.
Hello
s
Incorect.
Dac activai opiunea Option Strict, Visual Studio v va avertiza cu privire la incompatibilitatea dintre cele dou tipuri de date, astfel c vei putea rezolva uor problema n timp ce
scriei codul. Vei putea utiliza funcii de conversie de date precum CInt, Int i Integer.Parse
pentru convertirea stringului la ntreg, ns va trebui s efectuai explicit aceste aciuni.
Acest lucru va duce la diminuarea ansei de apariie a erorilor de conversie.
Pentru evitarea confuziilor sau a unor sesiuni lungi i obositoare de depanare, ar trebui s
setai permanent opiunile Option Explicit (On) i Option Strict (On).
Directiva de compilare Option Compare, poate lua valorile Binary sau Text. Dac setai
Option Compare la Binary, Visual Basic compar stringurile pe baza reprezentrii lor
binare. Dac setai Option Compare la Text, Visual Basic compar stringurile folosind o
metod case-insensitive care depinde de setrile calculatorului. Compare Binary este mai
rapid, dar poate s nu produc ntotdeauna rezultatele ateptate.
Ultima directiv de compilare, Option Infer poate lua valorile On sau Off. Odat setat pe
On, este activat opiunea de deducere a tipului unei variabile locale, n cazul n care,
pentru respectiva variabil, tipul acesteia nu a fost precizat explicit (vezi Figura 3.14).
Toate cele patru opiuni de compilare pot fi setate local, la nivel de fiier, acestea avnd
prioritate fa de opiunile globale, setate la nivel de proiect.
i n C# poate fi utilizat opiunea de deducere a unei variabile. Aceasta se aplic ns numai
variabilelor locale al nivel de metod, declarndu-le ca fiind de tipul implicit var. Compilatorul este cel care determin tipul variabilei n funcie de valoarea care i este atribuit.
Practic este acelai lucru ca i cnd ai fi declarat variabila ca fiind de tipul dedus. Exemplu:
static void Main(string[] args)
{
var a = 5.23;
var b = 5.23f;
var c = "text";
var d = 5;
Console.WriteLine("Variabila
Console.WriteLine("Variabila
Console.WriteLine("Variabila
Console.WriteLine("Variabila
}
a
b
c
d
este
este
este
este
de
de
de
de
tipul
tipul
tipul
tipul
{0}",
{0}",
{0}",
{0}",
a.GetType().ToString());
b.GetType().ToString());
c.GetType().ToString());
d.GetType().ToString());
a
b
c
d
este
este
este
este
de
de
de
de
tipul
tipul
tipul
tipul
System.Double
System.Single
System.String
System.Int32
Dac n Solution Explorer selectai numele soluiei apoi apelai comanda Properties din
meniul Project, Visual Studio afieaz fereastra cu proprietile soluiei (Figura 3.15). n
19
cazul n care soluia conine mai multe proiecte putei alege care s fie proiectul de start
(acesta va fi compilat i depanat prima dat).
Meniul BUILD
Meniul Build (Figura 3.16), conine comenzi care permit compilarea proiectelor dintr-o
soluie.
20
Configuraiile Release utilizeaz mai multe optimizri dect configuraiile Debug, astfel nct
ele furnizeaz programe executabile mai mici i mai rapide. Acestea nu includ suport pentru
depanare, astfel c nu vei putea depana un program compilat pentru release.
n caseta combo Active solution configuration selectai <New...> pentru crearea unei noi
configuraii. Atunci cnd selectai aceasta, Visual Studio va fia un dialog n care putei da un
nume configuraiei i putei selecta o configuraie existent de la care vor fi copiate valorile
implicite.
Vei putea astfel s indicai compilatorului c dorii ca unele proiecte din cadrul soluiei s
fie compilate folosind configuraia Debug, iar altele folosind configuraia Release. Evident,
cele compilate cu Release nu vor putea fi depanate ulterior. Acest mod de abordare a
problemei poate fi util n cazul n care dorii s predai anumite proiecte clienilor
(versiunile release), n timp ce versiunile debug ale proiectelor continuai s le dezvoltai.
Dac debifai caseta Build asociat unui proiect, acel proiect va fi exclus din cadrul
operaiilor de construire. La construirea soluiei acesta nu va fi compilat. Visual Studio
afieaz rezultatele compilrii n fereastra Output i contorizeaz numrul de proiecte
necompilate. Urmtoarea linie arat un exemplu n care, la construirea unei soluii care
conine dou proiecte, un proiect a fost compilat iar cellalt nu.
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 1 skipped ==========
Meniul DEBUG
Meniul Debug (Figura 3.18), conine comenzi care v ajut s depanai un program. Aceste
comenzi v ajut s rulai un program n mod depanare (debugger), s v deplasai prin cod,
s setai i s eliminai breakpoint-uri i, n general, s urmrii execuia codului pentru a
observa modul n care acesta ruleaz, n sperana gsirii eventualelor erori.
21
Figura 3.18. Meniul Debug conine comenzi pentru depanarea unei aplicaii.
Comenzile vizibile n meniu se modific n funcie mai multe condiii, cum ar fi: tipul
fiierului deschis, starea programului (dac programul ruleaz sau nu), linia de cod care
conine cursorul, sau dac linia de cod curent conine un breakpoint.
Iat o scurt descriere a unor comenzilor asociate meniului Debug:
Windows Acest submeniu pune la dispoziie alte ferestre ce au legtur cu depanarea.
Continue Comanda reia execuia unui program. Programul va rula pn la terminarea
execuiei acestuia, pn la ntlnirea unui breakpoint sau pn la oprirea manual a execuiei
lui.
Break All Oprete execuia tuturor programelor rulate n mod depanare. Comanda aceasta
poate fi util dac mai multe programe ruleaz n strns legtur unul cu cellalt.
Stop Debugging Oprete execuia programului i termin sesiunea de depanare.
Detach All Detaeaz debugger-ul de orice proces de care acesta este ataat. Acest lucru nu
duce la oprirea acestor procese.
Terminate All Oprete orice proces la care este ataat debugger-ul.
Restart Oprete procesul care ruleaz i repornete proiectul setat ca implicit.
Attach to Process Afieaz un dialog care permite s ataai debugger-ul la un proces care
ruleaz.
Exceptions Aceast comand afieaz un dialog care conine o list de erori posibile. Dac
bifai n dreptul unei erori caseta Thrown, debugger-ul se va opri ori de cte ori va aprea
eroarea selectat. Dac bifai User-unhandled, debugger-ul se va opri la apariia acelei
erori, iar programul nu va putea s o trateze nici mcar cu ajutorul codului de tratare a
excepiilor.
22
Step Into Face ca debugger-ul s execute linia de cod curent. Dac aceast linie apeleaz o
funcie sau procedur, punctul de execuie se mut n acea procedur.
Step Over Face ca debugger-ul s execute linia de cod curent. Dac aceast linie apeleaz
o funcie sau procedur debugger-ul apeleaz acea rutin, dar nu va intra n interiorul
codului acesteia dect dac acea rutin conine un breakpoint.
Step Out Aceast comand face ca debugger-ul s ruleze pn la prsirea rutinei care se
afl n execuie, apoi execuia se va ntrerupe la ntlnirea liniei de cod care a apelat acea
rutin.
QuickWatch Afieaz un dialog care ofer informaii legate de obiectul din secvena de cod
selectat. Din aceast fereastr putei modifica valoarea unei variabile sau s o adugai la
fereastra Watch.
Una dintre cele mai utile comenzi pentru fereastra Immediate este Debug.Print. De exemplu,
comanda Debug.Print x afieaz valoarea variabilei x. Putei utiliza semnul ? ca abreviere a
acestei comenzi (123 reprezint valoarea variabilei x afiat n fereastr):
>? x
123
n cazul de fa putei verifica n fereastra Immediate dac un anumit fiier exist, evitnd
astfel apariia unei erori n program:
? System.IO.File.Exists(C:\Program Files\Customer Orders\Summary & _
DateTime.Now().ToString(yymmdd) & .dat)
Meniul FORMAT
Meniul Format (Figura 3.19), conine comenzi pentru aranjarea controalelor pe un formular.
23
Figura 3.19. Meniul Format conine comenzi pentru formatarea i aranjarea controalelor pe un formular.
Meniul TOOLS
Meniul Tools (Figura 3.20), conine diverse utilitare i instrumente care nu au fost
considerate potrivite pentru a fi cuprinse n cadrul altor meniuri.
24
Spy++ Aceast comand lanseaz o unealt numit Spy++, care v permite s vedei
mesajele trimise ctre aplicaie.
External Tools Afieaz un dialog care v permite s adugai/tergei comenzi n/din
meniul Tools. Putei, de exemplu, s adugai comenzi pentru lansarea oricrei aplicaii pe
care o considerai util.
Customize permite modificarea comenzilor din meniurile i barele cu instrumente ale
Visual Studio 2012.
Fereastr de definire a codului care afieaz codul surs pentru un obiect sau
element.
O facilitate a editorului de cod din pachetul Microsoft Visual Studio, foarte util programatorilor, este IntelliSense. Aceasta implementeaz autocompletarea codului scris de
programator (automat n diverse momente ale scrierii codului surs, sau manual prin
combinaia de taste CTRL+Spaiu), servind de asemenea ca documentaie pentru numele
variabilelor, funciilor, metodelor sau evenimentelor unui obiect (Figura 3.22).
25
26
4
1
27
4.1. Programe C#
4.1.1. Punctul de intrare n aplicaie
Toate aplicaiile executabile C# (aplicaii n consol, aplicaii desktop Windows sau servicii
Windows) trebuie s conin o clas care s defineasc metoda Main(), care este utilizat
pentru a indica punctul de intrate n aplicaie.
Implicit, compilatorul C# va cuta o metod Main() i o va folosi automat ca punct de intrare
n program. Aceast metod trebuie s ntruneasc o serie de cerine. Prima dintre acestea
este ca metoda s fie static, ceea ce nseamn c, pentru invocarea metodei, nu este nevoie
de crearea unei instane pentru clasa care o conine. Nu este nevoie ca metoda Main() s
returneze ceva (lucru menionat prin utilizarea cuvntului cheie void, precum n exemplul de
mai jos), dei, dac dorii, putei returna o valoare int, lucru ce permite programului s
returneze un cod de ieire pe care sistemul de operare l va raporta la terminarea
programului. n ceea ce privete argumentele metodei Main(), aceasta trebuie fie s nu
primeasc nici unul, fie s accepte un singur argument: un vector de valori string pentru
argumentele transmise la apelul aplicaiei din linie de comand.
Formal vorbind, clasa care definete metoda Main() este numit obiect aplicaie. Este posibil
ca o aplicaie s aib mai multe obiecte aplicaie. Iat un astfel de exemplu:
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, world! My name is Program");
Console.ReadKey();
28
}
}
}
namespace ConsoleApplication1
{
class MyClass
{
static void Main(string[] args)
{
Console.WriteLine("Hello, world! My name is MyClass");
Console.ReadKey();
}
}
}
n acest caz, n care exist mai multe obiecte aplicaie, va trebui s i indicai compilatorului
care metod Main() ar trebui utilizat ca punct de intrare. Acest lucru putei s l indicai n
pagina de setri a aplicaiei (Figura 4.1).
Fiierul surs ncepe cu o serie de directive using. Acestea sunt opionale, ns aproape toate
fiierele surs le conin, iar acestea spun compilatorului ce spaii de nume dorim s folosim.
Despre spaii de nume vom vorbi puin mai ncolo n cadrul acestui capitol.
i pentru aplicaiile de tip Windows Forms Application punctul de intrare este tot metoda
Main(). Dac nu modificai codul implicit generat la crearea aplicaiei, comanda
Application.Run(new Form1()) va ncepe s ruleze bucla de mesaje standard pentru firul de
execuie curent i va face vizibil fereastra indicat. Despre aceste aspecte vom discuta n
cadrul acestui curs la seciunea Programarea orientat pe evenimente.
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Threading.Tasks;
System.Windows.Forms;
namespace WindowsFormsApplication5
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
29
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Opional, dac avei nevoie de o formatare mai elaborat pentru datele numerice, fiecare
substituent poate conine diverse caractere de formatare. n tabelul 4.2 putei observa care
sunt caracterele uzuale de formatare a datelor numerice.
Caracter formatare
C sau c
D sau d
E sau e
F sau f
G sau g
N sau n
X sau x
Semnificaie
Formatare de tip Currency.
Decimal (se poate specifica i numrul minim de cifre cu care s se afieze
valoarea).
Notaie exponenial.
Virgul fix (se poate specifica i numrul minim de cifre cu care s se
afieze valoarea).
General. Acest caracter poate fi utilizat pentru afiarea unui numr n format
fix sau exponenial.
Formatri numerice simple.
Formatare n hexazecimal.
Aceste caractere de formatare sunt sufixate la valoarea unui anumit substituent folosind
semnul : (de exemplu, {0:C}, {1:d}, {2:X}). Secvena de cod de mai jos ilustreaz aceste
cazuri de utilizare a caracterelor de formatare.
Console.WriteLine("Valoarea 99999 in diferite formate:");
Console.WriteLine("c format: {0:c}", 99999);
Console.WriteLine("d9 format: {0:d9}", 99999);
Console.WriteLine("f3 format: {0:f3}", 99999);
Console.WriteLine("n format: {0:n}", 99999);
Console.WriteLine("E format: {0:E}", 99999);
Console.WriteLine("e format: {0:e}", 99999);
Console.WriteLine("X format: {0:X}", 99999);
Console.WriteLine("x format: {0:x}", 99999);
La ieire se va afia:
Valoarea 99999 in diferite formate:
c format: $99,999.00
d9 format: 000099999
f3 format: 99999.000
n format: 99,999.00
E format: 9.999900E+004
30
e format: 9.999900e+004
X format: 1869F
x format: 1869f
Dup acelai principiu putei apela, n cadrul oricrei aplicaii, metoda string.Format pentru
formatarea irurilor de caractere care urmeaz a fi afiate.
4.2. Variabile
4.2.1. Variabile locale
n mod normal, programele preiau, prelucreaz i produc informaii, astfel c definirea i
identificarea acestora reprezint una dintre cele mai importante caracteristici ale unui
limbaj. La fel ca majoritatea limbajelor de programare, C# v permite s definii variabile
locale. Acestea reprezint elemente plasate n cadrul unei metode, care primesc un nume i
care pot stoca informaie.
Spre deosebire de unele limbaje, n C# nu este posibil crearea unor declaraii globale ci, mai
degrab, toi membrii i metodele trebuie coninute n cadrul definiiei unui tip (class,
interface, structure, enumeration sau delegate).
n sintaxa de declarare a unei variabile tipului de dat i urmeaz imediat numele variabilei.
Numele acesteia trebuie s nceap obligatoriu cu o liter sau cu caracterul underscore (_),
urmat de orice combinaie de litere, cifre sau underscore. Aceleai reguli se aplic oricrei
entiti definite de utilizator, cum ar fi clase sau metode.
La fel ca toate limbajele derivate din C, limbajul C# permite declaraii multiple de variabile
printr-o singur instruciune. n acest fel putei, dac dorii, s i atribuii valori acestor
variabile n aceeai linie de cod. inei minte faptul c toate variabilele declarate sau
atribuite n acest fel trebuie s fie de acelai tip.
static void Main()
{
int i = 1, j = 10, k = 100;
Console.WriteLine("{0} {1} {2}", i, j, k);
// Declararea si initializarea a trei constante string.
const string s1 = "unu", s2 = "doi", s3 = "trei";
Console.WriteLine("{0} {1} {2}", s1, s2, s3);
// Declararea a trei variabile. Initializam una dintre ele.
int x = 1, y, z;
Console.WriteLine(j);
y = z = 0; // Initializarea celorlalte
Console.WriteLine("{0} {1}", y, z);
}
//eroare
31
// Numarul de produse
// Greutatea totala a produselor
// Adresa de livrare
// variabila bloc
Variabilele la nivel de bloc (variabile bloc) pot fi utilizate numai n interiorul blocului n care
au fost definite:
// Acest cod nu se va compila.
if (x == 0)
{
int y = 0;
// variabila bloc
//
}
x = y;
// Eroare: y nu este accesibila din afara blocului if.
Corpul unei metode este un bloc, astfel c o variabil definit ntr-o metod nu este vizibil
n alt metod, deoarece este n afara domeniului de vizibilitate. Secvena de cod din
continuare va genera o eroare de acest tip.
static void Test1()
{
int val = 42;
}
static void Test2()
{
Console.WriteLine(val); //Eroare: variabila folosita in afara domeniului de vizibilitate
}
Variabilele bloc sporesc lizibilitatea codului deoarece acestea clarific locurile n care pot fi
i n care nu pot fi utilizate. O metod poate conine mai multe variabile bloc cu acelai nume
(chiar avnd tipuri diferite), cu condiia ca acestea s nu fie definite n blocuri suprapuse sau
ncuibate:
// Codul se va compila fara erori
//...
if (x == 0)
{
int y = 0;
// o variabila bloc
//
}
do
{
string y = null;
// alta variabila bloc
//
} while (true);
32
// Codul nu se va compila.
//...
int y = 0;
// Variabila la nivel de metoda
if (x == 0) {
string y = null;
// Eroare: variabila bloc cu acelasi nume
//
}
Nu este ns nimic greit n a utiliza variabile bloc avnd acelai nume cu o alt variabil
declarat n afara metodei, la nivel de clas.
Buclele for i foreach v permit s definii variabilele contor (variabile de control) ca
variabile bloc:
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
Sintaxa aceasta este preferat n toate cazurile datorit faptului c previne reutilizarea
accidental a valorii variabilei contor atunci cnd se prsete bucla for, lucru care este
considerat ca fiind o greeal de programare i o potenial surs de erori.
Ce valoare va avea variabila i la ieirea din bucla for anterioar?
Dar dac bucla este prsit mai devreme?
Putei declara i iniializa o variabil local sau o cmp de la nivelul unei clase ntr-o singur
declaraie. Aceast caracteristic v permite simplificarea codului i i sporete lizibilitatea.
Valoarea atribuit nu este nevoie s fie o constant, ci poate fi o expresie sau o funcie.
Iniializarea funcioneaz i pentru variabile care stocheaz referine la obiecte.
DataSet ds;
ds = new DataSet();
// sau
DataSet ds = new DataSet();
// sau chiar
DataSet ds = new DataSet("Studenti");
Chiar dac semnul egal funcioneaz la atribuirea de obiecte, trebuie s folosii totui metoda
ReferenceEquals() pentru a testa dac dou obiecte indic spre acelai obiect din memorie.
object
object
object
object
33
=
=
=
=
=
object.ReferenceEquals(obj1, obj2);
object.ReferenceEquals(obj3, obj2);
object.ReferenceEquals(mar, obj3);
mar;
object.ReferenceEquals(obj2, obj3);
protected: accesul este limitat la clasa care conine membrul sau la tipurile derivate
protected internal: accesul este limitat la ansamblul curent sau la tipurile derivate
din aceasta.
Pentru un membru sau tip este permis utilizarea unui singur modificator de acces, cu
excepia cazului n care este utilizat combinaia protected internal.
Utilizarea modificatorilor de acces nu este permis la nivel de spaii de nume (namespaces).
Acestea nu au restricii de acces.
n funcie de contextul n care apare declararea unui membru, sunt permii numai anumii
modificatori acces. Dac n declararea unui membru nu este indicat nici un modificator de
acces, atunci este folosit accesibilitatea implicit.
Tipurile situate la nivel superior, care nu sunt ncuibate n ale tipuri, pot avea accesibilitate
numai internal sau public. Accesibilitatea implicit pentru acestea este internal.
Tipurile ncuibate, care sunt membri ai altor tipuri, pot avea accesibiliti precum cele
indicate n Tabelul 4.3.
Membri ai
enum
class
Accesibilitate implicit
a membrilor
public
private
interface
struct
public
private
Accesibilitate permis a
membrilor
public
protected
internal
private
protected internal
public
internal
private
34
Ultima linie va duce al apariia unei erori deoarece compilatorul nu are cum s determine
dac n timpul rulrii condiia if va evaluat la true. Pe de alt parte, eroarea dispare dac
toate cile posibile de execuie vor atribuie o valoare variabilei n cauz:
string s;
int x = Console.Read();
if (x >= 48 && x <= 57)
s = "#";
else
s = "*";
Console.Write(s);
//nicio eroare
// eroare.
// i va fi de tip int.
Asupra declaraiilor de variabile al cror tip este dedus se aplic cteva restricii:
var poate fi utilizat atunci cnd o variabil local este declarat i iniializat n
cadrul aceleiai instruciuni; variabila nu poate fi iniializat la null.
35
// permis
// eroare
// eroare
Tipurile anonime sunt folosite de regul n clauza select a unei expresii de interogare (LINQ)
pentru a returna un subset a proprietilor pentru fiecare obiect din secvena de cod.
Tipurile anonime conin una sau mai multe proprieti read-only. Nicio alt categorie de
membri ai unei clase, cum ar fi metode sau evenimente, nu sunt valide. Expresia utilizat
pentru iniializarea unei proprieti nu poate fi null, o funcie anonim sau un tip pointer.
Tipurile anonime sunt clase care deriv direct din object i care nu pot fi convertite la nici un
alt tip, cu excepia tipului object. Compilatorul furnizeaz un nume pentru fiecare tip anonim,
chiar dac aplicaia voastr nu l poate accesa. Din perspectiva CLR, un tip anonim nu este cu
nimic diferit fa de un tip referin.
Nu putei declara un cmp, o proprietate, un eveniment sau tipul returnat de o metod ca
fiind de tip anonim.
36
Tip C#
byte
sbyte
ushort
short
uint
int
ulong
long
Nume CLR
System.Byte
System.SByte
System.UInt16
System.Int16
System.UInt32
System.Int32
System.UInt64
System.Int64
Cu semn?
nu
da
nu
da
nu
da
nu
da
Octei
1
1
2
2
4
4
8
8
float
System.Single
nu
Domeniu de valori
0 .. 255
-128 .. 127
0 .. 65535
-32768 .. 32767
0 .. 4294967295
-2147483648 .. 2147483647
0 .. 18446744073709551615
-9223372036854775808 ..
9223372036854775807
-3.4028235E+38 .. -1.401298E-45 pentru
valori negative;
double
System.Double
nu
decimal
char
string
System.Decimal
System.Char
System.String
nu
nu
nu
16
2
2 nr.
caractere
37
atunci cnd o valoare de un anumit tip este convertit la un tip de mrime mai mic. Tabelele
prezentate mai jos ilustreaz comportamentele tipurilor de baz n ambele cazuri de
conversie.
Unele conversii widening la Single la Double pot provoca o pierdere a preciziei. Tabelul 4.6.
descrie conversiile widening care, uneori, pot avea ca rezultat pierderea de informaii.
Tip
Int32
UInt32
Int64
UInt64
Decimal
38
byte b;
int i=1234;
b = Convert.ToByte(i);
Console.WriteLine(b);
Tabelul 4.7 afieaz conversiile care genereaz o excepie OverflowException n cazul n care
valoarea tipului surs se situeaz n afara domeniului definit pentru tipul destinaie.
Tip
Byte
SByte
Int16
UInt16
Int32
UInt32
Int64
UInt64
Decimal
Single
Double
Convertit la:
SByte
Byte, UInt16, UInt32, UInt64
Byte, SByte, UInt16
Byte, SByte, Int16
Byte, SByte, Int16, UInt16, UInt32
Byte, SByte, Int16, UInt16, Int32
Byte, SByte, Int16, UInt16, Int32, UInt32, UInt64
Byte, SByte, Int16, UInt16, Int32, UInt32, Int64
Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64
Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64
Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64
Dup rularea aplicaiei vei observa cu surprindere c suma va conine valoarea 94 (nu 350).
Motivul este c un System.Byte poate stoca numai valori cuprinse ntre 0 i 255, astfel c suma
va conine o valoare depit (350 256 = 94). Dac nu vei lua msuri de corectare a
acestor probleme, atunci situaiile de depire superioar sau inferioar vor aprea fr a se
semnala erori.
C# pune la dispoziia programatorilor cuvntul cheie checked. Dac vei cuprinde una sau mai
multe instruciuni ntr-un bloc checked, atunci compilatorul C# va verifica situaiile n care
pot aprea depiri la operaii de adunare, scdere, nmulire sau mprire a dou valori
numerice. Astfel, la apariia unei astfel de depiri vei primi o excepie de tipul
System.OverflowException cu mesajul: Arithmetic operation resulted in an overflow.
static void Main()
{
byte b1 = 100;
byte b2 = 250;
try
{
byte sum = checked((byte)Add(b1, b2));
39
n cazul n care dorii ca verificrile asupra depirilor s se extind asupra mai multor
instruciuni putei defini un bloc checked:
try
{
checked
{
byte sum = (byte)Add(b1, b2);
Console.WriteLine("sum = {0}", sum);
}
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);
}
40
Pentru valori double, float i decimal utilizai sufixele D, F, respectiv M. Aceste trei tipuri
suport formatul literal exponenial. Conform acestui format, valoarea 1.5E-20 este echivalent cu 1.5*10-20. Implicit, valorile astfel exprimate vor fi considerate de tip double, ns,
dac dorii, putei aduga la valoare sufixele F sau M pentru a indica tipul float sau decimal.
// eroare!
// corect.
41
Semnificaie
Returneaz un obiect DateTime setat la data i timpul curent.
Returneaz un obiect DateTime setat la data curent.
Returneaz un obiect DateTime setat la timpul curent.
Returneaz componentele din data reprezentat de instana curent.
42
Precum se poate observa n exemplul de mai sus, multe dintre metodele clasei System.Char au
dou convenii de apelare: cea cu un singur caracter i cea cu un string mpreun cu un index
numeric care specific poziia caracterului care trebuie testat.
=
=
=
=
True
99.884
8
w
Semnificaie
Returneaz lungimea stringului curent.
Compar dou stringuri.
Determin dac un string conine un anumit substring.
Testeaz dac dou obiecte string conin caractere identice.
Formateaz un string folosind un ir de formatare i substitueni (similar
metodei Console.WriteLine()).
Insereaz un string ntr-un alt string dat.
Metode utilizate pentru umplerea unui string cu anumite caractere.
43
PadRight()
Remove()
Replace()
Split()
ToLower()
ToUpper()
Trim()
Metode utilizate pentru obinerea unei copii modificate a unui string (avnd
caractere terse sau nlocuite).
Returneaz un vector de valori string care conine substringurile delimitate de
elementele char sau string specificate.
Returneaz o copie a instanei curente convertite la caractere minuscule sau
majuscule, utiliznd regulile culturii curente.
terge toate apariiile unui set de caractere specificate de la nceputul i
sfritul stringului curent.
Tabelul 4.10. Metode i proprieti utile ale clasei System.String.
Concatenarea irurilor
Pentru construirea unor iruri de caractere mai mari variabilele string pot fi alturate cu
ajutorul operatorului +. Aceast tehnic este cunoscut sub denumirea de concatenare. n
cazul lucrului cu variabile de tip string operatorul + este procesat de compilator, fiind apoi
efectuat un apel la metoda static String.Concat(). Astfel, putei concatena iruri de caractere
fie cu operatorul +, fie direct prin apelul metodei String.Concat(), singura diferen fiind c, n
al doilea caz, vei avea ceva mai mult de scris.
Stringurile i egalitatea
Implicit, atunci cnd efectuai un test de egalitate asupra tipurilor referin (cu ajutorul
operatorilor C# == i !=) vei obine valoarea True dac referinele pointeaz spre acelai
obiect din memorie. Totui, dei string este un tip referin, operatorii de egalitate au fost
redefinii pentru a compara valoarea obiectelor string, nu obiectele din memorie la care se
refer.
static void Main()
{
string s1 = "Salut!";
string s2 = "Pa!";
Console.WriteLine("s1 = {0}", s1);
Console.WriteLine("s2 = {0}", s2);
// Testeaza egalitatea acestor stringuri
Console.WriteLine("s1 == s2: {0}", s1 == s2);
Console.WriteLine("s1 == Salut!: {0}", s1 == "Salut!");
Console.WriteLine("s1 == SALUT!: {0}", s1 == "SALUT!");
44
Dac examinm mai aproape rezultatul rulrii aplicaiei, vom observa c obiectul original
(s1) nu este modificat la apelul metodei ToUpper(). n schimb, este returnat o copie a
stringului n formatul modificat.
s1 = Acesta este stringul meu
textMajuscule = ACESTA ESTE STRINGUL MEU
s1 = Acesta este stringul meu
Aceeai lege a invariabilitii este valabil chiar i la utilizarea operatorului de atribuire C#.
Pentru ilustrare vom examina secvena de cod de mai jos.
static void Main()
{
string s2 = "Stringul meu";
s2 = "Noua valoare string";
}
n continuare, dac vom compila aplicaia i vom ncrca ansamblul n utilitarul isdasm.exe
(acesta permite vizualizarea codului intermediar: MSIL, CIL sau IL), ar trebui s gsim
urmtorul cod intermediar generat pentru metoda anterioar:
.method private hidebysig static void Main() cil managed
{
// Code size 14 (0xe)
.maxstack 1
45
Putei observa numeroasele apeluri ale instruciunii ldstr. Aceasta ncarc un nou obiect
string n heap. Memoria ocupat de obiectul string anterior, cel care coninea valoarea
Stringul meu", va fi eventual eliberat de ctre garbage collector.
Ce am nvat de aici? n concluzie, clasa string poate fi ineficient i generatoare de mult cod
dac nu este folosit corespunztor, n special la operaiile de concatenare. Pentru operaii
simple, care manipuleaz cantiti mici de date, clasa string poate fi considerat acceptabil.
Dac ns scriei aplicaii care manipuleaz frecvent cantiti mari de date textuale (de
exemplu, un procesor de text), atunci utilizarea obiectelor string nu ar fi o alegere bun, din
cauza efecturii a numeroase copii inutile a datelor text.
n programul de mai sus a fost construit un obiect StringBuilder setat la valoarea iniial
Limbaje de programare, apoi adugm la bufferul intern, putnd s nlocuim sau tergem
caractere. Implicit, un obiect StringBuilder poate stoca iniial un string de maxim 16
caractere, urmnd ca acesta s i mreasc automat dimensiunea la nevoie. Aceas valoare
implicit poate fi modificat prin intermediul constructorului:
// Crearea unui obiect StringBuilder cu dimensiunea initiala de 256
StringBuilder sb = new StringBuilder("Limbaje de programare", 256);
Dac adugai mai multe caractere dect limita specificat, obiectul StringBuilder i va copia
datele ntr-o nou instan i va mri bufferul cu limita specificat.
46
Cuvntul cheie readonly este diferit de cuvntul cheie const. Un cmp const poate fi iniializat
numai la declaraia acestuia. Un cmp readonly poate fi iniializat fie la declaraia acestuia, fie
n constructor. n consecin, cmpurile readonly pot avea valori diferite n funcie de
constructorul utilizat. De asemenea, un cmp const reprezint o constant la compilare, pe
cnd un cmp readonly poate fi utilizat pentru atribuirea de constante la rulare, ca n
exemplul de mai jos:
public readonly DateTime time=DateTime.Now;
4.3.4. Constante
Cuvntul cheie const se folosete pentru declararea unui cmp constant sau a unei variabile
locale, specificnd faptul c valoarea cmpului sau a variabilei locale este constant, neputnd fi modificat. La declararea constantelor este obligatorie iniializarea acestora cu valori
de tipul declarat al cmpului const.
const int x = 0;
private const string denumireLimbaj = "Visual C#";
C# pune la dispoziie cuvntul cheie const, care pare a avea aceeai semnificaie cu readonly,
chiar dac readonly se aplic numai cmpurilor. Un cmp readonly este iniializat apoi nu i
mai schimb valoarea niciodat, pe cnd un cmp const definete o valoare care este invariabil aceeai. Un cmp readonly este mult mai flexibil: acesta poate fi de orice tip, iar
valoarea acestuia poate fi calculat n timpul execuiei programului. Valoarea unui cmp
const este determinat la compilare, ceea ce limiteaz valorile disponibile. Pentru majoritatea tipurilor referin, singura valoare const suportat este null, astfel c, n practic, este
util folosirea const numai cu tipurile suportate intrinsec de compilator. Dac dorii s utilizai alte valori dect null, un cmp const trebuie s fie de tip numeric, bool, string sau tip
enumerare.
Trebuie s fii ateni la cmpurile const compilatorul consider c valoarea nu se va
modifica niciodat. Codul care citete valoarea dintr-un cmp readonly va extrage din
memorie valoarea pe care o are cmpul la rulare. ns la utilizarea unui cmp const,
compilatorul citete valoarea la compilare i o copiaz n codul dumneavoastr ca i cnd ar
fi o valoare literal. Astfel c, dac scriei o bibliotec DLL care declar un cmp const, iar
ulterior i modificai valoarea, aceast modificare nu va fi luat n considerare dect dup
recompilarea codului.
Singurul beneficiu a cmpurilor const este c acesta poate fi utilizat n anumite contexte n
care un cmp readonly nu poate. De exemplu, eticheta pentru un case dintr-un bloc switch
trebuie s fie stabilit la compilare, astfel c nu poate fi referit sub forma unui cmp
47
readonly. n schimb putei defini un case cu ajutorul cmpurilor const. Putei de asemenea s
folosii cmpuri const n expresia care definete valoarea unui alt cmp const (cu condiia s
Aceast expresie de iniializare este opional pentru cmpurile obinuite i pentru cele
readonly. Dac omitei expresia de iniializare, atunci cmpul va fi iniializat la valoarea
implicit (0 pentru valori numerice, false pentru bool, null pentru obiecte etc.). Dac folosii
o expresie de iniializare pentru un cmp obinuit, acesta nu are nevoie s fie evaluat la
compilare, astfel c poate efectua la rulare operaii precum apeluri la metode sau citirea
proprietilor.
Iniializatorii cmpurilor non-statice ruleaz pentru fiecare instan pe care o creai i se
execut n ordinea n care apar n fiier, imediat dup rularea constructorului. Iniializatorii
cmpurilor statice se execut numai o singur dat, indiferent de numrul de instane create.
Acetia se execut de asemenea n ordinea n care sunt declarai, ns este greu de stabilit cu
exactitate cnd sunt rulai. Dac clasa nu are constructor static, C# garanteaz rularea
iniializatorilor de cmpuri nainte de prima accesare a unui cmp al clasei. Dac exist un
constructor static, atunci lucrurile sunt mai clare: iniializatorii pentru cmpurile statice
ruleaz imediat nainte de rularea constructorului static.
4.3.5. Enumerri
Cuvntul cheie enum declar un tip de dat foarte simplu care definete un set de valori care
primesc un nume. Exemplul de mai jos ilustreaz o enumerare care definete un set de
alegeri care se exclud reciproc.
public enum TemperaturaMancarii
{
PreaCalda,
PreaRece,
NumaiBuna
}
Tipurile enum sunt limitate, n sensul c nu putei defini ca membri metode sau proprieti, ci
numai constante numerice ntregi. Implicit valorile constantelor definite ntr-o declaraie
enum sunt de tip ntreg pe 32 de bii. Dac nu i este atribuit o valoare explicit, primul
membru al enumerrii primete valoarea 0, al doilea membru valoarea 1 i aa mai departe.
O enumerare poate fi utilizat n majoritatea situaiilor n care putei utiliza un tip (o variabil local, un cmp sau parametru al unei metode etc.). ns cea mai uzual modalitate de
folosire a unei enumerri este ntr-un bloc switch, precum n exemplul de mai jos.
switch (pranz.Temperatura)
{
case TemperaturaMancarii.PreaCalda:
AsteaptaPutin();
break;
case TemperaturaMancarii.PreaRece:
IncalzesteMancarea();
break;
case TemperaturaMancarii.NumaiBuna:
ConsumaMancarea();
break;
}
48
Atributul [Flags] (sau [System.Flags]) aplicat unei enumerri permite atribuirea de valori
multiple enumerrii cu ajutorul operatorilor binari. La ntlnirea acestui atribut compilatorul va permite atribuirea enumerrii cu ajutorul operatorilor binari.
Urmtorul exemplu ilustreaz modul de utilizare i efectele atributului System.Flags asupra
declaraiei enum.
[Flags]
public enum OptiuniAuto
{
// flagul este 0001
Trapa = 0x01,
// flagul este 0010
Spoiler = 0x02,
// flagul este 0100
FaruriCeata = 0x04,
// flagul este 1000
GeamuriFumurii = 0x08
}
class Program
{
static void Main()
{
// 0001 OR 0100 = 0101
OptiuniAuto optiuni = OptiuniAuto.Trapa | OptiuniAuto.FaruriCeata;
// Numele fiecarui element care corespunde unui flag ...
// ...care are valoarea 1 in variabila optiuni
Console.WriteLine(optiuni);
// Valoarea intreaga a lui 0101 este 5.
Console.WriteLine((int)optiuni);
}
}
Avantajul major al utilizrii atributului [Flags] apare la apelarea metodei ToString pentru o
valoare enum. Pentru tipul OptiuniAuto, metoda ToString va converti valoarea 5 la stringul
Trapa, FaruriCeata, ns fr atributul Flags, aceasta ar fi tratat ca o valoare necunoscut
iar metoda ToString va returna doar stringul care conine cifra 5.
Cu acest tip de enumerare bazat pe flaguri putei ajunge s rmnei fr bii disponibili.
Implicit, o enumerare folosete tipul int pentru reprezentarea valorii, iar pentru o secven
de valori ce se exclud mutual acesta este de regul suficient. Folosind un bit pentru fiecare
flag, tipul int ofer spaiu pentru 32 de flaguri. Datorit faptului c tipurile enum pot
specifica tipul elementelor ca fiind orice tip numeric ntreg, putei, n cazul n care specificai
acest tip ca fiind long s obinei spaiu pentru 64 de flaguri.
[Flags]
public enum Optiuni : long
{
49
//...
}
Tipurile enum pot s sporeasc uneori lizibilitatea codului. O mulime de API-uri accept
parametri bool care controleaz anumite aspecte ale comportamentului acestora, ns mult
mai util ar fi folosirea unui enum. n lipsa acestuia, numai IntelliSense v mai poate ajuta.
var sr = new StreamReader(stream, true);
var fs = new FileStream(path, FileMode.Append);
// cod opac
// claritate: utilizarea unui enum
4.4. Vectori
Un vector (ir) reprezint un set de elemente de acelai tip, care pot fi accesate printr-un
index numeric.
// Crearea unui vector de elemente int indexate cu 0, 1, 2
int[] valori = new int[3];
// Crearea unui vector de 100 de elemente string indexate de la 0 la 99
string[] carti = new string[100];
Atunci cnd declarai n C# un vector folosind aceast sintax, numrul utilizat n declaraia
vectorului nu reprezint indexul maxim, ci numrul total de elemente. Indexul primului
element din vector este ntotdeauna 0. Atunci cnd declarai un vector, ns nu i atribuii
explicit o valoare fiecrui element, elementele neatribuite vor primi valoarea implicit
pentru tipul de dat al elementelor vectorului. Astfel, un vector de valori bool va fi setat la
false, iar un vector de valori numerice ntregi va fi setat la 0.
Observai c, atunci cnd utilizai aceast sintax de iniializare, nu este nevoie s specificai
dimensiunea vectorului, deoarece aceasta va fi dedus din numrul de elemente introduse
ntre acolade. Putei observa de asemenea c folosirea cuvntului cheie new este opional (la
construirea vectorului boolean). n cazul declaraiei vectorului de ntregi, reapelarea valorii
numerice specificate reprezint numrul de elemente din vector, nu valoarea indexului
maxim. Dac n acest caz apare vreo neconcordan ntre dimensiunea declarat i numrul
de iniializatori, vei primi o eroare de compilare:
// Eroare! Nepotrivire intre dimensiune si numarul de elemente!
int[] intVector = new int[2] { 20, 22, 23, 0 };
50
Evident, la fel ca n cazul atribuirii explicite de elemente unui vector, elementele din lista de
iniializare trebuie s fie de acelai tip. Un vector cu tipul dedus nu va fi implicit de tip
System.Object, astfel c o astfel de declaraie va genera eroare la compilare:
// Eroare! Mai multe tipuri de elemente!
var d = new[] { 1, "doi", 3, "patru", false };
System.Int32, Valoare: 10
System.Boolean, Valoare: False
System.DateTime, Valoare: 22.12.1989 0:00:00
System.String, Valoare: Visual C#
51
int[,] myMatr;
myMatr = new int[6, 6];
// Popularea matricei de 6 x 6
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
myMatr[i, j] = i * j;
// Afisarea matricei de 6 x 6
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
Console.Write(myMatr[i, j] + "\t");
Console.WriteLine();
}
}
0
1
2
3
4
5
0
2
4
6
8
10
0
3
6
9
12
15
0
4
8
12
16
20
0
5
10
15
20
25
Al doilea tip de vector multidimensional este numit jagged array. Aa cum indic i
denumirea, acesta conine un numr de vectori interni, fiecare dintre ei putnd avea
dimensiuni diferite. De exemplu:
static void Main()
{
// Jagged array (un vector de 5 vectori)
int[][] myJagArray = new int[5][];
// Creare jagged array
for (int i = 0; i < myJagArray.Length; i++)
myJagArray[i] = new int[i + 7];
// Afisare rand cu rand (valoarea implicita a elementelor este 0)
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < myJagArray[i].Length; j++)
Console.Write(myJagArray[i][j] + " ");
Console.WriteLine();
}
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
52
{
string[] myStrings = { "Vector", "returnat", "de", "metoda" };
return myStrings;
}
Metodele care primesc argumente de tip vectori i cele care returneaz valori de tip vectori
pot fi apelate ca orice alt metod.
Dac pn acum am prezentat modul de definire, populare i examinare a coninutului unui
vector, n continuare vom prezenta rolul clasei System.Array.
Semnificaie
Seteaz un domeniu de elemente ale vectorului la 0, false sau null, n funcie de tipul
acestora.
Copiaz elementele dintr-un vector surs ntr-unul destinaie.
Returneaz numrul de elemente ale unui vector.
Returneaz numrul de dimensiuni ale vectorului curent.
Metod static ce inverseaz coninutul unui vector unidimensional.
Metod static ce sorteaz un vector unidimensional de elemente de tip intrinsec.
Dac elementele din vector implementeaz interfaa IComparer, putei sorta
elemente de orice tip.
Tabelul 4.11. Membri ai clasei System.Array
n exemplul de mai jos putei observa modul de folosire a metodele statice Reverse() i
Clear().
static void Main()
{
string[] limbajeProgramare = { "Java", "Visual Basic", "Visual C#" };
// Afisarea elementelor in ordine
Console.WriteLine("Vectorul este:");
for (int i = 0; i < limbajeProgramare.Length; i++)
Console.Write(limbajeProgramare[i] + ", ");
Console.WriteLine("\n");
// Inversarea elementelor
Array.Reverse(limbajeProgramare);
Console.WriteLine("Vectorul inversat:");
for (int i = 0; i < limbajeProgramare.Length; i++)
Console.Write(limbajeProgramare[i] + ", ");
Console.WriteLine("\n");
// Stergerea tuturor elementelor in afara de ultimul
Console.WriteLine("Sterge toate elementele in afara de unul...");
Array.Clear(limbajeProgramare, 1, 2);
for (int i = 0; i < limbajeProgramare.Length; i++)
Console.Write(limbajeProgramare[i] + ", ");
}
53
Visual C#, , ,
Observai c muli membri ai clasei System.Array sunt definii ca membri statici, fiind apelai
la nivel de clas (Array.Sort(), Array.Reverse()). Acestor metode le este pasat vectorul pe care
dorii s l prelucrai. Ali membri ai clasei System.Array sunt apelate la nivel de obiect, astfel
c putei s i invocai direct pentru vectorul n cauz.
Putei copia un vector prin crearea unui nou vector, ale crui elemente s fie copii ale
elementelor din vectorul original, prin intermediul metodei Clone() pe care o expune clasa
System.Array. Aceast metod creeaz o copie a vectorului original i returneaz o referin la
aceast copie. Iat cum ar arta exemplul precedent rescris prin folosirea metodei Clone:
int[] arr3 = {0,111,222,333};
// Creeaza o copie a vectorului
int[] arr4 = (int[])arr3.Clone();
// Modificam un element din noul vector
arr4[1] = 999;
// Vectorul original nu a fost afectat
Console.WriteLine(arr3[1]);
// => 111
Metoda Clone() returneaz o valoare de tip object, iar atribuirea este de fapt o conversie de
tip narrowing conversion. De aceea este nevoie de utilizarea operatorului cast pentru conversia de la object la int[].
Este permis atribuirea unui vector de elemente de tip referin (de exemplu, String) unui
alt vector de elemente Object. Urmtoarea secven de cod va rula fr erori:
string[] strArr = { "00", "11", "22", "33", "44" };
object[] objArr = strArr;
Console.WriteLine(objArr[2]); // => 22
54
Aici se verific dac variabila s conine valoarea null, ceea ce nseamn c aceasta nu se
refer la nicio valoare. Utilizarea operatorului && n acest caz este important, deoarece dac
s este null, evaluarea expresiei s.Length ar provoca apariia unei erori. Dac am utiliza
operatorul &, compilatorul ar genera cod care va evalua ambii operanzi, ceea ce nseamn c
la rulare am primi o excepie de tip NullReferenceException dac s este null, ns utilizarea
operatorului condiional evit acest lucru, deoarece operandul s.Length > 10 va fi evaluat
numai dac s nu este null.
Operaia x && y corespunde operaiei x & y, cu excepia c dac x este false, y nu este evaluat.
Acest evaluare este cunoscut sub numele de evaluare scurtcircuit, iar operatorii se mai
numesc operatori de scurtcircuitare.
class Program
{
static void Main()
{
Console.WriteLine("*** Operatorul AND:");
if (Metoda1() & Metoda2())
Console.WriteLine("Ambele metode au returnat true.");
else
Console.WriteLine("Cel putin o metoda a returnat false.");
Console.WriteLine("\n*** Operatorul de scurtcircuitare AND:");
if (Metoda1() && Metoda2())
Console.WriteLine("Ambele metode au returnat true.");
else
Console.WriteLine("Cel putin o metoda a returnat false.");
Console.ReadKey();
}
static bool Metoda1()
{
Console.WriteLine("Metoda1 a fost apelata.");
return false;
}
static bool Metoda2()
{
Console.WriteLine("Metoda2 a fost apelata.");
return true;
}
}
La ieire va fi afiat:
*** Operatorul AND:
Metoda1 a fot apelata.
Metoda2 a fot apelata.
Cel putin o metoda a returnat false.
*** Operatorul de scurtcircuitare AND:
Metoda1 a fot apelata.
Cel putin o metoda a returnat false.
4.6. Metode
Metodele reprezint secvene de cod denumite care, opional, pot returna un rezultat i care
pot primi argumente. C# face distincie ntre parametri i argumente: o metod definete o
list de intrri parametrii, iar codul din interiorul metodei se refer la acetia prin nume.
Valorile vzute de cod pot s difere de fiecare dat cnd metoda este invocat, iar argumentele se refer la valoarea specific transmis unui parametru la o invocare.
55
Atunci cnd un exist un specificator de acces, acesta apare la nceputul declaraiei metodei.
Urmeaz cuvntul cheie opional static, apoi declaraia metodei stabilete tipul returnat. n
cazul n care metoda nu returneaz nimic n locul tipului returnat va aprea cuvntul cheie
void. n interiorul metodei vei utiliza cuvntul cheie return urmat de expresia care specific
valoarea returnat de metod. n cazul unei metode void, putei utiliza cuvntul cheie return
fr nicio alt expresie, ns acest lucru este opional, deoarece o metod void va returna
oricum la terminarea execuiei acesteia.
n principiu, C# suport ca o metod s returneze numai un singur tip. Totui, este posibil ca
o metod s returneze valori multiple, deoarece putei alege parametri ca ieire nu ca
intrare. Exemplul de mai jos returneaz dou valori, ambele produse de mprirea
ntregilor. Principala valoare returnat este rezultatul, ns mai returneaz i restul
mpririi prin ultimul parametru, care a fost marcat cu cuvntul cheie out.
public static int Imparte(int x, int y, out int rest)
{
rest = x % y;
return x / y;
}
La invocarea unei astfel de metode este obligatorie utilizarea cuvntului cheie out. Unele
limbaje din familia C nu fac nicio distincie vizual ntre apelurile care transmit valori i cele
care transmit referine, ns semantica difer, astfel c C# face totul explicit.
int r;
// Apelul unei metode cu un parametru out
int q = Imparte(10, 3, out r);
Secvena de cod de mai sus ruleaz prin transmiterea unei referine la variabila r, astfel c
atunci cnd metoda Imparte atribuie o valoare n rest, o atribuie efectiv variabilei r a
apelantului. Acesta este de tip int, care este un tip valoare, astfel c n mod normal nu ar fi
transmis prin referin, iar acest tip de referin este limitat. Numai argumentele metodelor
pot utiliza aceast caracteristic. Nu putei declara o variabil local sau un cmp care s
rein o astfel de referin, deoarece referina este valid numai pe durata apelului.
O referin out necesit ca informaia s fie transmis de la metod napoi la apelant: dac
metoda returneaz fr a atribui ceva n toate argumentele out, vei primi eroare la
compilare (aceast cerin nu se aplic dac metoda arunc o excepie n loc s returneze).
Exist un cuvnt cheie asemntor ref, care are o semantic similar, ns care permite flux
de informaii bidirecional. n cazul unui argument ref metoda are acces direct la variabila pe
care apelantul o transmite (putem citi i modifica valoarea acesteia). Apelantul este obligat
s se asigure c orice variabile transmise cu ref conin o valoare nainte de a efectua apelul
astfel c, n acest caz, metoda nu mai este obligat s modifice valorile. Dac apelai o metod
cu un parametru notat cu ref, trebuie s indicai clar faptul c intenionai s transmitei o
referin la o variabil ca argument.
long x = 41;
Operatii.Increment(ref x); // Apelul unei metode ref
Putei utiliza cuvintele cheie out i ref i pentru tipurile referin. Aceasta poate suna redundant, ns poate fi util, deoarece ofer indirectare dubl metoda primete o referin la o
variabil care stocheaz o referin. n mod normal, atunci cnd transmitei un argument de
tip referin ctre o metod, acea metod primete acces la obiectul pe care i-l transmitei,
astfel c att timp ct metoda poate utiliza membrii acelui obiect, nu l poate nlocui cu un
obiect diferit. ns dac marcai un argument de tip referin cu ref, metoda are acces la
variabil i o poate nlocui cu o referin la un obiect complet diferit.
Argumentele constructorilor funcioneaz la fel ca la orice metod, astfel c putei utiliza out
i ref i la constructori. Cuvintele cheie out i ref fac parte din semntura metodei. Apelantul
transmite un argument out sau ref dac i numai dac parametrul a fost declarat ca out sau
ref. Nu putei decide s transmitei un argument prin referin unei metode care nu ateapt
aa ceva. Argumentele metodelor pot fi fcui opionali prin definirea de valori implicite.
Exemplul de mai jos specific valorile pe care argumentele ar trebui s le aib dac apelantul
56
Ce putei face este s specificai numele argumentului pe care dorii s l transmitei folosind
sintaxa artat n exemplul de mai jos. C# va nlocui argumentele omise cu valorile implicite
specificate.
// Specificarea numelui unui argument
Afiseaza(nume: "Popescu");
Evident, aceasta funcioneaz numai la invocarea metodelor care definesc valori implicite
pentru argumente. Totui, suntei liberi s specificai numele argumentelor la invocarea
oricrei metode uneori acest lucru poate fi util chiar dac nu omitei vreun argument,
deoarece poate face mai clar semnificaia argumentelor atunci cnd citii codul surs.
Cnd invocai o metod fr a-i transmite toate argumentele, compilatorul genereaz cod
care transmite un set complet de argumente, practic rescriind codul dumneavoastr prin
adugarea napoi a argumentelor lsate deoparte.
Uneori vei observa un mecanism alternativ care permite omiterea argumentelor: suprancrcarea, care semnific faptul c unui singur nume sau simbol i pot fi date mai multe
nelesuri.
public void Afiseaza(string titlu, string nume)
{
Console.WriteLine("Urmatorul client este {0} {1}.", titlu, nume);
}
public void Afiseaza(string titlu)
{
Afiseaza(titlu, "Ionescu");
}
public void Afiseaza()
{
Afiseaza("domnul", "Ionescu");
}
ntr-un anumit sens acest mecanism este mai puin flexibil dect cel de utilizare a valorilor
implicite pentru argumente, deoarece nu avei cum s mai specificai o valoare pentru
argumentul nume la alegerea titlului implicit. Pe de alt parte, suprancrcarea metodelor
ofer dou poteniale avantaje: v permite s decidei asupra valorilor implicite la rulare,
dac este nevoie, i ofer o modalitate prin care putei s operai cu argumente out i ref.
Acestea necesit referine la variabile locale, astfel c nu putei defini o valoare implicit,
ns putei oferi suprancrcri cu i fr aceste argumente, dac avei nevoie.
57
cuvntul cheie this adugat la primul parametru. Putei defini metode de extensie numai ca
membri ai claselor statice. Exemplul de mai jos adaug o metod de extensie tipului string:
namespace MyApp
{
public static class StringExtension
{
public static int WordCount(this string s)
{
return s.Split(new char[] { ' ', ',', '.' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
Am inclus n exemplu i declaraia spaiului de nume deoarece spaiile de nume sunt importante: metodele de extensie sunt disponibile numai dac ai scris o directiv using pentru
spaiul de nume n care este definit extensia, sau dac codul scris este definit n acelai
spaiu de nume. n caz contrar, clasa string va arta normal, fr metoda Show.
// Metoda de extensie este disponibila pentru ca este declarata in acelasi spatiu de nume
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, world! I am a new extension method".WordCount());
}
}
}
Codul de mai jos este plasat ntr-un spaiu de nume diferit, ns are acces la metoda de
extensie din cauza utilizrii directivei using.
// Metoda de extensie este disponibila din cauza utilizarii directivei using
using MyApp;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, world!".WordCount());
}
}
}
Metodele de extensie nu sunt n realitate membri ai claselor pentru care sunt definite (n
exemplul de mai sus clasa string nu va dobndi o nou metod), acest lucru fiind doar o
iluzie meninut de compilatorul C#. Acest mecanism poate fi util atunci cnd avei nevoie de
anumite funcii. Putei activa aceste caracteristici de limbaj pentru tipuri care nu le suport
direct, prin scrierea unor metode de extensie potrivite.
4.7. Proprieti
O proprietate reprezint un membru care ofer un mecanism flexibil de citire, scriere sau
calculare a valorii unui cmp privat. Proprietile pot fi utilizate ca orice ali membri publici,
ns acestea sunt de fapt metode speciale numite accesori. Acest lucru permite accesarea
uoar a datelor i ajut la promovarea siguranei i flexibilitii metodelor. Proprietile
permit unei clase s expun un mod public de obinere i setare a valorilor, n timp ce
implementarea sau codul pentru verificare sunt ascunse.
58
n exemplul de mai jos, clasa IntervalTimp stocheaz un interval de timp. Intern, clasa
stocheaz timpul n secunde, ns proprietatea Ore permite unui client s specifice timpul n
ore. Accesorii pentru proprietatea Ore efectueaz conversia ntre ore i secunde.
class IntervalTimp
{
private double secunde;
public double Ore
{
get { return secunde / 3600; }
set { secunde = value * 3600; }
}
}
class Program
{
static void Main()
{
IntervalTimp t = new IntervalTimp();
// Apelarea accesorului 'set'
t.Ore = 24;
// Apelarea accesorului 'get'
System.Console.WriteLine("Timpul in ore: " + t.Ore);
}
}
Exemplul de mai jos ilustreaz un ablon uzual: o proprietate cu accesori get i set, care ofer
acces la un cmp. De ce s nu facem acel cmp public? Pentru c acest lucru ar permite
codului extern s modifice statusul unui obiect fr ca obiectul s tie acest lucru. Ar putea fi
nevoie s scriei ulterior cod pentru ca obiectul s efectueze anumite operaii ori de cte ori
se modific valoarea. Un alt motiv pentru utilizarea proprietilor ar fi faptul c unele tipuri
nu suport cmpuri, ci numai proprieti (de exemplu, interfeele).
Accesorul get returneaz o valoare de tipul proprietii, iar accesorul set primete
parametrul implicit value, de acelai tip cu cel al proprietii, folosit pentru atribuirea unei
noi valori. Nu suntei obligai s folosii acea valoare pentru a o stoca ntr-un cmp; putei
scrie orice cod n interiorul celor dou metode. Oricum, o proprietate este vizibil din
exterior ca un cmp oarecare.
La declararea unei proprieti putei ignora accesorul set pentru a obine o proprietate readonly sau accesorul get pentru a obine o proprietate write-only. Proprietile read-only pot fi
utile pentru aspecte fixe ale unui obiect (de exemplu, un identificator). Accesorii unei
proprieti pot avea diferite niveluri de acces. Putei, de exemplu, s definii proprieti n
care accesorul get s fie public iar set privat.
public int Varsta { get; private set; }
4.8. Indexatori
Indexatorii reprezint o convenie de sintax care permit crearea unei clase, structuri sau
interfee pe care aplicaiile client le pot accesa ntocmai ca pe un vector. Indexatorii sunt cel
mai frecvent implementai n tipurile ale cror scop principal este s ncapsuleze o colecie
intern sau un vector.
S presupunem c avem o clas Temperaturi care reprezint temperatura msurat la 10
momente de timp diferite pe parcursul unui interval de 24 de ore. Clasa conine vectorul temp
59
care reprezint temperaturile. Prin implementarea unui indexator n aceast clas, clienii
pot accesa temperaturile dintr-o instan a clasei sub forma
float temp = tr[4]
n loc de
float temp = tr.temps[4]
Notaia indexatorului nu numai simplific sintaxa pentru aplicaiile client, ci face clasa mai
uor de neles pentru ali dezvoltatori.
Pentru declararea unui indexator al unei clase, folosii cuvntul cheie this, ca n exemplul de
mai jos:
class Temperaturi
{
private float[] temps = new float[10] { 16.2F, 16.7F, 16.5F, 16.9F, 18.8F,
21.3F, 25.9F, 22.1F, 19.2F, 17.5F };
// Declaratia indexatorului
public float this[int index]
{
get
{
return temps[index];
}
set
{
temps[index] = value;
}
}
}
class Program
{
static void Main()
{
Temperaturi temp = new Temperaturi();
temp[3] = 18.3F;
temp[5] = 20.1F;
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine("Elementul {0} = {1}", i, temp[i]);
}
System.Console.ReadKey();
}
}
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
=
=
=
=
=
=
=
=
=
=
16.2
16.7
16.5
18.3
18.8
20.1
25.9
22.1
19.2
17.5
C# nu limiteaz tipul indexului la int. De exemplu, ar putea fi util folosirea unui string
mpreun cu un indexator. Un astfel de indexator ar putea fi implementat prin cutarea dup
string n colecie i returnarea valorii potrivite. Dac accesorii get i set sunt suprancrcai
atunci versiunile string i int pot s co-existe.
Cu ajutorul accesorilor get i set putei declara indexatori read-only sau read-write. De asemenea, putei s declarai indexatori multidimensionali.
60
Indexator
Permite elementelor unei colecii interne a unui
obiect s fie accesate prin utilizarea notaiei
vectoriale asupra obiectului nsui.
Accesat printr-un index.
Trebuie s fie membru al unei instane.
Accesorul get al unui indexator are acelai
parametru formal ca i indexatorul.
Accesorul set al unui indexator are acelai
parametru formal ca i indexatorul i de asemenea
parametrul value.
4.9. Clase
O clas reprezint o structur de date care permite crearea unor tipuri personalizate, putnd
grupa mpreun mai multe tipuri de elemente, cum ar fi: cmpuri, metode, evenimente etc. O
clas este ntocmai unui plan care definete datele i comportamentul unui tip. Dac clasa nu
este declarat ca static, codul client o poate folosi prin crearea obiectelor sau instanelor
clasei. Ele rmn n memorie pn la prsirea domeniului de vizibilitate a tuturor
referinelor la acestea, dup care CLR le marcheaz ca eligibile pentru garbage collector.
Dac clasa este declarat ca static, atunci n memorie exist numai o singur copie iar codul
client o poate accesa prin intermediul clasei nsi, nu prin numele unei instane.
Clasele ofer un mecanism pentru ncapsulare ele pot defini o interfa programabil
public pentru utilizarea de ctre programatori, n timp ce detaliile de implementare intern
rmn inaccesibile. Exemplul de mai jos prezint declaraia unei clase.
public class Contor
{
private int _val;
public int GetNextVal()
{
_val += 1;
return _val;
}
}
Cu toate c adesea sunt confundate, o clas i un obiect sunt lucruri diferite. O clas definete
un tip de obiect, ns nu este obiectul nsui. Un obiect este o entitate concret bazat pe o
clas, uneori fiind referit ca instan a unei clase. Obiectele pot fi create prin utilizarea
cuvntului cheie new urmat de numele clasei de tipul creia va fi obiectul:
Contor c1 = new Contor
Contor c2 = new Contor
Console.WriteLine("c1:
Console.WriteLine("c1:
Console.WriteLine("c1:
Console.WriteLine("c2:
Console.WriteLine("c1:
();
();
" +
" +
" +
" +
" +
c1.GetNextVal());
c1.GetNextVal());
c1.GetNextVal());
c2.GetNextVal());
c1.GetNextVal());
61
Atunci cnd este creat o instan a unei clase, este pasat napoi ctre programator o
referin la obiect. n exemplul anterior, c1 este o referin la un obiect de tip Contor. Putei s
creai o referin la un obiect chiar fr s creai obiectul:
Contor c3;
Acest cod creeaz dou referine la obiecte care pointeaz spre acelai obiect. n consecin,
orice modificare asupra obiectului fcut prin intermediul lui c4 va fi reflectat i n obiectul
c5. Deoarece obiectele de tipul unor clase sunt adresate prin referin, clasele sunt cunoscute
ca tipuri referin.
C# nu necesit ca numele clasei s fie acelai cu numele fiierului surs, nici nu v limiteaz
la a avea o singur clas ntr-un fiier. Totui, prin convenie, proiectele C# conin clasele n
fiiere cu acelai nume.
n declaraia clasei Contor a fost folosit cuvntul cheie public. Opional, definiiile de clase pot
specifica accesibilitatea, care stabilete codul cruia i este permis s foloseasc clasa. Clasele
declarate la nivel de spaii de nume au doar dou posibiliti: public i internal, ultimul fiind
modificatorul de acces implicit. O clas intern este disponibil pentru utilizare numai n
interiorul componentei n care este declarat, astfel c, dac dorii s scriei o bibliotec de
clase, suntei liberi s definii att clase publice, ct i clase pe care numai componentele
bibliotecii le pot accesa.
Membrii claselor, incluznd aici i clasele ncuibate, pot avea urmtorii modificatori de
acces: public, protected internal, protected, internal sau private (modificatorul implicit).
Modificatorii de acces sunt opionali att pentru membri ct i pentru clase, modificatorul
implicit fiind cel mai restrictiv disponibil, n acest caz, private. Se recomand, totui,
precizarea explicit a modificatorilor de acces pentru claritatea codului (persoanele care vor
citi codul dumneavoastr nu vor ti dac omiterea unui modificator de acces a fost
intenionat sau accidental).
Definiiile claselor, structurilor, interfeelor sau a metodelor pot fi mprite ntre dou sau
mai multe fiiere surs cu ajutorul cuvntului cheie partial. Fiecare fiier surs conine o
parte din definiia tipului sau a metodei, iar la compilare toate prile sunt combinate. Acest
lucru poate fi util atunci cnd lucrai cu proiecte mari. Visual Studio folosete aceast
abordare, de exemplu, la proiectele de tip Windows Forms.
O clas poate conine declaraii ale urmtoarelor tipuri de membri: constructori, destructori,
constante, cmpuri, metode, proprieti, indexatori, operatori, evenimente, delegai, clase,
interfee, structuri.
Folosirea cmpurilor publice nu este o practic recomandat deoarece permite oricrei
metode de oriunde din program acces nerestricionat i neverificat la mecanismele interne
ale unui obiect. n general, cmpurile ar trebui s fie private, iar accesul la ele s fie fcut
numai prin metode sau proprieti ale clasei.
62
Cmpurile statice de obicei sunt utilizate pentru a pstra numrul de instane ale clasei sau
pentru a stoca o valoare care trebuie partajat ntre toate instanele clasei. n C# nu putei
declara variabile locale statice.
Exemplul de mai jos arat o versiune modificat a clasei Contor, cu doi noi membri statici:
public class Contor
{
private int _val;
private static int _total;
public int GetNextVal()
{
_val += 1;
_total += 1;
return _val;
}
public static int Total
{
get
{
return _total;
}
}
}
Total este o proprietate public read-only care returneaz valoarea membrului privat _total.
Cmpul static _total reine numrul total de apeluri ale metodei GetNextVal, spre deosebire
de membrul non-static _val, care reine numrul de apeluri numai pentru instana curent.
n codul din cadrul clasei, cmpurile statice sunt utilizate n acelai mod n care sunt utilizate
cmpurile non-statice. Diferena dintre cele dou apare la apelarea lor n codul din afara
clasei: un membru static poate fi accesat prin numele clasei.
Console.WriteLine(Contor.Total);
//5
n exemplul de mai sus sunt utilizate de fapt doi membri statici: proprietatea Total i metoda
WriteLine din clasa Console.
Metodele i proprietile statice nu pot accesa cmpurile i evenimentele non-statice din
aceeai clas i nici nu pot accesa vreo instan a unei clase dect dac aceasta este
transmis explicit ntre parametrii metodei. Deoarece proprietatea Total a fost declarat ca
static, codul pe care l conine are acces numai la ali membri statici. Dac am fi ncercat s
utilizm un membru non-static, de exemplu cmpul _val, compilatorul ar fi generat
urmtorul mesaj de eroare:
error CS0120: An object reference is required for the non-static field, method, or property
'ConsoleApplication1.Contor._val'
Membrii statici sunt iniializai nainte de prima accesare a acestora i nainte de apelarea
constructorului static, dac exist vreunul. Dac o clas conine cmpuri statice, putei
declara un constructor static care s le iniializeze atunci cnd clasa este ncrcat.
Deoarece cmpurile non-statice sunt asociate cu o anumit instan a clasei din care fac
parte, C# are nevoie s cunoasc ce instan s foloseasc. n cazul unei metode sau
proprieti non-statice poate fi utilizat orice instan pentru invocarea acestora. n
exemplul de mai sus, a fost scris fie c1.GetNextVal(), fie c2.GetNextVal(), pentru alegerea unuia
dintre cele dou instane ale clasei. C# a transmis referina stocat n c1 sau c2, dup caz, ca
un prim parametru implicit. Pe acesta o putei accesa folosind cuvntul cheie this. Astfel,
prima linie din metoda GetNextVal ar fi putut fi scris astfel:
this._val += 1;
Acest mod de scriere indic explicit faptul c ne referim la membrul _val al instanei pentru
care a fost invocat metoda GetNextVal. Accesarea explicit a membrilor prin cuvntul cheie
this este uneori necesar din motive de suprapunere a numelor unor variabile. Metodele
statice nu folosesc cuvntul cheie this deoarece acestea nu sunt asociate cu nicio instan
anume.
63
Metodele statice pot fi suprancrcate, ns nu pot fi suprascrise, deoarece ele aparin clasei,
nu vreunei instane a clasei.
Dei un cmp nu poate fi declarat ca static const, un cmp const se comport ca un cmp
static. Acesta aparine clasei, nu instanelor acesteia. Aadar, cmpurile const pot fi accesate
prin aceeai notaie NumeClas.NumeMembru folosit pentru cmpuri statice, nefiind nevoie de
vreo instan.
Un constructor care nu primete parametri este numit constructor implicit. Acetia sunt
invocai ori de cte ori este instaniat un obiect prin utilizarea operatorului new, nefiind
transmis ctre new nici un argument.
Declararea de constructori cu parametri este util pentru iniializarea cmpurilor clasei.
Dac nu creai un constructor pentru obiectul dumneavoastr, C# va crea unul implicit care
instaniaz obiectul i seteaz variabilele membre la valorile implicite. Clasele statice nu
primesc automat un constructor implicit.
Dac dorii ca o clas s nu poat fi instaniat, facei constructorul acesteia privat.
Constructorii pentru tipuri struct se aseamn cu cei ai claselor, ns structurile nu pot
conine un constructor explicit fr parametri, deoarece acesta este furnizat automat de
ctre compilator. Acest constructor iniializeaz fiecare cmp din structur la valoarea
implicit. Totui, acest constructor este invocat numai dac structura este instaniat cu
operatorul new. De exemplu, codul de mai jos folosete constructorul implicit pentru tipul
Int32, astfel c suntei siguri c ntregul este iniializat:
int i = new int();
Console.WriteLine(i);
Codul de mai jos va genera o eroare la compilare deoarece nu folosete operatorul new i
pentru c ncearc s foloseasc un obiect care nu a fost iniializat:
int i;
Console.WriteLine(i); // Eroare
64
Att clasele ct i structurile pot defini constructori cu parametri, care trebuie apelai prin
new sau base. Clasele i structurile pot de asemenea s defineasc constructori multipli,
nefiind nevoie de definirea unui constructor implicit. De exemplu:
public class Angajat
{
public int salariu;
public Angajat(int salariuAnual)
{
salariu = salariuAnual;
}
public Angajat(int salariuSaptamanal, int nrSaptamani)
{
salariu = salariuSaptamanal * nrSaptamani;
}
}
Aceast clas poate fi instaniat ntr-unul din cele dou moduri prezentate n continuare
Angajat a1 = new Angajat (30000);
Angajat a2 = new Angajat (500, 52);
Un constructor poate utiliza cuvntul cheie base pentru a apela explicit constructorul clasei
de baz:
public class Director : Angajat
{
public Director (int salariuAnual)
: base(salariuAnual)
{
//...
}
}
n acest exemplu, constructorul pentru clasa de baz este apelat nainte de rularea codului
constructorului. Cuvntul cheie base poate fi utilizat cu sau fr parametri. Oricare parametri
ai constructorului pot fi utilizai ca parametri ai constructorului bazei. ntr-o clas derivat,
dac un constructor al clasei de baz nu este apelat explicit prin cuvntul cheie base, atunci
este apelat automat constructorul implicit. Acest lucru nseamn c urmtoarele dou
declaraii sunt identice:
public Director(int initData)
{
//...
}
public Director(int initData)
: base()
{
//...
}
Dac o clas de baz nu ofer un constructor implicit, atunci clasa derivat trebuie s fac un
apel explicit la un constructor din clasa de baz. Un constructor poate invoca un alt
constructor al aceluiai obiect prin utilizarea cuvntului cheie this. La fel ca i base, this
poate fi utilizat cu sau fr parametri, oricare din parametrii constructorului fiind disponibili
acestuia. De exemplu, al doilea constructor din exemplul anterior poate fi rescris astfel:
public Angajat(int salariuSaptamanal, int nrSaptamani)
: this(salariuSaptamanal * nrSaptamani)
{
}
65
salariu = salariuAnual;
}
Constructorii pot fi marcai ca public, private, protected, internal sau protected internal.
Aceti modificatori de acces definesc modul n care utilizatorii clasei pot instania clasa. Un
constructor poate fi declarat ca static.
Constructorii statici sunt apelai automat imediat nainte de accesarea oricrui cmp static i
sunt utilizai pentru iniializarea claselor statice, a membrilor statici ai claselor non-statice,
sau pentru executarea unor aciuni specifice care trebuie efectuate numai o singur dat.
Constructorii statici au urmtoarele proprieti:
Destructorii sunt utilizai pentru distrugerea instanelor claselor. Iat cteva observaii
privitoare la acetia:
class Automobil
{
~Automobil()
{
// ...
}
}
Aceasta nseamn c metoda Finalize este apelat recursiv pentru toate instanele din lanul
de motenire, de la clasa cea mai derivat la cea mai puin derivat.
Programatorul nu poate controla momentul n care este apelat destructorul, deoarece acesta
este determinat de ctre garbage collector. Garbage collector caut acele obiecte care nu mai
sunt utilizate de ctre aplicaie i dac consider c un obiect poate fi distrus, atunci apeleaz destructorul acestuia (dac exist vreunul) i elibereaz memoria ocupat de acesta. Este
posibil forarea acestei operaii prin apelarea metodei Collect, ns de cele mai multe ori
66
acest lucru ar trebui evitat deoarece poate afecta performana aplicaiilor. Destructorii sunt
de asemenea apelai la prsirea programului.
Nu poate fi instaniat
Nu poate fi folosit n declaraia unei variabile locale, statice sau a unei instane, ca
argument al unui tip generic sau ca tip de element ntr-un vector.
Crearea unei clase statice este similar cu crearea unei clase care deriv din System.Object,
conine doar membri statici, fiind att sigilat ct i abstract. Avantajul utilizrii unei clase
statice este c compilatorul poate verifica faptul c nu au fost adugai accidental membri de
instan (non-statici). Compilatorul garanteaz faptul c nu pot fi create instane ale clasei.
Clasele statice sunt sigilate i, n consecin, nu pot fi motenite. Ele nu pot moteni nicio
clas, cu excepia clasei System.Object. O clas static nu poate conine un constructor
implicit, dar poate conine un constructor static. O clas non-static ar trebui s defineasc
un constructor static dac clasa conine membri statici care necesit n mod deosebit o
iniializare.
n continuare este prezentat un exemplu de clas static ce conine dou metode care
convertesc temperatura din grade Celsius n grade Fahrenheit i invers:
public static class ConvertorTemperatura
{
public static double Celsius2Fahrenheit(string tempC)
{
double celsius = Double.Parse(tempC);
// Conversie de la string la double
double fahrenheit = (celsius * 9 / 5) + 32;
return fahrenheit;
}
public static double Fahrenheit2Celsius(string tempF)
{
double fahrenheit = Double.Parse(tempF);
// Conversie de la string la double
double celsius = (fahrenheit - 32) * 5 / 9;
return celsius;
}
}
class Program
{
67
68
{
// Declara o lista de int
GenericList<int> list1 = new GenericList<int>();
// Declara o lista de string
GenericList<string> list2 = new GenericList<string>();
// Declara o lista de Element
GenericList<ExampleClass> list3 = new GenericList<Element>();
}
}
Biblioteca de clase din .NET Framework conine cteva clase colecie generice n spaiul de
nume System.Collections.Generic. Putei s v creai propriile interfee, clase, metode,
evenimente i delegai generici. Se recomand utilizarea claselor generice pentru
maximizarea reutilizrii codului, siguranei tipului i performanei.
Dac o clas generic implementeaz o interfa, atunci toate instanele clasei vor putea fi
convertite prin operatorul cast la acea interfa.
4.10. Interfee
O interfa definete un API care nu are nicio implementare. Clasele pot alege s ofere o
implementare a unei interfee. Putei scrie cod care s nu fie limitat la funcionarea numai cu
un anumit tip, ci cu orice tip care implementeaz interfaa.
O interfa declar metode, proprieti i evenimente, ns nu i coninutul acestora.
Proprietile indic doar faptul c accesorii get i set trebuie s fie prezeni. O interfa nu
este dect o list de membri pe care un tip va trebui s i pun la dispoziie dac va
implementa interfaa.
public interface IMyTest
{
string this[int i] { get; set; }
string Nume { get; set; }
int Id { get; }
void Show(string arg);
}
Pentru membrii interfeelor nu pot fi specificai modificatori de acces, deoarece accesibilitatea este controlat la nivelul interfeei (la fel ca i clasele, interfeele sunt publice sau
interne, ns n cazul n care sunt ncuibate pot avea orice modificator de acces). Interfeele
nu pot conine cmpuri sau tipuri ncuibate, deoarece ele definesc interfaa programabil
(API), nu implementarea. De asemenea, interfeele nu pot s declare constructori o
interfa indic acele servicii pe care un obiect ar trebui s le ofere dup ce a fost construit.
O clas declar interfeele pe care le implementeaz dup numele clasei, urmat de caracterul
: i lista de interfee (desprite prin virgul unele de altele). Clasa va trebui s implementeze toi membrii declarai n interfa, n caz contrar compilatorul va semnala eroare.
public class MyTest : IMyTest
{
public string this[int i]
{
get { return i.ToString(); }
}
public Nume { get; set; }
// ...
}
69
Atunci cnd un tip folosete implementarea explicit a membrilor unei interfee, acei
membri nu pot fi utilizai printr-o referin a tipului nsui. Acetia devin vizibili la referirea
unui obiect printr-o variabil de tipul interfeei.
MyTest c = new MyTest();
c.Show();
// Eroare
IMyTest i = c;
i.Show();
// OK
4.11. Motenire
Clasele C# suport mecanismul motenirii, specific programrii orientate pe obiecte. Motenirea este realizat prin derivarea unei clase dintr-o clas de baz de la care motenete
datele i comportamentul. O clas de baz este specificat prin adugarea la numele clasei
derivate a caracterului : urmat de numele clasei de baz.
Atunci cnd o clas declar o clas de baz, ea motenete toi membrii clasei de baz, cu
excepia constructorilor, la care poate s adauge noi membri. Spre deosebire de C++, o clas
C# poate moteni direct numai o singur clas de baz. Totui, deoarece o clas de baz
poate moteni la rndul ei o alt clas, o clas poate moteni indirect mai multe clase de
baz. De asemenea, o clas poate implementa direct mai multe interfee.
Tipurile valoare nu suport motenire. Unul dintre motive este c tipurile valoare nu sunt
folosite n mod normal prin referin, fapt care elimin unul dintre principalele beneficii ale
motenirii: polimorfismul n timpul rulrii. Motenirea nu este neaprat incompatibil cu
comportamentul tipurilor valoare, ns adesea are probleme. De exemplu, atribuirea unei
valori de un tip derivat unei variabile de tipul de baz duce la pierderea tuturor cmpurilor
pe care tipul derivat le-a adugat, problem cunoscut sub denumirea de slicing. C#
depete aceasta prin restricionarea motenirii la tipurile referin. Cnd atribuii o
variabil de un tip derivat uneia de tipul de baz, copiai de fapt o referin, nu obiectul
nsui, astfel c obiectul rmne intact. Slicing devine o problem dac clasa de baz ofer o
metod care cloneaz obiectul i nu ofer pentru clasele derivate o modalitate de extindere a
acestuia.
Clasele specific o clas de baz prin specificarea dup numele clasei a caracterului : urmat
de numele clasei de baz.
public class Baza
{
}
public class Deriv : Baza
{
}
public class Deriv1 : Baza, IDisposable
70
{
public void Dispose() { }
}
Precum se poate vedea n exemplul de mai sus, dac clasa implementeaz interfee, acestea
sunt listate de asemenea dup caracterul :. Dac dorii ca o clas s moteneasc alt clas
i s implementai i interfee, atunci clasa de baz trebuie s apar prima n list.
Se poate ca o clas s moteneasc o clas derivat din alt clas. Clasa Deriv2 din exemplul
de mai jos deriv din Deriv, care la rndul ei este derivat din Baza.
public class Baza
{
}
public class Deriv : Baza
{
}
public class Deriv2 : Deriv
{
}
Aceasta nseamn c, din punct de vedere tehnic, Deriv2 are mai multe clase de baz: este
derivat att din Deriv (direct) ct i din Baza (indirect, prin intermediul Deriv). Aceasta nu
poate fi numit motenire multipl, deoarece exist numai o singur linie de motenire.
O instan a clasei derivate poate face tot ceea ce face o instan a clasei de baz. Orice
instan a clasei Deriv2 este o Deriv i de asemenea o Baza. C# recunoate aceast relaie.
La fel ca n cazul motenirii claselor, interfeele motenesc toi membrii interfeelor de baz.
Exist conversii implicite de la interfaa derivat la interfeele de baz. De exemplu, o
referin de tip IBoth poate fi atribuit unei variabile de tip IBase1 sau IBase2.
O caracteristic mai subtil legat de motenirea interfeelor este aceea c orice clas care
implementeaz o interfa derivat trebuie s implementeze interfaa de baz a acelei
interfee. Precum putei observa n exemplul de mai jos, clasa trebuie doar s indice faptul c
implementeaz interfaa derivat, ns compilatorul va aciona ca i cum IBase1 i IBase2 ar fi
n lista de interfee implementate de clas.
public class Impl : IBoth
{
public void Base1Method()
{
}
71
Orice referin de tip Impl este implicit convertibil la IBoth, care n schimb este implicit
convertibil la IBase1 i IBase2. n general, dac o referin a unei clase C este implicit
convertibil la tipul T1 i o referin de tip T1 este implicit convertibil la tipul T2, aceasta
nu nseamn c C este implicit convertibil la T1. n C# conversiile implicite nu sunt
tranzitive.
4.12. Polimorfism
Alturi de ncapsulare i motenire, polimorfismul este unul dintre conceptele de baz ale
programrii orientate pe obiecte. Polimorfismul prezint dou aspecte distincte:
72
La rulare, obiectele de tipul unei clase derivate pot fi tratate ca obiecte de tipul clasei
de baz n locuri precum parametri ai metodelor, colecii sau vectori. Atunci cnd se
Metodele virtuale v permit s operai cu grupuri de obiecte asociate ntr-un mod uniform.
S presupunem, de exemplu, c avei o aplicaie grafic care permite utilizatorilor s creeze
diferite tipuri de forme, fiecare dintre ele avnd asociat propria clas, toate fiind derivate
dintr-o singur clas de baz.
Un efect al polimorfismului este acela c o metod dintr-o clas derivat poate avea acelai
nume cu al unei metode din clasa de baz. O metod virtual este una pe care un tip derivat
poate s o nlocuiasc.
n C# toate tipurile sunt polimorfice deoarece motenesc clasa System.Object (inclusiv cele
definite de utilizatori). Cteva dintre metodele definite de clasa System.Object sunt virtuale:
ToString, Equals, GetHashCode i Finalize, fiind proiectate pentru a fi nlocuite. Codul acestor
funcii va diferi de la un tip la altul.
Pentru declararea unei metode virtuale se folosete cuvntul cheie virtual, precum putei
vedea n exemplul de mai jos.
public class Baza
{
public virtual void Afiseaza()
{
Console.WriteLine("Salutari din Baza!");
}
}
O metod virtual este apelat exact n acelai fel n care este apelat orice alt metod:
public static void ApelMetVirt(Baza b)
{
b.Afiseaza();
}
Diferena dintre invocrile metodelor virtuale i a celor non-virtuale este c n cazul apelului
unei metode virtuale se va decide la rulare care metod va fi invocat. Codul din secvena de
mai sus va inspecta obiectul transmis i, dac tipul obiectului ofer o variant proprie de
implementare a metodei Afiseaza, o va apela pe aceasta n locul celei definite n clasa Baza.
Metoda este aleas pe baza tipului pe care obiectul l poate avea la rulare, nu a tipului static
determinat la compilare.
Deoarece la invocarea unei metode virtuale aceasta este selectat n funcie de tipul
obiectului prin care o invocai, metodele statice nu pot fi virtuale.
Tipurile derivate nu sunt obligate s nlocuiasc metodele virtuale. Exemplul de mai jos
ilustreaz dou clase care motenesc o clas de baz. Prima clas derivat las neschimbat
implementarea metodei virtuale Afiseaza. A doua clas o suprascrie. Observai utilizarea
cuvntului cheie override C# necesit utilizarea acestuia pentru a exprima explicit intenia
de suprascriere a unei metode virtuale.
public class Deriv1 : Baza
{
}
public class Deriv2 : Baza
{
public override void Afiseaza()
{
Console.WriteLine("Metoda suprascrisa");
73
}
}
Avnd definite aceste tipuri, putem utiliza acum metoda ApelMetVirt. Exemplul de mai jos o
apeleaz de trei ori, transmindu-i de fiecare dat un tip diferit de obiect.
ApelMetVirt(new Baza());
ApelMetVirt(new Deriv1());
ApelMetVirt(new Deriv2());
La rulare va fi afiat:
Salutari din Baza
Salutari din Baza
Metoda suprascrisa
74
Membrii ascuni ai clasei de baz pot fi accesai n continuare n cod prin casting al instanei
clasei derivate la o instan a clasei de baz.
Deriv B = new Deriv();
B.Metoda(); // Apeleaza metoda suprascrisa
Baza A = (Baza)B;
A.Metoda(); // Apeleaza metoda veche
O clas derivat poate opri motenirea caracterului virtual prin declararea unei suprascrieri
sigilate. Acest lucru necesit folosirea cuvntului cheie sealed nainte de cuvntul override n
declaraia membrului clasei.
public class C : B
{
public sealed override void Metoda() { }
}
n exemplul anterior, metoda Metoda nu mai este virtual pentru nicio clas derivat din clasa
C, ns rmne virtual pentru instanele clasei C. Metodele sigilate pot fi nlocuite de clasele
derivate prin utilizarea cuvntului cheie new.
75
public class D : C
{
public new void Metoda() { }
}
n acesta caz, dac Metoda este apelat printr-o instan a clasei D, va fi apelat noua metod
Metoda.
Se recomand folosirea de ctre membrii virtuali a cuvntului cheie base pentru apelarea n
cadrul propriei implementri a implementrii acelui membru din clasa de baz.
4.13. Delegai
Un delegat este un tip care definete semntura unei metode. Cnd instaniai un delegat,
putei s i asociai orice metod cu o semntur compatibil. Putei invoca metoda prin
instana delegatului.
Delegaii sunt utilizai pentru transmiterea metodelor ca argumente pentru alte metode.
Procedurile eveniment (event handlers) nu sunt altceva dect metode care sunt invocate
prin delegai. Putei crea o metod, iar o clas, cum ar fi un control Windows, poate apela
acea metod atunci cnd apare un anumit eveniment. n exemplul urmtor este ilustrat
declaraia unui delegat:
public delegate int EfectuareOperatii(int x, int y);
Delegatului i poate fi atribuit orice metod din orice clas sau structur accesibil, care
corespunde semnturii delegatului (tip returnat i parametri). Metoda poate s fie static
sau metod a unei instane. Acest lucru face posibil modificarea prin program a apelurilor
metodelor i, de asemenea, insereaz cod nou n clasele existente. Att timp ct cunoatei
semntura delegatului, i putei atribui propria voastr metod.
n contextul suprancrcrii metodelor, semntura unei metode nu include valoarea
returnat, ns n contextul delegailor aceasta include i valoarea returnat. n
concluzie, o metod trebuie s aib aceeai valoare returnat cu a delegatului.
Aceast abilitate de referire la o metod ca parametru face ca delegaii s fie ideali pentru
definirea metodelor cu apel invers (callback). De exemplu, pentru un algoritm de sortare ar
putea fi pasat ca argument o referin la o metod care compar dou obiecte. Deoarece
76
codul de comparare este ntr-o procedur separat, algoritmul de sortare poate fi scris ntrun mod mai general.
Delegaii au urmtoarele proprieti:
Delegaii sunt asemntori pointerilor la funcii din C++, ns sunt tipuri sigure.
Delegaii permit metodelor s fie transmise ca parametri.
Metodele nu trebuie s corespund exact semnturii delegatului. Atunci cnd atribuii unui delegat o metod, covariana i contravariana ofer flexibilitate pentru
potrivirea dintre semntura unei metode cu a unui delegat.
C# 2.0 a introdus conceptul de metode anonime, care permit blocurilor de cod s fie
transmise ca parametri n locul unei metode definite separat. C# 3.0 a introdus
expresii lambda ca un mod mai concis de scriere a blocurilor de cod inline. Att
metodele anonime ct i expresiile lambda (n anumite contexte) sunt compilate la
tipuri delegat. mpreun, aceste caracteristici sunt cunoscute ca funcii anonime.
Delegatul trebuie s fie instaniat cu o metod sau expresie lambda care are tip returnat i
parametri de intrare compatibili. Pentru utilizare cu metode anonime, delegatul i codul care
trebuie asociat cu acesta sunt declarate mpreun.
// Declararea delegatului defineste semnatura ceruta
delegate double MathAction(double num);
class Program
{
// Metoda care are semnatura identica cu a delegatului
static double Dublu(double input)
{
return input * 2;
}
static void Main()
{
// Instantierea delegatului cu o metoda
MathAction ma = Dublu;
// Invocarea delegatului ma:
double multByTwo = ma(4.5);
Console.WriteLine("Inmultire cu 2: {0}", multByTwo);
// Instantierea delegatului cu o metoda anonima
MathAction ma2 = delegate(double input)
{
return input * input;
};
double patrat = ma2(5);
Console.WriteLine("Patrat: {0}", patrat);
// Instantierea delegatului cu o expresie lambda
MathAction ma3 = s => s * s * s;
double cub = ma3(6.254);
Console.WriteLine("Cub: {0}", cub);
}
}
77
Contravariana permite unei metode s aib tipuri de parametri mai puin derivate dect
cele din tipul delegat. Acest exemplu demonstreaz modul n care delegaii pot fi utilizai cu
metode care au parametri de tipul de baz a tipului din semntura delegatului. Prin
contravarian putei utiliza un singur event handler n locul handlerelor separate. Putei
crea, de exemplu, un event handler care accept un parametru de intrare EventArgs, i s l
folosii cu un eveniment Button.MouseClick care trimite un parametru de tip MouseEventArgs i,
de asemenea, cu un eveniment TextBox.KeyDown care trimite un parametru KeyEventArgs.
// Event hander: primeste un parametru de tip EventArgs
private void MultiHandler(object sender, System.EventArgs e)
{
label1.Text = System.DateTime.Now.ToString();
}
public Form1()
{
InitializeComponent();
// Evenimentul asteapta un parametru KeyEventArgs ...
this.button1.KeyDown += this.MultiHandler;
// Evenimentul asteapta un parametru MouseEventArgs ...
this.button1.MouseClick += this.MultiHandler;
}
78
79
Putei vizualiza lista tuturor referinelor din proiectul curent prin deschiderea dosarului
References din Solution Explorer (Figura 4.14).
// Un bitmap
// Un control
// O componenta
Putei simplifica codul scris prin adugarea uneia sau a mai multor directive using la
nceputul fiierului surs curent; dac un spaiu de nume apare ca argument al directivei
using, putei omite ntregul spaiu de nume la declararea unei variabile, fcnd astfel codul
scris mai compact i mai lizibil:
80
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
//...
Bitmap bmp;
Control ctrl;
Component comp;
// identic cu System.Drawing.Bitmap
// identic cu System.Windows.Forms.Control
// identic cu System.ComponentModel.Component
Putei scrie mai puin i n cazul n care nu avei o directiv using care s corespund exact cu
spaiul de nume a elementului pe care vrei s l referii. De exemplu:
using System;
//...
Drawing.Bitmap bmp;
// identic cu System.Drawing.Bitmap
Directivele using trebuie s includ ntotdeauna spaiul de nume complet pe care dorii s l
importai. Nu v putei folosi de o directiv using anterioar pentru a scurta o alta:
// Codul nu va fi compilat!
using System;
using Drawing;
// Se doreste inlocuirea using System.Drawing
O alt restricie se refer la utilizarea a dou directive using care se refer la spaii de nume
care conin clase cu nume identic. Totui, i n acest caz pot fi utilizate directive using prin
specificarea unui alias pentru unul dintre spaiile de nume care a produs conflict:
// Codul urmator nu va putea fi compilat!
using Animals.Mammals;
using Computers.Accessories;
//...
Mouse m;
Majoritatea proiectelor C# se bazeaz pe cteva spaii de nume din .NET Framework, cum ar
fi spaiul de nume System, care conine toate tipurile elementare de date, sau
System.Collections.Generic, care conine clase i interfee care definesc colecii generice.
Importarea repetat a acestor spaii de nume n fiecare fiier surs din cadrul unui proiect
este un proces suprtor. Din fericire nu este nevoie s facei aa, deoarece n aplicaiile C#
putei defini un numr de referine globale la nivel de proiect.
81
Iniial denumirea spaiului de nume implicit este aceeai cu numele proiectului, ns, dac
dorii, putei s o modificai. Nu este indicat s scriei niciuna din clase n spaiul de nume
System, sau ntr-altul subordonat acestuia, din cauz c acest spaiu de nume este rezervat
pentru clasele din .NET Framework.
Putei s creai propriile voastre spaii de nume oriunde n cadrul fiierelor surs din proiect
cu ajutorul cuvntului cheie namespace. Spre exemplu, putei defini spaiul de nume Resurse ca
i container pentru clasa Persoana:
namespace Resurse
{
public class Persoana
{
//...
}
}
Dac o secven de cod refer un tip declarat n alt spaiu de nume, trebuie s includ
numele complet al spaiului de nume ce conine elementul referit, precum n exemplul de
mai jos:
// Utilizam clasa Persoana din cadrul altui spaiu de nume
Resurse.Persoana p = new Resurse.Persoana();
Desigur, putei utiliza cuvntul cheie using pentru simplificarea referirii claselor definite n
cadrul proiectului, ns nu uitai c toate fiierele surs dintr-un proiect C# sunt cuprinse n
spaiul de nume implicit, iar acest spaiu de nume trebuie s apar n declaraia using:
// Presupunem ca spatiul de nume radacina este Test
using Test.Resurse;
//...
Persoana p = new Persoana();
n cadrul unui spaiu de nume putei declara unul sau mai multe dintre urmtoarele tipuri:
clase, interfee, structuri, enumerri, delegai sau alte spaii de nume. Aceste categorii sunt
considerate ca fiind tipuri. Din aceast cauz nu putei include declaraii de variabile sau
metode direct n interiorul unui spaiu de nume, ci, de exemplu, n interiorul unei clase
inclus ntr-un spaiu de nume. Spre exemplu, urmtoarea secven de cod nu se va compila:
namespace MyNamespace
{
// Eroare la compilare!
void MyMethod()
{
}
}
Este interesant faptul c putei avea n cadrul proiectului mai multe blocuri de spaii de
nume avnd acelai nume, n acelai fiier sau n fiiere diferite. Aceast caracteristic v
permite pstrarea organizrii logice a entitilor din codul surs separat de structura fizic a
fiierelor. Spre exemplu, putei avea un fiier care s conin mai multe spaii de nume, sau
putei avea toate elementele dintr-un spaiu de nume mprtiate n diverse fiiere surs (de
fapt, toate fiierele surs din proiect aparin spaiului de nume implicit definit pe pagina
Application din fereastra de proprieti a proiectului).
Este important de tiut faptul c spaiul de nume este un concept ce ine de limbajul de
programare, ns nici spaiile de nume, nici directivele using nu exist la nivelul
limbajului intermediar (MSIL). Putei dovedi uor acest lucru prin dezasamblarea unui
proiect C# i verificarea faptului c toate instruciunile MSIL refer clase .NET
Framework folosind numele lor complet, chiar dac n cod ai utilizat directive using
pentru a face codul surs mai scurt.
82
Domeniul de vizibilitate pentru referirea claselor i a funciilor n alte spaii de nume poate fi
uor extins la spaiile de nume ncuibate. Spre exemplu, codul din clasa Caine poate referi
direct clasa Pisica, ns pentru accesarea clasei Soparla are nevoie s precizeze spaiul de
nume Reptile:
class Caine
{
Pisica mPisica = new Pisica();
Reptile.Soparla mSoparla = new Reptile.Soparla();
}
83
// Eroare la compilare!
Codul de mai sus duce la apariia unei erori de compilare din cauz c orice referire din
interiorul clasei Test la spaiul de nume System este de fapt o referire la spaiul de nume
definit de utilizator, iar nu spaiul de nume din .NET Framework (dup cum am mai amintit,
este de evitat utilizarea numelui System pentru spaiile de nume create de utilizatori).
Pentru gestionarea acestui gen de probleme C# introduce cuvntul cheie global, care foreaz referirea unui spaiu de nume pornind de la spaiul de nume rdcin, cum se poate
vedea i n exemplul de mai jos:
// Aceast secven de cod refer sigur clasa System.Console din .NET Framework
global::System.Console.WriteLine("Test");
Abilitatea de accesare a unui membru din spaiul de nume global este util cnd membrul ar
putea fi ascuns de o alt entitate cu acelai nume. De exemplu, n codul de mai jos, Console se
refer la TestApp.Console n loc de tipul Console din spaiul de nume System.
using System;
class TestApp
{
// Definirea unei noi clase 'System' pentru a provoca probleme
public class System { }
// Definirea unei constante 'Console' pentru a provoca probleme
const int Console = 7;
const int number = 66;
static void Main()
{
// Eroare! Accesarea constantei TestApp.Console, care nu are o metoda WriteLine
Console.WriteLine(number);
}
}
Utilizarea System.Console va provoca apariia unei erori, deoarece spaiul de nume System este
ascuns de clasa TestApp.System. Totui, putei depi aceast eroare prin utilizarea
global::System.Console, ca n codul de mai jos:
global::System.Console.WriteLine(number);
Atunci cnd identificatorul din stnga este global, cutarea dup identificatorul din dreapta
ncepe de la spaiul de nume global.
class TestClass : global::TestApp
Evident, nu este recomandat crearea propriului spaiu de nume System, i este puin
probabil c vei gsi cod n care s existe aa ceva. Totui, n cadrul proiectelor mari,
probabilitatea de duplicare a unor spaii de nume este mai mare, ns n aceste situaii
folosirea cuvntului cheie global v garanteaz accesarea spaiului de nume rdcin.
84
n general, se folosete :: pentru referirea unui alias, sau global:: pentru referirea spaiului
de nume global, iar . pentru accesarea tipurilor sau a membrilor.
Este o greeal utilizarea :: mpreun cu un alias care refer un tip n locul unui spaiu de
nume. De exemplu:
using Alias = System.Console;
class TestClass
{
static void Main()
{
// Eroare!
Alias::WriteLine("Hello!");
// OK
Alias.WriteLine("Hello!");
}
}
Reinei c cuvntul global nu este un alias predefinit. Astfel, global.X nu are nicio
semnificaie special, ci capt sens numai cnd este utilizat mpreun cu ::.
Compilatorul va semnala un avertisment dac definii un alias numit global, deoarece global::
se refer ntotdeauna la spaiul de nume global, nu la un alias. De exemplu, urmtoarea linie
de cod va genera avertisment:
using global = System.Collections;
// Warning
85
excepia, atunci utilizatorul este ntiinat de apariia erorii prin intermediul unei ferestre de
dialog (din nefericire, o excepie netratat duce la ncheierea prematur a execuiei programului).
Expresia aruncarea unei excepii este chiar potrivit deoarece un obiect excepie este de
fapt transmis napoi apelantului atunci cnd apare o excepie. Codul care prinde excepia
poate examina proprietile obiectului excepie i poate apela metodele acestuia, avnd
astfel la dispoziie mijloacele pentru luarea msurilor necesare: informarea utilizatorului
(descrierea erorii, locul de apariie al acesteia sau o legtur ctre un fiier help) sau
anularea discret a operaiei care a cauzat problema.
Figura 4.16. Ierarhia celor mai importante obiecte excepie din .NET Framework
Pentru o list complet de obiecte excepie, accesai comanda Exceptions din meniul
Debug. Vei observa o list impresionant cu obiecte de tip excepie (Figura 4.17).
86
Proprietatea TargetSite returneaz numele i semntura metodei din care a fost aruncat
excepia, exprimat n sintax C#:
Int32 DivideNumber(Int32 x, Int32 y)
Astfel de informaii (nume ale fiierelor surs, nume de funcii, numere ale liniilor de cod)
vor fi accesibile numai n cazul n care aplicaia a fost compilat n mod Debug (include
informaii pentru depanare).
Proprietatea HelpLink seteaz sau returneaz un URN (Uniform Resource Name) sau URL
(Uniform Resource Locator) ctre fiierul de help asociat obiectului excepie, sub forma:
file:///C:/Applications/Bazzal/help.html#ErrorNum42
87
Atunci cnd creai obiectul excepie, putei s specificai un mesaj mai clar prin transmiterea
unui argument constructorului obiectului excepie:
throw new FileNotFoundException("Initialization file not found");
O clas excepie poate expune mai muli constructori, fiecare dintre acetia permind iniializarea unui anume set de proprieti. De exemplu, constructorul clasei ArgumentException
poate primi ca argument numai mesajul, sau mesajul i numele argumentului care a
provocat excepia:
public void Afisare(string nume, int varsta)
{
if (string.IsNullOrEmpty(nume))
{
throw new ArgumentException("Sir gol sau nul");
}
else if (varsta < 0)
{
throw new ArgumentException("Nu sunt permise valori negative", "varsta");
}
Console.WriteLine("Nume: {0}, varsta: {1}", nume, varsta);
}
O alternativ posibil la utilizarea excepiilor este aceea de verificare n cadrul codului scris
a posibilelor cauze de eroare i afiarea pe ecran a unor mesaje (cu ajutorul metodei
MessageBox.Show), ns acesta este un mod de lucru mai puin profesional. Aruncarea unei
excepii n cadrul unei metode presupune faptul c secvena de cod care va apela metoda
respectiv va trata excepia n cauz, n caz contrar, execuia aplicaiei putnd fi ntrerupt
definitiv prin apariia unui mesaj de eroare.
88
De ndat ce apare o excepie programul sare n blocul catch, execut codul din cadrul
blocului, apoi sare la prima linie de dup blocul try...catch.
Desigur, putei avea mai multe blocuri catch, fiecare dintre acestea filtrnd un tip anume de
excepie:
try
{
x = x / y;
y = Convert.ToInt32(Math.Pow(10, x));
}
catch (DivideByZeroException ex)
{
// Tratarea exceptiilor de tip impartire cu zero
}
catch (OverflowException ex)
{
// Tratarea exceptiilor de tip depire (overflow)
}
catch (Exception ex)
{
// Tratarea celorlaltor tipuri de exceptii
}
C# compar tipul obiectului excepie aruncat cu cele din clauzele catch n ordinea n care
acestea apar i execut primul bloc care corespunde din punct de vedere al tipului excepiei.
Este o idee bun aceea de a avea un bloc final catch care s corespund obiectului
System.Exception, deoarece este sigur c acest cod va fi executat n cazul n care niciun alt bloc
catch anterior nu corespunde tipului excepiei aruncate. O clauz catch pentru obiectul
System.Exception corespunde ntotdeauna oricrui tip de excepie, deoarece toate obiectele
excepie deriv din System.Exception (clauza final catch este din punct de vedere conceptual
similar cu clauza default dintr-un bloc switch).
Deoarece toate blocurile catch sunt evaluate n ordinea n care apar, trebuie testate mai nti
excepiile mai particulare, apoi cele mai generale. Dac includei un test asupra obiectului
System.Exception, acesta trebuie plasat n ultimul bloc catch, deoarece acesta corespunde
oricrei excepii i, ca urmare, niciun alt bloc catch plasat dup acesta nu va fi executat
vreodat. Aceast regul poate fi generalizat, astfel c nu trebuie s prindei un obiect
excepie dup prinderea obiectului de tipul din care acesta este derivat.
Prezena declaraiei unui obiect excepie n blocul catch este opional, ns, n cazul n care o
omitei, nu putei obine detalii privitoare la eroarea tratat.
Motorul de execuie .NET definete cteva excepii care pot s apar doar n circumstane cu
adevrat excepionale (i catastrofale) cum ar fi: StackOverflowException, OutOfMemoryException
i ExecutionEngineException. Chiar dac aceste excepii nu sunt provocate de ctre aplicaia
dumneavoastr, putei totui s le prindei, dei, n asemenea cazuri, nu avei altceva de
fcut dect s ncheiai execuia aplicaiei printr-un mesaj de eroare corespunztor,
deoarece aceste erori las aplicaia ntr-o stare instabil.
89
n blocul try arunc sau nu o excepie. Blocul finally ruleaz chiar dac codul din blocul catch
arunc o excepie, sau chiar dac metoda este prsit ca urmare a apelului unei comenzi
return.
Iat un exemplu de cod care modific directorul curent i asigur faptul c directorul original
este restaurat nainte de prsirea blocului try:
string curdir = "";
try
{
// Memoreaza directorul curent, apoi il modifica
curdir = Environment.CurrentDirectory;
Environment.CurrentDirectory = "c:\\data";
}
catch (Exception ex)
{
// Tratarea eventualelor erori
}
finally
{
// In orice situatie reface directorul curent
Environment.CurrentDirectory = curdir;
}
Este perfect legal utilizarea unui bloc tryfinally fr un bloc catch. O astfel de secven de
cod poate fi potrivit atunci cnd dorii ca apelantul s prind i s proceseze toate erorile
aprute n metoda curent, dar n acelai timp avei o secven de cod de curare care
trebuie s fie executat indiferent de caz. Un bloc finally poate fi de asemenea util n cazul n
care dorii s furnizai o secven de cod de curare pentru o funcie care conine mai multe
instruciuni de ieire din funcie:
public static int TestFunction(int x, int y)
{
try
{
// ...
if (x > 0)
return 1;
// ...
if (y == 0)
return 2;
// ...
return 3;
}
finally
{
// Acest cod va rula indiferent de calea de iesire din functie
Console.Write("Finally");
}
}
Trebuie menionat faptul c n situaia n care codul din blocul finally arunc o excepie,
blocul este prsit imediat. n consecin, trebuie s v asigurai c nu va aprea nicio eroare
n blocul finally, iar pentru aceasta putei s utilizai un alt bloc try n interiorul blocului
finally.
90
este bine ca excepia s fie aruncat explicit, astfel nct s nu prei un programator lene
sau distrat:
try
{
// Efectuarea unor operatii aritmetice
}
catch (DivideByZeroException ex)
{
}
catch (OverflowException ex)
{
}
catch (Exception ex)
{
// Aruncarea explicita a exceptiei necunoscute catre apelant
throw;
}
Instruciunea throw fr argumente arunc mai departe obiectul excepie curent i trebuie s
apar ntr-o clauz catch pentru a fi considerat valid.
n unele cazuri, este posibil s dorii aruncarea mai departe a unui tip diferit de obiect
excepie, pentru cteva motive:
Putei traduce o excepie aprut ntr-una mai pe neles pentru codul apelant.
91
.NET Framework, ns altele (fiiere, conexiuni la baze de date, ferestre etc.) necesit adesea
o operaie explicit de eliberare a resurselor.
Acest ablon este frecvent utilizat, astfel c .NET Framework definete interfaa IDisposable
care ofer o singur metod: Dispose. Iat un exemplu de alocare i eliberare a unui obiect de
tip Brush:
SolidBrush br = new SolidBrush(Color.Red);
// ...
br.Dispose(); // Distrugerea obiectului
Unele tipuri de date (de regul cele care opereaz cu fiiere sau baze de date), dei implementeaz interfaa IDisposable, ofer metoda public Close care apeleaz metoda privat
Dispose.
Pentru prevenirea situaiilor de eroare i, n consecin, a prsirii codului nainte de
dealocarea resurselor, este bine s includei codul care opereaz cu aceste resurse ntr-un
bloc tryfinally:
public string ReadTextFile(string fileName)
{
StreamReader sr = null;
try
{
sr = new StreamReader(fileName);
return sr.ReadToEnd();
}
finally
{
sr.Close();
}
}
C# ofer programatorilor blocul using care poate elibera automat unul sau mai multe obiecte
IDisposable fr nicio aciune explicit din partea dumneavoastr. Iat aceeai secven de
cod transcris prin utilizarea blocului using:
public string ReadTextFile(string fileName)
{
using (StreamReader sr = new StreamReader(fileName))
{
return sr.ReadToEnd();
}
}
Instruciunea using apeleaz metoda Dispose a obiectului ntr-un mod corect i face ca, de
ndat ce este apelat metoda Dispose, acesta s nu mai fie accesibil. ntr-un bloc using,
obiectul este read-only i nu poate fi modificat sau reatribuit.
Instruciunea using asigur faptul c metoda Dispose este apelat chiar dac apare o excepie
la apelarea metodelor obiectului. Putei obine acelai rezultat prin plasarea obiectului ntrun bloc try, iar apelarea metodei Dispose ntr-unul finally; de fapt acesta este modul n care
instruciunea using este tradus de ctre compilator. Precum am mai amintit, orice excepie
va fi raportat codului apelant.
92
Toate proprietile unui obiect excepie trebuie s fie read-only, iar apelantul s le poat
iniializa numai prin intermediul constructorilor. Iat un exemplu de clas excepie
UnableToLoadIniFileException care are un mesaj de eroare implicit (poate fi suprascris) i care
permite codului apelant s iniializeze proprietatea InnerException:
public class UnableToLoadIniFileException : System.ApplicationException
{
private const string defaultMessage = "Unable to load initialization file";
// Constructorii cu mai putini parametri apeleaza constructorii cu mai multi parametri
public UnableToLoadIniFileException()
: this(defaultMessage, null)
{
}
public UnableToLoadIniFileException(string message)
: this(message, null)
{
}
public UnableToLoadIniFileException(Exception innerException)
: this(defaultMessage, innerException)
{
}
// Cel mai complet constructor apeleaza constructorul clasei de baza
public UnableToLoadIniFileException(string message, Exception innerException)
: base(message, innerException)
{
}
}
Putei folosi clasa UnableToLoadIniFileException ntr-o instruciune throw i ntr-un bloc catch:
// *** Codul apelant
public void TestCustomException()
{
try
{
LoadIniFile();
}
catch (UnableToLoadIniFileException ex)
{
Console.WriteLine(ex.Message); // "Unable to load initialization file"
}
catch (Exception ex)
{
// Tratarea altor erori
}
}
public void LoadIniFile()
{
try
{
// Incearca deschiderea fisierului ini.
}
catch (Exception ex)
{
// Indiferent cine provoaca eroarea, arunca o exceptie mai particulara
throw new UnableToLoadIniFileException(ex);
}
}
Obiectele excepie personalizate pot avea i alte ntrebuinri, altele dect simpla raportare a
unui mesaj de eroare. Ele pot include metode personalizate care ncearc s rezolve anumite
erori. De exemplu, putei crea clasa DriveNotReadyException avnd o metod ShowMessage, care
afieaz un mesaj de eroare i roag utilizatorul s introduc discul n unitate, apoi s
93
rencerce operaia care a provocat eroarea. Introducnd astfel de cod ntr-o clas excepie
facei mai uoar reutilizarea acesteia.
94
Declararea variabilelor cu cuvntul cheie WithEvents, fapt care permite programatorilor s selecteze mai uor obiectele construite astfel i evenimentele asociate din
editorul de cod.
Utilizarea structurilor With ... End With pentru accesarea rapid a membrilor unui
obiect.
Utilizarea modulelor
Cu ajutorul sintaxei de prsire a unei bucle, diferit pentru fiecare tip de bucl,
Visual Basic permite, n cazul buclelor de cod ncuibate, prsirea oricreia dintre
acestea (C# pune la dispoziie comanda break pentru prsirea oricrei bucle).
For i As Integer = 1 To 10
Dim j As Integer = 1
Do While j <= 10
' Paraseste bucla For
If Evaluare(i, j) = 0 Then Exit For
' ...
j += 1
Loop
Next
Comparativ cu C#, Visual Basic ofer multe funcii suplimentare: funcii de conversie
(CInt, CStr, CByte, CDbl, CBool, CByte, CDate, CLng, CCur, CObj), funcii de interaciune
(AppActivate, Beep, CallByName, Choose, Command, Environ, IIf, InputBox, MsgBox, Partition,
Shell, Switch), lucrul cu date calendaristice (DateAdd, DateDiff, DatePart, DateSerial,
DateValue, Year, Month, Day, Hour, Minute, Second, MonthName, Weekday, WeekdayName, TimeSerial,
i TimeValue) etc. Multe dintre aceste funcii pot fi utilizate n C# prin importarea
spaiului de nume Microsoft.VisualBasic.
95
Permite blocurilor de cod s ruleze cod nesigur prin intermediul cuvntului cheie
unsafe i asigurarea de suport pentru pointeri.
Clase i interfee pariale.
96
Controale
5. Controale
Un control reprezint un obiect care are o component grafic. Un control este situat pe un
formular (o fereastr) i interacioneaz cu utilizatorul, furniznd informaii pe care utilizatorul le poate utiliza. Exemple de controale sunt casetele text, etichetele, butoanele, listele
derulante, elementele de meniu, barele cu instrumente i, n general, tot ceea ce se poate
vedea i cu care se poate interaciona ntr-o aplicaie Windows.
O component este similar unui control, cu diferena c nu are nicio component vizual n
timpul rulrii. Atunci cnd adugai o component pe un formular (n modul design), aceasta
nu apare pe suprafaa formularului, ci n component tray, situat sub formular (Figura 5.1).
Putei s selectai componentele, apoi s utilizai panoul de proprieti pentru a le vizualiza
i modifica proprietile. n timpul rulrii componentele sunt invizibile utilizatorului, dei
acestea pot afia unele obiecte vizibile cum ar fi meniuri, ferestre dialog sau pictograme.
Figura 5.2 prezint toolbox-ul asociat cu Visual C#, care afieaz o gam standard de 67 de
controale i componente. Sgeata (arrow tool) din colul stnga sus nu reprezint practic un
control sau o component, ci instrumentul de selecie.
97
98
Controale
Aplicaiile cu interfee grafice sunt de obicei programate astfel nct sunt conduse de
evenimente. Sistemele de operare sunt un alt exemplu clasic de programe conduse de
evenimente pe cel puin dou nivele. La cel mai de jos nivel, handlerele de ntreruperi se
comport ca handlere de evenimente hardware, cu procesorul n rol de coordonator
(dispatcher). Sistemele de operare se comport, de asemenea, ca i coordonatori pentru
procese, transmind datele i ntreruperile soft ctre procese utilizator, care de multe ori
sunt programate ca i handlere de eveniment.
99
De obicei, un control etichet are doar rol de afiare a unui text, astfel c nu v vei folosi de
evenimentele acestuia. n schimb, alte controale, cum ar fi butoanele sau barele de derulare,
nu ne vor fi de mare folos dac programul nu poate rspunde la evenimentele lor. De
exemplu, dorii s creai pe un formular cte un buton pentru fiecare fiier dintr-un director,
iar la click pe un buton s se deschid fiierul asociat. Dac nu cunoatei dinainte numrul
de fiiere din director, nu vei ti de cte controale vei avea nevoie.
O soluie la aceast problem este utilizarea operatorului += care v permite adugarea unei
proceduri eveniment pentru un control sau component. Urmtoarea secven de cod
ilustreaz acest mod de lucru:
private void Form1_Load(object sender, EventArgs e)
{
Button btn = new Button();
btn.SetBounds(100, 100, 50, 32);
btn.Text = "Mesaj";
this.Controls.Add(btn);
btn.Click += btn_Click;
}
// handlerul de eveniment generat automat
void btn_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
Atunci cnd scriei cod pentru adugarea unei noi proceduri eveniment folosind operatorul
+=, mediul Visual Studio v propune, la apsarea tastei TAB, n prima etap asocierea
evenimentului n cauz cu o procedur eveniment (avnd numele compus din numele
controlului i al evenimentului: numeControl_numeEveniment), apoi crearea efectiv a procedurii eveniment respective (vezi Figura 5.4).
Figura 5.4. Oferirea de ajutor din partea Visual Studio pentru inserarea unei proceduri eveniment
Putei utiliza aceeai metod ca procedur eveniment pentru mai multe controale, de
exemplu butoane. n acest caz, codul poate converti parametrul sender ntr-un obiect buton,
putnd astfel s determinai prin proprietile Name sau Text butonul care a fost apsat.
Pentru a terge un control de pe un formular tergei-l din colecia de controale asociat
formularului. Pentru eliberarea resurselor asociate controlului n cauz, setai orice variabil
care se refer la acesta la valoarea null. Iat un exemplu:
this.Controls.Remove(btn);
btn = null;
100
Controale
n acest mod putei s tergei att controalele create dinamic, ct i pe cele create n modul
design. De asemenea, putei s tergei asocierea dintre evenimentul unui control i o
procedur eveniment, prin utilizarea operatorului -=, precum putei vedea n exemplul de
mai jos:
btn.Click -= btn_Click;
Figura 5.5. Fereastra de proprieti v permite modificarea proprietilor unui control n modul design.
Proprieti compuse
Doar cteva proprieti au valori compuse. Proprietatea Location include coordonatele X i Y
ale colului stnga-sus al controlului. Proprietatea Size conine limea i nlimea controlului. Proprietatea Font include numele fontului, dimensiunea acestuia i alte caracteristici
specifice fonturilor (bold, italic etc.).
Fereastra de proprieti afieaz aceste proprieti cu un semn plus (+) n stnga acestora,
prin intermediul cruia putei desfura proprietile respective. Figura 5.6 afieaz aceeai
fereastr de proprieti ca cea din Figura 5.5, ns cu proprietile Font i Location
desfurate.
101
Putei modifica toate caracteristicile unei proprieti compuse prin intermediul unei ferestre
dialog afiate la click pe butonul situat n partea dreapt a proprietii compuse. De exemplu,
pentru proprietatea Font va fi afiat fereastra dialog din Figura 5.7.
Figura 5.6. Fereastra de proprieti v permite modificarea proprietilor complexe n modul design.
Figura 5.7. Fereastra dialog care v permite modificarea caracteristicilor unui font.
Proprieti restrictive
Unele proprieti permit valori restrictive. De exemplu, proprietatea Visible poate primi
numai valorile true sau false. Dac dai click pe o asemenea proprietate, v va fi afiat o list
derulant din care putei alege una dintre valorile permise pentru acea proprietate.
102
Controale
Alte proprieti restrictive au asociate valori de tip enumerare. Proprietatea FlatStyle a unui
buton permite selectarea valorilor Flat, Popup, Standard i System. Putei da dublu click pe
valoarea unei proprieti restrictive pentru ciclarea valorilor posibile pe care le poate lua
acea proprietate.
Unele proprieti pot conine referine la alte controale. De exemplu, proprietatea ImageList
reprezint o referin la o component ImageList care conine imaginile pe care controlul le
poate afia.
n fine, alte proprieti restrictive pot afia diferite editoare care permit introducerea n mod
grafic a valorilor unei proprieti. Un astfel de exemplu este proprietatea Anchor, care
permite ancorarea marginilor unui control la marginile containerului, astfel nct controlul
i pstreaz poziia relativ n cazul redimensionrii containerului.
Proprieti colecie
Unele proprieti reprezint colecii de obiecte. De exemplu, un control de tip ListBox
afieaz o list de elemente text. Proprietatea Items a acestuia reprezint o colecie ce
conine acele elemente. Iniial, fereastra de proprieti afieaz valoarea acestei proprieti
ca i (Collection). La apsarea butonului din dreapta proprietii de tip colecie va aprea o
fereastr dialog n care putei edita elementele coleciei.
Alte proprieti pot conine o colecie de obiecte care, la rndul lor, s conin o colecie de
obiecte. De exemplu, controlul ListView are proprietatea Items care reprezint o colecie de
elemente de tip ListViewItem. Fiecare element din colecie reprezint un obiect care are o
proprietate SubItems, care la rndul ei este o colecie (Figura 5.8). La afiarea controlului n
mod detaliat, un obiect din colecia Items reprezint un rnd din afiarea tabelar, iar
proprietatea SubItems reprezint valorile secundare de pe acel rnd.
Figura 5.8. Obiectele din colecia Items a controlului ListView au fiecare cte o proprietate SubItems, care
reprezint de asemenea o colecie.
103
O proprietate poate fi vzut ca orice membru public al obiectului de tip control, putnd s o
accesai prin numele controlului urmat de caracterul punct i de numele proprietii. Dac o
proprietate conine o referin la un obiect, putei utiliza proprietile acelui obiect folosind
aceeai sintax:
if (textBox1.Font.Bold)
MessageBox.Show("Bold");
else
MessageBox.Show("Not bold");
Dac o proprietate reprezint o colecie sau un vector, putei s parcurgei ntr-o bucl
valorile din colecie la fel cum parcurgei o colecie sau un vector:
foreach (Object selected_item in lstChoices.SelectedItems())
Debug.WriteLine(selected_item.ToString());
Reinei faptul c unele proprieti ale controalelor sunt read-only, astfel c nu vei putea s
le modificai. Un astfel de exemplu este proprietatea Bottom care este calculat automat n
funcie de alte dou proprieti:
control.Bottom = control.Top + control.Height;
Descriere
Determin dac controlul permite operaii drag-and-drop.
Determin care dintre marginile controlului sunt ancorate de marginile
controlului container.
AutoSize
BackColor
BackgroundImage
BackgroundImageLayout
Bottom
Bounds
CanFocus
CanSelect
Capture
CausesValidation
ClientRectangle
ClientSize
ContainsFocus
104
ContextMenu
ContextMenuStrip
Controls
Cursor
Controale
DataBindings
DefaultBackColor
DefaultFont
DefaultForeColor
DisplayRectangle
Dock
Enabled
Focused
Font
ForeColor
Handle
HasChildren
Height
InvokeRequired
Left
Location
Margin
MaximumSize
MinimumSize
ModifierKeys
MouseButtons
MousePosition
Returneaz o structur Point care returneaz poziia curent a mouseului (punctul (0, 0) este situat n colul stnga-sus al ecranului). Este
read-only.
Name
Padding
Parent
PreferredSize
Region
Right
Size
TabIndex
TabStop
Tag
Text
Top
105
TopLevelControl
Visible
Width
5.2.3. Metode
O metod execut cod asociat cu un control. Metodele pot primi parametri la fel ca orice alt
funcie. Deoarece metodele execut cod, nu le putei apela n modul design, ci putei s le
apelai numai prin cod, n timpul rulrii.
Descriere
Aduce controlul n faa celorlalte controale (pe axa Z).
Returneaz true dac controlul ctl este coninut de acest
control.
Graphics CreateGraphics()
DragDropEffects DoDragDrop
(Object data, DragDropEffects
allowedEffects)
void DrawToBitmap(Bitmap bitmap,
Rectangle targetBounds)
Form FindForm ()
bool Focus ()
Control GetChildAtPoint (Point pt)
void Hide ()
void Invalidate ()
Object Invoke (Delegate method)
Point PointToClient (Point p)
106
Controale
Rectangle RectangleToClient
(Rectangle r)
Rectangle RectangleToScreen
(Rectangle r)
void Refresh ()
void Update()
5.2.4. Evenimente
Un control sau un alt obiect genereaz un eveniment pentru ntiinarea programului despre
apariia unor modificri n anumite circumstane. Adesea aciunea de generare a unui
eveniment se mai numete aruncarea unui eveniment. Clasele specifice controalelor
furnizeaz evenimente relevante pentru scopul pentru care au fost create. De exemplu,
controlul Button furnizeaz un eveniment Click pentru ntiinarea programului despre
apsarea unui buton de ctre utilizator.
Programul rspunde la un eveniment prin crearea unui handler de eveniment care prinde
evenimentul i ntreprinde aciunile potrivite acestuia. Fiecare eveniment i definete propriul format al handlerului i determin parametrii pe care handlerul de eveniment i va
primi. Adesea, aceti parametri ofer informaii suplimentare legate de eveniment.
De exemplu, atunci cnd o parte a formularului este acoperit sau expus, formularul
genereaz evenimentul Paint. Handler-ul evenimentului Paint primete ca parametru un
obiect de tip PaintEventArgs. Proprietatea Graphics a acestui parametru reprezint o
referin la un obiect de tip Graphics pe care programul l poate folosi pentru redesenarea
coninutului formularului.
Unele handlere de eveniment primesc parametri care sunt utilizai pentru trimiterea de
informaii legate de eveniment napoi la obiectul care le-a generat. De exemplu, handlerul
evenimentului FormClosing al unei clase formular are un parametru de tip
FormClosingEventArgs. Acesta reprezint un obiect care are o proprietate Cancel. Dac
aceasta este setat pe true, formularul anuleaz evenimentul FormClosing i formularul
rmne deschis n continuare. De exemplu, handlerul de eveniment poate verifica dac
datele introduse de utilizator sunt formatate corect, n caz contrar programul afind un
mesaj de eroare, iar formularul rmnnd deschis.
107
Majoritatea evenimentelor asociate unui control sunt specifice acelui tip de control.
Controalele motenesc un set de evenimente de la clasa de baz Control, ns le pot
suprancrca, modificndu-le astfel comportamentul.
108
Controale
Descriere
Apare atunci cnd se modific proprietatea AutoSize a
controlului.
Apare atunci cnd se modific proprietatea BackColor a
controlului.
Apare atunci cnd se modific proprietatea BackgroundImage a
controlului.
BackgroundImageLayoutChanged
Click
ContextMenuChanged
ContextMenuStripChanged
ControlAdded
ControlRemoved
CursorChanged
DockChanged
DoubleClick
DragDrop
DragEnter
109
DragLeave
DragOver
EnabledChanged
Enter
FontChanged
ForeColorChanged
GiveFeedback
GotFocus
HelpRequested
Invalidated
KeyDown
KeyPress
KeyUp
Layout
Leave
LocationChanged
LostFocus
MarginChanged
MouseCaptureChanged
MouseClick
MouseDoubleClick
110
MouseDown
MouseEnter
MouseHover
MouseLeave
MouseMove
MouseUp
Controale
MouseWheel
Move
PaddingChanged
Paint
ParentChanged
QueryContinueDrag
RegionChanged
Resize
SizeChanged
SystemColorsChanged
TabIndexChanged
TabStopChanged
TextChanged
Validated
Validating
VisibleChanged
Secvene de evenimente
n unele situaii sunt generate o serie de evenimente care apar ntr-o anumit ordine.
Evenimente de mouse
Atunci cnd dai click pe un control, apar urmtoarele evenimente n ordinea indicat. Prima
instan a evenimentului MouseMove poate aprea de oricte ori micai mouse-ul ct timp
butonul de mouse este apsat. Evenimentul final MouseMove apare indiferent dac micai
mouse-ul sau nu.
MouseDown
[MouseMove]
Click
MouseClick
MouseUp
111
MouseCaptureChanged
MouseMove
Atunci cnd dai dublu-click pe un control, apar urmtoarele evenimente n ordinea indicat:
MouseDown
[MouseMove]
Click
MouseClick
MouseUp
MouseCaptureChanged
MouseMove
MouseDown
DoubleClick
MouseDoubleClick
MouseUp
MouseCaptureChanged
MouseMove
Evenimente de redimensionare
Atunci cnd redimensionai un control, apar urmtoarele evenimente n ordinea indicat.
Aceste evenimente sunt repetate att timp ct redimensionai controlul.
Layout
Resize
SizeChanged
Evenimente de mutare
Atunci cnd mutai un control, apar urmtoarele evenimente n ordinea indicat. Aceste
evenimente sunt repetate att timp ct mutai controlul.
Move
LocationChanged
112
Controale
DateTimePicker
DirectoryEntry
DirectorySearcher
DomainUpDown
ErrorProvider
EventLog
FileSystemWatcher
FlowLayoutPanel
FolderBrowserDialog
FontDialog
GroupBox
HelpProvider
HScrollBar
ImageList
Label
LinkLabel
ListBox
ListView
Descriere
Acesta este instrumentul de selecie, nu un control.
Execut un task n mod asincron i notific programul principal cnd acesta sa terminat.
Ofer o interfa utilizator pentru navigarea printr-o surs de date (butoane
pentru parcurgerea nregistrrilor, butoane de adugare, tergere .a.m.d.).
ncapsuleaz sursa de date a unui formular i pune la dispoziie metode
pentru navigarea prin date.
Un buton simplu. Cnd un utilizator d click pe el, programul poate efectua
unele aciuni.
O caset pe care utilizatorul o poate bifa sau debifa.
O list de elemente cu casete de bifare pe care utilizatorul le poate bifa sau
debifa.
Permite utilizatorului selectarea unei culori.
O caset text cu o list sau list derulant ataat pe care utilizatorul o poate
folosi pentru introducerea sau selectarea unei valori text.
Un meniu care apare atunci cnd utilizatorul d click dreapta pe un control.
Un control tabelar care permite afiarea unui volum mare de date complexe.
Stocheaz n memorie date avnd proprieti similare unei baze de date
relaionale. Conine obiecte care reprezint tabele care conin nregistrri
(rows) i cmpuri (columns), i poate reprezenta multe concepte ale bazelor
de date, cum ar fi indeci i relaii de tip foreign key.
Permite utilizatorului selectarea n mai multe moduri a unei date
calendaristice i a orei.
Reprezint un nod ntr-o ierarhie de tip Active Directory.
Efectueaz cutri ntr-o ierarhie de tip Active Directory.
Permite utilizatorului derularea unei liste de opiuni prin click pe butoanele
sgeat sus i sgeat jos.
Permite afiarea unui indicator de eroare n dreptul unui control asociat n
cazul apariiei unei erori (de exemplu, validarea unor date).
Ofer acces la jurnalul de evenimente Windows.
Notific aplicaia despre modificri asupra unui director sau fiier.
Afieaz controalele pe care le conine pe rnduri i coloane.
Afiarea unui dialog care permite utilizatorului selectarea unui director.
Afiarea unui dialog care permite utilizatorului s specifice caracteristicile unui
font.
Grupeaz, pentru mai mult claritate, controale aflate n legtur.
Afieaz help pentru controalele care au setat aa ceva, dac utilizatorul
selecteaz controlul i apas F1.
Afieaz o bar de derulare orizontal.
Conine o serie de imagini pe care alte controale le pot utiliza. De exemplu,
imaginile pe care le afieaz un TabControl pe tab-urile proprii sunt stocate
ntr-un control ImageList asociat. Putei extrage i prin cod imagini dintr-un
astfel de control.
Afieaz text pe care utilizatorul nu l poate modifica sau selecta prin
intermediul interfeei grafice n timpul rulrii.
Afieaz o etichet care poate conine hyperlink-uri. Atunci cnd utilizatorul
d click pe un hyperlink, programul poate efectual unele aciuni.
Afieaz o list de elemente text pe care utilizatorul le poate selecta. n
funcie de setrile controlului, utilizatorul poate selecta unul sau mai multe
elemente.
Afieaz o list de elemente ntr-unul din cele patru moduri posibile de
113
MaskedTextBox
MenuStrip
MessageQueue
MonthCalendar
NotifyIcon
NumericUpDown
OpenFileDialog
PageSetupDialog
Panel
PerformanceCounter
PictureBox
PrintDialog
PrintDocument
PrintPreviewControl
PrintPreviewDialog
Process
ProgressBar
PropertyGrid
RadioButton
RichTextBox
SaveFileDialog
SerialPort
ServiceController
SplitContainer
StatusStrip
TabControl
TableLayoutPanel
114
Controale
TextBox
Timer
ToolStrip
ToolStripContainer
ToolTip
TrackBar
TreeView
VScrollBar
WebBrowser
tabelar.
Afieaz text pe care utilizatorul l poate modifica n timpul rulrii.
Implementeaz un temporizator care declaneaz un eveniment la intervale
de timp definite de utilizator.
Ofer un container pentru obiecte Windows toolbar (controale Button, Label,
ComboBox, TextBox, ProgressBar etc.).
Ofer panouri pe fiecare parte a ferestrei (acestea pot conine controale de
tip ToolStrip, MenuStrip i StatusStrip) i un panou central (acesta poate
conine orice controale).
Afieaz un text ajuttor dac utilizatorul poziioneaz cursorul de mouse
deasupra controlului asociat.
Permite utilizatorului s deplaseze un cursor de-a lungul unei bare pentru a
selecta o valoare numeric.
Afieaz date ierarhice ntr-o form grafic, arborescent.
Afieaz o bar de derulare vertical.
Permite navigarea prin pagini web din cadrul ferestrei. Controlul afieaz
rezultatele exact ca browserul Internet Explorer. O posibil utilizare a acestui
control este afiarea de help n format web.
Chiar dac avei la ndemn attea unelte puternice, nu ntotdeauna este uor s alegei cel
mai potrivit control pentru o anumit situaie. Pentru simplificarea codului de tratare a
erorilor, ar trebui s alegei cel mai restrictiv control care poate s ndeplineasc sarcina
dorit, deoarece cu ct controalele sunt mai restrictive, cu att utilizatorul are mai puine
opiuni de introducere a datelor invalide.
115
6. Formulare
Clasa Form din Visual C# este derivat indirect din clasa Control. Lanul complet de
moteniri este urmtorul: Control ScrollableControl ContainerControl Form. Un
formular motenete proprietile, metodele i evenimentele definite de clasa Control, cu
excepia cazului n care acestea sunt suprancrcate. Din multe puncte de vedere, un
formular este doar un alt tip de control.
6.1. Transparen
Obiectul Form ofer, suplimentar fa de obiectul Control, o serie de proprieti pe care le
putei folosi pentru a face o fereastr total sau parial transparent. Proprietatea Opacity
determin opacitatea unei ferestre. n modul design, fereastra de proprieti arat valoarea
acestei proprieti sub form de procentaj, unde 100% semnific faptul c formularul este
complet opac, iar 0% c fereastra este complet transparent. n timpul rulrii trebuie s
tratai valoarea proprietii Opacity ca numr n virgul mobil cu valori ntre 0.0 (complet
transparent) i 1.0 (complet opac).
O aplicaie poate utiliza pentru Opacity valori mai mici de 100% pentru a permite
utilizatorului s vad ce se ntmpl n spatele ferestrei. De exemplu, putei construi o
fereastr parial transparent pentru implementarea unui dialog de cutare, astfel nct
utilizatorul s poat vedea documentul pe msur ce cutarea avanseaz.
Figura 6.1 prezint un formular cu opacitatea 66%. Putei vedea nc marginile ferestrei,
bara de titlu, meniurile i butoanele, ns putei observa de asemenea fereastra de dedesubt.
Figura 6.1. O fereastr cu opacitate 66% permite vizualizarea ferestrei situate dedesubt.
116
Controale
Dac opacitatea este mai mare de 0%, fereastra se comport normal, cu excepia aspectului
imaterial al acesteia, utilizatorii putnd interaciona cu aceasta ca i cu orice alt fereastr.
Dac opacitatea este 0%, fereastra este complet transparent, iar utilizatorul poate interaciona cu ea numai prin intermediul tastaturii (schimbarea focalizrii controalelor cu tasta
TAB, acionarea comenzilor prin combinaii de taste). n orice caz, fereastra nu va detecta
click-urile de mouse.
Dac opacitatea este 1%, fereastra rmne invizibil n continuare, ns recunoate clickurile de mouse.
O a doua proprietate care poate determina transparena unei ferestre este TransparencyKey.
Aceast proprietate reprezint o culoare care indic mediului care parte a ferestrei trebuie
s fie complet transparent. La afiarea ferestrei, suprafeele care au ca fundal aceast
culoare nu vor fi desenate.
Figura 6.2 prezint o fereastr cu proprietatea TransparencyKey setat la culoarea de fundal a
ferestrei. Att fereastra ct i eticheta cu textul Spaiu gol au acelai fundal, astfel c vor fi
transparente la afiare. Evenimentul Paint al ferestrei deseneaz o elips n interiorul
ferestrei.
Figura 6.2. Proprietatea TransparencyKey a unei ferestre permite crearea unor zone complet transparente.
117
Figura 6.3 prezint o fereastr de forma unui smiley. Handler-ul evenimentului Paint al
ferestrei deseneaz imaginea dintr-un fiier bitmap. n acest fel putei construi de exemplu
splash screen-uri1 sau alte ferestre dialog interesante.
Figura 6.3. Proprietatea TransparencyKey permite crearea unor ferestre de diverse forme, precum aceasta.
Aceast fereastr nu prezint nici margini, nici bar de titlu i nici butoane sistem, astfel c
utilizatorul nu o poate muta, redimensiona, maximiza sau nchide (se seteaz proprietatea
FormBorderStyle a formularului la None). Pentru utilizarea acestei ferestre ca splash screen,
adugai un control Timer pentru a face ca fereastra s dispar dup cteva secunde. Pentru
utilizarea ferestrei ca o fereastr obinuit, adugai pe suprafaa ei un buton de nchidere.
Dac utilizai mpreun proprietile Opacity i TransparencyKey, pixelii care corespund cu
TransparencyKey nu sunt afiai, iar ceilali pixeli sunt afiai corespunztor cu valoarea setat
de proprietatea Opacity.
O fereastr de tip splash screen este afiat n timp ce o aplicaie iniializeaz sau ncarc datele necesare,
pregtindu-se de lucru. Aplicaia va nchide fereastra splash screen dup ce iniializarea este complet, sau dup
trecerea unui numr de secunde.
118
Controale
Aplicaiile MDI ofer de obicei unelte pentru gestionarea ferestrelor copil pe care acestea le
conin. Aceste unelte permit utilizatorului minimizarea ferestrelor copil, aranjarea acestora
n cadrul ferestrei printe, .a.m.d. Visual Studio este o aplicaie MDI deoarece poate afia n
cadrul ferestrei principale mai multe ferestre copil (designere de formulare, editoare de cod,
editoare de bitmap, .a.m.d.).
Figura 6.5. O aplicaie MDI afieaz documentele n ferestre copil coninute de fereastra printe
119
fereastr copil astfel nct acesta s depeasc marginile ferestrei printe, fereastra printe
va afia n mod automat bare de derulare pentru a putea vizualiza fereastra copil.
Programul va afia o pictogram n taskbar i Task Manager pentru fereastra printe, ns nu
i pentru ferestrele copil. Dac minimizai containerul MDI, toate ferestrele pe care le
conine vor fi ascunse mpreun cu acesta. Dac minimizai ns o fereastr copil, pictograma
acesteia va fi afiat n interiorul containerului, iar nu separat n taskbar. n cazul n care
maximizai o fereastr copil MDI, aceasta va umple suprafaa ferestrei printe.
Un container MDI ofer de asemenea unele metode pentru aranjarea ferestrelor copil. Codul
de mai jos ilustreaz modul n care, din cadrul containerului MDI, pot fi aranjate ferestrele
copil n diverse configuraii:
private void CascadeToolStripMenuItem_Click(object sender, EventArgs e)
{
LayoutMdi(MdiLayout.Cascade);
}
private void TileVerticalToolStripMenuItem_Click(object sender, EventArgs e)
{
LayoutMdi(MdiLayout.TileVertical);
}
private void TileHorizontalToolStripMenuItem_Click(object sender, EventArgs e)
{
LayoutMdi(MdiLayout.TileHorizontal);
}
private void ArrangeIconsToolStripMenuItem_Click(object sender, EventArgs e)
{
LayoutMdi(MdiLayout.ArrangeIcons);
}
Alte comenzi utile pe care le putei aduga unei aplicaii MDI sunt: Minimize All, Restore All,
Maximize All i Close All. Putei implementa aceste comenzi prin parcurgerea coleciei
MdiChildren asociate containerului MDI, precum se poate observa n continuare:
private void CloseAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Form childForm in MdiChildren)
{
childForm.Close();
}
}
private void minimizeAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Form childForm in MdiChildren)
{
childForm.WindowState = FormWindowState.Minimized;
}
}
private void maximizeAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Form childForm in MdiChildren)
{
childForm.WindowState = FormWindowState.Maximized;
}
}
private void restoreAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Form childForm in MdiChildren)
{
childForm.WindowState = FormWindowState.Normal;
}
}
120
Controale
Multe aplicaii MDI includ un meniu Windows care afieaz o list a ferestrelor copil
deschise. Apsarea uneia dintre aceste comenzi de meniu permite mutarea ferestrei copil
deasupra tuturor celorlalte ferestre coninute de containerul MDI.
Construirea unei liste de ferestre copil este o sarcin uoar n Visual C#. Selectai controlul
principal MenuStrip, iar n fereastra de proprieti a acestuia setai proprietatea
MdiWindowListItem la meniul care dorii s conin lista de ferestre copil. Dup fiecare
operaie de deschidere sau nchiderea unei ferestre copil, Visual C# va actualiza automat
aceast list.
Figura 6.6 arat un meniu care afieaz o list de ferestre copil MDI. Fereastra cu titlul
Program.cs are focalizarea, astfel c lista afieaz o bif n dreptul comenzii de meniu
asociate ferestrei.
Figura 6.6. Proprietatea MdiWindowListItem asociat controlului MenuStrip determin care element de meniu
va afia lista cu ferestrele copil MDI.
Majoritatea aplicaiilor Visual C# utilizeaz SDI, iar la crearea unei noi aplicaii, interfaa
acesteia va fi implicit SDI. Pentru a avea o aplicaie MDI, creai nti o aplicaie nou, apoi
setai proprietatea IsMdiContainer a formularului de start pe true. n Form Designer, acest
formular i va schimba nfiarea, astfel nct va fi evident c este un formular printe MDI.
Ca metod alternativ putei selecta din meniul Project comanda Add Windows Form. n
fereastra dialog ce se va deschide selectai MDI Parent Form i dai ferestrei un nume
rezonabil. Visual C# va aduga un nou formular printe MDI avnd un control meniu care va
include meniuri standard (File, Edit, View, .a.m.d.) i o bar de unelte cu unelte standard
(New, Open, Save, .a.m.d.).
n faza de design, un formular copil MDI are aceeai nfiare cu a unui formular standard.
Pentru a face ca formularul copil s fie afiat n interiorul unui container MDI, trebuie s i
setai acestuia n timpul rulrii proprietatea MdiParent la containerul MDI.
Putei vedea mai jos secvenele de cod adugate automat la comanda de deschidere a unei
noi ferestre copil din fereastra printe MDI. Ferestrele copil nou create vor fi automat afiate
n interiorul containerului MDI i vor fi adugate n lista de ferestre copil deschise.
private void OpenFile(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = Application.StartupPath;
openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
121
if (openFileDialog.ShowDialog(this) == DialogResult.OK)
{
string FileName = openFileDialog.FileName;
// Aici adaugarea codului pentru afisarea continutului fisierului
// ...
}
}
122
Controale
Dac utilizatorul apas butonul Cancel sau nchide formularul din meniul sistem, atunci
formularul seteaz automat proprietatea DialogResult la Cancel i se nchide. Dac
utilizatorul apas alt buton, handlerul de eveniment va seta DialogResult la o valoare
corespunztoare. Setarea acestei valori va duce la nchiderea automat a formularului.
123
124
Ori de cte ori formularul este ascuns (chiar i parial) apoi expus iari, minimizat apoi
restaurat, maximizat sau redimensionat handler-ul evenimentului Paint se execut i
redeseneaz cercul (Figura 7.1).
Figura 7.1. Acest program deseneaz unui cerc umplut cu culoare portocalie, avnd conturul de culoare
albastr i 10 pixeli grosime.
System.Drawing
Spaiul de nume System.Drawing conine clasele de baz GDI+ i cele mai importante dintre
acestea. Din aceste clase fac parte Graphics, Pen, Brush, Font, FontFamily, Bitmap, Icon i Image.
Tabelul urmtor descrie cele mai utile clase din System.Drawing.
Clas
Graphics
Descriere
Fr ndoial, acesta este cel mai important obiect utilizat la crearea de grafic. Un
obiect Graphics reprezint suprafaa pe care urmeaz s desenai. Aceasta poate fi un
PictureBox, formular, bitmap n memorie, sau altceva. Obiectul Graphics ofer metodele
pentru desenare de linii dreptunghiuri, elipse .a.m.d.
Pen
Aceast clas reprezint caracteristicile de desenare ale unei linii, incluznd culoarea
liniei, grosimea, stilul liniei .a.m.d.
125
Pens
Brush
Aceast clas reprezint modul n care sunt umplute suprafeele nchise. Ea determin
dac suprafaa este umplut cu culoare uniform, haur, ablon grafic etc.
Brushes
Aceast clas pune la dispoziie un mare numr de pensule simple (solid brushes) de
diferite culori. De exemplu, putei utiliza Brushes.Green pentru umplerea unei suprafee
cu culoare verde.
Aceast clas reprezint o pensul simpl, pe care o vei utiliza atunci cnd avei nevoie
s umplei o suprafa cu o culoare uniform.
SolidBrush
Bitmap
Icon
Metafile
Image
Font
FontFamily
Region
Aceast clas definete o form creat din dreptunghiuri i ci (paths), pe care o putei
utiliza n diverse scopuri.
Tabelul 7.2. Clase utile din spaiul de nume System.Drawing.
Spaiul de nume System.Drawing definete de asemenea unele structuri care pot fi utilizate n
vederea desenrii. Tabelul urmtor le descrie pe cele mai utile dintre acestea.
Structur
Color
Point
Size
Rectangle
Descriere
Acest obiect definete o culoare prin specificarea culorilor de baz (rou, verde,
albastru) ca valori cuprinse ntre 0 i 255, plus o valoare alpha care indic transparena
culorii. O valoare 0 pentru alpha semnific un obiect complet transparent, iar o valoare
de 255 semnific unul complet opac.
Acest obiect definete coordonatele X i Y ale unui punct.
Acest obiect definete nlimea i limea.
Acest obiect definete un dreptunghi utiliznd Point i Size.
Tabelul 7.3. Structuri din spaiul de nume System.Drawing.
Procedurile GDI+ care deseneaz pe ecranul monitorului, imprimant sau alte obiecte pe
care se poate desena, utilizeaz structurile Point, Size i Rectangle. Acestea stocheaz
coordonatele integrale i dimensiunile figurilor desenate. Spaiul de nume System.Drawing
definete de asemenea clasele PointF, SizeF i RectangleF pentru lucrul cu valori n virgul
mobil.
Clasa Color ofer un mare numr de culori predefinite. De exemplu, Color.PaleGreen
definete o culoare verde deschis. Putei s utilizai aceste culori predefinite n loc s creai
un nou obiect de tip culoare.
System.Drawing.Drawing2D
Spaiul de nume System.Drawing.Drawing2D conine majoritatea celorlalte obiecte de care
avei nevoie pentru elemente mai avansate de grafic 2D. Unele dintre aceste clase rafineaz
126
clasele de baz pentru desenare sau definesc valori pentru aceste clase. De exemplu, clasa
HatchBrush reprezint un tip specializat de pensul care umple o suprafa cu o haur.
Tabelul ce urmeaz descrie cteva dintre cele mai utile clase din acest spaiu de nume.
Clas
HatchBrush
Descriere
Aceast clas definete o pensul care umple o suprafa cu o haur. Ea
definete tipul haurii, culoarea acesteia, i o culoare de fundal.
LinearGradientBrush
System.Drawing.Text
Spaiul de nume System.Drawing.Text conine numai trei clase. Aceste trei clase ofer o
metod de a afla care sunt fonturile instalate pe sistem sau cele instalate pentru o aplicaie.
Tabelul urmtor descrie cele trei clase.
Clas
FontCollection
Descriere
Este clas de baz pentru clasele derivate InstalledFontCollection i
PrivateFontCollection i ofer o metod care returneaz un ir de obiecte
FontFamily.
InstalledFontCollection
PrivateFontCollection
n Figura 7.6 putei observa rezultatul obinut n urma rulrii codului de mai sus.
127
Figura 7.6. Un obiect InstalledFontCollection permite accesarea unui ir de obiecte FontFamily care reprezint
fonturile instalate pe sistem.
Figura 7.7. Anti-aliasing (deasupra) are ca efect afiarea mai neted a caracterelor.
Graphics
De fiecare dat cnd desenai n Visual C# .NET, avei nevoie de un obiect Graphics. Un obiect
Graphics reprezint o suprafa de desenare, indiferent c este un formular, un control
PictureBox, Bitmap n memorie, un metafile, sau imprimant.
Clasa Graphics ofer multe metode pentru desenarea de figuri i umplerea de suprafee. Ea
include de asemenea proprieti i metode care modific rezultatele operaiilor grafice. De
exemplu, metodele de transformare v permit operaii de scalare, translatare, i rotire a
elementelor grafice.
128
Seciunile ce urmeaz descriu proprieti i metode ale clasei Graphics pentru desenare,
umplere, respectiv de modificare a rezultatelor acestora.
Metode de desenare
Obiectul Graphics ofer o multitudine de metode pentru desenare de linii, dreptunghiuri,
curbe i alte figuri. Tabelul de mai jos descrie o parte dintre aceste metode.
Metod
DrawArc
DrawBezier
DrawBeziers
DrawClosedCurve
Descriere
Deseneaz un arc de elips.
Deseneaz o curb Bzier.
Deseneaz o serie de curbe Bzier.
Deseneaz o curb nchis care unete o serie de puncte, unind punctul final
cu cel iniial.
DrawCurve
DrawEllipse
DrawIcon
DrawImage
DrawLine
DrawLines
Deseneaz o linie.
Deseneaz o serie de linii conectate. Dac avei nevoie s desenai o serie de
lini conectate, aceast metod este mult mai rapid dect utilizarea repetat a
metodei DrawLine.
Deseneaz un obiect de tip GraphicsPath.
Deseneaz un sector de elips.
Deseneaz un poligon. Aceast metod este similar cu DrawLines, cu
excepia faptului c unete punctul iniial cu cel final.
DrawPath
DrawPie
DrawPolygon
DrawRectangle
DrawRectangles
DrawString
Deseneaz un dreptunghi.
Deseneaz o serie de dreptunghiuri. Dac avei nevoie s desenai o serie de
dreptunghiuri, aceast metod este mult mai rapid dect apelarea repetat a
metodei DrawRectangle.
Deseneaz text pe suprafaa de desenare.
Tabelul 7.8. Cteva metode ale obiectului Graphics.
Descriere
Cur obiectul Graphics i l umple cu o culoare specific. De exemplu,
handler-ul evenimentului Paint asociat unui formular poate utiliza
sintaxa e.Graphics.Clear(this.BackColor) pentru curarea
suprafeei formularului prin utilizarea culorii de fundal.
Dispose
129
FromHdc
FromHwnd
FromImage
MeasureCharacterRanges
MeasureString
PageScale
ResetTransformation
Restore
RotateTransform
Save
ScaleTransform
SmoothingMode
TextRenderingHint
Transform
TransformPoints
TranslateTransform
Clipping se refer la acea optimizare n care calculatorul deseneaz numai acea regiune care este vizibil
utilizatorului.
130
Handler-ul evenimentului Paint nu se execut atunci cnd fereastra este micorat. n acest
caz nu sunt expuse zone noi, astfel c nu se declaneaz nici un eveniment Paint.
Atunci cnd fereastra este micorat se declaneaz numai handler-ul evenimentului Resize.
Cnd fereastra este mrit, se declaneaz evenimentul Resize i este desenat toat
fereastra. Apoi este declanat evenimentul Paint care redeseneaz prile nou expuse.
Pentru imagini simple, aceasta nu este o problem. Pentru imagini complexe ns acesta
poate consuma mult timp pentru desenare, iar utilizatorul poate observa o ntrziere vizibil
a desenrii sau o plpire a imaginii.
Visual C# ofer o strategie alternativ care permite unei ferestre s se redeseneze numai o
singur dat atunci cnd este expus sau redimensionat. Precum se poate observa n codul
de mai jos, nti setai proprietatea ResizeRedraw a ferestrei pe true pentru a indica faptul c
fereastra ar trebui s se redeseneze atunci cnd este redimensionat. Apoi folosii metoda
SetStyle() a ferestrei pentru a seta pe true modul AllPaintingInWmPaint. Acesta precizeaz
limbajului Visual C# faptul c fereastra realizeaz toate operaiile de desenare n handler-ul
evenimentului Paint. Acum, dac fereastra este redimensionat, Visual C# declaneaz
suplimentar evenimentul Paint dup evenimentul Resize, astfel c programul poate face
desenarea numai la evenimentul Paint.
private void Form1_Load(object sender, EventArgs e)
{
this.ResizeRedraw = true;
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(this.BackColor);
e.Graphics.DrawRectangle(Pens.Black, 0, 0,
this.ClientSize.Width - 1, this.ClientSize.Height - 1);
e.Graphics.DrawLine(Pens.Black, 0, 0,
this.ClientSize.Width - 1, this.ClientSize.Height - 1);
e.Graphics.DrawLine(Pens.Black, this.ClientSize.Width - 1, 0,
0, this.ClientSize.Height - 1);
}
131
7.2.1. Pen
Obiectul Pen determin modul n care sunt desenate liniile. El determin culoarea, grosimea,
stilul i modul de mbinare i capetele liniilor.
Un program poate crea explicit obiecte Pen, ns adesea va fi utilizat unul dintre cele peste
280 de creioane predefinite de clasa Pen. De exemplu, codul urmtor deseneaz un
dreptunghi folosind o linie roz avnd un pixel grosime:
myGraphics.DrawRectangle(Pens.HotPink, 10, 10, 50, 50);
Creioanele sunt scalate prin transformrile aplicate unui obiect Graphics, astfel c rezultatul
nu va fi obligatoriu de un pixel grosime. Dac obiectul Graphics aplic o transformare care
scaleaz cu un factor de 10, linia rezultat va fi de grosime 10.
O soluie la aceast problem este crearea unui obiect nou Pen setnd grosimea acestuia la
0.1, dup cum se poate observa n codul urmtor. Grosimea este scalat la 0.1 * 10 = 1.
myGraphics.DrawRectangle(new Pen(Color.HotPink, 0.1f), 10, 10, 50, 50);
O alt soluie este crearea unui creion cu grosimea 0. ntotdeauna liniile care au specificat
grosimea 0 sunt desenate de procedurile GDI+ cu grosime de un pixel.
Tabelul urmtor descrie unele dintre cele mai utile proprieti i metode ale clasei Pen.
Proprietate sau Metod
Brush
Color
CustomEndCap
CustomStartCap
DashPattern
DashStyle
EndCap
Descriere
Determin pensula folosit pentru umplerea liniilor.
Determin culoarea liniilor.
Permite definirea captului final al liniilor.
Permite definirea captului iniial al liniilor.
Un ir de valori Single care specific un ablon particular care determin
stilul de desenare al liniilor. Valorile din ir indic numrul de pixeli
desenai, omii, desenai, omii .a.m.d. Aceste valori sunt scalate dac
creionul nu are grosimea de un pixel.
Determin stilul liniilor. Aceast valoare poate fi Dash (linie), DashDot
(linie-punct), DashDotDot (linie-punct-punct), Dot (punct), Solid sau
Custom. Dac setai proprietatea DashPattern, aceast valoare este
setat la Custom. Liniuele i distanele dintre ele sunt scalate dac
creionul nu are grosimea de un pixel.
Determin capetele utilizate la sfritul liniilor. Aceast valoare poate fi:
ArrowAnchor, DiamondAnchor, Flat, NoAnchor, Round, RoundAnchor,
Square, SquareAnchor, Triangle i Custom. Dac LineCap este Custom, ar
trebui s utilizai un obiect CustomLineCap pentru definirea capetelor.
Figura 7.11 arat valorile standard ale LineCap.
LineJoin
SetLineCap
StartCap
Width
132
Figura 7.11. Enumerarea LineCap determin modul n care sunt desenate capetele unei linii.
Figura 7.12. Enumerarea LineJoin determin modul n care sunt mbinate liniile.
7.2.2. Brush
Obiectul Brush determin modul n care sunt umplute suprafeele atunci cnd le desenai
utiliznd metodele FillClosedCurve(), FillEllipse(), FillPath(), FillPie(), FillPolygon(),
FillRectangle() i FillRectangles() ale obiectului Graphics. Tipuri diferite de pensule umplu
suprafee cu culori uniforme, abloane de hauri i gradieni de culoare.
Clasa Brush nsi este o clas abstract, astfel c nu putei crea instane ale clasei Brush. n
schimb, putei crea instane ale uneia din clasele derivate SolidBrush, TextureBrush,
HatchBrush, LinearGradientBrush i PathGradientBrush. Tabelul urmtor descrie sumar aceste
clase.
Clas
SolidBrush
TextureBrush
HatchBrush
LinearGradientBrush
PathGradientBrush
Descriere
Umple suprafee cu o singur culoare uniform
Umple suprafee cu o imagine repetat afiat
Umple suprafee cu un ablon repetat de haur
Umple suprafee cu un gradient liniar de dou sau mai multe culori
Umple suprafee cu un gradient de culoare care urmrete o cale
Tabelul 7.13. Clase derivate din clasa Brush.
SolidBrush
O pensul SolidBrush umple o suprafa cu o culoare uniform. Aceast clas este extrem de
simpl. Ea are un singur constructor care primete un parametru care specific culoarea
pensulei. Singura proprietate util este Color, care determin culoarea pensulei.
private void Form1_Paint(object sender, PaintEventArgs e)
{
// pensula creata de utilizator
SolidBrush redBrush = new SolidBrush(Color.Red);
e.Graphics.FillRectangle(redBrush, 10, 10, 200, 100);
// pensula predefinita
Brush blueBrush = Brushes.Blue;
133
TextureBrush
O pensul TextureBrush v d control deplin asupra fiecrui pixel din interiorul suprafeei
umplute. Putei s v construii propriile imagini pentru a fi utilizate ca abloane de hauri,
ns n general este mai simplu s utilizai clasa HatchBrush.
HatchBrush
HatchBrush este o clas relativ simpl. Cele mai utile trei proprieti sunt BackgroundColor,
ForegroundColor i HatchStyle. ForegroundColor i BackgroundColor determin culorile utilizate
de pensul. HatchStyle poate primi una dintre cele 54 de valori. Figura 7.14 listeaz valorile
posibile pentru HatchStyle i afieaz mostre pentru fiecare.
Figura 7.14. Enumerarea HatchStyle determin modul n care HatchBrush umple o suprafa.
Codul de mai jos arat modul n care un program poate umple un dreptunghi cu un
HatchBrush. Creeaz o pensul folosind stilul LargeGrid cu culoare albastr i fundal albastru
deschis. Apeleaz metoda FillRectangle() pentru a umple un dreptunghi cu aceast pensul,
apoi apeleaz DrawRectangle() pentru desenarea unui contur negru n jurul acestuia (vezi
Figura 7.15).
private void Form1_Paint(object sender, PaintEventArgs e)
{
HatchBrush myBrush = new HatchBrush(HatchStyle.LargeGrid, Color.Blue, Color.LightBlue);
e.Graphics.FillRectangle(myBrush, 10, 10, 200, 200);
e.Graphics.DrawRectangle(Pens.Black, 10, 10, 200, 200);
}
134
135
Visual Studio .NET permite utilizarea bazelor de date, a serviciilor web i a obiectelor n
aplicaia noastr. Ceea ce vom selecta noi n continuare, pentru a prelua datele dintr-o baz de
136
date, este opiunea Database, dup care vom trece la pasul urmtor, alegerea unui model de
baz de date, precum se poate observa n dialogul din Figura 8.2.
Dac n cadrul unor proiecte anterioare am creat conexiuni la baze de date, putem s le
selectm din lista derulant (Figura 8.3). Dac dorim s crem o noua conexiune, vom apsa
butonul New Connection, caz n care va aprea dialogul din Figura 8.4.
137
Figura 8.4. Fereastra dialog pentru crearea unei noi conexiuni la baza de date.
Dac baza noastr de date este n alt format dect cel implicit (n acest exemplu, Microsoft
Access Database), vom apsa butonul Change, caz n care va aprea o fereastr de dialog
(Figura 8.5) din care putem selecta unul din tipurile de baze de date existente, cum ar fi
Microsoft Access, o surs ODBC, SQL Server sau Oracle.
Dup ce am selectat tipul de baz de date dorit, ne vom ntoarce la fereastra Add Connection.
Coninutul acestei ferestre va diferi n funcie de tipul de baz de date selectat.
Pentru baza de date Microsoft Access trebuie s introducem numele fiierului sau s apsm
butonul Browse (vezi Figura 8.4) pentru a gsi i selecta fiierul de baz de date. Dac este
necesar, vom introduce suplimentar numele de utilizator i parola pentru accesul la baza de
date.
La ntoarcerea n fereastra Data Source Configuration Wizard, selectm noua conexiune, aa
cum este prezentat n Figura 8.6. Tot aici putem vizualiza irul de conectare la baza noastr de
date.
138
La pasul urmtor, vom fi informai c baza noastr de date este un fiier local i vom fi ntrebai
dac vrem s o adugm la proiectul nostru (Figura 8.7). Dac selectm Yes, baza de date va
fi inclus n proiect i va aprea n fereastra Project Explorer. Dac dorim s distribuim baza
de date mpreun cu aplicaia noastr, este mai convenabil s gestionm baza de date i codul
Visual C# mpreun. Totui, n acest caz exist un inconvenient (o setare implicit): ori de cte
ori proiectul va fi compilat, baza de date va fi copiat din locaia original n directorul
aplicaiei, suprascriind-o pe cea existent i existnd astfel riscul de a pierde ultimele date
adugate. Acest neajuns se poate remedia prin setarea, din Solution Explorer, pentru fiierul
de baz de date, a proprietii Copy to Output Directory la valoarea Copy if newer (Figura
8.8).
Figura 8.7. Dialogul care apare la selectarea unei baze de date dintr-un fiier local
139
n continuare vom fi ntrebai dac dorim s salvm informaiile despre conectare n fiierul
de configurare al aplicaiei noastre. Dac lsm selectat aceast opiune atunci aceste
informaii vor fi adugate n fiierul app.config. (Figura 8.9)
Mai jos este prezentat coninutul fiierului app.config unde sunt stocate informaiile despre
conectarea la baza de date.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="WindowsFormsApplication1.Properties.Settings.CatalogConnectionString"
connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Catalog.accdb"
providerName="System.Data.OleDb" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
Fereastra urmtoare conine informaii despre obiectele din baza de date. n acest exemplu,
baza de date conine dou tabele: Studenti i Note. Putem desfura informaiile despre tabele,
afiate arborescent, pentru a vedea ce cmpuri conin i, eventual, pentru a selecta numai o
parte dintre acestea (vezi Figura 8.10).
140
Dup ncheierea wizard-ului (comanda Finish) la aplicaia noastr vor fi adugate cteva
obiecte. Fereastra Solution Explorer (Figura 8.11) afieaz dou obiecte: baza de date
Catalog.accdb i fiierul CatalogDataSet.xsd care descrie sursa de date.
Dac dam dublu click pe fiierul *.xsd, Visual C# va deschide un editor (Figura 8.12) care va
afia tabele mpreun cu cmpurile lor i relaiile definite ntre tabele (dac exist aa ceva).
141
Linia trasat ntre cele 2 obiecte, avnd la un capt o pictogram cheie, iar la cellalt simbolul
infinit, indic faptul ca relaia dintre tabele este de tip one-to-many. n acest exemplu, cmpul
IDStudent din tabelul Studenti mpreun cu cmpul IDStudent din tabelul Note formeaz o
relaie de tip foreign key. Aceasta nseamn c fiecare valoare IDStudent din tabelul Note
trebuie s corespund unei singure valori IDStudent din tabelul Studenti. Dac se efectueaz
dublu click pe link-ul de relaionare (sau click dreapta), editorul va afia fereastra din Figura
8.13.
n partea de jos a tabelelor afiate n Figura 8.12, se pot observa dou obiecte de tip data
adapter care conin etichetele Fill, GetData(). Aceste obiecte vor fi folosite mai trziu n
aplicaie pentru a muta datele din/n baza de date.
142
n plus, wizard-ul a adugat n fereastra Data Sources un nou obiect DataSet (vezi Figura 8.14).
Dac fereastra Data Sources nu este vizibil, se poate selecta din meniul View > Other
Windows > Data Sources.
n Figura 8.14 este prezentat dataset-ul afiat sub form arborescent, fiind vizibile tabelele
i cmpurile acestora. Se poate observa c tabelul Note este listat sub tabelul Studenti,
deoarece exist o relaie printe-copil ntre cele dou.
Este nevoie de multe cuvinte i figuri pentru descrierea acestui proces, dar, utiliznd wizardul, aceast operaiune devine foarte rapid. Dup ce am creat legtura la sursa de date putem
construi pentru aplicaia noastr o interfa simpl fr nici un efort. Trebuie doar s tragem
pe formularul nostru cu draganddrop obiectele din fereastra Data Sources.
Dac facem click pe unul dintre tabelele afiate n Data Sources apoi l tragem cu drag-anddrop peste un formular, Visual C# creeaz automat controalele BindingNavigator i
DataGridView, precum i alte componente utile pentru afiarea datelor din tabel (vezi Figura
8.15)
n loc s afim tot tabelul pe formular, putem s tragem numai anumite cmpuri din tabel. n
acest caz, Visual C# adaug controale pe formular pentru a reprezenta coloanele.
Dac selectm un tabel din fereastra Data Sources, va aprea un meniu drop down care ne
ofer posibilitatea de a alege stiluri diferite de afiare, aa cum se poate observa n Figura 8.16.
De exemplu, dac setam stilul de afiare la Details i apoi tragem tabelul pe formularul noastru,
Visual C# va afia datele din tabel utiliznd o afiare detaliat a cmpurilor, precum se poate
vedea n Figura 8.17.
143
Figura 8.16. Meniul drop down obinut n urma efecturii click dreapta pe tabel
Similar, se poate schimba modul de afiare pentru orice cmp din tabelul nostru. Se selecteaz
cmpul din fereastra Data Sources, iar din meniul drop down care apare putem selecta
controlul n care s fie afiate pe formular datele din acel cmp (vezi Figura 8.18). Atunci cnd
tragei cmpul sau tabelul pe un formular pentru construirea interfeei aplicaiei, Visual C# va
utiliza acest tip de control pentru afiarea valorilor cmpului.
144
Programul stocheaz datele ntr-un obiect de tip DataSet. Un obiect DataSet reprezint o baz
de date. Acesta conine obiecte de tip DataTable, care reprezint tabelele din baza de date.
Fiecare obiect DataTable conine obiecte DataRow, care reprezint nregistrrile din baza de
date, iar fiecare obiect DataRow conine obiecte DataColumn, care reprezint cmpurile din
tabele.
Obiectele de tip TableAdapter sunt utilizate pentru comunicaia dintre aplicaie (mai precis,
un obiect DataSet) i o baz de date. Acestea ofer metode pentru efectuarea operaiilor
asupra bazei de date (cum ar fi selectare, inserare, modificare sau tergere). Un TableAdapter
se conecteaz la o baz de date, execut interogri SQL sau proceduri stocate i poate returna
fie un tabel nou, populat cu datele returnate, fie un obiect DataTable existent, populat cu datele
returnate. Obiectele TableAdapter sunt utilizate de asemenea pentru a trimite date actualizate
de la aplicaia curent napoi la baza de date.
TableAdapterManager reprezint o component care ofer funcii de salvare a datelor n
tabele relaionate. TableAdapterManager utilizeaz relaiile de tip foreign-key, care
relaioneaz tabelele, pentru determinarea ordinii corecte de trimitere a comenzilor Insert,
Update sau Delete de la un DataSet ctre o baz de date fr nclcarea regulilor definite prin
intermediul foreign-key-urilor din baza da date.
Obiectul BindingSource ncapsuleaz toate datele din DataSet i ofer funcii pentru controlul
acestora din cadrul programului. Aceste funcii efectueaz aciuni cum ar fi: mutarea datelor,
adugarea sau tergerea elementelor, etc.
BindingNavigator ofer o interfa grafic pentru ca utilizatorul s poat controla
BindingSource.
Figura 8.20 ilustreaz legturile dintre obiectele DataSet, TableAdapter, BindingSource i
BindingNavigator.
145
Chiar i aceste obiecte, lucrnd mpreun, nu fac totul pentru ca informaiile s fie afiate de
program. Cnd creeaz aceste obiecte, Visual C# adaug, de asemenea, la formularul nostru
codul prezentat mai jos. Event handler-ul Form1_Load() face ca TableAdapter-ul s copieze
toate datele din baza de date n DataSet, iar studentiBindingNavigatorSaveItem_Click() se
execut n momentul cnd utilizatorul face click pe comanda Save din BindingNavigator.
Aceast metod face ca TableAdapter-ul s salveze orice modificare a tabelului Studenti n
baza de date.
private void studentiBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.studentiBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.catalogDataSet);
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'catalogDataSet.Studenti' table.
// You can move, or remove it, as needed.
this.studentiTableAdapter.Fill(this.catalogDataSet.Studenti);
}
Visual C# creeaz toate acestea automat, iar dac rulm programul vom vedea toate
informaiile din baza de date afiate, avnd posibilitatea de a le manipula. Aplicaia obinut
nu este perfect, n sensul c, printre altele, nu exist nicio validare a datelor, lsnd
utilizatorul s nchid aplicaia fr s salveze modificrile fcute n baza de date. Este totui
un nceput bun, pentru att de puin efort depus.
146
DataContainer: stocheaz datele, dup ce acestea au fost ncrcate din baza de date n
aplicaie, similar DataSet-urilor. La aceste obiecte pot fi legate controale, pentru
afiarea automat a datelor i manipularea bazei de date.
Clasele de tip DataContainer i DataAdapter sunt generice i lucreaz cu diferite tipuri de baze
de date, n timp ce obiectele de tip Connection i Command sunt specifice anumitor tipuri de
baze de date. De exemplu, obiectele de conectare OleDbConnection, SqlConnection,
OdbcConnection i OracleConnection pot lucra cu OLE DB (Object Linking and Embedding
DataBase), SQL Server incluznd MSDE (MicroSoft Data Engine), ODBC (Open DataBase
Connectivity), respectiv baze de date Oracle.
Obiectele SQL Server i Oracle pot lucra doar cu baze de date care fac parte din acelai brand,
dar ele sunt mai complete i mai potrivite pentru baze de date SQL, respective Oracle.
Figura 8.21. Obiectele de tip Connection, Data adapter i DataSet sunt folosite pentru mutarea datelor n/din
baza de date.
147
Dac se compar Figura 8.21 cu Figura 8.20 se vor observa cteva similitudini. Ambele metode
folosesc un adaptor pentru a tranzita datele ntre baza de date i DataSet. La o prim privire
ar prea c n Figura 8.20 nu se folosete un obiect conexiune, dar de fapt obiectul
TableAdapter conine intern un obiect conexiune pentru accesarea bazei de date.
O diferen major este faptul c n Figura 8.20 se folosete un BindingSource i un
BindingNavigator pentru a-i permite utilizatorului s controleze BindingSource i s parcurg
nregistrrile.
Descriere
Stringul care definete conectarea la baza de date
Timpul pe care obiectul l ateapt pentru a se conecta la baza de date. Dac
acest timp expir, se abandoneaz conectarea i se semnaleaz eroare.
Database
DataSource
Provider
ServerVersion
State
Proprietatea ConnectionString include mai multe cmpuri separate prin punct i virgul.
Textul urmtor arat o valoare tipic a proprietii ConnectionString pentru o conexiune
OleDbConnection care va accesa o baz de date Access.
Jet OLEDB:Global Partial Bulk Ops=2;
Jet OLEDB:Registry Path=;
Jet OLEDB:Database Locking Mode=1;
Data Source="D:\Work\Angajati.mdb";
Mode=Share Deny None;Jet OLEDB:Engine Type=5;
Provider="Microsoft.Jet.OLEDB.4.0";
Jet OLEDB:System database=;
Jet OLEDB:SFP=False;
persist security info=False;
Extended Properties=;
Jet OLEDB:Compact Without Replica Repair=False;
Jet OLEDB:Encrypt Database=False;
Jet OLEDB:Create System Database=False;
148
Multe dintre cmpurile separate prin punct i virgul sunt opionale i pot fi omise. Ar fi destul
de complicat s ne amintim care sunt cmpurile obligatorii i care sunt cele opionale, ns,
din fericire, nu este necesar. n loc s compunem noi proprietatea ConnectionString, putem
lsa Visual C# s o alctuiasc pentru noi. Pentru aceasta, afim fereastra Server Explorer
(meniul View > Server Explorer), precum se poate observa n Figura 8.23.
Urmtorul fragment de cod ilustreaz crearea, deschiderea, folosirea i nchiderea unui obiect
OleDbConnection.
using System.Data.OleDb;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// ...
private void Form1_Load(object sender, EventArgs e)
{
// Creeaza conexiunea.
OleDbConnection conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=D:\Work\Catalog.accdb;
Persist Security Info=True;User ID=Admin;Password=;");
// Deschide conexiunea.
conn.Open();
// ...
// Inchide conexiunea.
conn.Close();
conn.Dispose();
}
}
}
Tabelul 8.24 prezint cele mai des folosite metode propuse de clasele OleDbConnection i
SqlConnection.
Metoda
BeginTransaction
Descriere
ncepe o tranzacie cu baza de date i returneaz un obiect de tip tranzacie. O
tranzacie asigur faptul c toate comenzile dintr-o secven de comenzi sunt
executate, n caz contrar operaia fiind anulat.
ChangeDatabase
Close
CreateCommand
Open
149
Cele mai utile evenimente ale obiectelor de tip Connection sunt InfoMessage i StateChange.
Evenimentul InfoMessage apare cnd provider-ul de baz de date semnaleaz o avertizare sau
un mesaj de informare. Programul poate citi mesajul i, n consecin, poate efectua o aciune
sau, pur i simplu, l afieaz utilizatorului. Evenimentul StateChange apare cnd se modific
starea conexiunii la baza de date.
Metodele Fill() i Update() ale unui obiect DataAdapter deschid automat o conexiune,
efectueaz aciunile, apoi nchid conexiunea, astfel nct utilizatorul nu este nevoit s
gestioneze conexiunile. De exemplu, cnd programul apeleaz metoda Fill(), adaptorul
deschide rapid o conexiune, copiaz datele din baza de date n DataSet i apoi nchide
conexiunea.
UpdateCommand
Descriere
Obiectul de tip Command pe care obiectul DataAdapter l folosete pentru a
terge nregistrri.
Obiectul de tip Command pe care DataAdapter-ul l folosete pentru a insera
nregistrri.
Obiectul de tip Command pe care DataAdapter-ul l folosete pentru a selecta
nregistrri.
O colecie de obiecte DataTableMapping care determin modul n care vor fi
mapate tabelele din baza de date n DataSet. Fiecare obiect are o colecie
ColumnMappings care va determina modul n care vor fi mapate coloanele din
tabele n tabelele din DataSet.
Obiectul de tip Command pe care DataAdapter-ul l folosete pentru a modifica
nregistrri.
Tabelul 8.25. Proprieti utile ale obiectelor DataAdapter.
Exist mai multe modaliti de creare a obiectelor de tip Command. De exemplu, dac pentru
construirea adaptorului folosim Data Adapter Configuration Wizard, atunci wizardul creeaz
automat aceste obiecte (vom vedea n continuare cum). Putem selecta adaptorul i expanda
obiectele de tip Command n fereastra Properties, pentru a le vizualiza proprietile.
Data Adapter Configuration Wizard v ajut la setarea proprietilor unui nou adaptor de date
sau a unuia existent. Un adaptor de date conine comenzi SQL pe care aplicaia le poate utiliza
pentru citirea datelor dintr-o baz de date ntr-un DataSet i scrierea lor napoi. Wizard-ul
poate opional s creeze o conexiune de date care permite adaptorului s comunice cu o baz
de date.
O alt modalitate de creare a acestor comenzi este folosirea unui obiect CommandBuilder.
Dac atam un astfel de obiect adaptorului, acesta va folosi obiectul CommandBuilder pentru
a genera comenzile de care are nevoie, n mod automat.
Urmtoarea secven de cod ilustreaz modul n care un program poate asocia un obiect de
tip OleDbCommandBuilder la un OleDbDataAdapter. Cnd se apeleaz metoda Update(),
adaptorul folosete obiectul CommandBuilder, dac este necesar, pentru a efectua comenzi de
inserare, modificare sau tergere asupra bazei de date. Metoda Debug.WriteLine() va afia
textul comenzilor generate n mod automat.
150
Figura 8.26. Data Adapter Configuration Wizard ne ajuta s definim un adaptor nou
La apsarea butonului Next va aprea un dialog ca cel din Figura 8.27. De aici putem selecta
metoda cu care adaptorul va opera asupra bazei de date i va determina modul n care datele
vor fi selectate, inserate sau modificate n baza de date. Dac selectm opiunea Use SQL
151
Statements, atunci adaptorul va lucra cu instruciuni simple SQL. Dac selectm opiunea
Create new stored procedures, adaptorul va genera noi proceduri stocate n baza de date.
Pentru ca wizardul s foloseasc proceduri stocate existente, selectm opiunea Use existing
stored procedures. n Figura 8.27 prima opiune este singura disponibil pentru un
OleDbDataAdapter ataat unei baze de date Access, aa cum am folosit n acest exemplu.
Figura 8.27. Selectarea unei metode prin care adaptorul va opera asupra bazei de date
Dac selectm Use SQL Statements i apsm butonul Next, va aprea dialogul din Figura
8.28.
Dac avem experien n lucrul cu interogrile SQL, atunci putem introduce manual o
interogare SQL pe care adaptorul o va folosi cnd va selecta datele din baza de date. n caz
contrar, putem da click pe butonul Query Builder (vezi Figura 8.29), apoi vom selecta
tabelele din baza de date care conin nregistrrile pe care vrem s le folosim i dm click pe
butonul Add. Dup ce am selectat tabelele nchidem dialogul Add Table. Coloanele din al
doilea panou pot fi utilizate pentru determinarea modului n care sunt sortate cmpurile. n al
treilea panou este afiat comanda SQL pe care o construim. Putem da click pe butonul
152
Execute Query pentru rularea interogrii SQL i afiarea rezultatelor n partea de jos. Cnd
am terminat, apsm butonul OK.
Figura 8.29. Putem utiliza instrumentul Query Builder pentru definirea datelor pe care le va folosi adaptorul.
Dup ce apsm butonul Next, Data Adapter Configuration Wizard va afia un sumar similar
celui din Figura 8.30. Acest sumar descrie aciunile pe care wizard-ul le va executa i pe cele
pe care nu le va executa. n funcie de interogarea folosit pentru selectarea datelor, wizardul s-ar putea s nu genereze toate comenzile de selectare, modificare, inserare sau tergere a
nregistrrilor. De exemplu, dac interogarea cuprinde mai multe tabele, wizard-ul nu va fi
capabil s i dea seama cum s modifice nregistrrile, aa c nu va genera comenzi de
inserare, modificare sau tergere.
153
Cnd apsm comanda Finish, wizard-ul creeaz un nou adaptor i un nou obiect de tip
conexiune care va fi asociat adaptorului. Se vor seta proprietile DeleteCommand, InsertCommand,
SelectCommand i UpdateCommand ale adaptorului n concordan cu conexiunea la baza de date
pe care am selectat-o. De asemenea, se genereaz mapri iniiale ale tabelelor pentru a
transforma valorile din baza de date n valori pentru DataSet.
Configurarea unui adaptor de date este unul din paii necesari pentru utilizarea unui DataSet
n cadrul aplicaiei. Dup rularea wizard-ului, vei mai avea nevoie s adugai separat un
DataSet la aplicaia dumneavoastr i s scriei cod care s furnizeze pentru adaptorul de date
o modalitate de lucru cu DataSet-ul.
n versiunile anterioare ale Visual Studio, adaptoarele de date erau utilizate pentru
comunicarea dintre aplicaie i o baz de date. Dei adaptoarele de date sunt nc o
component de baz a .NET Framework Data Providers (ADO .NET), componentele
TableAdapter, generate automat de ctre designer, simplific procesul de
manipulare a datelor ntre o aplicaie i o baz de date.
TableDirect (CommandText va conine numele unuia sau mai multor tabele din care vor
fi furnizate datele)
n colecia Parameters a obiectului de tip Command vor fi stocai parametrii care vor defini
orice valoare necesar pentru a executa comanda. De exemplu, codul urmtor creeaz un
obiect de tip OleDbCommand care va executa comanda SQL "INSERT INTO Studenti (IDStudent,
Nume, Prenume) VALUES (?, ?, ?)". Semnele de ntrebare marcheaz locul parametrilor care
vor fi adugai mai trziu. Apoi se vor aduga n cod dou obiecte noi de tip OleDbParameter
la colecia Parameters a comenzii. Cnd n cod se invoca metoda ExecuteNonQuery(), adaptorul
nlocuiete semnele de ntrebare cu valorile parametrilor n ordinea n care apar n colecia
Parameters. n acest exemplu, valoarea din txtID.Text nlocuiete primul semn de ntrebare,
valoarea din txtNume.Text nlocuiete al doilea semn de ntrebare, iar valoarea txtPrenume.Text
al treilea semn de ntrebare.
private void btnAdauga_Click(object sender, EventArgs e)
{
// Deschide conexiunea
string conString = @"Provider=Microsoft.ACE.OLEDB.12.0;
Data source=D:\Work\Catalog.accdb;User ID=Admin;Password=;";
OleDbConnection connStudenti = new OleDbConnection(conString);
connStudenti.Open();
154
Obiectele de tip Command ofer cteva metode utile (vezi Tabelul 8.31), dintre care trei
pentru executarea unor comenzi SQL.
Metod
ExecuteNonQuery()
ExecuteScalar()
ExecuteReader()
CreateParameter()
Prepare()
Descriere
Execut o comand care nu este o interogare i care nu returneaz nici o valoare.
Execut o comand i returneaz prima coloan din prima nregistrare gsit.
Este util atunci cnd se execut comenzi care returneaz o singur valoare (de
exemplu, "SELECT COUNT * FROM Studenti").
Execut o interogare SQL i returneaz un obiect de tip DataReader (de exemplu,
OleDbDataReader). Programul poate folosi acest obiect pentru a naviga printre
nregistrrile returnate.
Adaug obiecte noi la colecia Parameters a obiectului.
Compileaz comanda ntr-o form n care va fi executat mai rapid de baza de
date.
Tabelul 8.31. Metode utile ale obiectelor de tip Command
155
programul i ncheie execuia, modificrile vor fi salvate n baza de date. Alte variante sunt
ncrcarea datelor dintr-un XML, respectiv construirea unui DataSet n memorie, fr
utilizarea unei baze de date. Programul poate folosi controale pentru a se conecta la DataSet
iar utilizatorul va putea vizualiza i manipula date complexe.
Secvena de cod prezentat n continuare creeaz i iniializeaz un DataSet i ncepe prin
crearea unui nou DataSet numit Catalog. Este creat apoi un DataTable numit Studenti, care
este adugat la colecia de tabele a DataSet-ului. Sunt adugate la coloanele obiectului
DataTable noi coloane: Nume, Prenume i IDStudent. Se seteaz apoi pe true proprietatea
Unique a coloanei IDStudent, astfel nefiind permise valori duplicate n coloana IDStudent.
Se creeaz apoi un vector de obiecte de tip DataColumn n care se introduc referinele ctre
coloanele Nume i Prenume. Acest vector este folosit pentru crearea unei constrngeri
UniqueConstraint adugat la colecia Constraints a tabelului. Prin aceasta se asigur c fiecare
pereche de Nume+Prenume este unic.
n mod similar se creeaz tabelul Note avnd coloanele IDStudent, Disciplina i Nota, apoi se
adaug o constrngere UniqueConstraint, astfel nct fiecare pereche IDStudent+Nota s fie
unic.
Este adugat o relaie ntre tabelul Studenti, coloana IDStudent i tabelul Note, coloana
IDStudent, dup care cele dou tabele sunt populate cu cteva nregistrri.
n final, programul ataeaz tabelele la dou controale DataGridView pentru afiarea
rezultatelor. Utilizatorul poate folosi controalele DataGridView pentru examinarea i
modificarea datelor, ca i cum ar fi fost ncrcate din baza de date.
private void Form1_Load(object sender, EventArgs e)
{
// Creeaza DataSet-ul.
DataSet dsCatalog = new DataSet("Catalog");
// Creeaza tabelul Studenti.
DataTable dtStudenti = dsCatalog.Tables.Add("Studenti");
// Adauga coloane la tabelul Studenti.
dtStudenti.Columns.Add("IDStudent", typeof(int));
dtStudenti.Columns.Add("Nume", typeof(string));
dtStudenti.Columns.Add("Prenume", typeof(string));
// Face campul IDStudent unic.
dtStudenti.Columns["IDStudent"].Unique = true;
// Face campul combinat Nume+Prenume unic.
DataColumn[] colNumePrenume = { dtStudenti.Columns["Nume"],
dtStudenti.Columns["Prenume"] };
dtStudenti.Constraints.Add(new UniqueConstraint(colNumePrenume));
// Creeaza tabelul Note.
DataTable dtNote = dsCatalog.Tables.Add("Note");
// Adauga coloane la tabelul Note.
dtNote.Columns.Add("IDStudent", typeof(int));
dtNote.Columns.Add("Disciplina", typeof(string));
dtNote.Columns.Add("Nota", typeof(int));
// Face campul combinat IDStudent/Disciplina unic.
DataColumn[] colStudentDisciplina = { dtNote.Columns["IDStudent"],
dtNote.Columns["Disciplina"] };
dtNote.Constraints.Add(new UniqueConstraint(colStudentDisciplina));
// Creeaza o relatie intre doua tabele prin campul IDStudent.
dsCatalog.Relations.Add("Studenti_Note", dtStudenti.Columns["IDStudent"],
dtNote.Columns["IDStudent"]);
156
nota.Next(5,
nota.Next(5,
nota.Next(5,
nota.Next(5,
10)
10)
10)
10)
});
});
});
});
Tabelul 8.32 descrie cele mai folositoare proprieti ale obiectelor DataSet.
Proprietate
CaseSensitive
Descriere
Determin dac string-urile din DataTable sunt comparate case sensitive.
DataSetName
DefaultViewManager
EnforceConstraints
HasErrors
Returneaz true dac sunt erori n vreunul din tabelele din DataSet.
Prefix
Relations
Tables
Tabelul 8.33 descrie cele mai folosite metode ale obiectelor DataSet.
Metoda
AcceptChanges
Descriere
Opereaz toate modificrile care au fost fcute de la ncrcarea datelor sau de
cnd s-a apelat ultima oar AcceptChanges.
Clear
Clone
Copy
GetChanges
GetXml
GetXmlSchema
HasChanges
Merge
ReadXml
157
ReadXmlSchema
RejectChanges
WriteXml
WriteXmlSchema
Scrie structura DataSet-ului ntr-un fiier sub forma unei scheme XML.
Tabelul 8.33. Metode utile ale obiectelor DataSet.
Cteva dintre aceste metode sunt coninute i de ctre alte obiecte. De exemplu, metoda
HasChanges() returneaz true dac un tabel din DataSet conine modificri. Obiectele
DataTable i DataRow au de asemenea metoda HasChanges() care returneaz true dac acestea
conin modificri. Alte metode de acest fel ar mai fi AcceptChanges(), Clear(), Clone(), Copy(),
GetChanges() i RejectChanges().
Descriere
Determin dac string-urile din DataTable sunt comparate case sensitive
O colecie de obiecte de tip DataRelation care definete relaii printe-copil,
unde tabelul este printele.
Columns
Constraints
DataSet
DefaultView
HasErrors
MinimumCapacity
Returneaz true dac una din nregistrrile din DataTable conine o eroare.
Dimesiunea iniial a tabelului.
ParentRelations
Prefix
PrimaryKey
Rows
TableName
Cele mai folosite metode ale obiectelor DataTable sunt prezentate n Tabelul 8.35:
Metoda
AcceptChanges()
Clear()
158
Descriere
Accept toate modificrile care au fost fcute de la ncrcarea datelor sau de la
ultima apelare a metodei AcceptChanges().
terge toate nregistrrile din DataTable.
Clone()
Compute()
Copy()
GetChanges()
GetErrors()
ImportRow()
LoadDataRow()
NewRow()
Creeaz un obiect nou de tip DataRow care are aceeai schem cu a tabelului.
RejectChanges()
Select()
Evenimentele cele mai folosite pentru obiectele DataTable sunt prezentate n Tabelul 8.36.
Eveniment
ColumnChanged
RowChanged
Descriere
Apare dup ce o valoare a fost modificat ntr-o anumit coloan dintr-o
nregistrare.
Apare n timp ce o valoare este modificat ntr-o anumit coloan dintr-o
nregistrare.
Apare dup ce o nregistrare a fost modificat cu succes.
RowChanging
RowDeleted
RowDeleting
ColumnChanging
159