Professional Documents
Culture Documents
In urma compil arii unei aplicatii poate rezulta un sier cu extensia exe,
dar care nu este un executabil portabil Windows, ci un executabil portabil
.NET (.NET PE). Acest cod nu este deci un executabil nativ, ci se va rula
de c atre CLR, ntocmai cum un sier class este rulat n cadrul JVM. CLR
foloseste tehnologia compil arii JIT - o implementare de masin a virtual a, n
care o metod a sau o functie, n momentul n care este apelat a pentru prima
oar a, este tradus a n cod masin a. Codul translatat este depus ntr-un cache,
evit and-se astfel recompilarea ulterioar a. Exist a 3 tipuri de compilatoare
JIT:
1. Normal JIT - a se vedea descrierea de mai sus.
2. Pre-JIT - compileaz a ntregul cod n cod nativ singur a dat a.
In mod
normal este folosit la instal ari.
3. Econo-JIT - se foloseste pe dispozitive cu resurse limitate. Compileaz a
codul CIL bit cu bit, eliber and resursele folosite de codul nativ ce este
stocat n cache.
In esent a, activitatea unui compilator JIT este destinata a mbun at ati per-
formanta executiei, ca alternativ a la compilarea repetat a a aceleiasi buc ati
de cod n cazul unor apel ari multiple. Unul din avantajele mecanismului JIT
apare n clipa n care codul, o dat a ce a fost compilat, se execut a pe diverse
16 CURS 1. PLATFORMA MICROSOFT .NET
procesoare; dac a masina virtual a este bine adaptat a la noua platform a, atunci
acest cod va benecia de toate optimiz arile posibile, f ar a a mai nevoie
recompilarea lui (precum n C++, de exemplu).
1.3.4 Common Type System
Pentru a asigura interoperabilitatea limbajelor din .NET Framework, o
clas a scris a n C# trebuie s a e echivalent a cu una scris a n VB.NET, o
interfat a scris a n Managed C++ trebuie s a e perfect utilizabil a n Managed
Cobol. Toate limbajele care fac parte din pleiada .NET trebuie s a aibe un set
comun de concepte pentru a putea integrate. Modul n care acest deziderat
s-a transformat n realitate se numeste Common Type System (CTS); orice
limbaj trebuie s a recunoasc a si s a poat a manipula niste tipuri comune.
O scurt a descriere a unor facilit ati comune (ce vor exhaustiv enumerate
si tratate n cadrul prezent arii limbajului C#):
1. Tipuri valoare - n general, CLR-ul (care se ocup a de managementul si
executia codului CIL, vezi mai jos) suport a dou a tipuri diferite: tipuri
valoare si tipuri referint a. Tipurile valoare reprezint a tipuri alocate
pe stiv a si nu pot avea valoare de null. Tipurile valoare includ tipu-
rile primitive, structuri si enumer ari. Datorit a faptului c a de regul a
au dimensiuni mici si sunt alocate pe stiv a, se manipuleaza ecient,
reduc and overhead-ul cerut de mecanismul de garbage collection.
2. Tipuri referinta - se folosesc dac a variabilele de un anumit tip cer
resurse de memorie semnicative. Variabilele de tip referint a contin
adrese de memorie heap si pot null. Transferul parametrilor se face
rapid, dar referintele induc un cost suplimentar datorit a mecanismului
de garbage collection.
3. Boxing si unboxing - motivul pentru care exist a tipuri primitive este
acelasi ca si n Java: performanta.
Ins a orice variabil a n .NET este
compatibil a cu clasa Object, r ad acina ierarhiei existente n .NET. De
exemplu, int este un alias pentru System.Int32, care se deriveaz a din
System.ValueType. Tipurile valoare se stocheaz a pe stiv a, dar pot
oric and convertitentr-un tip referint a memoratn heap; acest mecanism
se numeste boxing. De exemplu:
int i = 1; //i - un tip valoare
Object box = i; //box - un obiect referinta
C and se face boxing, se obtine un obiect care poate gestionat la fel
ca oricare altul, f ac anduse abstractie de originea lui.
1.3. COMPONENTE ALE LUI .NET FRAMEWORK 17
Inversa boxing-ului este unboxing-ul, prin care se poate converti un
obiect n tipul valoare echivalent, ca mai jos:
int j = (int)box;
unde operatorul de conversie este sucient pentru a converti de la un
obiect la o variabil a de tip valoare.
4. Clase, proprietati, indexatori - platforma .NET suport a pe deplin programarea
orientat a pe obiecte, concepte legate de obiecte (ncapsularea, mostenirea,
polimorsmul) sau tr as aturi legate de clase (metode, c ampuri, membri
statici, vizibilitate, accesibilitate, tipuri imbricate, etc). De asemenea
se includ tr as aturi precum propriet ati, indexatori, evenimente.
5. Interfete - reprezint a acelasi concept precum clasele abstracte din C++
(dar contin and doar functii virtuale pure), sau interfetele Java. O
clas a care se deriveaz a dintr-o interfat a trebuie s a implementeze toate
metodele acelei interfete. Se permite implementarea simultan a a mai
multor interfete (n rest mostenirea claselor este simpl a).
6. Delegati - inspirati de pointerii la functii din C, ce permit programarea
generic a. Reprezint a versiunea sigur a a pointerilor c atre functii din
C/C++ si sunt mecanismul prin care se trateaz a evenimentele.
Exist a numeroase componente ale lui CLR care l fac cea mai important a
parte a lui .NET Framework: metadata, assemblies, assembly cache, reection,
garbage collection.
1.3.5 Metadate
Metadatele nseamn a date despre date.
In cazul .NET, ele reprezint a
detalii destinate a citite si folosite de c atre platform a. Sunt stocatempreun a
cu codul pe care l descrie. Pe baza metadatelor, CLR stie cum s a instantieze
obiectele, cum s a le apeleze metodele, cum s a acceseze propriet atile. Printr-
un mecanism numit reectare, o aplicatie (nu neap arat CLR) poate s a interogheze
aceast a metadat a si s a ae ce expune un tip de date (clasa, structur a, etc).
Mai pe larg, metadata contine o declaratie a ec arui tip si c ate o declaratie
pentru ecare metod a, c amp, proprietate, eveniment al tipului respectiv.
Pentru ecare metod a implementat a, metadata contine informatie care permite
nc arc atorului clasei respective s a localizeze corpul metodei. De asemena
mai poate contine declaratii despre cultura aplicatiei respective, adic a despre
localizarea ei (limba folosit a n partea de interfat a utilizator).
18 CURS 1. PLATFORMA MICROSOFT .NET
1.3.6 Assemblies
Un assembly reprezint a un bloc functional al unei aplicatii .NET. El
formeaz a unitatea fundamentala de distribuire, versionare, reutilizare si permisiuni
de securitate. La runtime, un tip de date exist a n interiorul unui assembly
(si nu poate exista n exteriorul acestuia). Un assembly contine metadate
care sunt folosite de c atre CLR. Scopul acestor assemblies este s a se asigure
dezvoltarea softului n mod plug-and-play. Dar metadatele nu sunt suciente
pentru acest lucru; mai sunt necesare si manifestele.
Un manifest reprezint a metadate despre assembly-ul care g azduieste ti-
purile de date. Contine numele assembly-ului, num arul de versiune, referiri
la alte assemblies, o list a a tipurilor n assembly, permisiuni de securitate si
altele.
Un assembly care este mp artit ntre mai multe aplicatii are de asemenea
un shared name. Aceast a informatie care este unic a este optional a, neap ar and
n manifestul unui assembly daca acesta nu a fost g andit ca o aplicatie
partajat a.
Deoarece un assembly contine date care l descriu, instalarea lui poate
f acut a copiind assemblyul n directorul destinatie dorit. C and se doreste
rularea unei aplicatii continute n assembly, manifestul va instrui mediul
.NET despre modulele care sunt continute n assembly. Sunt folosite de
asemenea si referintele c atre orice assembly extern de care are nevoie aplicatia.
Versionarea este un aspect deosebit de important pentru a se evita asa
numitul DLL Hell. Scenariile precedente erau de tipul: se instaleaza o
aplicatie care aduce niste siere .dll necesare pentru functionare. Ulterior, o
alt a aplicatie care se instaleaz a suprascrie aceste siere (sau m acar unul din
ele) cu o versiune mai nou a, dar cu care vechea aplicatie nu mai functioneaz a
corespunz ator. Reinstalarea vechii aplicatii nu rezolva problema, deoarece
a doua aplicatie nu va functiona. Desi sierele dll contin informatie relativ
la versiune n interiorul lor, ea nu este folosit a de c atre sistemul de operare,
ceea ce duce la probleme. O solutie la aceast a dilem a ar instalarea sierelor
dll n directorul aplicatiei, dar n acest mod ar disp area reutilizarea acestor
biblioteci.
1.3.7 Assembly cache
Assembly cache este un director aatn mod normal n directorul %windir%
\Assembly. Atunci c and un assembly este instalat pe o masin a, el va
ad augat n assembly cache. Dac a un assembly este desc arcat de pe Internet,
el va stocat n assembly cache, ntr-o zon a tranzient a. Aplicatiile instalate
vor avea assemblies ntr-un assembly cache global.
1.4. TR
AS
In acest assembly cache vor exista versiuni multiple ale aceluiasi assembly.
Dac a programul de instalare este scris corect, va evita suprascrierea assembly-
urilor deja existente (si care functioneaz a perfect cu acplicatiile instalate),
ad aug and doar noul assembly. Este un mod de rezolvare a problemei DLL
Hell, unde suprascrierea unei biblioteci dinamice cu o variant a mai nou a
putea duce la nefunctionarea corespunz atoare a aplicatiilor anterior instalate.
CLR este cel care decide, pe baza informatiilor din manifest, care este versiunea
corect a de assembly de care o aplicatie are nevoie. Acest mecanism pune
cap at unei epoci de trist a amintire pentru programatori si utilizatori.
1.3.8 Garbage collection
Managementul memoriei este una din sarcinile cele mai consumatoare de
timp n programare. Garbage collection este mecanismul care se declanseaz a
atunci c and alocatorul de memorie r aspunde negativ la o cerere de alocare de
memorie. Implementarea este de tip mark and sweep: se presupune initial
c a toat a memoria alocat a se poate disponibiliza, dupa care se determin a
care din obiecte sunt referite de variabilele aplicatiei; cele care nu mai sunt
referite sunt dealocate, celelalte zone de memorie sunt compactate. Obiectele
a c aror dimensiune de memorie este mai mare dec at un anumit prag nu mai
sunt mutate, pentru a nu creste semnicativ penalizarea de performant a.
AS
In general, conventia tip Pascal este folosit a pentru tot ce este vizibil (public),
precum nume de clase, metode, propriet ati, etc. Parametrii metodelor si
numele c ampurilor se scriu cu conventia c amil a. Se recomand a evitarea
folosirii notatiei ungare (numele unei entit ati trebuie s a se refere la semantica
ei, nu la tipul de reprezentare).
2.2 Tipuri de date
C# prezint a dou a grupuri de tipuri de date: tipuri valoare si tipuri
referinta. Tipurile valoare includ tipurile simple (ex. char, int, oat)
5
, tipu-
rile enumerare si structur a si au ca principale caracteristici faptul c a ele contin
direct datele referite si sunt alocate pe stiv a sau inline ntro structur a. Ti-
purile referint a includ tipurile clas a, interfat a, delegat si tablou, toate av and
proprietatea c a variabilele de acest tip stocheaz a referinte c atre obiectele
continute. Demn de remarcat este c a toate tipurile de date sunt derivate
(direct sau nu) din tipul System.Object, pun and astfel la dispozitie un mod
unitar de tratare a lor.
2.2.1 Tipuri predenite
C# contine un set de tipuri predenite, pentru care nu este necesar a
referirea vreunui spatiu de nume via directiva using sau calicare complet a:
string, object, tipurile ntregi cu semn si f ar a semn, tipuri numerice n virgul a
mobil a, tipurile bool si decimal.
Tipul string este folosit pentru manipularea sirurilor de caractere codicate
Unicode; continutul obiectelor de tip string nu se poate modica
6
. Clasa
object este r ad acina ierarhiei de clase din .NET, la care orice tip (inclusiv un
tip valoare) poate convertit.
Tipul bool este folosit pentru a reprezenta valorile logice true si false.
Tipul char este folosit pentru a reprezenta caractere Unicode, reprezentate
pe 16 biti. Tipul decimal este folosit pentru calculen care erorile determinate
de reprezentarea n virgul a mobil a sunt inacceptabile, el pun and la dispozitie
28 de cifre zecimale semnicative.
5
De fapt acestea sunt structuri, prezentate n alt curs
6
Spunem despre un string ca este invariabil - engl. immutable
26 CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI
Tabelul de mai jos contine lista tipurilor predenite, ar at and totodat a
cum se scriu valorile corespunz atoare:
2.2. TIPURI DE DATE 27
Tabelul 2.1: Tipuri predenite.
Tip Descriere Exemplu
object r adacina oric arui tip object a = null;
string o secvent a de caractere Unicode string s = hello;
sbyte tip ntreg cu semn, pe 8 biti sbyte val = 12;
short tip ntreg cu semn, pe 16 biti short val = 12;
int tip ntreg cu semn, pe 16 biti int val = 12;
long tip ntreg cu semn, pe 64 biti long val1 = 12;
long val2=34L;
byte tip ntreg f ar a semn, pe 8 biti byte val = 12;
ushort tip ntreg f ar a semn, pe 16 biti ushort val = 12;
uint tip ntreg f ar a semn, pe 32 biti uint val = 12;
ulong tip ntreg f ar a semn, pe 64 de biti ulong val1=12;
ulong val2=34U;
ulong val3=56L;
ulong val4=76UL;
oat tip cu virgul a mobil a, simpl a precizie oat val=1.23F;
double tip n virgul a mobil a, dubl a precizie double val1=1.23;
double val2=4.56D;
bool tip boolean bool val1=false;
bool val2=true;
char tip caracter din setul Unicode char val=h;
decimal tip zecimal cu 28 de cifre semnicative decimal val=1.23M;
Fiecare din tipurile predenite este un alias pentru un tip pus la dispozitie
de sistem. De exemplu, string este alias pentru clasa System.String, int este
alias pentru System.Int32.
2.2.2 Tipuri valoare
C# pune programatorului la dispozitie tipuri valoare, care sunt e structuri,
e enumer ari. Exist a un set predenit de structuri numite tipuri simple,
identicate prin cuvinte rezervate. Un tip simplu este e de tip numeric
7
, e
boolean. Tipurile numerice sunt tipuri ntregi, n virgul a mobil a sau decimal.
Tipurile intregi sunt sbyte, byte, short, ushort, int, uint, long, ulong, char;
cele n virgul a mobil a sunt oat si double. Tipurile enumerare se pot deni
de c atre utilizator.
7
Engl: integral type
28 CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI
Toate tipurile valoare deriveaz a din clasa System.ValueType, care la r andul
ei este derivat a din clasa object (alias pentru System.Object). Nu este posibil
ca dintrun tip valoare s a se deriveze. Atribuirea pentru un astfel de tip
nseamn a copierea valorii dintro parte n alta.
Structuri
Un tip structur a este un tip valoare care poate s a contina declaratii de
constante, c ampuri, metode, propriet ati, indexatori, operatori, constructori
sau tipuri imbricate. Vor descrise ntrun capitol urm ator.
Tipuri simple
C# pune are predenit un set de tipuri structuri numite tipuri simple.
Tipurile simple sunt identicate prin cuvinte rezervate, dar acestea reprezint a
doar aliasuri pentru tipurile struct corespunz atoare din spatiul de nume
System; corespondenta este dat a n tabelul de mai jos:
Tabelul 2.2: Tipuri simple si corespondentele lor cu ti-
purile din spatiul de nume System.
Tabelul 2.2
Cuv ant rezervat Tipul alias
sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
long System.Int64
ulong System.UInt64
char System.Char
oat System.Single
double System.Double
bool System.Boolean
decimal System.Decimal
Deoarece un tip simplu este un alias pentru un tip struct, orice tip simplu
are membri. De exemplu, tipul int, ind un tip alias pentru System.Int32,
urm atoarele declaratii sunt legale:
2.2. TIPURI DE DATE 29
int i = int.MaxValue; //constanta System.Int32.MaxValue
string s = i.ToString(); //metoda System.Int32.ToString()
string t = 3.ToString(); //idem
double d = Double.Parse("3.14");
Tipuri ntregi
C# suport a nou a tipuri ntregi: sbyte, byte, short, ushort, int, uint, long,
ulong si char. Acestea au urm atoarele dimensiuni si domeniu de valori:
sbyte reprezint a tip cu semn pe 8 biti, cu valori de la -128 la 127;
byte reprezint a tip f ar a semn pe 8 biti, ntre 0 si 255;
short reprezint a tip cu semn pe 16 biti, ntre -32768 si 32767;
ushort reprezint a tip f ar a semn pe 16 biti, ntre 0 si 65535;
int reprezint a tip cu semn pe 32 de biti, ntre 2
31
si 2
31
1;
uint reprezint a tip f ar a semn pe 32 de biti, ntre 0 si 2
32
1;
long reprezint a tip cu semn pe 64 de biti, ntre 2
63
si 2
63
1;
ulong reprezint a tip f ar a semn pe 64 de biti, ntre 0 si 2
64
1;
char reprezint a tip f ar a semn pe 16 biti, cu valori ntre 0 si 65535.
Multimea valorilor posibile pentru char corespunde setului de caractere
Unicode.
Reprezentarea unei variable de tip ntreg se poate face sub form a de sir
de cifre zecimale sau hexazecimale, urmate eventual de un prex. Numerele
exprimate n hexazecimal sunt prexate cu 0x sau 0X. Regulile dup a care
se asigneaz a un tip pentru o valoare sunt:
1. dac a sirul de cifre nu are un sux, atunci el este considerat ca ind
primul tip care poate s a contin a valoarea dat a: int, uint, long, ulong;
2. dac a sirul de cifre are suxul u sau U, el este considerat ca ind din
primul tip care poate s a contin a valoarea dat a: uint, ulong;
3. dac a sirul de cifre are suxul l sau L, el este considerat ca ind din
primul tip care poate s a contin a valoarea dat a: long, ulong;
4. dac a sirul de cifre are suxul ul, uL, Ul, UL, lu, lU, Lu, LU, el este
considerat ca ind din tipul ulong.
30 CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI
Dac a o valoare este n afara domeniului lui ulong, apare o eroare la
compilare.
Literalii de tip caracter au forma: caracter unde caracter poate
exprimat printrun caracter, printro secvent a escape simpl a, secvent a escape
hexazecimal a sau secvent a escape Unicode.
In prima form a poate folosit
orice caracter except and apostrof, backslash si new line. Secvent a escape
simpl a poate : \, \", \\, \0, \a, \b, \f, \n, \r, \t, \v, cu semnicatiile
cunoscute din C++. O secvent a escape hexazecimal a ncepe cu \x urmat de
14 cifre hexa. Desi ca reprezentare, char este identic cu ushort, nu toate
operatiile ce de pot efectua cu ushort sunt valabile si pentru char.
1), precum si
un set nit de numere. Tipul oat poate reprezenta valori cuprinse ntre
1.510
45
si 3.410
38
(si din domeniul negativ corespunz ator), cu o precizie
de 7 cifre. Double poate reprezenta valori cuprinse ntre 5.0 10
324
si
1.7 10
308
cu o precizie de 15-16 cifre.
Operatiile cu oating point nu duc niciodat a la aparitia de exceptii, dar
ele pot duce, n caz de operatii invalide, la valori 0, innit sau NaN.
Literalii care specic a un num ar reprezentat n virgul a mobil a au forma:
literalreal::
cifre-zecimale . cifre-zecimale exponent
optional
sux-de-tip-real
optional
. cifre-zecimale exponent
optional
sux-de-tip-real
optional
cifre-zecimale exponent sux-de-tip-real
optional
cifre-zecimale sux-de-tip-real,
unde
exponent::
e semn
optional
cifre-zecimale
E semn
optional
cifre-zecimale,
semn este + sau -, sux-de-tip-real este F, f, D, d. Dac a nici un sux de tip
real nu este specicat, atunci literalul dat este de tip double. Suxul f sau
F specic a tip oat, d sau D specic a double. Dac a literalul specicat nu
poate reprezentat n tipul precizat, apare eroare de compilare.
Tipul decimal
Este un tip de date reprezentat pe 128 de biti, g andit a folosit n calcule
nanciare sau care necesit a precizie mai mare. Poate reprezenta valori aate
n intervalul 1.0 10
28
si 7.9 10
28
, cu 28 de cifre semnicative. Acest tip
nu poate reprezenta zero cu semn, innit sau NaN. Dac a n urma operatiilor,
un num ar este prea mic pentru a putea reprezentat ca decimal, atunci el
este f acut 0, iar dac a este prea mare, rezult a o exceptie. Diferenta principal a
fat a de tipurile n virgul a mobil a este c a are o precizie mai mare, dar un
domeniu de reprezentare mai mic. Din cauza aceasta, nu se fac conversii
implicite ntre nici un tip n virgul a mobil a si decimal si nu este posibil a
mixarea variabilelor de acest tip ntr-o expresie, f ar a conversii explicite.
Literalii de acest tip se exprim a folosind ca sux-de-tip-real caracterele m
sau M. Dac a valoarea specicat a nu poate reprezentat a prin tipul decimal,
apare o eroare la compilare.
32 CURS 2. TIPURI PREDEFINITE, TABLOURI, STRING-URI
Tipul bool
Este folosit pentru reprezentarea valorilor de adev ar true si false. Literalii
care se pot folosi sunt true si false. Nu exist a conversii standard ntre bool
si nici un alt tip.
2.2.3 Tipul enumerare
Tipul enumerare este un tip valoare, construit pentru a permite declararea
constantelor nrudite, ntro manier a clar a si sigur a din punct de vedere al
tipului. Un exemplu este:
using System;
public class Draw
{
public enum LineStyle
{
Solid
Dotted,
DotDash
}
public void DrawLine(int x1, int y1, int x2, int y2,
LineStyle lineStyle)
{
if (lineStyle == LineStyle.Solid)
{
//cod desenare linie continua
}
else
if (lineStyle == LineStyle.Dotted)
{
//cod desenare linie punctata
}
else
if (lineStyle == LineStyle.DotDash)
{
//cod desenare segment linie-punct
}
else
{
throw new ArgumentException(Invalid line style);
2.2. TIPURI DE DATE 33
}
}
}
class Test
{
public static void Main()
{
Draw draw = new Draw();
draw.DrawLine(0, 0, 10, 10, Draw.LineStyle.Solid);
draw.DrawLine(0, 0, 10, 10, (Draw.LineStyle)100);
}
}
Al doilea apel este legal, deoarece valorile care se pot specica pentru un
enum nu sunt limitate la valorile declarate n enum. Ca atare, programatorul
trebuie s a fac a valid ari suplimentare pentru a determina consistenta valorilor.
A 49
{
return new MyClass(a.myField + b.myField);
}
}
class Test
{
static void Main()
{
MyClass a = new MyClass();
MyClass b = new MyClass(1);
Console.WriteLine("MyConst={0}", MyClass.MyConst);
//a.myField++;//gradul de acces nu permite lucrul direct cu campul
a.MyMethod();
a.MyProperty++;
Console.WriteLine("a.MyProperty={0}", a.MyProperty);
a[3] = a[1] = a[2];
Console.WriteLine("a[3]={0}", a[3]);
a.MyEvent += new EventHandler(MyHandler);
MyClass c = a + b;
}
static void MyHandler(object Sender, EventArgs e)
{
Console.WriteLine("Test.MyHandler");
}
internal class MyNestedType
{}
}
Constanta este un membru al unei clase care reprezint a o valoare nemodicabil a,
care poate evaluat a la compilare. Constantele pot depinde de alte
constante, at ata timp c at nu se creeaz a dependente circulare. Ele sunt
considerate automat membri statici (dar este interzis s a se foloseasc a
specicatorul static n fata lor). Ele pot accesate exclusiv prin
intermediul numelui de clas a (MyClass.MyConst), si nu prin intermediul
vreunei instante (a.MyConst).
Campul este un membru asociat ec arui obiect; c ampul stocheaza o valoare
care contribuie la starea obiectului.
50 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
Metoda este un membru care implementeaz a un calcul sau o actiune care
poate efectuat a asupra unui obiect sau asupra unei clase. Metodele
statice (care aun antet cuv antul cheie static) sunt accesate prin intermediul
numelui de clas a, pe c and cele nestatice (metode instant a) sunt apelate
prin intermediul unui obiect.
Proprietatea este un membru care d a acces la o caracteristic a a unui obiect
sau unei clase. Exemplele folosite p an a acum includeau lungimea unui
vector, num arul de caractere ale unui sir de caractere, etc. Sintaxa
pentru accesara c ampurilor si a propriet atilor este aceeasi. Reprezint a
o alt a modalitate de implementare a accesorilor pentru obiecte.
Evenimentul este un membru care permite unei clase sau unui obiect s a
pun a la dispozitia altora notic ari asupra evenimentelor. Tipul acestei
declaratii trebuie s a e un tip delegat. O instant a a unui tip delegat
ncapsuleaz a una sau mai multe entit ati apelabile. Exemplu:
public delegate void EventHandler(object sender,
System.EventArgs e);
public class Button
{
public event EventHandler Click;
public void Reset()
{
Click = null;
}
}
using System;
public class Form1
{
Button Button1 = new Button1();
public Form1()
{
Button1.Click += new EventHandler(Button1_Click);
}
void Button1_Click(object sender, EventArgs e )
{
Console.WriteLine("Button1 was clicked!");
3.2. TRANSMITEREA DE PARAMETRI 51
}
public void Disconnect()
{
Button1.Click -= new EventHandler(Button1_Click);
}
}
Mai sus clasa Form1 adaug a Button1_Click ca tratare de eveniment
1
pentru evenimentul Click al lui Button1.
In metoda Disconnect(), acest
event handler este nl aturat.
Operatorul este un membru care deneste semnicatia (supranc arcarea)
unui operator care se aplic a instantelor unei clase. Se pot supranc arca
operatorii binari, unari si de conversie.
Indexatorul este un membru care permite unui obiect s a e indexat n
acelasi mod ca un tablou (pentru programatorii C++: supranc arcarea
operatorului []).
Constructorii instanta sunt membri care implementeaz a actiuni cerute
pentru initializarea ec arui obiect.
Destructorul este un membru special care implementeaz a actiunile cerute
pentru a distruge o instant a a unei clase. Destructorul nu are parametri,
nu poate avea modicatori de acces, nu poate apelat explicit si este
apelat automat de c atre garbage collector.
Constructorul static este un membru care implementeaz a actiuni necesare
pentru a initializa o clas a, mai exact membrii statici ai clasei. Nu poate
avea parametri, nu poate avea modicatori de acces, nu este apelat
explicit, ci automat de c atre sistem.
Mostenirea este de tip simplu, iar r ad acina ierarhiei este clasa object (alias
System.Object).
3.2 Transmiterea de parametri
In situatia de mai sus, conictul (care poate foarte usor n cazul n care se
folosesc tipuri produse de dezvoltatori diferiti) poate rezolvat de o calicare
complet a:
namespace N3
{
using N1;
using N2;
class B
{
74 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
N1.A a1 = null;
N2.A a2 = null;//ok, nu mai este ambiguitate
}
}
Tipurile declarate n interiorul unui spatiu de nume pot avea modicatori de
acces public sau internal (modicatorul implicit). Un tip internal nu poate
folosit prin import n afara assembly-ului, pe c and unul public, da.
Directiva using ca alias
Introduce un identicator care serveste drept alias pentru un spatiu de
nume sau pentru un tip.
Exemplu:
namespace N1.N2
{
class A{}
}
namespace N3
{
using A = N1.N2.A;
class B
{
A a = null;
}
}
Acelasi efect se obtine crend un alias la spatiul de nume N1.N2:
namespace N3
{
using N = N1.N2;
class B
{
N.A a = null;
}
}
Identicatorul dat unui alias trebuie s a e unic, adic a n interiorul unui
namespace nu trebuie s a existe tipuri si aliasuri cu acelasi nume:
3.6. SPA TII DE NUME 75
namespace N3
{
class A{}
}
namespace N3
{
using A = N1.N2.A;//eroare, deoarece simbolul A mai este definit
}
O directiv a alias afecteaz a doar blocul n care este denit a:
namespace N3
{
using R = N1.N2;
}
namespace N3
{
class B
{
R.A a = null;//eroare, R nu este definit aici
}
}
adic a directiva de alias nu este tranzitiv a. Situatia de mai sus se poate rezolva
prin declarearea aliasului n afara spatiului de nume:
using R = N1.N2;
namespace N3
{
class B
{
R.A a = null;
}
}
namespace N3
{
class C
{
R.A b = null;
}
}
Numele create prin directive de alias sunt ascunse de c atre alte declaratii
care folosesc acelasi identicator n interiorul unui bloc:
76 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
using R = N1.N2;
namespace N3
{
class R{}
class B
{
R.A a;//eroare, clasa R nu are membrul A
}
}
Directivele de alias nu se inuenteaz a reciproc:
namespace N1.N2{}
namespace N3
{
using R1 = N1;
using R2 = N1.N2;
using R3 = R1.N2;//eroare, R1 necunoscut
}
3.7 Declararea unei clase
Declararea unei clase se face n felul urm ator:
atribute
opt
modicatori-de-clasa
opt
class identicator clasa-de-baza
opt
corp-
clasa ;
opt
Modicatorii de clas a sunt:
public - clasele publice sunt accesibile de oriunde; poate folosit at at pentru
clase imbricate, c at si pentru clase care sunt continuten spatii de nume;
internal - se poate folosi at at pentru clase imbricate, c at si pentru clase care
sunt continute n spatii de nume (este modicatorul implicit pentru
clase care sunt continute n spatii de nume). Semnic a acces permis
doar n clasa sau spatiul de nume care o cuprinde;
protected - se poate specica doar pentru clase imbricate; tipurile astfel
calicate sunt accesibile n clasa curent a sau n cele derivate (chiar
dac a clasa derivat a face parte din alt spatiu de nume);
private - doar pentru clase imbricate; semnic a acces limitat la clasa con-
tin atoare; este modicatorul implicit;
3.8. MEMBRII UNEI CLASE 77
protected internal - folosibil doar pentru clase imbricate; tipul denit este
accesibil n spatiul de nume curent, n clasa contin atoare saun tipurile
derivate din clasa contin atoare;
new - permis pentru clasele imbricate; clasa astfel calicat a ascunde un
membru cu acelasi nume care este mostenit;
sealed - o clas a sealed nu poate mostenit a; poate clas a imbricat a sau
nu;
abstract - clasa care este incomplet denit a si care nu poate instantiat a;
folosibil a pentru clase imbricat sau continute n spatii de nume;
partial - clasa este denit a n mai multe siere
3.8 Membrii unei clase
Corpul unei clase se specic a n felul urm ator:
{ declaratii-de-membri };
opt
Membrii unei clase sunt mp artiti n urm atoarele categorii:
constante
c ampuri
metode
propriet ati
evenimente
indexatori
operatori
constructori (de instant a)
destructor
constructor static
tipuri
Acestor membri le pot atasati modicatorii de acces:
public - membrul este accesibil de oriunde;
78 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
protected - membrul este accesabil de c atre orice membru al clasei contin atoare
si de c atre clasele derivate;
internal - membrul este accesabil doar n assembly-ul curent;
protected internal - reuniunea precedentelor dou a;
private - accesabil doar n clasa contin atoare; este specicatorul implicit.
3.9 Constructori de instant a
Un constructor de instant a este un membru care implementeaz a actiuni
care sunt cerute pentru a initializa o instant a a unei clase. Declararea unui
astfel de constructor se face n felul urm ator:
atribute
opt
modicatori-de-constructor declarator-de-constructor corp-constructor
Un modicator de constructor poate : public, protected, internal, private,
extern. Un declarator de constructor are forma:
nume-clasa (lista-parametrilor-formali
opt
) initializator-de-constructor
opt
unde initializatorul-de-constructor are forma:
: base( lista-argumente
opt
) sau
: this( lista-argumente
opt
).
Corp-constructor poate : un bloc de declaratii si instructiuni delimitat de
acolade sau caracterul punct si virgul a.
Un constructor are acelasi nume ca si clasa din care face parte si nu
returneaz a un tip. Constructorii de instant a nu se mostenesc. Dac a o clas a
nu contine nici o declaratie de constructor de instant a, atunci compilatorul va
crea automat unul implicit. O clas a care este mostenit a dintr-o alt a clas a ce
nu are constructori f ar a parametri va trebui s a utilizeze un apel de constructor
de clas a de baz a pentru care s a furnizeze parametrii potriviti; acest apel se
face prin intermediul initializatorului de constructor. Un constructor poate
apela la un alt constructor al clasei din care face parte pentru a efectua
initializri. C and exist a c ampuri instant a care au o expresie de initializare
n afara constructorilor clasei respective, atunci aceste initializ ari se vor face
nainte de apelul de constructor al clasei de baz a.
3.10 C ampuri
Un c amp reprezint a un membru asociat cu un obiect sau cu o clas a.
Modicatorii de c amp care se pot specica optional naintea unui c amp sunt
cei de mai sus, la care se adaug a modicatorii new, readonly, volatile, static,
3.10. C
AMPURI 79
ce vor prezentati mai jos. Pentru orice c amp este necesar a precizarea unui
tip de date, ce trebuie s a aibe gradul de accesibilitate cel putin cu al c ampului
ce se declar a. Optional, c ampurile pot initializate cu valori compatibile.
Un astfel de c amp se poate folosi e prin specicarea numelui s au, e printr-o
calicare bazat a pe numele clasei sau al unui obiect.
Exemplu:
class A
{
int a;//acces implicit de tip privat
static void Main()
{
A objA = new A();
objA.a = 1;//se poate accesa in interiorul clasei
}
}
3.10.1 Campuri instante
Dac a o declaratie de c amp nu include modicatorul static, atunci acel
c amp se va reg asi n orice obiect de tipul clasei curente care va instantiat.
Modic ari ale acestor c ampuri se vor face independent pentru ecare obiect.
Deoarece un astfel de c amp are o valoare specic a ec arui obiect, accesarea
lui se va face prin calicarea cu numele obiectului:
objA.a = 1;
(dac a modicatorii de acces permit asa ceva).
In interiorul unui instante de
clase se poate folosi cuv antul this, reprezent and referint a la obiectul curent.
3.10.2 Campuri statice
C and o declaratie de c amp include un specicator static, c ampul respectiv
nu apatine ec arei instante n particular, ci clasei ns asi. Accesarea unui
c amp static din exteriorul clasei nu se face prin intermediul unui obiect, ci
prin numele clasei:
class B
{
public static int V = 3;
static void Main()
{
80 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
B.V++;//corect
V++;//corect
B b = new B();
b.V++//incorect
}
}
Dac a se face calicarea unui astfel de c amp folosind un nume de obiect se
semnaleaz a o eroare de compilare.
3.10.3 Campuri readonly
Declararea unui c amp de tip readonly (static sau nu) se face prin specicarea
cuv antului readonly n declaratia sa:
class A
{
public readonly string salut = Salut;
public readonly string nume;
public class A(string nume)
{
this.nume = nume;
}
}
Atribuirea asupra unui c amp de tip readonly se poate face doar la declararea
sa (can exemplu de mai sus) sau prin intermediul unui constructor. Valoarea
unor astfel de c ampuri nu e obligatorie a cunoscute la compilare.
3.10.4 Campuri volatile
Modicatorul "volatile" se poate specica doar pentru tipurile:
byte, sbyte, short, ushort, int, uint, char, oat, bool;
un tip enumerare av and tipul de reprezentare byte, sbyte, short, ushort,
int, uint;
un tip referint a
Pentru c ampuri nevolatile, tehnicile de optimizare care reordoneaz a instructiunile
pot duce la rezultate neasteptate sau nepredictibilen programe multithreading
care acceseaz a c ampurile f ar a sincronizare (efectuabil a cu instructiunea lock).
3.11. CONSTANTE 81
Aceste optimiz ari pot f acute de c atre compilator, de catre sistemul de
rulare
5
sau de c atre hardware. Urm atoarele tipuri de optimiz ari sunt afectate
n prezenta unui modicator volatile:
citirea unui c amp volatile este garantat a c a se va nt ampla nainte de
orice referire la c amp care apare dup a citire;
orice scriere a unui c amp volatile este garantat a c a se va petrece dup a
orice instructiune anterioar a care se refer a la c ampul respectiv.
3.10.5 Initializarea campurilor
Pentru ecare c amp declarat se va asigna o valoare implicit a astfel:
numeric: 0
bool: false
char: \0
enum: 0
referint a: null
3.11 Constante
O constant a este un c amp a c arui valoare poate calculata la compilare.
O constant a poate prexat a de urm atorii modicatori: new, public, protected,
internal, protected internal,private. Cuvantul new poate s a se combine cu
unul din ceilalti 4 modicatori de acces. Pentru un c amp constant e obligatoriu
s a se asigneze o valoare calculabil a la compilare:
class A
{
public const int n=2;
}
Tipul unei constante poate sbyte, byte, short, ushort, int, uint, long, ulong,
char, oat, double decimal, bool, string, enum, referint a. Valoarea care se
asignez a unei constante trebuie s a admit a o conversie implicit a c atre tipul
constantei. Tipul unei constante trebuie s a e cel putin la fel de accesibil ca
si constanta ns asi.
5
Engl: runtime system
82 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
Orice c amp constant este automat un c amp static. Un c amp constant
difer a de un c amp static readonly: const-ul are o valoare cunoscut a la compilare,
pe c and valoarea unui readonly poate initializat a la runtime n interiorul
constructorului (cel mai t arziu, de altfel).
3.12 Metode
O metod a este un membru care implementeaz a o actiune care poate
efectuat a de c atre un obiect sau o clas a. Antetul unei metode se declar a n
felul urm ator:
atribute
opt
modicator-de-metoda
opt
tip-de-retur nume (lista-parametrilor-
formali
opt
) corp-metoda
unde modicator-de-metoda poate :
orice modicator de acces
new
static
virtual
sealed
override
abstract
extern
Tipul de retur poate orice tip de dat a care este cel putin la fel de accesibil
ca si metoda ns asi sau void (absenta informatiei returnate); nume poate
un identicator de metod a din clasa curent a sau un identicator calicat cu
numele unei interfete pe care o implementeaz a (NumeInterfata.numeMetoda);
parametrii pot de tip ref, out, params, sau f ar a nici un calicator; corp-
metoda este un bloc cuprins ntre acolade sau doar caracterul ; (dac a este
vorba de o metod a ce nu se implementeaz a n tipul curent).
Despre calicatorii virtual, override, sealed, new, abstract se va discuta
mai pe larg ntro sectiune viitoare.
3.12. METODE 83
3.12.1 Metode statice si nestatice
O metod a se declar a a static a dac a numele ei este prexat cu modicatorul
de metod a static. O astfel de metod a nu opereaz a asupra unei instante
anume, ci doar asupra clasei. Este o eroare ca o metod a static a s a fac a
referire la un membru nestatic al unei clase. Apelul unei astfel de metode se
face prin NumeClasa.NumeMetoda sau direct NumeMetoda dac a este apelat a
din context static al aceleiasi clase (de exemplu de c atre o metod a static a,
dar se poate si dintro clas a imbricat a a se vedea sectiunea dedicat a 4.5).
O metod a nestatic a nu are cuv antul static specicat; ea este apelabil a
pentru un obiect anume.
3.12.2 Metode externe
Metodele externe se declar a folosind modicatorul extern; acest tip de
metode sunt implementate extern, de obicei n alt limbaj dec at C#. Deoarece
o astfel de metod a nu contine o implementare, corpul acestei metode este ;.
Exemplu: se utilizeaz a metoda MessageBox importat a din biblioteca dll
User32.dll:
using System;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c,
int type);
static void Main(string[] args)
{
int retVal = MessageBox(0, "Hello", "Caption", 0);
}
}
84 CURS 3. CLASE, INSTRUC TIUNI, SPA TII DE NUME
Curs 4
Clase (continuare)
4.1 Propriet ati
O proprietate este un membru care permite acces la partea de stare a
unei clase. Exemple de propriet ati sunt: lungimea unui sir, numele unui
client, textul continut ntrun control de tip TextBox. Propriet atile sunt
extensii naturale ale c ampurilor, cu deosebirea c a ele nu presupun alocarea
de memorie. Ele sunt de fapt niste metode (accesori) care permit citirea sau
setarea unor atribute ale unui obiect sau clase; reprezint a modalitatea de
scriere a unor metode de tip get/set pentru clase sau obiecte.
Declararea unei propriet ati se face astfel:
modicator-de-proprietate
opt
tip numeproprietate denitie-get
opt
denitie-set
opt
unde modicator-de-proprietate este: atribute
opt
modicator-de-acces
opt
get
corp-get
atribute
opt
modicator-de-acces
opt
set corp-set
Modicatorii de acces sunt: protected, internal, private, protected internal,
public.
Tipul unei propriet ati specic a tipul de dat a ce poate accesat, i.e. ce
valori vor putea atribuite propriet atii respective (dac a accesorul de tip set a
fost denit), respectiv care este tipul valorii returnate de aceast a proprietate
(corespunz ator accesorului de tip get).
Exemplu:
using System;
class Circle
{
private double radius;
public double Radius
{
85
86 CURS 4. CLASE (CONTINUARE)
get
{
return radius;
}
set
{
radius = value;
}
}
public double Area
{
get
{
return Math.PI * radius * radius;
}
set
{
radius = Math.Sqrt(value/Math.PI);
}
}
}
class Test
{
static void Main()
{
Circle c = new Circle();
c.Radius = 10;
Console.WriteLine(Area: {0}, c.Area);
c.Area = 15;
Console.WriteLine(Radius: {0}. c.Radius)
}
}
Un accesor de tip get corespunde unei metode f ar a parametri, care returneaz a
o valoare de tipul propriet atii. C and o proprietate este folosit antro expresie,
accesorul get este o apelat pentru a returna valoarea cerut a.
Un accesor de tip set corespunde unei metode cu un singur parametru
de tipul propriet atii si tip de retur void. Acest parametru implicit al lui set
este numit ntotdeauna value. C and o proprietate este folosit a ca destinatar
ntro atribuire, sau c and se folosesc operatorii ++ si , accesorului set i
4.1. PROPRIET
A TI 87
se transmite un parametru care reprezint a noua valoare.
Intrun asemenea caz, trebuie respectat a urm atoarea regul a: ntreaga proprietate
trebuie s a e declarat a cu grad de acces mai larg dec at accesorul pentru care
se restictioneaz a gradul de acces.
Demn de mentionat este c a propriet atile pot folosite nu doar pentru
a asigura o sintax a simplu de folosit pentru metodele traditionale de tip
get/set, ci si pentru scrierea controalelor .NET utilizator.
A TI 89
#region Component Designer generated code
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.streetTextBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.numberTextBox = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.phoneTextBox = new System.Windows.Forms.TextBox();
this.SuspendLayout();
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(8, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(34, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Street";
this.streetTextBox.Location = new System.Drawing.Point(56, 14);
this.streetTextBox.Name = "streetTextBox";
this.streetTextBox.TabIndex = 1;
this.streetTextBox.Text = "";
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(8, 48);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(44, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Number";
this.numberTextBox.Location = new System.Drawing.Point(56, 44);
this.numberTextBox.Name = "numberTextBox";
this.numberTextBox.TabIndex = 3;
this.numberTextBox.Text = "";
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(8, 79);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(37, 13);
this.label3.TabIndex = 4;
this.label3.Text = "Phone";
this.phoneTextBox.Location = new System.Drawing.Point(56, 75);
this.phoneTextBox.Name = "phoneTextBox";
this.phoneTextBox.TabIndex = 5;
this.phoneTextBox.Text = "";
90 CURS 4. CLASE (CONTINUARE)
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.phoneTextBox,
this.label3,
this.numberTextBox,
this.label2,
this.streetTextBox,
this.label1});
this.Name = "UserControl1";
this.Size = new System.Drawing.Size(168, 112);
this.ResumeLayout(false);
}
#endregion
[Category ("Data"), Description ("Contents of Street Control")]
public string Street
{
get{ return streetTextBox.Text; }
set{ streetTextBox.Text = value; }
}
[Category ("Data"), Description ("Contents of Number Control")]
public string Number
{
get{ return numberTextBox.Text; }
set{ numberTextBox.Text = value; }
}
[Category ("Data"), Description ("Contents of Phone Control")]
public string Phone
{
get{ return phoneTextBox.Text; }
set{ phoneTextBox.Text = value; }
}
}
}
Interesante sunt aici propriet atile publice Street, Number si Phone care vor
vizibile n fereastra Properties atunci c and acest control va ad augat la o
form a. Atributele cuprinse ntre paranteze drepte sunt optionale, dar vor face
ca aceste propriet ati s a e grupate n sectiunea de date a ferestrei Properties,
si nu n cea Misc.
4.2. INDEXATORI 91
4.2 Indexatori
Uneori are sens tratarea unui obiect ca ind un vector de elemente. Un
indexator este o generalizare a supranc arc arii operatorului [] din C++.
Declararea unui indexator se face n felul urm ator:
atribute
opt
modicatori-de-indexator
opt
declarator-de-indexator {declaratii-
de-accesori}
Modicatorii de indexator pot : new, public, protected, internal, private,
protected internal, virtual, sealed, override, abstract, extern. Declaratorul de
indexator are forma:
tip-de-retur this[lista-parametrilor-formali].
Lista parametrilor formali trebuie s a contin a cel putin un parametru si nu
poate s a aibe vreun parametru de tip ref sau out. Declaratiile de accesor vor
contine accesor get sau accesor set, asem an ator cu cei de la propriet ati.
Exemple:
1. Exemplul 1: un indexator simplu:
using System;
class MyVector
{
private double[] v;
public MyVector( int length )
{
v = new double[ length ];
}
public int Length
{
get
{
return length;
}
}
public double this[int index]
{
get
{
return v[ index];
}
set
{
92 CURS 4. CLASE (CONTINUARE)
v[index] = value;
}
}
}
class Test
{
static void Main()
{
MyVector v = new MyVector( 10 );
v[0] = 0;
v[1] = 1;
for( int i=2; i<v.Length; i++)
{
v[i] = v[i-1] + v[i-2];
}
for( int i=0; i<v.Length; i++)
{
Console.WriteLine(v[ + i.ToString() + ]= + v[i]);
}
}
}
2. Exemplul 2: supranc arcarea indexatorilor:
using System;
using System.Collections;
class DataValue
{
public DataValue(string name, object data)
{
this.name = name;
this.data = data;
}
public string Name
{
get
{
return(name);
}
set
4.2. INDEXATORI 93
{
name = value;
}
}
public object Data
{
get
{
return(data);
}
set
{
data = value;
}
}
string name;
object data;
}
class DataRow
{
ArrayList row;
public DataRow()
{
row = new ArrayList();
}
public void Load()
{
row.Add(new DataValue("Id", 5551212));
row.Add(new DataValue("Name", "Fred"));
row.Add(new DataValue("Salary", 2355.23m));
}
public object this[int column]
{
get
{
return(row[column - 1]);
}
94 CURS 4. CLASE (CONTINUARE)
set
{
row[column - 1] = value;
}
}
private int findColumn(string name)
{
for (int index = 0; index < row.Count; index++)
{
DataValue dataValue = (DataValue) row[index];
if (dataValue.Name == name)
return(index);
}
return(-1);
}
public object this[string name]
{
get
{
return this[findColumn(name)];
}
set
{
this[findColumn(name)] = value;
}
}
}
class Test
{
public static void Main()
{
DataRow row = new DataRow();
row.Load();
DataValue val = (DataValue) row[0];
Console.WriteLine("Column 0: {0}", val.Data);
val.Data = 12; // set the ID
DataValue val = (DataValue) row["Id"];
Console.WriteLine("Id: {0}", val.Data);
4.2. INDEXATORI 95
Console.WriteLine("Salary: {0}",
((DataValue) row["Salary"]).Data);
((DataValue)row["Name"]).Data = "Barney"; // set the name
Console.WriteLine("Name: {0}", ((DataValue) row["Name"]).Data);
}
}
3. Exemplul 3: Indexator cu mai multi parametri:
using System;
namespace MyMatrix
{
class Matrix
{
double[,] matrix;
public Matrix( int rows, int cols )
{
matrix = new double[ rows, cols];
}
public double this[int i, int j]
{
get
{
return matrix[i,j];
}
set
{
matrix[i,j] = value;
}
}
public int RowsNo
{
get
{
return matrix.GetLength(0);
}
}
96 CURS 4. CLASE (CONTINUARE)
public int ColsNo
{
get
{
return matrix.GetLength(1);
}
}
static void Main(string[] args)
{
MyMatrix m = new MyMatrix(2, 3);
Console.WriteLine("Lines: {0}", m.RowsNo);
Console.WriteLine("Columns: {0}", m.ColsNo);
for(int i=0; i<m.RowsNo; i++)
{
for( int j=0; j<m.ColsNo; j++)
{
m[i,j] = i + j;
}
}
for(int i=0; i<c.RowsNo; i++)
{
for( int j=0; j<c.ColsNo; j++)
{
Console.Write(c[i,j] + " ");
}
Console.WriteLine();
}
}
}
}
Remarc am ca accesarea elementelor se face prin perechi de tipul get/set,
precum la propriet ati. Ca si n cazul propriet atilor, este posibil ca un accesor
s a aibe un alt grad de acces dec at cel alalt, folosind acelasi mecanism: se
declar a indexatorul ca av and un anumit grad de accesibilitate, iar pentru un
accesor se va declara un grad de acces mai restrictiv.
4.3. OPERATORI 97
4.3 Operatori
Un operator este un membru care deneste semnicatia unei expresii
operator care poate aplicat a unei instante a unei clase. Corespunde supranc arc arii
operatorilor din C++. O declaratie de operator are forma:
atribute
opt
modicatori-de-operator declaratie-de-operator corp-operator
Se pot declara operatori unari, binari si de conversie.
Urm atoarele reguli trebuie s a e respectate pentru orice operator:
1. Orice operator trebuie s a e declarat public si static.
2. Parametrii unui operator trebuie s a e transmisi prin valoare;
3. Acelasi modicator nu poate ap area de mai multe ori n antetul unui
operator
4.3.1 Operatori unari
Supranc arcarea operatorilor unari are forma:
tip operator operator-unar-supraincarcabil (tip identicator) corp
Operatorii unari supranc arcabili sunt: + - ! ++ true false. Urm atoarele
reguli trebuie s a e respectate la supranc arcarea unui operator unar (T
reprezint a clasa care contine denitia operatorului):
1. Un operator +, -, !, trebuie s a preia un singur parametru de tip T si
poate returna orice tip.
2. Un operator ++ sau trebuie s a preia un singur parametru de tip T
si trebuie s a returneze un rezultat de tip T.
3. Un operator unar true sau false trebuie s a preia un singur parametru
de tip T si s a returneze bool.
Operatorii true si false trebuie s a e ori ambii deniti, ori nici unul (altfel
apare o eroare de compilare). Ei sunt necesari pentru cazuri de genul:
if( a==true )
sau
if( a==false )
98 CURS 4. CLASE (CONTINUARE)
Mai general, ei pot folositi ca expresii de control n if, do, while si for,
precum si n operatorul ternar "? :". Desi pare paradoxal, nu este obligatoriu
ca if (a==true) s a e echivalent a cu if (!(a==false)), de exemplu pentru
tipuri SQL care pot avea valoare de nul, ceea ce nu nseam a nici true, nici
false, ci lips a de informatie.
Exemplu:
public struct DBBool
{
private int x;
public static bool operator true(DBBool x)
{
return x.value > 0;
}
public static bool operator false(DBBool x)
{
return x.value <= 0;
}
...
}
Exemplul de mai jos arat a modul n care se face supranc arcarea operatorului
++, care poate folosit at at ca operator de preincrementare c at si ca
operator de postincrementare:
public class IntVector
{
public int Length { ... } // read-only property
public int this[int index] { ... } // read-write indexer
public IntVector(int vectorLength) { ... }
public static IntVector operator++(IntVector iv)
{
IntVector temp = new IntVector(iv.Length);
for (int i = 0; i < iv.Length; ++i)
temp[i] = iv[i] + 1;
return temp;
}
}
class Test
{
static void Main()
4.3. OPERATORI 99
{
IntVector iv1 = new IntVector(4); // vector of 4x0
IntVector iv2;
iv2 = iv1++; // iv2 contains 4x0, iv1 contains 4x1
iv2 = ++iv1; // iv2 contains 4x2, iv1 contains 4x2
}
}
4.3.2 Operatori binari
Declararea unui operator binar se face astfel:
tip operator operator-binar-supraincarcabil ( tip identicator, tip identicator)
corp
Operatorii binari supranc arcabili sunt: + - * / % & | ^ << >> == != > < >= <=.
Cel putin unul dintre cei doi parametri preluati trebuie s a e de tipul contin ator.
Operatorii de shiftare trebuie s a aib a primul parametru de tipul clasei n care
se declar a, iar al doilea parametru de tip int. Unii operatori trebuie s a se
declare n pereche:
1. operatorii == si !=
2. operatorii > si <
3. operatorii >= si <=
Pentru operaratorul ==, este indicat a si denirea metodei Equals(), deoarece
tipul respectiv va putea astfel folosit si de c atre limbaje care nu suport a
supranc arcarea operatorilor, dar pot apela metoda polimorc a Equals() denit a
n clasa object.
Nu se pot supanc arca operatorii + =, =, / =, =; dar pentru ca
acestia s a functioneze, este sucient s a se suprancarce operatorii corespunz atori:
+, , /, .
4.3.3 Operatori de conversie
O declaratie de operator de conversie trebuie introduce o conversie denit a
de utilizator, care se va ad auga (dar nu va suprascrie) la conversiile predenite.
Declararea unui operator de conversie se face astfel:
implicit operator tip (tip parametru) corp
explicit operator tip (tip parametru) corp
Dup a cum se poate deduce, conversiile pot implicite sau explicite. Un astfel
de operator va face conversia de la un tip surs a, indicat de tipul parametrului
100 CURS 4. CLASE (CONTINUARE)
din antet la un tip destinatie, indicat de tipul de retur. O clas a poate s a
declare un operator de conversie de la un tip surs a S la un tip destinatie T
cu urm atoarele conditii:
1. S si T sunt tipuri diferite
2. Unul din cele dou a tipuri este clasa n care se face denirea.
3. T si S nu sunt object sau tip interfat a.
4. T si S nu sunt baze una pentru cealalt a.
Un bun design asupra operatorilor de conversie are n vedere urm atoarele:
Conversiile implicite nu ar trebui s a duc a la pierdere de informatie sau
la aparitia de exceptii;
Dac a prima conditie nu estendeplinit a, atunci neap arat trebuie declarat a
ca o conversie explicit a.
Exemplu:
using System;
public class Digit
{
byte value;
public Digit(byte value)
{
if (value < 0 || value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
return d.value;
}
public static explicit operator Digit(byte b)
{
return new Digit(b);
}
}
Prima conversie este implicit a pentru c a nu va duce la pierderea de informatie.
Cea de doua poate s a arunce o exceptie (via constructor) si de aceea este
declarat a ca si conversie explicit a.
4.3. OPERATORI 101
4.3.4 Exemplu: clasa Fraction
using System;
public class Fraction
{
public Fraction(int numerator, int denominator)
{
Console.WriteLine("In constructor Fraction(int, int)");
this.numerator=numerator;
this.denominator=denominator;
}
public Fraction(int wholeNumber)
{
Console.WriteLine("In Constructor Fraction(int)");
numerator = wholeNumber;
denominator = 1;
}
public static implicit operator Fraction(int theInt)
{
System.Console.WriteLine("In conversie implicita la Fraction");
return new Fraction(theInt);
}
public static explicit operator int(Fraction theFraction)
{
System.Console.WriteLine("In conversie explicita la int");
return theFraction.numerator /
theFraction.denominator;
}
public static bool operator==(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator ==");
if (lhs.denominator * rhs.numerator ==
rhs.denominator * lhs.numerator )
{
return true;
}
return false;
}
public static bool operator !=(Fraction lhs, Fraction rhs)
{
102 CURS 4. CLASE (CONTINUARE)
Console.WriteLine("In operator !=");
return !(lhs==rhs);
}
public override bool Equals(object o)
{
Console.WriteLine("In metoda Equals");
if (! (o is Fraction) )
{
return false;
}
return this == (Fraction) o;
}
public static Fraction operator+(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator+");
// 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8
int firstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction(
firstProduct + secondProduct,
lhs.denominator * rhs.denominator
);
//ar mai trebui facuta reducerea termenilor
}
public override string ToString( )
{
String s = numerator.ToString( ) + "/" +
denominator.ToString( );
return s;
}
private int numerator;
private int denominator;
}
public class Tester
{
static void Main( )
{
Fraction f1 = new Fraction(3,4);
Console.WriteLine("f1: {0}", f1.ToString( ));
Fraction f2 = new Fraction(2,4);
Console.WriteLine("f2: {0}", f2.ToString( ));
4.4. CONSTRUCTOR STATIC 103
Fraction f3 = f1 + f2;
Console.WriteLine("f1 + f2 = f3: {0}", f3.ToString( ));
Fraction f4 = f3 + 5;
Console.WriteLine("f3 + 5 = f4: {0}", f4.ToString( ));
Fraction f5 = new Fraction(2,4);
if (f5 == f2)
{
Console.WriteLine("F5: {0} == F2: {1}",
f5.ToString( ),
f2.ToString( ));
}
}
}
4.4 Constructor static
Un constructor static este un membru care implementeaz a actiunile cerute
pentru initializara unei clase. Declararea unui constructor static se face ca
mai jos:
atribute
opt
modicator-de-constructor-static identicator( ) corp
Modicatorii de constructori statici se pot da sub forma:
extern
opt
static sau
static extern
opt
Constructorii statici nu se mostenesc, nu se pot apela direct si nu se pot supra-
nc arca. Un constructor static se va executa cel mult o dat a ntr-o aplicatie.
Se garanteaz a faptul c a acest constructor se va apela naintea primei cre ari
a unei instante a clasei respective sau naintea primului acces la un membru
static. Acest apel este nedeterminist, necunosc anduse exact c and sau dac a
se va apela. Un astfel de constuctor nu are specicator de acces si poate s a
acceseze doar membri statici.
Exemplu:
class Color
{
public Color(byte red, byte green, byte blue)
{
this.red = red;
this.green = green;
this.blue = blue;
}
byte red;
104 CURS 4. CLASE (CONTINUARE)
byte green;
byte blue;
public static readonly Color Red;
public static readonly Color Green;
public static readonly Color Blue;
// constructor static
static Color()
{
Red = new Color(255, 0, 0);
Green = new Color(0, 255, 0);
Blue = new Color(0, 0, 255);
}
}
class Test
{
static void Main()
{
Color background = Color.Red;
}
}
4.5 Clase imbricate
O clas a contine membri, iar n particular acestia pot si clase.
Exemplul 1:
using System;
class A
{
class B
{
public static void F()
{
Console.WriteLine(A.B.F);
}
}
static void Main()
{
A.B.F();
}
4.5. CLASE IMBRICATE 105
}
Exemplul 2:
public class List
{
// Private data structure
private class Node
{
public object Data;
public Node Next;
public Node(object data, Node next)
{
this.Data = data;
this.Next = next;
}
}
private Node first = null;
private Node last = null;
//Interfata publica
public void AddToFront(object o) {...}
public void AddToBack(object o) {...}
public object RemoveFromFront() {...}
public object RemoveFromBack() {...}
public int Count { get {...} }
}
Accesarea unei clase imbricate se face prin NumeClasaExterioara.NumeCla-
saInterioara (asa cum este ar atat n Exemplul 1), de unde se deduce c a o
clas a imbricat a se comport a ca un membru static al tipului contin ator. O
clas a declarat a n interiorul unei alte clase poate avea unul din gradele de
accesibilitate public, protected internal, protected, internal, private (implicit
este private). O clas a declarat a n interiorul unei structuri poate declarat a
public, internal sau private (implicit private).
Crearea unei instante a unei clase imbricate nu trebuie s a e precedat a
de crearea unei instante a clasei exterioare contin atoare, asa cum se vede
din Exemplul 1. O clas a imbricat a nu are vreo relatie special a cu membrul
predenit this al clasei contin atoare. Altfel spus, nu se poate folosi this n
interiorul unei clase imbricate pentru a accesa membri instant a din tipul
contin ator. Dac a o clas a imbricat a are nevoie s a acceseze membri instant a ai
clasei contin atoare, va trebui s a primeasc a prin constructor parametrul this
care s a se refer a la o astfel de instant a:
106 CURS 4. CLASE (CONTINUARE)
using System;
class C
{
int i = 123;
public void F()
{
Nested n = new Nested(this);
n.G();
}
public class Nested
{
C this_c;
public Nested(C c)
{
this_c = c;
}
public void G()
{
Console.WriteLine(this_c.i);
}
}
}
class Test
{
static void Main()
{
C c = new C();
c.F();
}
}
Se observ a de mai sus c a o clas a imbricat a poate manipula toti membrii din
interiorul clasei contin atoare, indiferent de gradul lor de accesibilitate.
In
cazul n care clasa exterioar a (contin atoare) are membri statici, acestia pot
utilizati f ar a a se folosi numele clasei contin atoare:
using System;
class C
{
private static void F()
{
4.6. DESTRUCTORI 107
Console.WriteLine("C.F");
}
public class Nested
{
public static void G()
{
F();
}
}
}
class Test
{
static void Main()
{
C.Nested.G();
}
}
Clasele imbricate se folosesc intens n cadrul containerilor pentru care trebuie
s a se construiasc a un enumerator. Clasa imbricat a va n acest caz str ans
legat a de container si va duce la o implementare usor de urm arit si de
ntretinut.
4.6 Destructori
Managementul memoriei este f acut sub platforma .NETn mod automat,
de c atre garbage collector, parte component a a CLRului.
Acest mecanism de garbage collection scuteste programatorul de grija
dealoc arii memoriei. Dar exist a situatii n care se doreste s a se fac a management
manual al dealoc arii resurselor (de exemplu al resurselor care tin de sistemul
de operare sau de servere: siere, conexiuni la retea sau la serverul de baze
de date, ferestre, etc, sau al altor resurse al c aror management nu se face
de c atre CLR).
In C# exist a posibilitatea de a lucra cu destructori sau cu
metode de tipul Dispose(), Close().
Un destructor se declar a n felul urm ator:
atribute
opt
extern
opt
~identicator() corp-destructor
unde identicator este numele clasei. Un destructor nu are modicator de
acces, nu poate apelat manual, nu poate supranc arcat, nu este mostenit.
Un destructor este o scurt atur a sintactic a pentru metoda Finalize(), care
este denit a n clasa System.Object. Programatorul nu poate s a suprascrie
sau s a apeleze aceast a metod a.
108 CURS 4. CLASE (CONTINUARE)
Exemplu:
~MyClass()
{
// Perform some cleanup operations here.
}
Metoda de mai sus este automat translatat a n:
protected override void Finalize()
{
try
{
// Perform some cleanup operations here.
}
finally
{
base.Finalize();
}
}
Problema cu destructorul este c a el e chemat doar de c atre garbage
collector, dar acest lucru se face nedeterminist (cu toate c a apelarea de
destructor se face n cele din urm a, dac a programatorul nu mpiedic a explicit
acest lucru).
Exist a cazuri n care programatorul doreste s a fac a dealocarea manual,
astfel nc at s a nu astepte ca garbage collectorul s a apeleze destructorul.
Programatorul poate scrie o metoda care s a fac a acest lucru. Se sugereaz a
denirea unei metode Dispose() care ar trebui s a e explicit apelat a atunci
c and resurse de sistem de operare trebuie s a e eliberate.
In plus, clasa
respectiv a ar trebui s a implementeze interfata System.IDisposable, care contine
aceast a metod a.
In cazul n care sar face conversia explicit a iar obiectul nu este de tipul la
care se face conversia ar rezulta o exceptie: System.InvalidCastException.
Operatorul as
Acest operator este folosit pentru conversii explicite, return and un obiect
de tipul la care se face conversia sau null dac a conversia nu se poate face
(nu se arunc a exceptii). Determinarea validit atii conversiei se face test and
valoarea rezultat a fat a de null: dac a rezultatul e null atunci conversia nu sa
putut face. Ca si precedentul operator se foloseste n special la downcasting.
Exemplu:
Employee e = ...;
Salaried s = e as Salaried;
if (s != null)
{
//se lucreaza cu instanta valida de tip Salaried
}
4.9 Clase sealed
Specicatorul sealed care se poate folosi naintea cuv antului cheie class
specic a faptul c a clasa curent a nu se poate deriva. Este o eroare de compilare
ca o clas a sealed s a e declarat a drept clas a de baz a.
Curs 5
Clase - polimorsm, clase
abstracte. Structuri, interfete,
delegati
5.1 Polimorsmul
Polimorsmul este capacitatea unei entit ati de a lua mai multe forme.
In urma execut arii metodei Main din clasa de mai sus, se va asa:
Polygon.Draw()
Polygon.Draw()
adic a sa apelat metoda corespunz atoare tipului efectiv de la rulare, n ecare
caz.
In functie de specicatorii metodelor f() din ecare clas a, se obtin iesirile din
tabelul 5.1:
Tabelul 5.1: Efecte ale diferitilor specicatori.
Metoda A.f() B.f() C.f() D.f()
Specicator virtual override override override
Iesire secv. 1 A.f B.f C.f D.f
Iesire secv. 2 A.f B.f C.f D.f
Specicator virtual override new override
Iesire secv. 1 A.f B.f B.f D.f
Iesire secv. 2 A.f B.f C.f D.f
Specicator virtual new new new
Iesire secv. 1 A.f A.f A.f A.f
Iesire secv. 2 A.f B.f C.f D.f
Specicator virtual new override override
Eroare de compilare deoarece
C.f nu poate suprascrie
metoda nevirtual a B.f()
Specicator virtual virtual override override
Iesire secv. 1 A.f A.f A.f D.f
Iesire secv. 2 A.f B.f C.f D.f
Avertisment la
compilare deoarece
124 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
Tabelul 5.1(continuare)
Metoda A.f() B.f() C.f() D.f()
B.f nlocuieste A.f
Specicator virtual sealed override override
override
Eroare de compilare deoarece
deoarece B.f nu poate
suprascris a de C.f
5.2 Clase si metode abstracte
Deseori pentru o anumit a clas a nu are sens crearea de instante, din cauza
unei generalit ati prea mari a tipului respectiv. Spunem c a aceast a clas a este
abstracta, iar pentru a mpiedica efectiv crearea de instante de acest tip, se
va specica cuv antul abstract naintea metodei.
In exemplele de anterioare,
clasele Employee si Shape ar putea g andite ca ind abstracte: ele contin
prea putin a informatie pentru a putea crea instante utile.
Analog, pentru o anumit a metod a din interiorul unei clase uneori nu se
poate specica o implementare. De exemplu, pentru clasa Shape de mai
sus, este imposibil s a se dea o implementare la metoda Draw(), tocmai
din cauza generalit atii acestei clase. Ar util dac a pentru aceast a metod a
programatorul ar obligat s a dea implement ari specice ale acestei metode
pentru diversele clase derivate. Pentru a se asigura tratarea polimorc a a
acestui tip abstract, orice metod a abstract a este automat si virtual a. Orice
metod a care este declarat a abstract a implic a declararea clasei ca ind abstract a.
Exemplu:
abstract class Shape
{
public abstract void Draw();
//remarcam lipsa implementarii si semnul punct si virgula
}
Orice clas a care este derivat a dintro clas a abstract a va trebui e s a nu aib a
nici o metod a abstract a mostenit a f ar a implementare, e s a se declare ca ind
abstract a. Existenta de metode neimplementate nu va permite instantierea
clasei respective.
5.3. TIPURI PAR TIALE 125
5.3 Tipuri partiale
Incep and cu versiunea 2.0 a platfomei .NET este posibil ca denitia unei
clase, interfete sau structuri s a e f acut a n mai multe siere surs a. Denitia
clasei se obtine din reuniunea p artilor componente, lucru f acut automat de
c atre compilator. Aceast a spargere n fragmente este benec a n urm atoarele
cazuri:
atunci c and se lucreaz a cu proiecte mari, este posibil ca la o clas a s a
trebuiasc a s a lucreze mai multi programatori simultan - ecare concentr andu
se pe aspecte diferite.
c and se lucreaz a cu cod generat automat, acesta poate scris separat
stfel nc at programatorul s a nu interfereze accidental cu el. Situatia
este frecvent nt alnit a n cazul aplicatiilor de tip Windows Forms.
De exemplu, pentru o form a nou creat a (numit a Form1) mediul Visual Studio
va scrie un sier numit Form1.Designer.cs care contine partea de initializare
a controalelor si componentelor introduse de utilizator. Partea de tratare a
evenimentelor, constructori, etc este denit a ntr-un alt sier (Form1.cs).
Declararea unei p arti a unei clase se face folosind cuv antul cheie partial
naintea lui class.
Exemplu:
//fisierul Browser1.cs
public partial class Browser
{
public void OpenPage(String uri)
{...}
}
//fisierul Browser2.cs
public partial class Browser
{
public void DiscardPage(String uri)
{...}
}
Urm atoarele sunt valabile pentru tipuri partiale:
cuv antul partial trebuie s a apara exactnainte cuvintelor: class, interface,
struct
dac a pentru o parte se specic a un anumit grad de acces, aceasta nu
trebuie s a duc a la conicte cu declaratiile din alte p arti
126 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
dac a o parte de clas a este declarat a ca abstract a, atunci ntreaga clas a
este considerat a abstract a
dac a o parte declar a clasa ca ind sealed, atunci ntreaga clas a este
considerat a sealed
dac a o parte declar a c a mosteneste o clas a, atunci ntr-o alt a parte nu
se mai poate specica o alt a derivare
p arti diferite pot s a declare c a se implementeaz a interfete multiple
aceleasi c ampuri si metode nu pot denite n mai multe p arti.
clasele imbricate pot s a e declarate n p arti diferite, chiar dac a clasa
contin atoare e denit a ntr-un singur sier:
class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
Urm atoarele elemente vor reunite pentru denitia clasei: comentarii
XML, interfete, atribute, membri.
Exemplu:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
este echivalent cu:
class Earth : Planet, IRotate, IRevolve { }
5.4. STRUCTURI 127
5.4 Structuri
Structurile reprezint a tipuri de date asem an atoare claselor, cu principala
diferent a c a sunt tipuri valoare (o astfel de variabil a va contine direct valoarea,
si nu o adres a de memorie). Sunt considerate versiuni usoare ale claselor,
sunt folosite predilect pentru tipuri pentru care aspectul comportamental
este mai putin pronuntat.
Declaratia unei structuri se face astfel:
atribute
opt
modicatori-de-struct
opt
struct identicator :interfete
opt
corp ;
opt
Modicatorii de structur a sunt: new, public, protected, internal, private. O
structur a este automat derivat a din System.ValueType, care la r andul ei este
derivat a din System.Object; de asemenea, este automat considerat a sealed
(nederivabil a). Poate ns a s a implementeze una sau mai multe interfete.
O structur a poate s a contin a declaratii de constante, c ampuri, metode,
propriet ati, evenimente, indexatori, operatori, constructori, constructori statici,
tipuri imbricate. Nu poate contine destructor.
La atribuire, se face o copiere a valorilor continute de c atre surs a n
destinatie (indiferent de tipul c ampurilor: valoare sau referint a).
Exemplu:
using System;
public struct Point
{
public Point(int xCoordinate, int yCoordinate)
{
xVal = xCoordinate;
yVal = yCoordinate;
}
public int X
{
get
{
return xVal;
}
set
{
xVal = value;
}
}
public int Y
{
128 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
get
{
return yVal;
}
set
{
yVal = value;
}
}
public override string ToString( )
{
return (String.Format({0}, {1}, xVal,yVal));
}
public int xVal;
public int yVal;
}
public class Tester
{
public static void MyFunc(Point loc)
{
loc.X = 50;
loc.Y = 100;
Console.WriteLine(In MyFunc loc: {0}, loc);
}
static void Main( )
{
Point loc1 = new Point(200,300);
Console.WriteLine(Loc1 location: {0}, loc1);
MyFunc(loc1);
Console.WriteLine(Loc1 location: {0}, loc1);
}
}
Dup a cum este dat n exemplul de mai sus, crearea unei instante se face
folosind operatorul new; dar n acest caz, nu se va crea o instant a n memoria
heap, ci pe stiv a. Transmiterea lui loc1 f ac anduse prin valoare, adic a metoda
myFunc nu face dec at s a modice o copie de pe stiv a a lui loc1. La revenire,
se va asa tot valoarea original a, deoarece loc1 a r amas nemodicat:
Loc1 location: 200, 300
5.4. STRUCTURI 129
In MyFunc loc: 50, 100
Loc1 location: 200, 300
Deseori pentru o structur a se declar a c ampurile ca ind publice, pentru a
nu mai necesare denirea accesorilor (simplicare implement arii). Alti
programatori consider ans a c a accesarea membrilor trebuie s a se fac a precum
la clase, folosind propriet ati. Oricare ar alegerea, limbajul o sprijin a.
Alte aspecte demne de retinut:
C ampurile nu pot initializate la declarare; altfel spus, dac an exemplul
de mai sus se scria:
public int xVal = 10;
public int yVal = 20;
s-ar semnalat o eroare la compilare.
Nu se poate deni un constructor implicit. Cu toate acestea, compilatorul
va crea un astfel de constructor, care va initializa c ampurile la valorile
lor implicite (0 pentru tipuri numerice sau pentru enumer ari, false
pentru bool, null pentru tipuri referint a).
Pentru tipul Point de mai sus, urm atoarea secvent a de cod este corect a:
Point a = new Point(0, 0);
Point b = new Point();
si duce la crearea a dou a puncte cu abcisele si ordonatele 0. Un
constructor implicit este apelat atunci c and se creeaz a un tablou de
structuri:
Point[] points = new Points[10];
for( int i=0; i<points.Length; i++ )
{
Console.WriteLine(points[i]);
}
va asa de 10 ori puncte de coordonate (0, 0). De asemenea este apelat
la initializarea membrilor de tip structur a ai unei clase.
De mentionat pentru exemplul anterior c a se creeaz a un obiect de tip
tablou n heap, dup a care n interiorul lui (si nu pe stiv a!) se creeaz a
cele 10 puncte (alocare inline).
130 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
Nu se poate declara destructor. Acestia se declar a numai pentru clase.
Dac a programatorul deneste un constructor, atunci acesta trebuie s a
dea valori initiale pentru c ampurile continute, altfel apare eroare la
compilare.
Dac a pentru instantierea unei structuri declarate n interiorul unei
metode sau bloc de instructiuni n general nu se apeleaz a new, atunci
respectiva instant a nu va avea asociat a nici o valoare (constructorul
implicit nu este apelat automat!). Nu se poate folosi respectiva variabil a
de tip structur a dec at dup a ce i se initializeaz a toate c ampurile:
{//bloc de instructiouni
Point p;
Console.WriteLine(p);
}
va duce la aparitia erorii de compilare:
Use of unassigned local variable p
Dar dup a niste asign ari de tipul:
p.xVal=p.yVal=0;
asarea este posibil a (practic, orice apel de metod a pe instant a este
acum acceptat).
Dac a se ncearc a denirea unei structuri care contine un c amp de tipul
structurii, atunci va ap area o eroare de compilare:
struct MyStruct
{
MyStruct s;
}
va genera un mesaj din partea compilatorului:
Struct member MyStruct.s of type MyStruct causes a
cycle in the structure layout
Dac a o instant a este folosit a acolo unde un object este necesar, atunci
se va face automat o conversie implicit a c atre System.Object (boxing).
Ca atare, utilizarea unei structuri poate duce (dar nu obligatoriu, ci n
functie de context) la un overhead datorat conversiei.
5.5. INTERFE TE 131
5.4.1 Structuri sau clase?
Structurile pot mult mai eciente n alocarea memoriei atunci c and sunt
retinute ntrun tablou. De exemplu, crearea unui tablou de 100 de elemente
de tip Point (de mai sus) va duce la crearea unui singur obiect (tabloul),
iar cele 100 de instante de tip structur a ar alocate inline n vectorul creat
(si nu referinte ale acestora). Dac a Point ar declarat ca si clas a, ar fost
necesar a crearea a 101 instante de obiecte n heap (un obiect pentru tablou,
alte 100 pentru puncte), ceea ce ar duce la mai mult lucru pentru garbage
collector si ar putea duce la fragmentarea heap-ului.
Dar n cazul n care structurile sunt folosite n colectii de tip Object
(de exemplu un ArrayList), se va face automat un boxing, ceea ce duce la
overhead (consum suplimentar de memorie si cicli procesor). De asemenea,
la transmiterea prin valoare a unei structuri, se va face copierea tuturor
c ampurilor continute pe stiv a, ceea ce poate duce la un overhead semnicativ.
5.5 Interfete
O interfat a deneste un contract. O clas a sau o structur a care implementeaz a
o interfat a ader a la acest contract. Relatia dintre o interfat a si un tip care o
implementeaz a este deosebit a de cea existent a ntre clase (este un/o): este o
relatie de implementare.
O interfat a contine metode, propriet ati, evenimente, indexatori. Ea ns a
nu va contine implement ari pentru acestea, doar declaratii. Declararea unei
interfete se face astfel:
atribute
opt
modicatori-de-interfat a
opt
interface identicator baza-interfetei
opt
corp-interfat a ;
opt
Modicatorii de interfat a sunt: new, public, protected, internal, private. O
interfat a poate s a mosteneasc a de la zero sau mai multe interfete. Corpul
interfetei contine declaratii de metode, f ar a implement ari. Orice metod a are
gradul de acces public. Nu se poate specica pentru o metod a din interiorul
unei interfete: abstract, public, protected, internal, private, virtual, override,
ori static.
Exemplu:
interface IStorable
{
void Read( );
void Write();
}
O clas a care implementeaz a o astfel de interfat a se declar a ca mai jos:
132 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
class Document: IStorable
{
public void Read(){/*cod*/}
public void Write(){/*cod*/}
//alte declaratii
}
O clas a care implementeaz a o interfat a trebuie s a deneasc a toate metodele
care se reg asesc n interfata respectiv a, sau s a declare metodele din interfat a
ca ind abstracte. Urm atoarele reguli trebuie respectate la implementarea
de interfete:
1. Tipul de retur al metodei din clas a trebuie s a coincid a cu tipul de retur
al metodei din interfat a
2. Tipul parametrilor formali din metod a trebuie s a e acelasi cu tipul
parametrilor formali din interfat a
3. Metoda din clas a trebuie s a e declarat a public a si nestatic a.
Aceste implement ari pot declarate folosind specicatorul virtual (deci subclasele
clasei curente pot folosi new si override).
Exemplu:
using System;
interface ISavable
{
void Read();
void Write();
}
public class TextFile : ISavable
{
public virtual void Read()
{
Console.WriteLine("TextFile.Read()");
}
public void Write()
{
Console.WriteLine("TextFile.Write()");
}
}
public class ZipFile : TextFile
{
5.5. INTERFE TE 133
public override void Read()
{
Console.WriteLine("ZipFile.Read()");
}
public new void Write()
{
Console.WriteLine("ZipFile.Write()");
}
}
public class Test
{
static void Main()
{
Console.WriteLine("\nTextFile reference to ZipFile");
TextFile textRef = new ZipFile();
textRef.Read();
textRef.Write();
Console.WriteLine("\nISavable reference to ZipFile");
ISavable savableRef = textRef as ISavable;
if(savableRef != null)
{
savableRef.Read();
savableRef.Write();
}
Console.WriteLine("\nZipFile reference to ZipFile");
ZipFile zipRef = textRef as ZipFile;
if(zipRef!= null)
{
zipRef.Read();
zipRef.Write();
}
}
}
La iesire se va asa:
TextFile reference to ZipFile
ZipFile.Read()
TextFile.Write()
ISavable reference to ZipFile
ZipFile.Read()
134 CURS 5. CLASE, STRUCTURI, INTERFE TE, DELEGA TI
TextFile.Write()
ZipFile reference to ZipFile
ZipFile.Read()
ZipFile.Write()
In C#, orice obiect poate s a publice un set de evenimente la care alte clase
pot s a subscrie. C and obiectul care a publicat evenimentul l si semnaleaza a,
toate obiectele care au subscris la acest eveniment sunt noticate.
In acest
mod se deneste o dependent a de tip onetomany ntre obiecte astfel nc at
dac a un obiect si schimb a starea, atunci toate celelate obiecte dependente
sunt noticate si modicate automat.
De exemplu, un buton poate s a notice un num ar oarecare de observatori
atunci c and a fost ap asat. Butonul va numit publicator
1
deoarece publi-
c a evenimentul Click iar celelalte clase sunt numite abonati
2
deoarece ele
subscriu la evenimentul Click.
6.2.2 Evenimente si delegati
Tratarea evenimentelor n C# se face folosind delegati. Clasa ce public a
deneste un delegat pe care clasele abonate trebuie s a l implementeze. C and
evenimentul este declansat, metodele claselor abonate vor apelate prin
intermediul delegatului (pentru care se prevede posibilitatea de a multicast,
astfel nc at s a se permit a mai multi abonati).
Metodele care r aspund la un eveniment se numesc event handlers. Prin
conventie, un event handler n .NET Framework returneaz a void si preia doi
parametri: primul parametru este sursa evenimentului (obiectul publicator);
al doilea parametru are tip EventArgs sau derivat din acesta.
Declararea unui eveniment se face astfel:
atribute
opt
modicatori-de-eveniment
opt
event tip numeeveniment
Modicator-de-eveniment poate abstract, new, public, protected, internal,
private, static, virtual, sealed, override, extern. Tip este un handler de
eveniment (delegat multicast).
Exemplu:
public event SecondChangeHandler OnSecondChange;
Vom da mai jos un exemplu care va construi urm atoarele: o clas a Clock
care foloseste un eveniment (OnSecondChange) pentru a notica potentialii
abonati atunci c and timpul local se schimb a cu o secund a. Tipul acestui
eveniment este un delegat SecondChangeHandler care se declar a astfel:
public delegate void SecondChangeHandler(
object clock, TimeInfoEventArgs timeInformation );
1
Engl: publisher
2
Engl: subscribers
6.2. EVENIMENTE 151
n conformitate cu metodologia de declarare a unui event handler, pomenit a
mai sus. Tipul TimeInfoEventArgs este denit de noi ca o clas a derivat a din
EventArgs:
public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs( int hour, int minute, int second )
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}
Aceast a clas a va contine informatie despre timpul curent. Informatia este
accesibil a readonly.
Clasa Clock va contine o metod a Run():
public void Run()
{
for(;;)
{
//dormi 10 milisecunde
Thread.Sleep(10);
//obtine timpul curent
System.DateTime dt = System.DateTime.Now();
//daca timpul s-a schimbat cu o secunda
//atunci notifica abonatii
if( dt.Second != second)
//second este camp al clasei Clock
{
//creeaza obiect TimeInfoEventArgs
//ce va fi transmis abonatilor
TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(dt.Hour, dt.Minute, dt.Second);
//daca cineva este abonat, atunci anunta-l
if (OnSecondChange != null)
{
OnSeconChange(this, timeInformation);
152 CURS 6. METODE ANONIME. EVENIMENTE. EXCEP TII.
}
}
//modifica timpul curent in obiectul Clock
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}
}
Metoda Run creeaz a un ciclu innit care interogheaz a periodic ceasul sistem.
Dac a timpul sa schimbat cu o secund a fat a de timpul precedent, se vor
notica toate obiectele abonate dup a care si va modica starea, prin cele
trei atribuiri nale.
Tot ce r am ane de f acut este s a se scrie niste clase care s a subscrie la
evenimentul publicat de clasa Clock. Vor dou a clase: una numit a DisplayClock
care va asa pe ecran timpul curent si o alta numit a LogCurrentTime care
ar trebui s a nregistreze evenimentul ntrun sier, dar pentru simplitate va
asa doar la dispozitivul curent de iesire informatia transmis a:
public class DisplayClock
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);
}
void TimeHasChanged(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current Time: {0}:{1}:{2}",
ti.hour.ToString( ),
ti.minute.ToString( ),
ti.second.ToString( ));
}
}
public class LogCurrentTime
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);
}
6.2. EVENIMENTE 153
//Aceasta metoda ar trebui sa scrie intr-un fisier
//dar noi vom scrie la consola
void WriteLogEntry(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1}:{2}",
ti.hour.ToString( ),
ti.minute.ToString( ),
ti.second.ToString( ));
}
}
De remarcat faptul c a evenimentele sunt ad augate folosind operatorul +=.
Exemplul n ntregime este dat mai jos:
using System;
using System.Threading;
//o clasa care va contine informatie despre eveniment
//in acest caz va contine informatie disponibila in clasa Clock
public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}
//clasa care publica un eveniment: OnSecondChange
//clasele care se aboneaza vor subscrie la acest eveniment
public class Clock
{
//delegatul pe care abonatii trebuie sa il implementeze
public delegate void SecondChangeHandler(
object clock, TimeInfoEventArgs timeInformation );
//evenimentul ce se publica
public event SecondChangeHandler OnSecondChange;
//ceasul este pornit si merge la infinit
//va declansa un eveniment pentru fiecare secunda trecuta
154 CURS 6. METODE ANONIME. EVENIMENTE. EXCEP TII.
public void Run( )
{
for(;;)
{
//inactiv 10 ms
Thread.Sleep(10);
//citeste timpul curent al sistemului
System.DateTime dt = System.DateTime.Now;
//daca s-a schimbat fata de secunda anterior inregistrata
//atunci notifica pe abonati
if (dt.Second != second)
{
//creaza obiectul TimeInfoEventArgs
//care va fi transmis fiecarui abonat
TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(
dt.Hour,dt.Minute,dt.Second);
//daca cineva a subscris la acest eveniment
//atunci anunta-l
if (OnSecondChange != null)
{
OnSecondChange(this,timeInformation);
}
}
//modifica starea curenta
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}
}
private int hour;
private int minute;
private int second;
}
//un observator (abonat)
//DisplayClock va subscrie la evenimentul lui Clock
//DisplayClock va afisa timpul curent
public class DisplayClock
{
//dandu-se un obiect clock, va subscrie
//la evenimentul acestuia
6.2. EVENIMENTE 155
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);
}
//handlerul de eveniment de pe partea
//clasei DisplayClock
void TimeHasChanged(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current Time: {0}:{1}:{2}",
ti.hour.ToString( ),
ti.minute.ToString( ),
ti.second.ToString( ));
}
}
//un al doilea abonat care ar trebui sa scrie intr-un fisier
public class LogCurrentTime
{
public void Subscribe(Clock theClock)
{
theClock.OnSecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);
}
//acest handler ar trebui sa scrie intr-un fisier
//dar va scrie la standard output
void WriteLogEntry(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1}:{2}",
ti.hour.ToString( ),
ti.minute.ToString( ),
ti.second.ToString( ));
}
}
public class Test
{
static void Main( )
{
//creaza un obiect de tip Clock
Clock theClock = new Clock( );
156 CURS 6. METODE ANONIME. EVENIMENTE. EXCEP TII.
//creaza un obiect DisplayClock care
//va subscrie la evenimentul obiectului
//Clock anterior creat
DisplayClock dc = new DisplayClock( );
dc.Subscribe(theClock);
//analog se creeaza un obiect de tip LogCurrentTime
//care va subscrie la acelasi eveniment
//ca si obiectul DisplayClock
LogCurrentTime lct = new LogCurrentTime( );
lct.Subscribe(theClock);
//porneste ceasul
theClock.Run( );
}
}
La iesire se va asa:
Current Time: 14:53:56
Logging to file: 14:53:56
Current Time: 14:53:57
Logging to file: 14:53:57
Current Time: 14:53:58
Logging to file: 14:53:58
Current Time: 14:53:59
Logging to file: 14:53:59
6.2.3 Comentarii
Sar putea pune urm atoarea ntrebare: de ce este nevoie de o astfel de
redirectare de eveniment, c andn metoda Run() se poate asa direct pe ecran
sau ntrun sier informatia cerut a? Avantajul abordarii anterioare este c a
se pot crea oric ate clase care s a e noticate atunci c and acest eveniment se
declanseaz a. Clasele abonate nu trebuie s a stie despre modul n care lucreaz a
clasa Clock, iar clasa Clock nu trebuie s a stie despre clasele care vor subscrie
la evenimentul s au. Similar, un buton poate s a publice un eveniment OnClick
si orice num ar de obiecte pot subscrie la acest eveniment, primind o noticare
atunci c and butonul este ap asat.
Publicatorul si abonatii sunt decuplati. Clasa Clock poate s a modice
modalitatea de detectare a schimb arii de timp f ar a ca acest lucru s a impun a
o schimbare n clasele abonate. De asemenea, clasele abonate pot s a si
modice modul de tratare a evenimentului, n mod transparent fat a de clasa
Clock. Toate aceste caracteristici fac ntretinerea codului extrem de facil a.
6.3. TRATAREA EXCEP TIILOR 157
6.3 Tratarea exceptiilor
C#, la fel ca alte limbaje, permite tratarea erorilor si a situatiilor deosebite
prin exceptii. O exceptie este un obiect care ncapsuleaz a informatie despre o
situatie anormal a. Ea este folosit a pentru a semnala contextul n care apare
situatia deosebit a
Un programator nu trebuie s a confunde tratarea exceptiilor cu erorile sau
bugurile. Un bug este o eroare de programare care ar trebui s a e xat a
nainte de livrarea codului. Exceptiile nu sunt g andite pentru a preveni bug
urile (cu toate c a un bug poate s a duc a la aparitia unei exceptii), pentru c a
acestea din urm a ar trebui s a e eliminate.
Chiar dac a se scot toate bugurile, vor exista erori predictibile dar neprevenibile,
precum deschiderea unui sier al c arui nume este gresit sau mp artiri la 0.
Nu se pot preveni astfel de situatii, dar se pot manipula astfel nc at nu
vor duce la pr abusirea programului. C and o metod a ntalneste o situatie
exceptional a, atunci se va arunca o exceptie; cineva va trebui s a sesizeze (s a
prind a) aceast a exceptie, sau eventual s a lase o functie de nivel superior s a
o trateze. Dac a nimeni nu trateaz a aceast a exceptie, atunci CLR o va face,
dar aceasta duce la oprirea threadului
3
.
6.3.1 Tipul Exception
Intotdeauna apelati metoda Close() pe un DataReader si pe conexiunea
asociat a c at mai repede posibil; n caz contrar conexiunea nu poate
reutilizat a
Procesarea datelor citite trebuie s a se fac a dup a nchiderea conexiunii;
n felul acesta conexiunea se las a liber a pentru a putea reutilizat a.
8.6.4 Utilizarea de seturi de date multiple
Este posibil ca ntrun DataReader s a se aduc a mai multe seturi de date.
Acest lucru ar micsora num arul de apeluri pentru deschiderea de conexiuni
la stratul de date. Obiectul care permite acest lucru este chiar cel de tip
Command:
string select = "select * from Categories; select * from customers";
SqlCommand command = new SqlCommand ( select, conn );
conn.Open ();
SqlDataReader reader = command.ExecuteReader ();
Trecerea de la un set de date la altul se face cu metoda NextResult() a
obiectului de tip Reader:
do
{
while ( reader.Read () )
{
Console.WriteLine ( "{0}\t\t{1}", reader[0], reader[1] );
}
}while ( reader.NextResult () );
8.6.5 Accesarea datelor ntro maniera sigura din punct
de vedere a tipului
S a consider am urm atoarea secvent a de cod:
while ( reader.Read () )
{
218 CURS 8. ADO.NET
object id = reader["OrderID"];
object date = reader["OrderDate"];
object freight = reader["Freight"];
Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight );
}
Dup a cum se observ a, este posibil ca valorile c ampurilor dintro nregistrare
s a e accesate prin intermediul numelui coloanei (sau a indicelui ei, pornind
de la 0). Dezavantajul acestei metode este c a tipul datelor returnate este
pierdut (ind returnate obiecte de tip Object), trebuind f acut a un downcasting
pentru a utiliza din plin facilit atile tipului respectiv. Pentru ca acest lucru s a
nu se nt ample se pot folosi metodele GetXY care returneaz a un tip specic
de date:
while ( reader.Read () )
{
int id = reader.GetInt32 ( 0 );
DateTime date = reader.GetDateTime ( 3 );
decimal freight = reader.GetDecimal ( 7 );
Console.WriteLine ( "{0}\t{1}\t\t{2}", id, date, freight );
}
Avantajul secventei anterioare este c a dac a se ncearc a aducerea valorii unui
c amp pentru care tipul nu este dat corect se arunc a o exceptie InvalidCastException;
altfel spus, accesul la date se face sigur din punct de verere al tipului datelor.
Pentru a evita folosirea unor constante magice ca indici de coloan a
(precum mai sus: 0, 3, 7), se poate folosi urm atoarea strategie: indicii se
obtin folosind apel de metod a GetOrdinal la care se specic a numele coloanei
dorite:
private int OrderID;
private int OrderDate;
private int Freight;
...
OrderID = reader.GetOrdinal("OrderID");
OrderDate = reader.GetOrdinal("OrderDate");
Freight = reader.GetOrdinal("Freight");
...
reader.GetDecimal ( Freight );
...
Curs 9
ADO.NET (2)
9.1 Obiecte DataAdapter
La fel ca si Connection, Command, DataReader, obiectele de tip DataAdapter
fac parte din furnizorul de date .NET specic ec arui tip de surs a de date.
Scopul clasei este s a permit a umplerea unui obiect DataSet cu date si reectarea
schimb arilor efectuate asupra acestuianapoi n baza de date (DataSet permite
lucrul deconectat de la baza de date).
Orice clas a de tipul DataAdapter (de ex SqlDataAdapter si OleDbDataAdapter)
este derivat a din clasa DbDataAdapter (clas a abstract a). Pentru orice obiect
de acest tip trebuie specicat a minim comanda de tip SELECT care s a
populeze un obiect de tip DataSet; acest lucru este stabilit prin intermediul
propriet atii SelectCommand de tip Command (SqlCommand, OleDbCommand,
. . . ).
In cazul n care se doreste si modicarea informatiilor din sursa de date
(inserare, modicare, stergere) trebuie specicate obiecte de tip comand a via
propriet atile: InsertCommand, UpdateCommand, DeleteCommand.
Exemplu: mai jos se preiaunregistr arile din 2 tabele: Authors si TitleAuthor
si se trec ntrun obiect de tip DataSet pentru a procesate ulterior.
using System;
using System.Data;
using System.Data.SqlClient;
class DemoDataSource
{
static void Main()
{
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings["constring"]
219
220 CURS 9. ADO.NET (2)
.ConnectionString);
DataSet ds = new DataSet();
SqlDataAdapter daAuthors = new SqlDataAdapter("SELECT au_id,
au_fname, au_lname FROM authors",conn);
daAuthors.Fill(ds,"Author");
SqlDataAdapter daTitleAuthor = new SqlDataAdapter("SELECT
au_id, title_id FROM titleauthor", conn);
daTitleAuthor.Fill(ds,"TitleAuthor");
}
}
Prezent am mai jos cele mai importante componente ale unei clase de tip
DataAdapter.
9.1.1 Metode
1. Constructori de la cei impliciti p an a la cei n care se specic a o comand a
de tip SELECT si conexiunea la sursa de date. Pentru un obiect de
tip SqlDataAdapter se poate crea o instant a n urm atoarele moduri:
SqlDataAdapter da = new SqlDataAdapter();
sau:
SqlCommand cmd = new SqlCommand("SELECT * FROM Employees");
SqlDataAdapter da = new SqlDataAdapter(cmd);
sau:
String strCmd = "SELECT * FROM Employees";
String strConn = "...";
SqlDataAdapter da = new SqlDataAdapter(strCmd, strConn);
2. Fill() metod a polimorc a, permit and umplerea unei tabele dintr
un obiect de tip DataSet cu date. Permite specicarea obiectului
DataSet n care se depun datele, eventual a numelui tablei din acest
DataSet, num arul denregistrare cu care s a senceap a popularea (prima
av and indicele 0) si num arul de nregistr ari care urmeaz a a aduse.
Returneaz a de ecare dat a num arul de nregistr ari care au fost aduse
din baz a.
In clipa n care se apeleaz a Fill() se procedeaz a astfel:
9.2. CLASA DATASET 221
(a) Se deschide conexiunea (dac a ea nu a fost explicit deschis a)
(b) Se aduc datele si se populeaz a un obiect de tip DataTable din
DataSet
(c) Se nchide conexiunea (dac a ea nu a fost explicit deschis a!)
De remarcat c a un DataAdapter si poate deschide si nchide singur
conexiunea, dar dac a aceasta a fost deschis a naintea metodei Fill()
atunci tot programatorul trebuie s a o nchid a.
3. Update() metod a polimorc a, permit and reectarea modic arilor efectuate
ntreun DataSet. Pentru a functiona are nevoie de obiecte de tip
comand a adecvate: propriet atile InsertCommand, DeleteCommand si
UpdateCommand trebuie s a indice c atre comenzi valide. Returneaz a
de ecare dat a num arul de nregistr ari afectate.
9.1.2 Proprietati
1. DeleteCommand, InsertCommand, SelectCommand, UpdateCommand
de tip Command, contin comenzile ce se execut a pentru selectarea sau
modicarea datelorn sursa de date. M acar proprietatea SelectCommand
trebuie s a indice c atre un obiect valid, pentru a se putea face popularea
setului de date.
2. MissingSchemaAction de tip enumerare MissingSchemaAction, determin a
ce se face atunci c and datele care sunt aduse nu se potrivesc peste
schema tablei n care sunt depuse. Poate avea urm atoarele valori:
Add - implicit, DataAdapter adaug a coloana la schema tablei
AddWithKey - ca mai sus, dar adaug a si informatii relativ la cine
este cheia primar a
Ignore - se ignor a lipsa coloanei respective, ceea ce duce la pierdere
de date pe DataSet
Error - se genereaz a o exceptie de tipul InvalidOperationException.
9.2 Clasa DataSet
Clasa DataSet nu mai face parte din biblioteca unui furnizor de date
ADO.NET, ind parte din .NET Framework. Ea poate s a contin a reprezent ari
tabelare ale datelor din baz a precum si diferite restrictii si relatii existente.
Marele ei avantaj este faptul c a permite lucrul deconectat de la sursa de date,
222 CURS 9. ADO.NET (2)
elimin and necesitatea unei conexiuni permanente precum la DataReader.
Incep and cu versiunea 2.0 a lui ADO.NET s-a introdus clasa DataTableReader
care permite manipularea unui obiect de tip DataTable ca si cum ar un
DataReader: ntr-o manier a forward-only si read-only. Crearea unui obiect
de tip DataTableReader se face prin:
DataTableReader dtReader = dt.CreateDataReader();
iar folosirea lui:
while (dtReader.Read())
{
for (int i = 0; i < dtReader.FieldCount; i++)
{
Console.Write("{0} = {1} ",
dtReader.GetName(i),
dtReader.GetValue(i).ToString().Trim());
}
Console.WriteLine();
}
dtReader.Close();
9.2. CLASA DATASET 227
9.2.6 Propagarea modicarilor catre baza de date
Pentru a propaga modic arile efectuate asupra continutului tabelelor
dintr-un DataSet c atre baza de date este nevoie s a se deneasc a adecvat
obiecte comand a de tip INSERT, UPDATE, DELETE. Pentru cazuri simple
se poate folosi clasa SqlCommandBuilder care va construi singur a aceste
comenzi.
Clasa CommandBuilder
Un obiect de tip CommandBuilder (ce provine din furnizorul de date) va
analiza comanda SELECT care a adus datele n DataSet si va construi cele
3 comenzi de update n functie de aceasta. E nevoie s a se satisfac a 2 conditii
atunci c and se uzeaz a de un astfel de obiect:
1. Trebuie specicat a o comand a de tip SELECT care s a aduc a datele
dintr-o singur a tabel a
2. Trebuie specicat a cel putin cheia primar a sau o coloan a cu constr angere
de unicitate n comanda SELECT.
Pentru cea de a doua conditie se poate proceda n felul urmator: n comanda
SELECT se specic a si aducerea cheii, iar pentru obiectul DataAdapter
care face aducerea din baz a se seteaz a proprietatea MissingSchemaAction
pe valoarea MissingSchemaAction.AddWithKey (implicit este doar Add).
Fiecare linie modicat a din colectia Rows a unei tabele va avea modicat a
valoarea propriet atii RowState astfel: DataRowState.Added pentru o linie
nou a ad augat a, DataRowState.Deleted dac a e stears a si DataRowState.Modied
dac a a fost modicat a. Apelul de update pe un dataReader va apela comanda
necesar a pentru ecare linie care a fost modicat a, n functie de starea ei.
Ar at am mai jos modul de utilizare a clasei SqlCommandBuilder pentru
ad augarea, modicarea, stergerea de nregistr ari din baza de date.
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings["constring"]
.ConnectionString);
da = new SqlDataAdapter("SELECT id, name, address FROM
customers",conn);
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
da.Fill(ds);
SqlCommandBuilder cb = new SqlCommandBuilder(da);
//determina liniile care au fost schimbate
228 CURS 9. ADO.NET (2)
DataSet dsChanges = ds.GetChanges();
if (dsChanges != null)
{
// modifica baza de date
da.Update(dsChanges);
//accepta schimbarile din dataset
ds.AcceptChanges();
}
INREGISTR
ARI
IN TABEL
A265
inserare automat a a acestui nou obiect de rol si apoi inserarea nregistr arii
de persoan a, cu referint a c atre rolul creat:
using ( PersonDataContext pdc = new PersonDataContext ( ) )
{
pdc . Log = Consol e . Out ;
Person p = new Person { FirstName = " i ns e r t e d " , LastName = "from code " ,
Rol e = new Rol e { Rol eDes cr i pt i on = " t e s t e r " } };
pdc . Persons . InsertOnSubmi t ( p ) ;
pdc . SubmitChanges ( ) ;
}
11.2.2 Modicarea unei nregistrari
Modicarea unei nregistr ari se poate face prin obtinerea obiectului care
va modicat, se modic a propriet atile lui si n nal apelul metodei SubmitChanges
pentru contextul de date:
using ( PersonDataContext pdc = new PersonDataContext ( ) )
{
pdc . Log = Consol e . Out ;
Person person = pdc . Persons . Where( p => p . i d == 1 ) . Si ngl e ( ) ;
person . LastName = " Modi f i ed" ;
pdc . SubmitChanges ( ) ;
}
Mention am doar c a se poate face modicarea unui obiect care a fost adus
ntrun context deschis si nchis anterior, dar cu reatasare la context.
11.2.3 Stergerea unei nregistrari
Printro manevr a asem an atoare cu cea de mai sus, se preia nregistrarea
care trebuie stears a si apoi pentru obiectul rezultat se face stergerea via
metoda DeleteOnSubmit pentru obiectul reprezent and tabelul din care se
sterge:
using ( PersonDataContext pdc = new PersonDataContext ( ) )
{
pdc . Log = Consol e . Out ;
Person person = pdc . Persons . Where( p => p . i d == 9 ) .
Def aul tIf Empty ( ) . Si ngl e ( ) ;
i f ( person != nul l )
{
266 CURS 11. LINQ (II): LINQ TO SQL
pdc . Persons . DeleteOnSubmit ( person ) ;
}
pdc . SubmitChanges ( ) ;
}
11.3 Optiuni de nc arcare a datelor
S a presupunem c a dorim s a aducem lista de rolurim iar pentru ecare rol
persoanele care fac parte din el:
using ( PersonDataContext pdc = new PersonDataContext ( ) )
{
pdc . Log = Consol e . Out ;
var queryRol es = from r o l e in pdc . Rol es
s e l e c t r o l e ;
foreach ( var r o l e in queryRol es )
{
foreach ( var person in r o l e . Persons )
{
Consol e . Wri teLi ne ( " person : FirstName {0} , LastName {1}" ,
person . FirstName , person . LastName ) ;
}
}
}
Se poate observa c a la ecare executare a ciclului foreach exterior se va
apela c ate o instructiune SQL care aduce persoanele care fac parte din rolul
respectiv. Dat a ind abilitatea SQL-ului de a face jonctiuni, ne dorim s a
mbun at actim acest ecomportament. Se poate obtine o nc arcare complet a a
datelor prin utilizarea obiectului DataLoadOptions:
using ( PersonDataContext pdc = new PersonDataContext ( ) )
{
pdc . Log = Consol e . Out ;
DataLoadOptions dl o = new DataLoadOptions ( ) ;
dl o . LoadWith<Role >( r o l e => r o l e . Persons ) ;
pdc . LoadOptions = dl o ;
var queryRol es = from r o l e in pdc . Rol es
s e l e c t r o l e ;
foreach ( var r o l e in queryRol es )
{
foreach ( var person in r o l e . Persons )
11.3. OP TIUNI DE
INC
in cazul acestor din urm a atribute, specicarea lor se face dup a toate declaratiile
using, dar naintea oric arui cod.
In general, specicarea unui atribut se face
prin scrierea lui imediat naintea elementului asupra c aruia se aplic a:
using System;
[Serializable]
class ClasaSerializabila
{
//definitia clasei
}
12.1. ATRIBUTE 271
12.1.2 Atribute predenite
Tabelul 12.2 prezint a c ateva dintre ele:
Tabelul 12.2: Atribute predenite.
Atribut Descriere
System.SerializableAttribute [Serializable] Permite unei clase s a e serializat a
pe disc sau ntro retea
System.NonSerializedAttribute [NonSerialized] Permite unor membri s a nu e
salvati pe retea sau pe disc
System.Web.Services.WebServiceAttribute Permite specicarea unui nume si a
[WebService] unei descrieri pentru un serviciu Web
System.Web.Services.WebMethodAttribute Marcheaz a o metod a ca ind expus a
[WebMethod] ca parte a unui serviciu Web
System.AttributeUsageAttribute Deneste parametrii de utilizare
[AttributeUsage] pentru atribute
System.ObsoleteAttribute [Obsolete] Marcheaz a o secvent a ca ind scoas a
din uz
System.Reection.AssemblyVersionAttribute Specic a num arul de versiune al
[AssemblyVersion] unui assembly
System.Attribute.CLSCompliant Indic a dac a un element de program este
[CLSCompliant] compatibil cu CLS
System.Runtime.InteropServices. specic a locatia DLL care contine
DllImportAttribute [DllImport] implementarea pentru o metod a extern a
A TI
IN C# 4.0
Prezent am urm atorul exemplu preluat din [9]:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace TestPLINQ
{
class Program
{
static void Main()
{
Stopwatch sw = new Stopwatch();
sw.Start();
DoIt();
sw.Stop();
Console.WriteLine("Elapsed = " +
sw.ElapsedMilliseconds.ToString());
}
private static bool isPrime(int p)
{
int upperBound = (int)Math.Sqrt(p);
for (int i = 2; i <= upperBound; i++)
{
if (p % i == 0) return false;
}
return true;
}
static void DoIt()
{
IEnumerable<int> arr = Enumerable.Range(2, 10000000);
var q =
from n in arr
where isPrime(n)
select n.ToString();
IList list = q.ToList();
13.2. PARAMETRI CU NUME SI PARAMETRI OP TIONALI 299
Console.WriteLine(list.Count.ToString());
}
}
}
Pentru un sistem oarecare, timpul mediu de rulare este de aproximativ 17
secunde; rescriind interogarea pentru obtinerea variabilei q astfel:
var q =
from n in arr.AsParallel()
where isPrime(n)
select n.ToString();
timpul mediu obtinut este de aproximativ 10 secunde.
Se pot congura aspecte precum gradul de paralelism, controlul ordinii,
optiuni de utilizare de buere, dac a anumite portiuni s a se ruleze secvential
etc prin folosirea metodelor de extensie precum AsOrdered, AsUnordered
sau prin folosirea enumer arii ParallelQueryMergeOptions.
13.2 Parametri cu nume si parametri optionali
Parametrii optionali permit precizarea unor valori implicite pentru parametrii
unor metode; dac a acestia nu sunt precizati la apel, atunci valorile trimise
metodei sunt cele declarate implicit.
Exemplu:
class Program
{
static void Main(string[] args)
{
MyMethod(3);
MyMethod(3, 4);
}
private static void MyMethod(int p, int q = 100)
{
Console.WriteLine("p= {0}, q={1}", p, q);
}
}
Desi avem o singur a metod a, aceasta suport a cele dou a apeluri. Se poate
chiar s a avem mai multi de un parametru cu valoare implicit a:
300 CURS 13. NOUT
A TI
IN C# 4.0
private static void MyMethod(int p=1, int q = 100)
{
Console.WriteLine("p= {0}, q={1}", p, q);
}
A TI
IN C# 4.0
// The following line causes a compiler error if exampleMethod1 has only
// one parameter.
//ec.exampleMethod1(10, 4);
dynamic dynamic_ec = new ExampleClass();
// The following line is not identified as an error by the
// compiler, but it causes a run-time exception.
dynamic_ec.exampleMethod1(10, 4);
// The following calls also do not cause compiler errors, whether
// appropriate methods exist or not.
dynamic_ec.someMethod("some argument", 7, null);
dynamic_ec.nonexistentMethod();
}
Se pot declara ca ind dinamici si parametrii unei metode:
public static void Log(dynamic x)
{
Console.WriteLine(x.ToString());
}
static void Main(string[] args)
{
var x = 3;
string y = "abc";
Log(x);
Log(y);
}
13.4 COM Interop
COM Interop este o facilitate existent a n versiuni mai vechi ale lui .NET
framework care permite codului managed s a apeleze cod unmanaged de tip
Component Object Model, cum ar aplicatiile Word sau Excel. De exemplu,
pentru utilizarea unei celule de Excel, varianta veche de cod ce trebuia scris a
era:
((Excel.Range)excel.Cells[1, 1]).Value2 = "Hello";
13.4. COM INTEROP 303
pe c and n .NET 4 a se scrie mai inteligibil astfel:
excel.Cells[1, 1].Value = "Hello";
sau n loc de
Excel.Range range = (Excel.Range)excel.Cells[1, 1];
se scrie:
Excel.Range range = excel.Cells[1, 1];
Exemplu:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
namespace TestOffice
{
class Program
{
static void Main(string[] args)
{
var excel = new Excel.Application();
// Make the object visible.
excel.Visible = true;
// Create a new, empty workbook and add it to the collection returned
// by property Workbooks. The new workbook becomes the active workbook.
// Add has an optional parameter for specifying a praticular template.
// Because no argument is sent in this example, Add creates a new workbook.
excel.Workbooks.Add();
excel.Cells[1, 1].Value = "Hello";
var processes = Process.GetProcesses()
.OrderByDescending(p => p.WorkingSet64)
.Take(10);
int i = 2;
foreach (var p in processes)
304 CURS 13. NOUT
A TI
IN C# 4.0
{
excel.Cells[i, 1].Value = p.ProcessName; // no casts
excel.Cells[i, 2].Value = p.WorkingSet64; // no casts
i++;
}
Excel.Range range = excel.Cells[1, 1]; // no casts
dynamic chart = excel.ActiveWorkbook.Charts.
Add(After: excel.ActiveSheet); // named and optional arguments
chart.ChartWizard(
Source: range.CurrentRegion,
Title: "Memory Usage in " + Environment.MachineName); //named+optional
chart.ChartStyle = 45;
chart.CopyPicture(Excel.XlPictureAppearance.xlScreen,
Excel.XlCopyPictureFormat.xlBitmap,
Excel.XlPictureAppearance.xlScreen);
}
}
}
13.5 Covariant a si contravariant a
S a presupunem c a avem secventa de cod:
var strList = new List<string>();
List<object> objList = strList;
Linia a doua, dac a ar permis a, ar predispune la erori, deoarece s-ar permite
mai departe:
objList.Add(new MyClass());
deci sar nc alca punctul de plecare pentru colectie: elementele ar trebui s a
e toate de tip String. Ca atare, acest lucru nu ar avea sens s a e permis.
A SI CONTRAVARIAN T
A 305
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator
{
bool MoveNext();
T Current { get; }
}
unde cuv antul out are un alt sens dec at la transmiterea de parametri: n
C# 4.0,n acest context, semnic a faptul c a tipul T poate s a apar a doar n
pozitia de iesire a unei metode din interfat a. Interfata IEnumerable devine
astfel covariant a n T si se poate face conversie de la IEnumerable<B> la
IEnumerable<A> pentru tipul B derivat din A.
Acest lucru este util pentru o situatie de genul:
var result = strings.Union(objects);
unde se face reuniune ntre o coletie de stringuri si una de obiecte.
Contravarianta permite conversii n sens invers ca la covariant a. De
exemplu, prin declaratia:
public interface IComparer<in T>
{
public int Compare(T left, T right);
}
prin arat a contravariant a a tipului T, deci se poate face ca un IComparer<object>
s a e considerat drept un IComparer<String>. Are sens, deoarece dac a un
IComparer poate s a compare orice dou a obiecte, atunci cu sigurant a poate
s a compare si doua string-uri.
306 CURS 13. NOUT
A TI
IN C# 4.0
Curs 14
Fluxuri
Pentru aplicatiile reale, datele care se prelucreaz a nu se mai preiau din
tastatur a, ci se acceseaz a din siere de date. C#, precum alte limbaje
anterioare, pune la dispozitie o abstractizare numit a ux
1
care permite
manipularea datelor, indiferent de sursa acestora.
Intrun astfel de ux,
pachete de date urmeaz a unele dup a altele.
A SI ASINCRON
A 323
// delegated method
private AsyncCallback myCallBack;
// buffer to hold the read data
private byte[] buffer;
// the size of the buffer
const int BufferSize = 256;
// constructor
AsynchIOTester( )
{
// open the input stream
inputStream =
File.OpenRead(
@"C:\test\source\AskTim.txt");
// allocate a buffer
buffer = new byte[BufferSize];
// assign the call back
myCallBack =
new AsyncCallback(this.OnCompletedRead);
}
public static void Main( )
{
// create an instance of AsynchIOTester
// which invokes the constructor
AsynchIOTester theApp =
new AsynchIOTester( );
// call the instance method
theApp.Run( );
}
void Run( )
{
inputStream.BeginRead(
buffer, // holds the results
0, // offset
buffer.Length, // (BufferSize)
myCallBack, // call back delegate
null); // local state object
// do some work while data is read
for (long i = 0; i < 500000; i++)
{
if (i%1000 == 0)
{
324 CURS 14. FLUXURI
Console.WriteLine("i: {0}", i);
}
}
}
// call back method
void OnCompletedRead(IAsyncResult asyncResult)
{
int bytesRead =
inputStream.EndRead(asyncResult);
// if we got bytes, make them a string
// and display them, then start up again.
// Otherwise, were done.
if (bytesRead > 0)
{
String s =
Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(s);
inputStream.BeginRead(
buffer, 0, buffer.Length, myCallBack, null);
}
}
}
Iesirea ar :
i: 47000
i: 48000
i: 49000
Date: January 2001
From: Dave Heisler
To: Ask Tim
Subject: Questions About OReilly
Dear Tim,
Ive been a programmer for about ten years. I had heard of
OReilly books,then...
Dave,
You might be amazed at how many requests for help with
school projects I get;
i: 50000
i: 51000
i: 52000
Cele dou a re de execuctie lucreaz a deci concurent.
14.4. STREAMURI WEB 325
14.4 Streamuri Web
C# contine clase g andite pentru a usura interoperarea cu webul. Aducerea
informatiei de pe web se face n doi pasi: primul pas const a n a face o cerere
de conectare la un server; dac a cererea se poate face, atunci n pasul al doilea
const a n obtinerea informatiei propriuzise de la server. Cei doi pasi se fac
respectiv cu clasele HttpWebRequest, respectiv HttpWebResponse
Un obiect HttpWebRequest poate obtinut prin metoda static a Create()
din clasa WebRequest:
string page = http://www.cetus-links.org/index.html;
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(page);
Pe baza obiectului HttpWebRequest obtinut se va crea un obiect HttpWebResponse:
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();