Sunteți pe pagina 1din 21

ParteaI

E le m e n te le
d e b aăz a le p la tfo rm e i
M ic ro s o ft .N E T F ra m e w o rk
Capitolul 1
Arhitectura platformei
de dezvoltare .NET Framework
Platforma Microsoft .NET Framework introduce multe concepte, tehnologii şi termeni roi. Scopul
acestui capitol este de a realiza o prezentare a arhitecturii platformei .NET Framework, a prezenta
câteva dintre noile tehnologii incluse în acest cadru de lucru şi a defini mulţi dintre termenii pe care-i vom
întâlni atunci când vom începe utilizarea sa. De asemenea, vom parcurge procesul de construire a codului
sursă ca o aplicaţie sau un set de componente (tipuri) care pot fi redistribuite, după care vom explica
modul în care sunt executate aceste componente.

C o m p ila re a c o d u lu ăi sînu rs
m o d u le a d m in is tra te
Ei bine, deci v-aţi decis să utilizaţi .NET Framework drept platformă de dezvoltare Foarte bine!
Primul pas constă în a stabili ce tip de aplicaţie sau componente intenţionaţi să construiţi. Vom presupune că
aţi stabilit acest detaliu minor, că totul este conceput specificaţiile sunt scrise, iar dumneavoastră sunteţi
gata de a începe dezvoltarea.
Acum, trebuie să decideţi ce limbaj de programare veţi folosi. De obicei, această sarcină este dificilă,
deoarece diversele limbaje pun la dispoziţie capacităţi diferite. De exemplu, în limbajele C/C++
neadministrate avem un control destul de scăzut asupra sistemului Putem administra memoria exact aşa
cum dorim, crea cu uşurinţă fire, dacă este necesar, şi aşa mai departe. Pe de altă parte, limbajul Visual Basic
permite construirea foarte rapidă a aplicaţiilor UI şi facilitează controlul obiectelor COM şi bazelor de date.
Rularea în limbaj comun (CLR) înseamnă exact ceea ce sugerează denumirea sa: este un mod de rulare
care poate fi utilizat de către diverse limbaje de programare diferite Caracteristicile rulării CLR sunt
disponibile pentru toate limbajele de programare care o ai în vedere - punct. Dacă rularea foloseşte
excepţiile pentru a raporta erorile, atunci toate limbajele vor raporta erorile prin intermediul excepţiilor.
Dacă rularea permite crearea unui fir, atunci oricare dintre limbaje îl poate crea.
De fapt, în momentul execuţiei, rularea CLR nu „ştie" ce limbaj de programare a folosit realizatorul de
aplicaţii pentru a scrie codul sursă. Aceasta înseamnă că putem alege orice limbaj de programare care
permite exprimarea cea mai simplă a intenţiilor noastre. Putem dezvolta codul în orice limbaj de
programare dorim, atâta vreme cât compilatorul folosit pentru a compila codul are în vedere rularea
CLR.
Deci, dacă ceea ce am afirmat mai sus este adevărat, care este avantajul utilizării unui limbaj de
programare faţă de altul? Ei bine, consideraţi compilatoarele ca pe nişte verificatoare de sintaxă şi
analizatoare de „cod corect". Ele analizează codul sursă, se asigură că ceea ce aţi scris dumneavoastră are
un sens oarecare şi apoi trimit la ieşire un cod care descrie intenţiile dumneavoastră. Diversele limbaje
de programare permit dezvoltarea folosind diverse sintaxe.

1
Valoarea acestor opţiuni nu trebuie subestimată. Pentru aplicaţiile matematice sau financiare,
exprimarea intenţiilor dumneavoastră folosind sintaxa APL poate duce la economisirea multor zile de
dezvoltare, comparativ cu exprimarea aceloraşi intenţii cu ajutorul sintaxei Perl, de exemplu.
Compania Microsoft a creat o serie de compilatoare de limbaje care au în vedere momentul
execuţiei: C++ cu extensii administrate, C# (pronunţat „C sharp"), Visual Basic, JScript, J# (un
compilator de limbaj Java) şi un asamblor de limbaj intermediar (IL). In afară de Microsoft, o serie de alte
companii creează compilatoare care produc coduri ce au în vedere rularea CLR. în ceea ce mă priveşte,
cunosc compilatoare pentru Alice, APL, COBOL, Component Pascal, Eiffel, Fortran, Haskell, Mercury,
ML, Mondrian, Oberon, Perl, Python, RPG, Scheme şi Smalltalk.
în Figura 1.1 este prezentat procesul de compilare a fişierelor de cod sursă. După cum se poate observa
aici, puteţi crea fişiere de cod sursă folosind orice limbaj de programare care acceptă rularea CLR. Apoi
se utilizează compilatorul corespunzător, pentru a verifica sintaxa şi a analiza codul sursă. Indiferent de
compilatorul folosit, rezultatul este un modul administrat. Un modul administrat este un fişier executabil
Windows standard portabil (PE11), care necesită rularea CLR pentru a fi executat. în viitor, şi alte sisteme
de operare ar putea utiliza formatul de fişier PE.

Figura 1-1 Compilarea codului sursă, în vederea obţinerii unui modul administrat.

Tabelul 1-1 Componentele unui modul administrat

Componenta Descriere
Antetul PE Antetul de fişier PE standard din Windows, care este similar cu
antetul Common Object File Format2 (COFF). Acest antet indică tipul de fişier -
GUI, CUI sau DLL - şi are o marcă de timp care arată când a fost construit
fişierul. Pentru modulele care conţin numai cod IL, informaţiile masive din
antetul PE sunt ignorate. Pentru modulele care conţin cod CPU nativ, antetul
conţine informaţii despre acesta.
Antetul CLR Conţine informaţiile (interpretate de CLR şi programele
utilitare) care fac ca acesta să fie un modul administrat. Antetul cuprinde
versiunea CLR necesară, câteva indicatoare, token-ul de metadate MethodDef al
metodei punctului de intrare (metoda Main) din modulul administrat şi
locaţia/dimensiunea metadatelor modulului, resursele, numele tare, câteva
indicatoare şi alte informaţii mai puţin interesante.
Metadatele Fiecare modul administrat conţine tabele de metadate. Există
două tipuri principale de tabele: tabelele care descriu tipurile şi membrii definiţi
în codul sursă şi tabelele care descriu tipurile şi membrii la care se face referire în
codul sursă.
Codul în limbaj Codul pe care 1-a produs compilatorul atunci când a compilat
intermediar (IL) codul sursă. Ulterior, componenta CLR compilează limbajul IL
în instrucţiuni CPU native.

1
Acronim pentru portable executable (file), (n.t.)
2
Formatul de fişiere comun pentru obiecte, (n.t.)
2
Majoritatea compilatoarelor din trecut produceau coduri care aveau ca ţintă o anumită arhitectură CPU,
cum ar fi x86, IA64, Alpha sau PowerPC. în schimb, toate compilatoarele care se conformează rulării
CLR produc cod IL. (Mai târziu în cadrul acestui capitol, vom intra în detalii despre codul IL.) Codul IL
este numit uneori cod administrat, deoarece componenta CLR îi administrează timpul de viaţă şi execuţia.
In afară de crearea codului IL, este necesar ca fiecare compilator care are ca ţintă rularea CLR să emită
metadate complete în fiecare modul administrat. Pe scurt, metadatele reprezintă pur şi simplu un set de
tabele care descriu ceea ce este definit în modul, cum ar fi tipurile şi membrii lor. In plus, metadatele au de
asemenea tabele care indică la ce se referă modulul administrat, cum ar fi tipurile importate şi membrii lor.
Metadatele reprezintă un superset de tehnologii mai vechi, cum ar fi bibliotecile de tipuri şi fişierele
limbajului de definiţie a interfeţei (IDL3 ). Important de remarcat este că metadatele CLR sunt cu mult
mai complete. Şi, spre deosebire de bibliotecile de tipuri şi fişierele IDL, metadatele sunt întotdeauna
asociate cu fişierul care conţine codul IL. De fapt, metadatele sunt întotdeauna înglobate în acelaşi fişier
EXE/DLL ca şi codul, ceea ce face imposibilă separarea celor două. Deoarece compilatorul produce
metadatele şi codul în acelaşi timp şi le leagă în modulul administrat rezultant, metadatele şi codul IL care
le descrie nu sunt niciodată nesincronizate.
Metadatele au multe utilizări. Mai jos sunt enumerate câteva dintre ele:
■ Metadatele elimină necesitatea fişierelor antet şi de bibliotecă la compilare, din moment ce toate
informaţiile despre tipurile/membrii la care se face referire sunt conţinute în cadrul fişierului care
conţine codul IL ce implementează tipurile/membrii. Compilatoarele pot citi metadatele direct din
modulele administrate.
■ Mediul Visual Studio .NET utilizează metadatele, pentru a ajuta la scrierea codului. Caracteristica sa
IntelliSense analizează datele, pentru a arăta ce metode oferă un tip şi ce parametri aşteaptă metoda
respectivă.
■ Procesul de verificare a codului la rularea CLR utilizează metadatele, pentru a garanta că în cod se
efectuează numai operaţii „sigure". (Vom analiza în curând verificarea.)
■ Metadatele permit serializarea câmpurilor unui obiect într-un bloc de memorie, plasarea lor la distanţă
pe un alt calculator şi apoi deserializarea lor, creând din nou obiectul şi starea sa pe calculatorul
îndepărtat.
■ Metadatele permit colectorului de gunoaie să urmărească timpul de viaţă al obiectelor.
Pentru orice obiect, colectorul de gunoaie poate determina tipul de obiect şi, din metadate, poate afla
ce câmpuri din obiectul respectiv se referă la alte obiecte.
În Capitolul 2 vom descrie mai detaliat metadatele.
Limbajele C#, Visual Basic, JScript, J# şi IL Assembler de la Microsoft produc întotdeauna module
administrate, care necesită rularea CLR pentru a fi executate. Utilizatorii finali trebuie să aibă instalată
componenta CLR pe calculatoarele lor pentru a executa orice fel de modul administrat, tot aşa cum trebuie să
aibă biblioteca Microsoft Foundation Class (MFC) sau fişierele DLL din Visual Basic, pentru a rula aplicaţii
MFC sau Visual Basic.
Compilatorul C++ de la Microsoft construieşte implicit module neadministrate: fişierele EXE sau DLL,
cu care suntem obişnuiţi. Aceste module nu necesită componenta CLR pentru a putea fi executate. Dar,
prin specificarea în linia de comandă a unui nou comutator, compilatorul C++ poate realiza module
administrate, care necesită componenta CLR pentru a putea fi executate. Dintre toate compilatoarele
Microsoft menţionate, C++ este unic, prin faptul că este singurul limbaj care permite realizatorilor să scrie
atât cod administrat, cât şi neadministrat, care să poată fi emis într-un singur modul. Aceasta poate
reprezenta un mare avantaj, deoarece permite realizatorilor de aplicaţii să scrie partea masivă a aplicaţiei
în cod administrat (pentru siguranţa tipurilor şi interoperabilitatea componentelor), continuând să
acceseze codul C++ neadministrat existent.

C o m b in a re a m o d u le lo r a d mîninais
s atra
m te
b la je
Componenta CLR nu lucrează efectiv cu module, ci cu asamblaje. Un asamblaj este un concept abstract,
care poate fi dificil de înţeles iniţial. Mai întâi, un asamblaj este o grupare logică de unul sau mai multe
module administrate sau fişiere de resurse.

3
Acronim pentru Interface Definition Language. (n.t.)

3
În al doilea rând, un asamblaj este cea mai mică unitate pentru reutilizare, securitate şi realizarea de
versiuni, în funcţie de opţiunile alese pentru compilatoare sau instrumente, putem produce un asamblaj
format dintr-un singur fişier sau din mai multe.
În Capitolul 2 vom trata detaliat asamblajele, aşa că acum nu vom mai zăbovi asupra lor. Tot ceea ce
dorim acum este să vă atragem atenţia asupra faptului că există o noţiune conceptuală suplimentară, care
oferă o modalitate grozavă de a trata un grup de fişiere ca pe o singură entitate.
Ilustrarea din Figura 1-2 vă va ajuta să înţelegeţi ce sunt asamblajele. în această figură, câteva module
administrate şi fişiere de resurse (sau date) sunt prelucrate cu ajutorul unui instrument. Acesta produce un
singur fişier PE, care reprezintă gruparea logică a fişierelor. Acest fişier PE conţine un bloc de date numit
declaraţie. Declaraţia reprezintă pur şi simplu un alt set de tabele de metadate. Aceste tabele descriu
fişierele din care se compune asamblajul, tipurile exportate public implementate de către fişierele din
cadrul asamblajului şi fişierele de resurse sau date asociate acestuia.

Figura 1-2 Combinarea modulelor administrate în asamblaje.

Implicit, compilatoarele efectuează, de fapt, transformarea modului administrat emis într-un


asamblaj; cu alte cuvinte, compilatorul C# emite un modul administrat care conţine o declaraţie.
Declaraţia arată că asamblajul este format dintr-un singur fişier. Astfel, pentru proiectele care au un
singur modul administrat şi nici un fişier de resurse (sau date), asamblajul va fi modulul administrat, iar
dumneavoastră nu va mai trebui să faceţi nimic suplimentar în timpul procesului de construcţie. Dacă
doriţi să grupaţi un set de fişiere într-un asamblaj, va trebui să aveţi în vedere mai multe instrumente
(cum ar fi programul de legături de asamblare, AL.exe) şi opţiunile lor din linia de comandă. Vom
explica aceste instrumente şi opţiuni în Capitolul 2.
Un asamblaj permite decuplarea noţiunilor logice şi fizice ale unei componente reutili-zabile, care
acceptă desfăşurarea şi realizarea de versiuni. Modul de partiţionare a codului şi resurselor în diverse fişiere
este în întregime la latitudinea dumneavoastră. De exemplu, aţi putea plasa tipurile de resurse rar utilizate
în fişiere separate, care fac parte dintr-un asamblaj. Fişierele separate ar putea fi descărcate de pe Web,
după cum este necesar. Dacă fişierele nu sunt niciodată necesare, atunci nu sunt niciodată descărcate, prin
aceasta economisindu-se spaţiul de pe disc şi reducând timpul de instalare. Asamblajele permit
fragmentarea desfăşurării fişierelor, tratând în continuare toate fişierele ca pe o singură colecţie. Modulele
unui asamblaj cuprind şi informaţii — inclusiv numărul versiunii — despre asamblajele la care se face
referire. Aceste informaţii fac ca asamblajul să fie cu autodescriere. Cu alte cuvinte, componenta CLR ştie tot
ceea ce este necesar pentru ca un asamblaj să fie executat. Nu mai sunt necesare informaţii suplimentare în
registru sau în Active Directory.

n cărc
Î a re a c o m p o n e n te i C o m m o n L a n g u a g e R u n tim e
Fiecare asamblaj construit poate fi ori o aplicaţie executabilă, ori o bibliotecă DLL, care conţine un set de
tipuri (componente) care vor fi utilizate de către aplicaţia executabilă. Desigur că rularea CLR este
responsabilă de administrarea execuţiei codului conţinut în cadrul acestor asamblaje. Aceasta înseamnă că
platforma .NET Framework trebuie instalată pe calculatorul gazdă.

4
Compania Microsoft a creat un pachet de redistribuire, care poate fi livrat gratuit, pentru a instala
platforma .NET Framework pe calculatoarele clienţilor. în cele din urmă, platforma .NET Framework va
face parte din pachetul de versiuni Windows viitoare, astfel încât nu va mai trebui livrat împreună cu
asamblajele.
Puteţi vedea dacă platforma .NET Framework a fost instalată căutând fişierul MSCorEE.dll în directorul
%windir%\system32. Existenţa acestui fişier arată că platforma .NET Framework este instalată. Totuşi, pe un
singur calculator pot fi instalate simultan mai multe versiuni ale platformei .NET Framework. Dacă doriţi
să stabiliţi exact ce versiuni .NET Framework sunt instalate, analizaţi subcheile aflate sub următoarea cheie
din registru: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy
Atunci când construiţi un asamblaj EXE, compilatorul/programul de legături emite câteva informaţii
speciale în antetul fişierului PE al asamblajului rezultant şi în porţiunea .text a fişierului. Atunci când este
invocat fişierul EXE, aceste informaţii speciale determină încărcarea şi iniţializarea componentei CLR.
Apoi aceasta localizează metoda din punctul de intrare al aplicaţiei şi permite începerea execuţiei acesteia.
Similar, dacă o aplicaţie neadministrată apelează metoda LoadLibrary pentru a încărca un asamblaj
administrat, atunci funcţia din punctul de intrare ştie că trebuie să încarce componenta CLR, pentru a
prelucra codul conţinut în cadrul asamblajului.
în cea mai mare parte, nu este necesar ca dumneavoastră să ştiţi sau să înţelegeţi cum este încărcată
componenta CLR. Pentru majoritatea programatorilor, aceste informaţii speciale permit pur şi simplu
execuţia aplicaţiei şi nu mai trebuie să se gândească la ele. Dar, pentru cei curioşi, vom dedica ceea ce a mai
rămas din acest paragraf explicării modului în care aplicaţia EXE sau DLL administrată porneşte rularea
CLR. Dacă nu vă interesează acest subiect, puteţi să săriţi la următorul paragraf. De asemenea, dacă vă
interesează construirea unei aplicaţii neadministrate care găzduieşte componenta CLR, consultaţi
Capitolul 20.
în Figura 1-3 este prezentat schematic modul în care o aplicaţie EXE administrată încarcă si
iniţializează rularea CLR.

Figura 1-3 încărcarea şi iniţializarea componentei CLR.

Atunci când compilatorul/programul de legături creează, un asamblaj executabil, în porţiunea .text a


fişierului PE este emisă următoarea funcţie de racordare de 6 octeţi x86:
JMP _CorExeMain

Deoarece funcţia _CorExeMain este importată din biblioteca de legături dinamice Microsoft MSCorEE.dll,
fişierul MSCorEE.dll este referenţiat în porţiunea de import (.idata) a fişierului de asamblare.
MSCorEE.dll vine de la Microsoft Component Object Runtime Execution Engine.

5
Atunci când este invocat fişierul EXE administrat, sistemul de operare Windows îl tratează ca pe orice
fişier EXE normal (neadministrat): încărcătorul Windows încarcă fişierul şi analizează porţiunea . idata,
pentru a vedea dacă în spaţiul de adrese trebuie încărcată biblioteca MSCorEE.dll. Apoi încărcătorul obţine
adresa funcţiei _CorExeMain din interiorul bibliotecii MSCorEE.dll şi stabileşte instrucţiunea JMP a
funcţiei în fişierul EXE administrat.
Firul primar al procesului începe execuţia acestei funcţii de racordare x86, care sare imediat la funcţia
_CorExeMain din biblioteca MSCorEE.dll. Funcţia _CorExeMain iniţializează rularea CLR şi apoi caută în
antetul CLR al asamblajului executabil,- pentru a determina ce metodă administrată din punctul de intrare
trebuie să execute. Codul IL pentru această metodă este apoi compilat în instrucţiuni CPU native, iar rularea
CLR trece la codul nativ (utilizând firul primar al procesului). în acest punct, este rulat codul aplicaţiei
administrate.
Pentru o aplicaţie DLL administrată, situaţia este similară. Atunci când se construieşte o aplicaţie DLL
administrată, compilatorul/programul de legături emite o funcţie de racordare de 6 octeţi x86 similară,
în porţiunea .text corespunzătoare asamblajului DLL:
JMP _CorDllMain
Funcţia _CorDllMain este de asemenea importată din biblioteca MSCorEE.dll, făcând ca porţiunea .idata a
aplicaţiei DLL să se refere la fişierul MSCorEE.dll. Atunci când sistemul de operare Windows încarcă
aplicaţia DLL, va încărca automat şi biblioteca MSCorEE.dll (dacă nu este deja încărcată), va obţine adresa
funcţiei _CorDllMain şi va corecta racordul JMP x86 de 6 octeţi din aplicaţia DLL administrată. Firul care a
apelat metoda LoadLibrary pentru a încărca aplicaţia DLL administrată va trece acum la racordul x86 din
asamblajul DLL administrat, care va sări imediat la funcţia _CorDllMain din biblioteca MSCorEE.dll.
Funcţia _CorDllMain iniţializează rularea CLR (dacă nu a fost deja iniţializată pentru proces) şi apoi revine,
astfel încât aplicaţia îşi poate continua execuţia normală.
Funcţiile de racordare x86 de 6 octeţi sunt necesare pentru a rula asamblaje administrate pe sistemele
de operare Windows 98, Windows 98 Second Edition, Windows Me, Windows NT 4 şi Windows 2000,
deoarece toate acestea au fost livrate cu mult înainte de a fi disponibilă componenta CLR. Observaţi că
funcţia de racordare de 6 octeţi este construită special pentru calculatoarele x86. Acest racord nu
funcţionează corespunzător în cazul în care componenta CLR este portată astfel încât să funcţioneze pe
alte arhitecturi CPU. Deoarece sistemele de operare Windows XP şi Windows .NET Server Family
acceptă atât arhitectura x86, cât şi IA64, încărcătoarele acestora au fost modificate astfel încât să caute
tocmai asamblaje administrate.
Pe sistemele de operare Windows XP şi Windows .NET Server Family, atunci când este invocat un
asamblaj administrat (de regulă, prin intermediul metodei CreateProcess sau LoadLibrary), încărcătorul
OS1 detectează faptul că fişierul conţine un cod administrat, analizând intrarea directoare 14 din antetul
fişierului PE. (A se vedea IMAGE_DIRECTORY_ ENTRY_C0M_DESCRIPT0R din antetul WinNT.h.) Dacă această
intrare directoare există şi nu este 0, atunci încărcătorul ignoră porţiunea de import a fişierului (.idata) şi
încarcă automat biblioteca MSCorEE.dll în spaţiul de adresă al procesului. O dată încărcată, încărcătorul
OS face ca firul procesului să treacă direct la funcţia corectă din biblioteca MSCorEE.dll. Funcţiile de
racordare x86 de 6 octeţi sunt ignorate pe calculatoarele care rulează sistemele de operare Windows XP şi
Windows .NET Server Family.
O ultimă observaţie referitoare la fişierele PE: acestea utilizează întotdeauna formatul de fişier PE de 32
de biţi, nu cel de 64 de biţi. Pe sistemele Windows de 64 de biţi, încărcătorul OS detectează fişierul PE
administrat de 32 de biţi şi ştie automat să creeze un spaţiu de adresă de 64 de biţi.

E x e c u ta re a c o d u lu i d in a s a m b la j
După cum am menţionat mai devreme, modulele administrate pot conţine atât metadate, cât şi limbaj
intermediar (IL). IL este un limbaj maşină independent de CPU, creat de compania Microsoft, după
consultări cu o serie de realizatori externi de limbaje/compilatoare comerciale. Este de un nivel mult mai
înalt decât majoritatea limbajelor maşină CPU. Limbajul IL înţelege tipurile de obiecte şi are instrucţiuni
care creează şi iniţializează obiecte, apelează metode virtuale pentru obiecte şi manipulează direct
elementele tablourilor. Are chiar şi instrucţiuni care introduc şi detectează excepţii pentru tratarea erorilor.
IL poate fi considerat un limbaj maşină orientat spre obiecte.
De regulă, realizatorii de aplicaţii vor programa într-un limbaj de nivel înalt, cum ar fi C# sau Visual
1
Acronim pentru Operating System, (n.t.)

6
Basic. Compilatoarele pentru aceste limbaje de nivel înalt produc cod IL. Totuşi, ca orice alt limbaj
maşină, IL poate fi scris în limbaj de asamblare, iar compania Microsoft pune la dispoziţie un program de
asamblare IL Assembler, ILAsm.exe. De asemenea, compania Microsoft oferă şi un program de
dezasamblare IL Disassembler, ILDasm.exe.

Limbajul IL şi protejarea proprietăţii intelectuale _________


Unele persoane sunt preocupate de faptul că limbajul IL nu oferă suficientă protecţie a proprietăţii
intelectuale pentru algoritmi. Cu alte cuvinte, ei cred ca am putea construi un modul administrat, iar
altcineva ar putea utiliza un instrument, cum ar fi IL Disassembler, pentru a reproduce tehnologia exactă a
ceea ce face codul aplicaţiei.
Da, este adevărat că limbajul IL este de nivel mai înalt decât majoritatea celorlalte limbaje de
asamblare şi că, în general, reproducerea tehnologiei codului IL este relativ simplă. Dar, atunci când se
implementează un serviciu Web XML sau o aplicaţie Web Forms, modulul administrat se află pe server.
Deoarece nimeni din afara companiei nu poate accesa modulul, nimeni din afara companiei nu poate
utiliza un instrument pentru a vedea codul IL - iar proprietatea intelectuală este în perfectă siguranţă.
Dacă vă preocupă modulele administrate pe care le distribuiţi, puteţi obţine un program utilitar de
eclipsare de la o terţă partidă. Aceste utilitare „amestecă" numele tuturor simbolurilor private din
metadatele modului administrat. Va fi dificil pentru cineva să „descurce" numele şi să înţeleagă scopul
fiecărei metode. Observaţi că aceste programe de eclipsare pot oferi doar o mică protecţie, din moment ce
codul IL trebuie să fie într-o oarecare măsură disponibil, pentru ca rularea CLR să îl poată prelucra.
Dacă nu credeţi că un program de eclipsare oferă un tip adecvat de protecţie a proprietăţii
intelectuale, puteţi avea în vedere implementarea algoritmilor mai sensibili într-un modul oarecare
neadministrat, care va conţine instrucţiuni native CPU, în loc de cod IL şi metadate. Apoi puteţi folosi
caracteristicile de interoperabilitate ale componentei CLR pentru a comunica între porţiunile
administrate şi cele neadministrate din aplicaţia dumneavoastră. Desigur că aceasta presupune că nu vă
îngrijorează posibilitatea de reproducere a tehnologiei prin interpretarea instrucţiunilor native CPU din
codul neadministrat.

Reţineţi că orice limbaj de nivel înalt va expune, probabil, doar un subset din facilităţile oferite de
componenta CLR. Totuşi, utilizarea limbajului de asamblare IL permite realizatorului să acceseze toate
facilităţile CLR. Astfel, în cazul în care limbajul de programare ales nu conţine o facilitate pe care
componenta CLR o oferă şi de care doriţi să profitaţi, atunci puteţi alege să scrieţi respectiva porţiune de
cod în limbajul de asamblare IL - sau eventual un alt limbaj de programare care pune la dispoziţie
caracteristica CLR căutată.
Singura modalitate prin care puteţi afla ce facilităţi oferă componenta CLR este de a citi documentaţia
specifică acesteia. în această carte, vom încerca să ne concentrăm asupra caracteristicilor CLR şi a
modului în care acestea sunt revelate sau nu în limbajul C#.
Presupun că majoritatea celorlalte cărţi şi articole vor prezenta componenta CLR într-o perspectivă
legată de limbaj, iar majoritatea realizatorilor de aplicaţii vor ajunge la concluzia că rularea CLR oferă doar
ceea ce expune limbajul de programare ales de realizator. Atâta vreme cât limbajul de programare ales
permite realizarea a ceea ce intenţionaţi, această perspectivă nebuloasă nu este prea rea.

Un alt aspect important care trebuie reţinut despre limbajul IL se referă la faptul că nu este legat

7
de vreo anumită platformă CPU. Aceasta înseamnă că un modul administrat care conţine cod IL
poate fi rulat pe orice platformă CPU, atâta vreme cât sistemul de operare rulat pe aceasta
găzduieşte o versiune a componentei CLR. Deşi versiunea CLR iniţială este rulată doar pe
platforme Windows de 32 de biţi, dezvoltarea unei aplicaţii care să utilizeze cod IL conferă
realizatorului o mai mare independenţă de arhitectura CPU aflată la bază.

Standardizarea platformei .NET Framework _______________


In octombrie 2000, compania Microsoft (împreună cu Intel şi Hewlett-Packard, toate ca sponsori)
a propus asociaţiei ECMA (European Computer Manufacturer's Association) un subset larg al
platformei .NET Framework, cu scopul de a o standardiza. ECMA a acceptat această propunere şi a
creat un comitet tehnic (TC39), care să supravegheze procesul de standardizare. Comitetul tehnic
are următoarea structură şi sarcină:
■ Technical Group 1 trebuie să dezvolte un standard pentru un limbaj de scriptare dinamic
(ECMAScript). Implementarea Microsoft a standardului ECMAScript este JScript;
■ Technical Group 2 trebuie să realizeze o versiune standardizată a limbajului de programare
C#;
Technical Group 3 trebuie să realizeze o infrastructură Common Language Infrastructure1 (CLI), pe
baza unui subset de funcţionalităţi oferite de componenta CLR a platformei .NET Framework şi
bibliotecii de clase. Mai exact, CLI va defini un format de fişiere, un sistem de tipuri comun, un sistem
de metadate extensibil, un limbaj intermediar (IL) şi accesul la platforma aflată la bază (P/Invoke). în
plus, CLI va defini o bibliotecă intermediară de clase de bază (pentru a permite mici dispozitive
hardware), concepută pentru a fi utilizată de mai multe limbaje de programare.
O dată ce standardizarea va fi încheiată, aceste standarde vor fi introduse în standardul ISO/IEC JTC 1
(Information Technology). Atunci, comitetul tehnic va analiza şi direcţiile viitoare ale infrastructurii
CLI, limbajului C# şi standardului ECMAScript şi va susţine propuneri de tehnologii complementare
sau suplimentare. Pentru mai multe informaţii despre ECMA, consultaţi siturile Web aflate la adresele:
http:// www.ECMA.ch şi http://MSDN.Microsoft.eom/.NET/ECMA.
O dată cu standardizarea infrastructurii CLI, a limbajului C# şi standardului ECMAScript, compania
Microsoft nu va „poseda" nici una dintre aceste tehnologii. Microsoft va fi pur şi simplu una dintre multele
(sperăm) companii care produc implementări ale acestor tehnologii. Desigur că cei de la Microsoft speră
că implementarea lor va fi cea mai bună, în termeni de performanţe şi caracteristici realizate la cererea
utilizatorilor. Aceasta va ajuta la vânzarea sistemului de operare Windows, deoarece cea mai bună
implementare Microsoft va fi rulată numai sub Windows. Totuşi, alte companii ar putea implementa aceste
standarde, intra în competiţie cu Microsoft şi, posibil, câştiga.
---------------------------------------------------------------------------------------------------
Deşi unităţile CPU din zilele noastre nu pot executa direct instrucţiunile IL, este posibil ca cele viitoare
să aibă această capacitate. Pentru a executa o metodă, este necesar ca instrucţiunile IL corespunzătoare să
fie transformate în instrucţiuni native CPU. Aceasta este sarcina compilatorului JIT (just-in-time2) al
componentei CLR.
în Figura 1-4 se ilustrează ceea ce are loc atunci când este apelată pentru prima dată o metodă.
Chiar înainte de execuţia metodei Main, componenta CLR detectează toate tipurile la care se face
referire în codul acesteia. Aceasta face ca CLR să aloce o structură de date internă, care este folosită
pentru a administra accesul la tipul la care se face referire. în Figura 1-4, metoda Main se referă la un
singur tip, Console, ceea ce face ca rularea CLR să aloce o singură structură internă. Structura de date
internă conţine o intrare pentru fiecare metodă definită de către tip. Fiecare intrare conţine adresa la care
poate fi găsită implementarea metodei. La iniţializarea acestei structuri, componenta CLR atribuie
fiecare intrare unei funcţii interne nedocumentate, conţinută în componenta CLR însăşi. Am numit
această funcţie JITCompiler.
Atunci când metoda Main face prima apelare a metodei WriteLine, este apelată funcţia JITCompiler.
Funcţia JITCompiler este responsabilă de compilarea codului IL al unei metode în instrucţiuni native

1
Infrastructura comună a limbajelor, (n.t.)
2
Tocmai la timp. (n.t.)

8
CPU. Deoarece codul IL este compilat „just in time", această componentă CLR este numită în mod
frecvent JITer sau compilator JIT.
Atunci când este apelată, funcţia JITCompiler ştie ce metodă este apelată şi ce tip o defineşte. Apoi
funcţia JITCompiler caută metadatele asamblajului de definiţie pentru codul IL al metodei apelate. în
etapa următoare, funcţia JITCompiler verifică şi compilează codul IL în instrucţiuni native CPU. Acestea
sunt salvate într-un bloc de memorie alocat dinamic. Apoi, funcţia JITCompiler merge înapoi la
structura de date internă a tipului şi înlocuieşte adresa metodei apelate cu adresa blocului de memorie
care conţine instrucţiunile native CPU. In final, funcţia JITCompiler trece la codul din blocul de
memorie. Acest cod reprezintă implementarea metodei WriteLine (versiunea care are un parametru de
tip string). Atunci când acest cod efectuează returnarea, se revine la codul din metoda Main, care
continuă execuţia în mod normal.

Figura 1-4 Apelarea unei metode pentru prima dată.

Acum metoda Main apelează a doua oară metoda WriteLine. De această dată, codul pentru metoda
WriteLine a fost deja verificat şi compilat. Astfel, apelarea merge direct la blocul de memorie, sărind cu
totul peste funcţia JITCompiler. După execuţia metodei WriteLine, se revine la metoda Main. In Figura
1-5 se arată care este situaţia atunci când metoda WriteLine este apelată a doua oară.
Impactul negativ asupra performanţelor apare doar prima dată când este apelată metoda. Toate
apelările ulterioare ale acesteia sunt executate la viteza maximă a codului nativ: verificarea şi compilarea
în cod nativ nu sunt efectuate din nou.
Compilatorul JIT stochează instrucţiunile native CPU în memoria dinamică. Aceasta înseamnă că,
la terminarea aplicaţiei, codul compilat este înlăturat. Astfel, dacă aplicaţia va fi rulată din nou în viitor
sau dacă sunt rulate simultan două instanţe ale acesteia (în două procese diferite ale sistemului de
operare), atunci compilatorul JIT va trebui să efectueze din nou compilarea codului IL în instrucţiuni
native.

9
Figura 1-5 Apelarea unei metode a doua oară.

Pentru majoritatea aplicaţiilor, impactul asupra performanţelor datorat compilării JIT nu este
semnificativ. Majoritatea aplicaţiilor au tendinţa de a apela în mod repetat aceleaşi metode. Aceste
metode vor avea un impact negativ asupra performanţelor numai o dată, pentru o execuţie a
aplicaţiei. De asemenea, este mai probabil să fie necesar un timp mai mare pentru execuţia din
interiorul metodei decât pentru apelarea acesteia.
De asemenea, trebuie avut în vedere faptul că în compilatorul JIT al componentei CLR se
efectuează optimizarea codului nativ, tot la fel cum face şi compilatorul back-end C++ neadministrat.
Din nou, s-ar putea să fie necesar un timp mai îndelungat pentru a realiza codul optimizat, dar acesta
va fi executat cu performanţe mult mai bune decât dacă nu ar fi fost optimizat.
Realizatorii de aplicaţii care au un fond de cunoştinţe C sau C++, fără a fi la curent cu administrarea,
probabil că se gândesc la implicaţiile tuturor acestor consideraţii în ceea ce priveşte performanţele. în fond,
codul neadministrat este compilat pentru o anumită platformă CPU şi, atunci când este invocat, el poate
fi executat pur şi simplu. în acest mediu administrat, compilarea codului se realizează în două faze. Mai
întâi, compilatorul parcurge codul sursă, făcând cât mai mult posibil pentru a realiza codul IL. Dar, pentru
a executa codul, însuşi codul IL trebuie compilat în momentul execuţiei în instrucţiuni native CPU, ceea
ce necesită o alocare de memorie mai mare şi timp CPU suplimentar pentru realizare.
Puteţi să mă credeţi că, atunci când am abordat componenta CLR, pornind de la un fond de cunoştinţe
C/C++, am fost destul de sceptic şi preocupat de această suprasarcină suplimentară. Adevărul este că acest
al doilea stadiu al compilării, care apare în timpul execuţiei, afectează performanţele şi alocă memorie
dinamică. Dar cei de la Microsoft s-au străduit mult pentru a reduce la minimum această suprasarcină.
Dacă şi dumneavoastră sunteţi sceptic, atunci va trebui să construiţi câteva aplicaţii şi să testaţi singur
performanţele. în plus, va trebui să rulaţi câteva aplicaţii administrate, care nu sunt uzuale - realizate de
Microsoft sau de altcineva - şi să le măsuraţi performanţele. Cred că veţi fi surprins de cât de bune sunt,
în realitate, performanţele.
De fapt, probabil că vi se pare greu de crezut, dar multe persoane (inclusiv eu) sunt de părere că
aplicaţiile administrate le-ar putea depăşi efectiv pe cele neadministrate, în ceea ce priveşte performanţele.
Există mai multe raţiuni în favoarea acestei afirmaţii. De exemplu, în timpul execuţiei, atunci când
compilatorul JIT efectuează compilarea codului IL în cod nativ, el ştie mai multe despre mediul de
execuţie decât ar putea şti un compilator neadministrat. Mai jos sunt enumerate câteva modalităţi prin
care codul administrat ar putea avea performanţe superioare faţă de cel neadministrat:

10
■ Un compilator JIT poate detecta că aplicaţia este rulată pe un calculator Pentium 4, producând un cod
nativ care să profite de orice instrucţiuni speciale ar putea fi oferite de acesta. De obicei, aplicaţiile
neadministrate sunt compilate pentru o unitate CPU de nivelul „celui-mai-mic-numitor-comun",
evitându-se utilizarea instrucţiunilor speciale, care ar putea oferi o creştere a performanţelor aplicaţiei
pentru noile unităţi CPU.
■ Un compilator JIT poate detecta că un anumit test returnează întotdeauna valoarea de adevăr false pe
calculatorul pe care este rulat. De exemplu, vom considera o metodă cu codul următor:
if (numberOfCPUs > 1) {

}
Acest cod ar putea determina compilatorul JIT să nu genereze instrucţiuni CPU, în cazul în care
calculatorul gazdă are o singură unitate CPU. în acest caz, codul nativ este reglat pentru calculatorul
gazdă: este mai mic şi este executat mai repede.
■ Componenta CLR ar putea schiţa execuţia codului, recompilând codul IL în cod nativ, în timpul
rulării aplicaţiei. Codul recompilat ar putea fi reorganizat, pentru a reduce ipotezele incorecte din
ramificaţii, în funcţie de tiparele de execuţie observate.
Acestea sunt numai câteva dintre motivele pentru care este de aşteptat ca viitoarele coduri
administrate să fie executate mai bine decât cele neadministrate, din zilele noastre. După cum am spus,
performanţele sunt, la ora actuală, destul de bune şi se vor îmbunătăţi, pe măsură ce va trece timpul.
Dacă experimentele dumneavoastră demonstrează că performanţele aplicaţiilor obţinute cu compilatorul
JIT nu sunt de un nivel adecvat, aţi putea profita de avantajele oferite de instrumentul Ngen.exe, care este
livrat împreună cu setul SDK .NET Framework. Acest instrument realizează compilarea întregului cod IL
dintr-un asamblaj în cod nativ, pe care îl salvează într-un fişier de pe disc. în timpul execuţiei, atunci
când este încărcat un asamblaj, componenta CLR verifică automat dacă există şi o versiune precompilată
a asamblajului şi, în acest caz, încarcă acest cod precompilat, astfel încât nu mai este necesară nici o
compilare în timpul execuţiei.
C o d u l IL şi v e r ific a r e a
Limbajul IL se bazează pe stive, ceea ce înseamnă că toate instrucţiunile sale „împing" operanzii într-o
stivă de execuţie şi „extrag" rezultatele din aceasta. Deoarece limbajul IL nu oferă instrucţiuni de
manipulare a registrelor, realizatorii de compilatoare pot produce mai uşor codul IL; nu mai este necesar să
se mai gândească la administrarea registrelor şi sunt necesare mai puţine instrucţiuni IL (din moment ce
nu există nici una pentru manipularea registrelor).
De asemenea, instrucţiunile IL nu depind de tip. De exemplu, limbajul IL pune la dispoziţie o
instrucţiune add, care efectuează sumarea ultimilor doi operanzi introduşi în stivă; nu există instrucţiuni
add separate pentru 32 de biţi şi 64 de biţi. Atunci când este executată instrucţiunea add, aceasta determină
tipurile operanzilor din stivă şi efectuează operaţia corespunzătoare.
După părerea mea, cel mai mare beneficiu al limbajului IL constă în faptul că nu abstractizează
unitatea CPU aflată la bază. Cel mai mare avantaj este robusteţea aplicaţiilor, în timpul compilării codului
IL în instrucţiuni native CPU, componenta CLR efectuează un proces numit verificare. în timpul verificării,
se examinează codul IL, de nivel înalt, şi se verifică dacă tot ceea ce face acesta este „sigur". De exemplu, se
verifică să nu se citească din memorie fără ca, în prealabil, să se fi scris în aceasta, ca fiecare metodă să fie
apelată cu numărul corect de parametri şi ca fiecare parametru să fie de tipul corect, ca valoarea de
returnare a fiecărei metode să fie utilizată adecvat, ca fiecare metodă să aibă o instrucţiune de returnare şi
aşa mai departe.
Metadatele modului administrat cuprind toate informaţiile despre metodă şi tip utilizate în procesul de
verificare. în cazul în care codul IL este considerat a fi „nesigur", este introdusă o excepţie
System.Security.Verif ierException, care împiedică executarea metodei.
C o d u l d u m n e a v o a s tr ă e ste_ _s ig
_ _u_r ?_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
Implicit, compilatoarele Microsoft C# şi Visual Basic produc coduri sigure. Codul sigur poate fi verificat
ca având această calitate. Totuşi, este posibilă utilizarea cuvântului cheie unsafe1 în limbajul C# sau alte
limbaje (cum ar fi C++ with Managed Extensions sau limbajul de asamblare IL) pentru a produce un cod
care nu se poate verifica drept fiind sigur.
Codul poate fi, de fapt, sigur, dar aceasta nu se poate demonstra prin verificarea sa.
1
Nesigur, (n.t.)

11
Pentru a garanta că toate metodele modulului administrat conţin cod IL care poate fi verificat ca fiind
sigur, se poate utiliza programul utilitar PEVerify (PEVerify.exe), livrat împreună cu pachetul .NET
Framework SDK. Atunci când cei de la Microsoft testează compilatoarele C# şi Visual Basic, ei rulează
modulul rezultant în programul PEVerify, pentru a garanta că întotdeauna compilatorul produce un cod
sigur, care poate fi verificat.
Dacă programul PEVerify detectează un cod care nu este sigur, atunci cei de la Microsoft remediază
compilatorul.
Puteţi încerca rularea programului PEVerify pe propriile dumneavoastră module, înainte de a le
împacheta şi livra. Dacă programul PEVerify detectează o problemă, atunci în compilator există o hibă, iar
dumneavoastră ar trebui s-o anunţaţi companiei Microsoft (sau celei care a produs compilatorul pe care îl
utilizaţi). Dacă programul PEVerify nu detectează un cod care nu poate fi verificat, atunci ştiţi că acesta va
putea fi rulat fără a introduce o excepţie Verif ierException pe calculatorul utilizatorului final.
Trebuie să ştiţi că verificarea necesită accesul la metadatele conţinute în orice asamblaje independente.
Astfel, atunci când se utilizează programul PEVerify pentru a verifica un asamblaj, acesta trebuie să poată
localiza şi încărca toate asamblajele referenţiate. Deoarece programul PEVerify utilizează componenta
CLR pentru a localiza asamblajele dependente, amplasarea acestora este detectată folosind aceleaşi reguli
de asociere şi probare care ar fi utilizate în mod normal la execuţia asamblajului respectiv. (Vom analiza
aceste reguli de asociere şi probare în capitolele 2 şi 3.)
Observaţi că un administrator poate opta pentru dezactivarea verificării (folosind instrumentul
administrativ Microsoft .NET Framework Configuration). Atunci când verificarea este dezactivată,
compilatorul JIT va compila un limbaj IL neverificabil în instrucţiuni native CPU; dar administratorul îşi
asumă întreaga responsabilitate pentru comportamentul codului.

în sistemul de operare Windows, fiecare proces are propriul său spaţiu de adrese virtual. Sunt necesare
spaţii de adrese separate, deoarece nu se poate avea încredere în codul aplicaţiei. Este cu totul posibil (şi,
din păcate, foarte obişnuit) ca o aplicaţie să citească şi să scrie într-o adresă de memorie care nu este valabilă.
Prin plasarea fiecărui proces Windows într-un spaţiu de adresă separat, se obţine o mai mare robusteţe: un
proces nu poate afecta în sens negativ un alt proces.
Prin verificarea codului administrat, ştim că acesta nu accesează necorespunzător memoria care nu
trebuie şi că nu poate afecta în sens negativ codul altei aplicaţii. Aceasta înseamnă ca putem rula mai multe
aplicaţii administrate, în cadrul unui singur spaţiu de adrese virtual.
Deoarece procesele Windows necesită o mulţime de resurse ale sistemului de operare, multitudinea
acestora poate afecta performanţele şi limita resursele disponibile. Reducerea numărului de procese prin
rularea mai multor aplicaţii în cadrul unui singur proces OS poate duce la îmbunătăţirea performanţelor
şi necesită mai puţine resurse, totodată fiind la fel de robust. Acesta este un alt avantaj al codului
administrat, faţă de cel neadministrat.
De fapt, componenta CLR oferă capacitatea de a executa mai multe aplicaţii administrate în cadrul unui
singur proces OS. Fiecare aplicaţie administrată se numeşte AppDomain. Implicit, fiecare fişier EXE
administrat va fi rulat în propriul său spaţiu de adrese separat, care are o singură aplicaţie AppDomain.
Totuşi, un proces care găzduieşte componenta CLR (cum ar fi serviciile Internet Information Services,
IIS, sau o versiune viitoare a limbajului SQL Server) poate hotărî să ruleze aplicaţiile AppDomain în
cadrul unui singur proces OS. O parte din Capitolul 20 este dedicată unei analize a aplicaţiilor
AppDomain.

B ib lio te c a d e c la s e .N E T F ra m e w o rk
în cadrul platformei .NET Framework este inclus un set de asamblaje .NET Framework Class Library
(FCL), care conţine câteva mii de definiţii de tipuri, fiecare tip oferind o anumită funcţionalitate. Pe scurt,
componenta CLR şi biblioteca FCL permit realizatorilor de aplicaţii să construiască următoarele feluri de
aplicaţii:
■ Servicii Web XML Metode care pot fi accesate foarte uşor prin Internet, serviciile XML Web
reprezintă, desigur, elementul esenţial al iniţiativei .NET a companiei Microsoft.

■ Web Forms Sunt aplicaţii bazate pe limbajul HTML (situri Web). De regulă, aplicaţiile Web Forms

12
vor efectua interogări de baze de date şi apelări ale serviciilor Web, vor combina şi filtra informaţiile
returnate, pe care le vor prezenta apoi în cadrul unui browser, folosind o interfaţă cu utilizatorul
bogată, bazată pe HTML. Aplicaţiile Web Forms oferă un mediu de dezvoltare în stil Visual Basic 6 şi
Visual InterDev pentru aplicaţiile Web scrise în orice limbaj din componenta CLR.
■ Windows Forms Sunt aplicaţii Windows GUI bogate. în loc de a utiliza o pagină Web Forms
pentru a crea interfaţa UI a aplicaţiei, se poate folosi funcţionalitatea mai puternică, cu performanţe
mai ridicate, oferită de suprafaţa de lucru Windows. Aplicaţiile Windows Forms pot profita de
controalele, meniurile şi evenimentele declanşate de mouse şi tastatură şi pot trata direct cu sistemul de
operare aflat la bază. Ca şi aplicaţiile Web Forms, şi acestea efectuează interogări ale bazelor de date şi
apelări ale serviciilor XML Web. Aplicaţiile Windows Forms oferă un mediu de dezvoltare asemănător
cu Visual Basic 6 pentru aplicaţiile GUI scrise în orice limbaj CLR.
■ Aplicaţii Windows de consolă Pentru aplicaţiile cu cerinţe UI foarte simple, o aplicaţie de consolă
reprezintă o modalitate rapidă şi facilă de a construi o aplicaţie. Compilatoarele, utilitarele şi
instrumentele sunt implementate, de regulă, ca aplicaţii de consolă.
■ Servicii Windows Da, folosind platforma .NET Framework, este posibil să se construiască aplicaţii
de serviciu, controlabile prin intermediul facilităţii Windows Service Control Manager (SCM).
■ Biblioteca de componente Platforma .NET Framework permite construirea de componente (tipuri)
autonome, care pot fi cu uşurinţă încorporate în oricare dintre tipurile de aplicaţii menţionate mai sus.
Deoarece biblioteca FCL conţine efectiv mii de tipuri, realizatorului de aplicaţii îi este prezentat un set de
tipuri înrudite în cadrul unui singur spaţiu de nume. De exemplu, spaţiul de nume System (cu care veţi
ajunge cât se poate de familiarizat) conţine tipul de bază Object, din care derivă, în ultimă instanţă, toate
celelalte tipuri. în plus, spaţiul de nume System conţine tipuri pentru numere întregi, caractere, şiruri,
tratarea excepţiilor şi operaţii I/O de la consolă, ca şi un set de tipuri utilitare care efectuează în siguranţă
transformări între tipurile de date, formateaza tipurile de date, generează numere aleatorii şi efectuează
diverse funcţii matematice. Toate aplicaţiile vor utiliza tipuri din spaţiul denume System.
Pentru a accesa oricare dintre caracteristicile platformei, este necesar să ştim ce spaţiu denume conţine
tipurile care oferă facilităţile căutate. Dacă dorim să personalizăm compor tamentul oricăruia dintre tipuri,
putem pur şi simplu deriva propriul tip din tipul FCLdorit. Modul în care platforma .NET Framework
prezintă realizatorilor de software o paradigmă de programare coerentă este prin natura orientată spre
obiecte a acesteia. De asemenea, realizatorii îşi pot crea cu uşurinţă propriile spaţii de nume, care să
conţină propriile tipuri. Spaţiile de nume şi tipurile se îmbină impecabil în paradigma programării.
Comparativ cu paradigma programării Win32, această nouă abordare simplifică substan ţial dezvoltarea de
software.
Majoritatea spaţiilor de nume din biblioteca FCL prezintă tipuri care pot fi utilizate pentru orice fel de
aplicaţie. în Tabelul 1-2 sunt prezentate câteva dintre spaţiile de nume generale, împreună cu o scurtă
descriere a utilizării tipurilor din cadrul acestora.
Tabelul 1-2 Câteva spaţii de nume generaled i n biblioteca FCL
Spaţiul de nume Descrierea conţinutului
System Toate tipurile de bază, folosite de fiecare aplicaţie.
System.Collections Tipurile pentru administrarea colecţiilor de obiecte;
cuprinde tipurile de colecţii uzuale, cum ar fi stivele,cozile, tabelele
hash şi aşa mai departe.
System. Diagnostics Tipurile care ajută la instrumentarea şi depanarea
aplicaţiilor.
System.Drawing Tipurile pentru manipularea graficelor 2-D; sunt utilizate,
de regulă, pentru aplicaţiile Windows Forms şi pentrucrearea de imagini
care vor apărea în pagina Web Forms.
System.EnterpriseServices Tipurile pentru gestionarea tranzacţiilor, componentelor
plasate în cozi, crearea de rezervoare de obiecte, activareaJIT, securitatea şi
alte caracteristici, care fac ca utilizareacodului administrat să fie mai
eficientă pe server.

System.Globalization Tipurile pentru suportul National Language Support (NLS),

13
cum ar fi comparările de şiruri, formatările şi calendarele.
System. 10 Tipurile pentru realizarea operaţiilor I/O cu derulare
continuă, parcurgerea directoarelor şi fişierelor.
System.Management Tipurile utilizate pentru gestionarea altor calculatoare din
cadrul întreprinderii, prin intermediul facilităţii WindowsManagement
Instrumentation (WMI).
System.NET Tipurile care permit comunicaţiile prin reţea.
System.Reflection Tipurile care permit inspectarea metadatelor şi asocierea
ulterioară cu tipuri şi membrii acestora.

Spaţiul de nume Descrierea conţinutului


System.Resources Tipurile pentru manipularea resurselor de date externe.
System. Runtime. InteropSer Tipurile care permit codului administrat să acceseze
vices facilităţile platformei OS, cum ar fi componentele COM
şi funcţiile din bibliotecile DLL din Win32.
System. Runtime. Remotlng Tipurile care permit accesarea de la distanţă a tipurilor.
System.Runtime.Serializat Tipurile care permit ca instanţele obiectelor să fie
ion persistente şi să poată fi regenerate dintr-un flux.
System.Security Tipurile utilizate pentru protejarea datelor şi resurselor.
System.Text Tipurile necesare pentru a lucra cu text în diverse
codificări, cum ar fi ASCII sau Unicode.
System.Threading Tipurile utilizate pentru operaţiile asincrone şi
sincronizarea accesului la resurse.
System.Xml Tipurile utilizate pentru prelucrarea schemelor şi datelor
XML.

Această carte are ca subiect componenta CLR şi tipurile generale care interacţionează cu aceasta (care
reprezintă majoritatea spaţiilor de nume enumerate în Tabelul 1-2). Astfel, conţinutul acestei cărţi este util
pentru toţi programatorii .NET Framework, indiferent de tipul de aplicaţie pe care o realizează.
In afară de spaţiile de nume generale, componenta CLR pune la dispoziţie şi spaţii de nume ale căror
tipuri sunt utilizate pentru construirea de tipuri de aplicaţii specifice. în Tabelul 1-3 sunt enumerate
câteva dintre spaţiile de nume specifice aplicaţiilor din biblioteca FCL.
Tabelul 1-3 Câteva spaţii de nume specifice aplicaţiilor, din biblioteca FCL

Spaţiul de nume Tipul de aplicaţie


System.Web.Services Tipurile utilizate pentru a construi servicii XML Web.
System.Web.UI Tipurile utilizate pentru a construi aplicaţii Web Forms.
System.Windows.Forms Tipurile utilizate pentru a construi aplicaţii Windows GUI.
System.ServiceProcess Tipurile utilizate pentru a construi un serviciu Windows,
care poate fi controlat de facilitatea SCM.

Este de aşteptat să apară multe cărţi bune, care să explice cum se construiesc tipuri specifice de aplicaţii
(cum ar fi serviciile Windows, aplicaţiile Web Forms şi Windows Forms). Aceste cărţi vă vor oferi un
excelent punct de plecare pentru a vă ajuta la construirea aplicaţiilor dumneavoastră. Sunt înclinat să
cred că aceste cărţi specifice aplicaţiilor vă vor ajuta să învăţaţi de sus în jos, deoarece ele se concentrează
asupra tipului de aplicaţie, nu asupra platformei de dezvoltare. In această carte vom oferi informaţii care vă
vor ajuta să învăţaţi de jos în sus. După parcurgerea acestei cărţi şi a uneia specifice aplicaţiilor, veţi
putea construi cu uşurinţă şi eficient orice tip de aplicaţie .NET Framework doriţi.

14
S p e cifica
ţia C o m m o n T yp e S y stem
Până acum trebuie să fi devenit evident că, în totalitate, componenta CLR se ocupă de tipuri. Tipurile
oferă funcţionalitate aplicaţiilor şi componentelor acestora. Tipurile reprezintă mecanismul prin care codul
scris într-un limbaj de programare poate comunica cu cel scris în alt limbaj de programare. Deoarece
tipurile reprezintă baza componentei CLR, compania Microsoft a creat o specificaţie formală — Common
Type System1 (CTS) — care descrie modul în care sunt definite şi se comportă tipurile.
Specificaţia CTS stabileşte că un tip poate conţine zero sau mai mulţi membri. în Partea a IlI-a ne vom
ocupa detaliat de aceşti membri. Pentru moment, vom oferi doar o scurtă prezentare a lor:
■ câmpul O variabilă de date, care face parte din starea obiectului. Câmpurile sunt identificate prin
nume şi tip;
■ metoda O funcţie care efectuează o operaţie asupra obiectului, adeseori modificând starea acestuia.
Metodele au un nume, o semnătură şi modificatori. Semnătura arată convenţia de apelare, numărul de
parametri (şi secvenţa lor), tipurile de parametri şi tipul de valoare returnată de către metodă;
■ proprietatea Pentru apelant, acest membru arată ca un câmp. Dar pentru imple mentatorul tipului,
arată ca o metodă (sau două). Proprietăţile permit unui imple mentator să valideze parametrii de
intrare şi starea obiectului, înainte de a accesa va loarea şi/sau calcula o valoare numai atunci când este
necesar. De asemenea, ele permit unui utilizator al tipului respectiv să folosească o sintaxă simplificată.
în sfârşit, proprietăţile permit crearea de „câmpuri" numai pentru citire sau numai pentru scriere;
■ evenimentul Un eveniment permite existenţa un mecanism de notificare între un obiect şi alte
obiecte interesate. De exemplu, un buton ar putea reprezenta un eveniment care să anunţe alte obiecte
atunci când se efectuează clic pe el.
De asemenea, specificaţia CTS arată regulile pentru vizibilitatea tipurilor şi pentru accesul la membrii
unui tip. De exemplu, prin marcarea unui tip capublic (numit public), acesta este exportat, făcându-1
vizibil şi accesibil pentru orice asamblaj. Pe de altă parte, prin marcarea unui tip ca assembly (numit
internai în C#), acesta este făcut vizibil şi accesibil numai pentru codul din acelaşi asamblaj. Astfel,
specificaţia CTS stabileşte regulile prin care asamblajele formează o limită de vizibilitate pentru un tip,
iar componenta CLR întăreşte regulile de vizibilitate.
Indiferent dacă un tip este vizibil pentru un apelant, el reuşeşte să controleze dacă apelantul are acces
la membrii săi. în lista următoare sunt prezentate opţiunile valabile pentru controlul accesului la o
metodă sau un câmp:
■ Private Metoda poate fi apelată numai de către alte metode din acelaşi tip de clasă.
■ Family Metoda poate fi apelată de către tipurile derivate, indiferent dacă ele se afla în acelaşi
asamblaj sau nu. De remarcat că multe limbaje de programare (cum ar fi C++ şi C#) se referă la opţiunea
family ca protected.
■ Family and assembly Metoda poate fi apelată de către tipurile derivate, dar numai
dacă tipul derivat este definit în acelaşi asamblaj. Multe limbaje de programare (cum
ar fi C# şi Visual Basic) nu oferă acest control al accesului. Desigur că limbajul IL
Assembly îl pune la dispoziţie;
■ Assembly Metoda poate fi apelată de către orice cod din acelaşi asamblaj. Multe
limbaje de programare se referă la opţiunea assembly ca internai.
■ Family or assembly Metoda poate fi apelată de către tipurile derivate din orice
asamblaj. De asemenea, metoda poate fi apelată de către orice tipuri din acelaşi asamblaj,
în limbajul C# cypţmne.2. family or assembly este considerată ca protected internai.
■ Public Metoda poate fi apelată de către orice cod, din orice asamblaj.
în plus, specificaţia CTS defineşte regulile care guvernează moştenirea tipurilor, funcţiile virtuale,
timpul de viaţă al obiectului şi aşa mai departe. Aceste reguli au fost concepute pentru a cuprinde semantica
ce poate fi exprimată în limbajele de programare din epoca modernă. De fapt, nici nu va fi necesar să
învăţaţi regulile CTS per se, deoarece limbajul pe care îl veţi alege va dezvălui propria sa sintaxă şi regulile
referitoare la tipuri, în acelaşi mod cu care sunteţi familiarizat acum, şi va potrivi sintaxa specifică limbajului
după „limbajul" componentei CLR, atunci când va emite modulul administrat.
Atunci când am început să lucrez prima dată cu componenta CLR, mi-am dat seama repede că cel mai
bine este să considerăm limbajul şi comportamentul codului ca două lucruri separate şi diferite. Folosind
limbajul C++, putem defini propriile tipuri, cu propriii membri.
Desigur că am fi putut utiliza şi limbajul C# sau Visual Basic pentru a defini acelaşi tip, cu aceiaşi
1
Sistemul de tipuri comun, (n.t.)
15
membri. Cu siguranţă că sintaxa folosită pentru a defini acest tip este diferită, depinzând de limbajul ales,
dar comportamentul tipului va fi absolut identic, indiferent de limbaj, deoarece specificaţia CTS a
componentei CLR este cea care defineşte comportamentul tipului.
Pentru a ajuta la clarificarea acestei idei, vom da un exemplu. Specificaţia CTS acceptă numai
moştenirea unică. Astfel, dacă limbajul C++ acceptă tipuri care moştenesc mai multe tipuri de bază,
specificaţia CTS nu poate accepta şi opera cu astfel de tipuri. Pentru a ajuta realizatorul de aplicaţii,
compilatorul Visual C++ raportează o eroare, dacă detectează că se încearcă crearea unui cod administrat,
care să includă un tip care moşteneşte de la mai multe tipuri de bază.
Mai există încă o regulă a specificaţiei CTS. Toate tipurile trebuie (în cele din urmă) să moştenească un
tip predefinit: System.Object. După cum puteţi vedea, Object este numele unui tip definit în spaţiul de
nume System. Tipul Object este rădăcina tuturor celorlalte tipuri şi, prin urmare, garantează că fiecare
apariţie a tipului are un set minim de comportamente. Mai exact, tipul System.Object permite să se
efectueze următoarele:
■ compararea egalităţii a două instanţe;
■ obţinerea codului hash corespunzător instanţei respective;
■ interogarea adevăratului tip al unei instanţe;
■ efectuarea unei copii superficiale (după biţi) a instanţei;
■ obţinerea unei reprezentări sub formă de şir a stării curente a obiectului.

S p ecifica
ţia C o m m o n L an g u ag e S p ec ificatio n
Specificaţia COM permite obiectelor create în diverse limbaje de programare să comunice unele cu
altele. Pe de altă parte, componenta CLR integrează acum toate limbajele şi permite ca obiectele create
într-un limbaj să fie tratate în mod egal de către codul scris într-un limbaj complet diferit. Această integrare
este posibilă datorită setului de tipuri standard al componentei CLR, informaţiilor cu autodescriere
(metadatelor) şi mediului de execuţie comun.
Deşi această integrare a limbajelor reprezintă un scop extraordinar, adevărul este că limbajele de
programare sunt foarte diferite unele de altele. De exemplu, unele limbaje nu tratează simbolurile ca fiind
dependente de tipul de litere sau nu permit tipuri de numere întregi fără semn, supraîncărcarea
operatorilor sau metode care să accepte un număr variabil de parametri.
Dacă intenţionaţi să creaţi tipuri care să fie uşor accesibile din alte limbaje de programare, atunci este
necesar să utilizaţi numai caracteristicile din limbajul de programare care este sigur că sunt disponibile şi
în toate celelalte limbaje. Pentru a vă ajuta, compania Microsoft a definit o specificaţie numită Common
Language Specification1 (CLS), care prezintă detaliat pentru producătorii de compilatoare setul minim
de caracteristici pe care trebuie să le accepte produsele lor, în cazul în care compilatoarele au ca ţintă
componenta CLR.
Specificaţia CLR/CTS acceptă mult mai multe caracteristici decât
subsetul definit de către specificaţia CLS, astfel încât, dacă nu ţineţi la
operabilitatea dintre limbaje, atunci puteţi realiza tipuri foarte bogate,
limitate doar de către setul de caracteristici al limbajului respectiv.
Mai exact, specificaţia CTS defineşte regulile pe care trebuie să le
respecte tipurile şi metodele vizibile extern, pentru a fi accesibile din
orice limbaj de programare compatibil CLR. De remarcat că regulile
CLS nu se aplică pentru codurile care sunt accesibile numai în cadrul
asamblajului de definiţie. în Figura 1-6 este prezentat un rezumat al
ideilor exprimate în acest paragraf.
Figura 1-6 Limbajele oferă o După cum se poate observa în Figura 1-6, specificaţia CLR/CTS oferă un
submulţime din specificaţia set de caracteristici. Unele limbaje expun o submulţime cuprinzătoare din
CLR/CTS şi includ specificaţia
specificaţia CLR/CTS. De exemplu, un programator care doreşte să scrie
CLS (dar limbajele nu reprezintă
neapărat aceeaşi mulţime).
codul în limbajul de asamblare IL poate utiliza toate caracteristicile oferite de
specificaţia CLR/CTS.
Majoritatea celorlalte limbaje, cum ar fi C#, Visual Basic şi Fortran, oferă programatorului un subset

1
Specificaţia comună pentru limbaje, (n.t.)

16
al caracteristicilor CLR/CTS. Specificaţia CLS defineşte setul minim de caracteristici pe care trebuie să le
accepte toate limbajele.
În cazul în care proiectaţi un tip într-un limbaj şi intenţionaţi ca acesta să fie utilizat de un alt limbaj, nu
trebuie să utilizaţi vreuna dintre caracteristicile din afara specificaţiei CLS. Aceasta ar însemna că s-ar
putea ca membrii tipului dumneavoastră să nu fie accesibili pentru programatorii care scriu coduri în alte
limbaje de programare.
în codul de mai jos este definit un tip compatibil cu specificaţia CLS, în limbajul C#. Totuşi, tipul are
câteva construcţii care nu sunt compatibile cu specificaţia CLS, ceea ce determină compilatorul C# să
emită mesaje de eroare. using System;
//Cere compilatorului sa verifice compatibilitatea CLS.
[assembly:CLSCompliant(true)]

//Erorile care urmeaz ă apar deoarece clasa este de tip public.


public class App {

1
//Eroare: tipul de returnare pentru 'App.Abcf) nu e compatibil CLS.
public UInt32 Abc() { return 0; }

//Eroare: identificatorul 'App.abcO' difer ă.


//numai in cazul in care nu este compatibil CLS
public void abc() { }

//Nu exista nici o eroare: metoda este de tip private.


private UInt32 ABC() { return 0; }

}
În acest cod, atributul [assembly:CLSCompliant(true)] este aplicat asamblajului. Acest atribut îi cere
compilatorului să se asigure că orice tip expus public nu are vreo construcţie care să-1 împiedice de a fi
accesat din alte limbaje de programare. Atunci când acest cod este compilat, compilatorul C# emite două
erori. Prima este raportată deoarece metoda Abc returnează un număr întreg fără semn; Visual Basic şi alte
câteva limbaje de programare nu pot manipula valori întregi fără semn. Cea de-a doua eroare apare
pentru că acest tip expune două metode publice care diferă doar prin tipul de litere: Abc şi abc. Visual Basic
şi alte câteva limbaje nu pot apela ambele metode.
Interesant este că, dacă ştergem opţiunea public de dinaintea clasei ' class App' şi efectuăm din nou
compilarea, ambele erori vor dispărea. Motivul este că tipul Abc va fi stabilit implicit ca internai şi, prin
urmare, nu va mai fi expus în afara asamblajului. Pentru o listă completă a regulilor specificaţiei CLS,
consultaţi secţiunea „Cross-Language Interope-rability1 ", din documentaţia .NET Framework SDK.
Vom distila regulile CLS în ceva foarte simplu. în componenta CLR, fiecare membru al unui tip este fie un
câmp (date), fie o metodă (comportament). Aceasta înseamnă că fiecare limbaj de programare trebuie să
poată accesa câmpuri şi să apeleze metode. Anumite câmpuri şi metode sunt utilizate în moduri speciale
şi comune. Pentru a facilita programarea, limbajele oferă de regulă abstractizări suplimentare, care
uşurează codificarea acestor structuri de programare uzuale. De exemplu, limbajele etalează concepte cum
ar fi enumerările, tablourile, proprietăţile, indexatorii, delegaţii, evenimentele, constructorii, destructorii,
supraîncărcările de operatori, operatorii de conversie şi aşa mai departe. Atunci când un compilator
întâlneşte în codul sursă una dintre aceste construcţii, el trebuie să o traducă în câmpuri şi metode, astfel
încât componenta CLR şi alte limbaje de programare să o poată accesa.
Vom considera următoarea definiţie a unui tip, care conţine un constructor, un destructor,
câţiva operatori supraîncărcaţi, o proprietate, un indexator şi un eveniment. Codul prezentat este doar
pentru a permite compilarea; nu reprezintă o modalitate corectă de implementare a unui tip.

using System;
class Test //Constructor
{ public
Test() {}
//Destructor
-Test() {}
//Supra
încărcarea unor operatori.
1
Interoperabilitatea între limbaje, (n.t.)

17
public static Boolean operator == (Test t1, Test t2) {
return true;
}
public static Boolea n operator != (Test t1, Test t2) {
return false;
}
//O supraincarcare de operator.
pu bl ic st at ic Te st op er at o r + ( Te st t1 , T es t t 2) { re tu rn nu ll ; }

// 0 proprietate.
public String Aproperty {
get { return nuli; }
set { }}
//Un indexator.
public String this[Int32 x] {
get { return nuli; }
set { }
}
//Un eveniment.
event EventHandler AnEvent;
}
Atunci când compilatorul compilează acest cod, rezultatul este un tip care are un număr de câmpuri şi
metode definite în cadrul său. Puteţi observa cu uşurinţă aceasta, utilizând instrumentul IL Disassembler
(ILDasm.exe) din cadrul setului .NET Framework SDK, pentru a analiza modulul administrat rezultant,
care este prezentat în Figura 1-7.
în Tabelul 1-4 se arată cum s-a realizat corespondenţa dintre construcţiile limbajului de programare şi
câmpurile şi metodele echivalente din specificaţia CLR.

Figura 1-7 Cu ajutorul instrumentului ILDasm sunt afişate câmpurile şi metodele tipului Test (obţinute din
metadate).

Tabelul 1-4 Câmpurile şi metodele tipului Test (obţinute din metadate).


Membrul tipului Tipul de membru Construcţia echivalentă din limbajul
de programare
AnEvent Câmp Eveniment; numele câmpului este AnEvent,
iar tipul său este System.EventHandler.
.ctor Metodă Constructor.
Finalize Metodă Destructor.
add_AnEvent Metodă Metoda accesorie add a evenimentului.
get_AProperty Metodă Metoda accesorie get a proprietăţii.
get_Item Metodă Metoda accesorie get a indexatorului.
op_Addition Metodă Operatorul +.
op_Equality Metodă Operatorul ==.
op_Inequality Metodă Operatorul !=.

18
remove_AnEvent Metodă Metoda accesorie remove a evenimentului.
set_AProperty Metodă Metoda accesorie set a proprietăţii.
set_Item Metodă Metoda accesorie set a indexatorului.

Nodurile suplimentare de sub tipul Test, care nu sunt menţionate în Tabelul 1-4 - .class, .custom,
AnEvent, AProperty şi Item - identifică metadate suplimentare despre tip. Aceste noduri nu corespund
câmpurilor sau metodelor; ele nu fac decât să ofere o serie de informaţii suplimentare despre tipul la care
poate avea acces componenta CLR, limbajele de programare sau instrumentele. De exemplu, un
instrument poate vedea că tipul Test pune la dispoziţie un eveniment numit AnEvent, care este expus prin
intermediul celor două metode, add_AnEvent şi remove_AnEvent.
In tero p e rab ilitatea cu co d u l n ead m in is trat
Platforma .NET Framework oferă o tonă de avantaje, faţă de alte platforme de dezvoltare. Totuşi,
foarte puţine companii îşi pot permite să reproiecteze şi să reimplementeze toate codurile existente.
Compania Microsoft îşi dă seama de aceasta şi a construit componenta CLR astfel încât să ofere mecanisme
care permit ca o aplicaţie să fie formată atât din părţi administrate, cât şi din părţi neadministrate. Mai
exact, componenta CLR acceptă trei ipoteze de interoperabilitate:
■ Codul administrat poate apela o funcţie neadministrată dintr-o bibliotecă DLL.
Codul administrat poate apela cu uşurinţă funcţiile conţinute într-o bibliotecă DLL, folosind un
mecanism numit P/Invoke (de la Platform Invoke1 ). în fond, multe dintre tipurile definite intern în
biblioteca FCL apelează funcţii exportate din fişierele Kernel32.dll, User32.dll şi aşa mai departe.
Multe limbaje de programare vor prezenta un mecanism care facilitează apelarea funcţiilor
neadministrate conţinute în fişierele DLL de către codul administrat. De exemplu, o aplicaţie C# sau
Visual Basic poate apela funcţia CreateSemaphore, exportată din fişierul Kernel32.dll.
■ Codul administrat poate folosi o componentă COM existentă (server). Multe companii au
implementat deja un număr de componente COM neadministrate. Prin utilizarea bibliotecii de tipuri
din aceste componente, poate fi creat un asamblaj administrat, care să descrie componenta COM.
Codul administrat poate accesa tipul din asamblajul administrat, la fel ca orice alt tip administrat.
Pentru mai multe informaţii, folosiţi instrumentul Tlblmp.exe, care este livrat împreună cu setul .NET|
Framework SDK. Uneori, este posibil să nu existe o bibliotecă de tipuri sau este necesar un control mai
mare asupra a ceea ce produce programul Tlblmp.exe. în aceste situaţii, se poate construi manual în
codul sursă un tip, pe care componenta CLR îl poate utiliza pentru a realiza interoperabilitatea
adecvată. De exemplu, se pot folosi componentele DirectX COM dintr-o aplicaţie C# sau Visual
Basic.
■ Codul neadministrat poate utiliza un tip administrat (server). O mare cantitate de cod
neadministrat existent necesită furnizarea unei componente COM, pentru a funcţiona corect. Este
mult mai uşor să se implementeze aceste componente folosind un cod administrat, astfel încât se pot
evita toate codurile referitoare la numerotarea referinţelor şi interfeţe. De exemplu, s-ar putea crea un
control ActiveX sau o extensie shell în limbajul C# sau Visual Basic. Pentru mai multe informaţii, folosiţi
instrumentele TlbExp.exe şi RegAsm.exe, care sunt livrate împreună cu setul .NET Framework SDK.
În afară de aceste trei situaţii, compilatorul Microsoft Visual C++ (versiunea 13) acceptă un nou
comutator de la linia de comandă, /cir. Acest comutator îi cere compilatorului să emită cod IL, în loc de
instrucţiuni native x86. Dacă aveţi o cantitate mare de cod C++ existent, puteţi să-1 recompilaţi folosind
acest nou comutator de compilare. Pentru a fi executat, noul cod va necesita componenta CLR, iar acum
codul poate fi modificat în timp, astfel încât să se profite de avantajele caracteristicilor specifice
componentei CLR.
Cu ajutorul comutatorului /cir nu se poate efectua compilarea în limbajul IL pentru metodele care
conţin un limbaj de asamblare inline (prin intermediul cuvântului cheie _asm), acceptă un număr de
argumente variabil, apelări setjmp sau care conţin rutine intrinsece (cum ar fi enable, disable,
_ReturnAddress şi _AddressOfReturnAddress).

Pentru o listă completă a construcţiilor pe care compilatorul C++ nu le poate compila în limbajul IL,
consultaţi documentaţia compilatorului Visual C++. Atunci când compilatorul nu poate efectua

1
Invocarea platformei, (n.t.)

19
compilarea metodei în limbajul IL, el o realizează în x86, astfel încât aplicaţia este totuşi rulată.
Reţineţi că, deşi codul IL produs este administrat, datele nu sunt; adică obiectele de tip date nu sunt
alocate din memoria heap administrată şi nu se realizează colectarea gunoaielor pentru ele. De fapt,
tipurile de date nu au metadate produse pentru ele, iar numele metodelor tipurilor sunt denaturate.
În următorul cod C se apelează funcţia printf din biblioteca de rulare C standard şi metoda
System.ConsoleWriteLine. Tipul System.Console este definit în cadrul bibliotecii FCL. Astfel, în codul
C/C++ se pot utiliza bibliotecile disponibile pentru limbajele C/C++, ca şi tipurile administrate.
#include <stdio.h> //Pentru printf
#using <mscorlib.dll> //Pentru tipurile administrate definite in
//acest asamblaj
using namespace System; //Acceseaz ă cu u şurin ţa tipurile din
//spa ţiul de nume System

//Implementeaz ă o func ţie main C/C++ normala


void main() {

//Apeleaz ă func ţia printf din biblioteca de rulare C


printf("Afi ş at de printf. \ r \ n " );

//Apeleaz ă metoda WriteLine a tipului System.Console din biblioteca FCL.


Console::WriteLine("Afi şat de Console::WriteLine. ");
}
Compilarea acestui cod este cât se poate de uşoară. Dacă acest cod s-ar afla într-un fişier MgdCApp.cpp,
l-am compila folosind în promptul de comandă următoarea linie:
ci /cir MgdCApp.cpp
Rezultatul este un fişier de asamblare MgdCApp.exe. Dacă rulăm fişierul MgdCApp.exe, vom obţine
următoarea ieşire:
C:\>MgdCApp
Afi ş at de printf.
Afi şat de Console::WriteLine.
Dacă folosim utilitarul ILDasm.exe pentru a analiza acest fişier, vom obţine ieşirea prezentată în Figura
1-8.
în Figura 1-8 se observă că programul utilitar ILDasm arată toate funcţiile şi câmpurile globale definite
în cadrul asamblajului. Este evident că o mulţime de materiale au fost generate automat de către
compilator. Dacă se efectuează de două ori clic pe metoda Main, programul utilitar ILDasm va prezenta
codul IL:
.method public static int32
modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
main() cil managed
{

Figura 1-8 Utilitarul ILDasm prezintă metadatele din asamblajul MgsCApp.exe.

.vtentry 1 : 1
//Code size 1 28 (0x1c)
.maxstack 1

1
Dimensiunea codului, (n.t.)

20
IL_0000: ldsflda valuetype
$ArrayType$0x0faed885 '?A0x44d29f64.unnamed-global-0'
IL_0005: caii vararg int32
modopt ţ[rascorlib]System.Runtime.CompilerServices.CallConvCdecl)
printf(int8
modopt([Microsoft.VisualC]Microsoft.VisualC.NoSignSpecifiedModifier)
modopt([Microsoft.VisualC]Microsoft.VisualC.IsConstModifier)*)
IL_000a: pop
IL_000b: ldsflda valuetype
$ArrayType$0x0e6cb2b2 '?A0x44d29f64.unnamed-global-1'
IL_0010: newobj instance void [mscorlib]System.String::.ctor(int8*)
IL_0015: caii void [mscorlib]System.Console::WriteLine(string)
IL_001a: ldc.i4.0 IL_OO1b: ret
2
} //end of method 'Global Functions' : :main
Ceea ce se vede aici nu arată prea frumos, deoarece compilatorul generează o cantitate mare de cod
special, pentru a face ca toate acestea să funcţioneze. Totuşi, din acest cod IL se poate vedea că este apelată
atât metoda printf, cât şi Console.WriteLine.

2
Sfârşitul metodei, (n.t.)

21