CATEGORII DOCUMENTE |
DOCUMENTE SIMILARE |
||
|
||
|
Elementele
de baza ale platformei
Microsoft .NET Framework
Arhitectura platformei
de dezvoltare .NET Framework
Platforma Microsoft .NET Framework introduce multe concepte, tehnologii si termeni roi. Scopul acestui capitol este de a realiza o prezentare a arhitecturii platformei .NET Framework, a prezenta cateva dintre noile tehnologii incluse in acest cadru de lucru si a defini multi dintre termenii pe care-i vom intalni atunci cand vom incepe utilizarea sa. De asemenea, vom parcurge procesul de construire a codului sursa ca o aplicatie sau un set de componente (tipuri) care pot fi redistribuite, dupa care vom explica modul in care sunt executate aceste componente.
Compilarea codului sursa in module administrate
Ei bine, deci v-ati decis sa utilizati .NET Framework drept platforma de dezvoltare Foarte bine! Primul pas consta in a stabili ce tip de aplicatie sau componente intentionati sa construiti. Vom presupune ca ati stabilit acest detaliu minor, ca totul este conceput specificatiile sunt scrise, iar dumneavoastra sunteti gata de a incepe dezvoltarea.
Acum, trebuie sa decideti ce limbaj de programare veti folosi. De obicei, aceasta sarcina este dificila, deoarece diversele limbaje pun la dispozitie capacitati diferite. De exemplu, in limbajele C/C++ neadministrate avem un control destul de scazut asupra sistemului Putem administra memoria exact asa cum dorim, crea cu usurinta fire, daca este necesar, si asa mai departe. Pe de alta parte, limbajul Visual Basic permite construirea foarte rapida a aplicatiilor UI si faciliteaza controlul obiectelor COM si bazelor de date.
Rularea in limbaj comun (CLR) inseamna exact ceea ce sugereaza denumirea sa: este un mod de rulare care poate fi utilizat de catre diverse limbaje de programare diferite Caracteristicile rularii CLR sunt disponibile pentru toate limbajele de programare care o ai in vedere - punct. Daca rularea foloseste exceptiile pentru a raporta erorile, atunci toate limbajele vor raporta erorile prin intermediul exceptiilor. Daca rularea permite crearea unui fir, atunci oricare dintre limbaje il poate crea.
De fapt, in momentul executiei, rularea CLR nu "stie' ce limbaj de programare a folosit realizatorul de aplicatii pentru a scrie codul sursa. Aceasta inseamna ca putem alege orice limbaj de programare care permite exprimarea cea mai simpla a intentiilor noastre. Putem dezvolta codul in orice limbaj de programare dorim, atata vreme cat compilatorul folosit pentru a compila codul are in vedere rularea CLR.
Deci, daca ceea ce am afirmat mai sus este adevarat, care este avantajul utilizarii unui limbaj de programare fata de altul? Ei bine, considerati compilatoarele ca pe niste verificatoare de sintaxa si analizatoare de "cod corect'. Ele analizeaza codul sursa, se asigura ca ceea ce ati scris dumneavoastra are un sens oarecare si apoi trimit la iesire un cod care descrie intentiile dumneavoastra. Diversele limbaje de programare permit dezvoltarea folosind diverse sintaxe.
Valoarea acestor optiuni nu trebuie subestimata. Pentru aplicatiile matematice sau financiare, exprimarea intentiilor dumneavoastra folosind sintaxa APL poate duce la economisirea multor zile de dezvoltare, comparativ cu exprimarea acelorasi intentii cu ajutorul sintaxei Perl, de exemplu.
Compania Microsoft a creat o serie de compilatoare de limbaje care au in vedere momentul executiei: C++ cu extensii administrate, C# (pronuntat "C sharp'), Visual Basic, JScript, J# (un compilator de limbaj Java) si un asamblor de limbaj intermediar (IL). In afara de Microsoft, o serie de alte companii creeaza compilatoare care produc coduri ce au in vedere rularea CLR. in ceea ce ma priveste, cunosc compilatoare pentru Alice, APL, COBOL, Component Pascal, Eiffel, Fortran, Haskell, Mercury, ML, Mondrian, Oberon, Perl, Python, RPG, Scheme si Smalltalk.
in Figura 1.1 este prezentat procesul de compilare a fisierelor de cod sursa. Dupa cum se poate observa aici, puteti crea fisiere de cod sursa folosind orice limbaj de programare care accepta rularea CLR. Apoi se utilizeaza compilatorul corespunzator, pentru a verifica sintaxa si a analiza codul sursa. Indiferent de compilatorul folosit, rezultatul este un modul administrat. Un modul administrat este un fisier executabil Windows standard portabil (PE1[1]), care necesita rularea CLR pentru a fi executat. in viitor, si alte sisteme de operare ar putea utiliza formatul de fisier PE.
Figura
1-1 Compilarea codului sursa, in vederea
obtinerii unui modul administrat.
Tabelul 1-1 Componentele
unui modul administrat
Componenta Descriere
Antetul PE Antetul de fisier PE standard din Windows, care este similar cu
antetul Common Object File Format[2] (COFF). Acest antet indica tipul de fisier - GUI, CUI sau DLL - si are o marca de timp care arata cand a fost construit fisierul. Pentru modulele care contin numai cod IL, informatiile masive din antetul PE sunt ignorate. Pentru modulele care contin cod CPU nativ, antetul contine informatii despre acesta.
Antetul CLR Contine informatiile (interpretate de CLR si programele
utilitare) care fac ca acesta sa fie un modul administrat. Antetul cuprinde versiunea CLR necesara, cateva indicatoare, token-ul de metadate MethodDef al metodei punctului de intrare (metoda Main) din modulul administrat si locatia/dimensiunea metadatelor modulului, resursele, numele tare, cateva indicatoare si alte informatii mai putin interesante.
Metadatele Fiecare modul administrat contine tabele de metadate. Exista
doua tipuri principale de tabele: tabelele care descriu tipurile si membrii definiti in codul sursa si tabelele care descriu tipurile si membrii la care se face referire in codul sursa.
Codul in limbaj Codul pe care 1-a produs compilatorul atunci cand a compilat
intermediar (IL) codul sursa. Ulterior, componenta CLR compileaza limbajul IL
in instructiuni CPU native.
Majoritatea
compilatoarelor din trecut produceau coduri care aveau ca tinta o
anumita arhitectura CPU, cum ar fi x86, IA64, Alpha sau
PowerPC. in schimb, toate compilatoarele care se conformeaza rularii
CLR produc cod IL. (Mai tarziu in cadrul acestui capitol, vom intra in
detalii despre codul IL.) Codul IL este numit uneori cod administrat, deoarece componenta CLR ii
administreaza timpul de viata si executia.
In
afara de crearea codului IL, este necesar ca fiecare compilator care are
ca tinta rularea CLR
sa emita metadate complete in fiecare modul administrat. Pe
scurt, metadatele reprezinta pur
si simplu un set de tabele care descriu ceea ce este definit in modul, cum
ar fi tipurile si membrii lor.
In plus, metadatele au de asemenea tabele care indica la ce se refera
modulul administrat, cum ar fi
tipurile importate si membrii lor. Metadatele reprezinta un superset
de tehnologii mai vechi, cum ar fi bibliotecile de tipuri si
fisierele limbajului de definitie a interfetei (IDL
). Important de remarcat este ca metadatele CLR sunt cu mult mai complete. Si, spre deosebire de
bibliotecile de tipuri si fisierele IDL, metadatele sunt intotdeauna asociate cu fisierul care
contine codul IL. De fapt, metadatele sunt intotdeauna inglobate in acelasi fisier EXE/DLL
ca si codul, ceea ce face imposibila separarea celor doua. Deoarece compilatorul produce
metadatele si codul in acelasi timp si le leaga in modulul administrat rezultant, metadatele si
codul IL care le descrie nu sunt niciodata nesincronizate.
Metadatele au multe utilizari. Mai jos sunt enumerate cateva dintre ele:
Metadatele elimina necesitatea fisierelor antet si de biblioteca la compilare, din moment ce toate informatiile despre tipurile/membrii la care se face referire sunt continute in cadrul fisierului care contine codul IL ce implementeaza tipurile/membrii. Compilatoarele pot citi metadatele direct din modulele administrate.
Mediul Visual Studio .NET utilizeaza metadatele, pentru a ajuta la scrierea codului. Caracteristica sa IntelliSense analizeaza datele, pentru a arata ce metode ofera un tip si ce parametri asteapta metoda respectiva.
Procesul de verificare a codului la rularea CLR utilizeaza metadatele, pentru a garanta ca in cod se efectueaza numai operatii "sigure'. (Vom analiza in curand verificarea.)
Metadatele permit serializarea campurilor unui obiect intr-un bloc de memorie, plasarea lor la distanta pe un alt calculator si apoi deserializarea lor, creand din nou obiectul si starea sa pe calculatorul indepartat.
Metadatele permit colectorului de gunoaie
sa urmareasca timpul de viata al obiectelor.
Pentru
orice obiect, colectorul de gunoaie poate determina tipul de obiect si,
din metadate, poate afla ce campuri din obiectul respectiv se refera
la alte obiecte.
In Capitolul 2 vom descrie mai detaliat metadatele.
Limbajele C#, Visual Basic, JScript, J# si IL Assembler de la Microsoft produc intotdeauna module administrate, care necesita rularea CLR pentru a fi executate. Utilizatorii finali trebuie sa aiba instalata componenta CLR pe calculatoarele lor pentru a executa orice fel de modul administrat, tot asa cum trebuie sa aiba biblioteca Microsoft Foundation Class (MFC) sau fisierele DLL din Visual Basic, pentru a rula aplicatii MFC sau Visual Basic.
Compilatorul C++ de la Microsoft construieste implicit module neadministrate: fisierele EXE sau DLL, cu care suntem obisnuiti. Aceste module nu necesita componenta CLR pentru a putea fi executate. Dar, prin specificarea in linia de comanda a unui nou comutator, compilatorul C++ poate realiza module administrate, care necesita componenta CLR pentru a putea fi executate. Dintre toate compilatoarele Microsoft mentionate, C++ este unic, prin faptul ca este singurul limbaj care permite realizatorilor sa scrie atat cod administrat, cat si neadministrat, care sa poata fi emis intr-un singur modul. Aceasta poate reprezenta un mare avantaj, deoarece permite realizatorilor de aplicatii sa scrie partea masiva a aplicatiei in cod administrat (pentru siguranta tipurilor si interoperabilitatea componentelor), continuand sa acceseze codul C++ neadministrat existent.
Combinarea modulelor administrate in asamblaje
Componenta CLR nu lucreaza efectiv cu module, ci cu asamblaje. Un asamblaj este un concept abstract, care poate fi dificil de inteles initial. Mai intai, un asamblaj este o grupare logica de unul sau mai multe module administrate sau fisiere de resurse.
In al doilea rand, un asamblaj
este cea mai mica unitate pentru reutilizare, securitate si
realizarea de versiuni, in functie de optiunile alese
pentru compilatoare sau instrumente, putem produce un asamblaj format dintr-un
singur fisier sau din mai multe.
In Capitolul 2 vom trata detaliat
asamblajele, asa ca acum nu vom mai zabovi asupra lor. Tot ceea ce dorim acum este sa va
atragem atentia asupra faptului ca exista o notiune conceptuala suplimentara, care
ofera o modalitate grozava de a trata un grup de fisiere ca pe o
singura entitate.
Ilustrarea din Figura 1-2 va va ajuta sa intelegeti ce sunt asamblajele. in aceasta figura, cateva module administrate si fisiere de resurse (sau date) sunt prelucrate cu ajutorul unui instrument. Acesta produce un singur fisier PE, care reprezinta gruparea logica a fisierelor. Acest fisier PE contine un bloc de date numit declaratie. Declaratia reprezinta pur si simplu un alt set de tabele de metadate. Aceste tabele descriu fisierele din care se compune asamblajul, tipurile exportate public implementate de catre fisierele din cadrul asamblajului si fisierele de resurse sau date asociate acestuia.
Figura 1-2 Combinarea modulelor administrate in asamblaje.
Implicit, compilatoarele efectueaza, de fapt, transformarea modului administrat emis intr-un asamblaj; cu alte cuvinte, compilatorul C# emite un modul administrat care contine o declaratie. Declaratia arata ca asamblajul este format dintr-un singur fisier. Astfel, pentru proiectele care au un singur modul administrat si nici un fisier de resurse (sau date), asamblajul va fi modulul administrat, iar dumneavoastra nu va mai trebui sa faceti nimic suplimentar in timpul procesului de constructie. Daca doriti sa grupati un set de fisiere intr-un asamblaj, va trebui sa aveti in vedere mai multe instrumente (cum ar fi programul de legaturi de asamblare, AL.exe) si optiunile lor din linia de comanda. Vom explica aceste instrumente si optiuni in Capitolul 2.
Un asamblaj permite decuplarea notiunilor logice si fizice ale unei componente reutili-zabile, care accepta desfasurarea si realizarea de versiuni. Modul de partitionare a codului si resurselor in diverse fisiere este in intregime la latitudinea dumneavoastra. De exemplu, ati putea plasa tipurile de resurse rar utilizate in fisiere separate, care fac parte dintr-un asamblaj. Fisierele separate ar putea fi descarcate de pe Web, dupa cum este necesar. Daca fisierele nu sunt niciodata necesare, atunci nu sunt niciodata descarcate, prin aceasta economisindu-se spatiul de pe disc si reducand timpul de instalare. Asamblajele permit fragmentarea desfasurarii fisierelor, tratand in continuare toate fisierele ca pe o singura colectie. Modulele unui asamblaj cuprind si informatii - inclusiv numarul versiunii - despre asamblajele la care se face referire. Aceste informatii fac ca asamblajul sa fie cu autodescriere. Cu alte cuvinte, componenta CLR stie tot ceea ce este necesar pentru ca un asamblaj sa fie executat. Nu mai sunt necesare informatii suplimentare in registru sau in Active Directory.
Incarcarea componentei Common Language Runtime
Fiecare asamblaj construit poate fi ori o aplicatie executabila, ori o biblioteca DLL, care contine un set de tipuri (componente) care vor fi utilizate de catre aplicatia executabila. Desigur ca rularea CLR este responsabila de administrarea executiei codului continut in cadrul acestor asamblaje. Aceasta inseamna ca platforma .NET Framework trebuie instalata pe calculatorul gazda.
Compania Microsoft a creat un pachet de redistribuire, care poate fi livrat gratuit, pentru a instala platforma .NET Framework pe calculatoarele clientilor. in cele din urma, platforma .NET Framework va face parte din pachetul de versiuni Windows viitoare, astfel incat nu va mai trebui livrat impreuna cu asamblajele.
Puteti vedea daca platforma .NET Framework a fost instalata cautand fisierul MSCorEE.dll in directorul %windir%system32. Existenta acestui fisier arata ca platforma .NET Framework este instalata. Totusi, pe un singur calculator pot fi instalate simultan mai multe versiuni ale platformei .NET Framework. Daca doriti sa stabiliti exact ce versiuni .NET Framework sunt instalate, analizati subcheile aflate sub urmatoarea cheie din registru: HKEY_LOCAL_MACHINESOFTWAREMicrosoft.NETFrameworkpolicy
Atunci cand construiti un asamblaj EXE, compilatorul/programul de legaturi emite cateva informatii speciale in antetul fisierului PE al asamblajului rezultant si in portiunea .text a fisierului. Atunci cand este invocat fisierul EXE, aceste informatii speciale determina incarcarea si initializarea componentei CLR. Apoi aceasta localizeaza metoda din punctul de intrare al aplicatiei si permite inceperea executiei acesteia.
Similar, daca o aplicatie neadministrata apeleaza metoda LoadLibrary pentru a incarca un asamblaj administrat, atunci functia din punctul de intrare stie ca trebuie sa incarce componenta CLR, pentru a prelucra codul continut in cadrul asamblajului.
in cea mai mare parte, nu este necesar ca dumneavoastra sa stiti sau sa intelegeti cum este incarcata componenta CLR. Pentru majoritatea programatorilor, aceste informatii speciale permit pur si simplu executia aplicatiei si nu mai trebuie sa se gandeasca la ele. Dar, pentru cei curiosi, vom dedica ceea ce a mai ramas din acest paragraf explicarii modului in care aplicatia EXE sau DLL administrata porneste rularea CLR. Daca nu va intereseaza acest subiect, puteti sa sariti la urmatorul paragraf. De asemenea, daca va intereseaza construirea unei aplicatii neadministrate care gazduieste componenta CLR, consultati Capitolul 20.
in Figura 1-3 este prezentat schematic modul in care o aplicatie EXE administrata incarca si initializeaza rularea CLR.
Figura 1-3 incarcarea si initializarea componentei CLR.
Atunci cand compilatorul/programul de legaturi creeaza, un asamblaj executabil, in portiunea .text a fisierului PE este emisa urmatoarea functie de racordare de 6 octeti x86:
JMP _CorExeMain
Deoarece functia _CorExeMain este importata din biblioteca de legaturi dinamice Microsoft MSCorEE.dll, fisierul MSCorEE.dll este referentiat in portiunea de import (.idata) a fisierului de asamblare. MSCorEE.dll vine de la Microsoft Component Object Runtime Execution Engine.
Atunci cand este invocat fisierul EXE administrat, sistemul de operare Windows il trateaza ca pe orice fisier EXE normal (neadministrat): incarcatorul Windows incarca fisierul si analizeaza portiunea . idata, pentru a vedea daca in spatiul de adrese trebuie incarcata biblioteca MSCorEE.dll. Apoi incarcatorul obtine adresa functiei _CorExeMain din interiorul bibliotecii MSCorEE.dll si stabileste instructiunea JMP a functiei in fisierul EXE administrat.
Firul primar al procesului incepe executia acestei functii de racordare x86, care sare imediat la functia _CorExeMain din biblioteca MSCorEE.dll. Functia _CorExeMain initializeaza rularea CLR si apoi cauta in antetul CLR al asamblajului executabil,- pentru a determina ce metoda administrata din punctul de intrare trebuie sa execute. Codul IL pentru aceasta metoda este apoi compilat in instructiuni CPU native, iar rularea CLR trece la codul nativ (utilizand firul primar al procesului). in acest punct, este rulat codul aplicatiei administrate.
Pentru o aplicatie DLL administrata, situatia este similara. Atunci cand se construieste o aplicatie DLL administrata, compilatorul/programul de legaturi emite o functie de racordare de 6 octeti x86 similara, in portiunea .text corespunzatoare asamblajului DLL:
JMP _CorDllMain
Functia _CorDllMain este de asemenea importata din biblioteca MSCorEE.dll, facand ca portiunea .idata a aplicatiei DLL sa se refere la fisierul MSCorEE.dll. Atunci cand sistemul de operare Windows incarca aplicatia DLL, va incarca automat si biblioteca MSCorEE.dll (daca nu este deja incarcata), va obtine adresa functiei _CorDllMain si va corecta racordul JMP x86 de 6 octeti din aplicatia DLL administrata. Firul care a apelat metoda LoadLibrary pentru a incarca aplicatia DLL administrata va trece acum la racordul x86 din asamblajul DLL administrat, care va sari imediat la functia _CorDllMain din biblioteca MSCorEE.dll. Functia _CorDllMain initializeaza rularea CLR (daca nu a fost deja initializata pentru proces) si apoi revine, astfel incat aplicatia isi poate continua executia normala.
Functiile de racordare x86 de 6 octeti sunt necesare pentru a rula asamblaje administrate pe sistemele de operare Windows 98, Windows 98 Second Edition, Windows Me, Windows NT 4 si Windows 2000, deoarece toate acestea au fost livrate cu mult inainte de a fi disponibila componenta CLR. Observati ca functia de racordare de 6 octeti este construita special pentru calculatoarele x86. Acest racord nu functioneaza corespunzator in cazul in care componenta CLR este portata astfel incat sa functioneze pe alte arhitecturi CPU. Deoarece sistemele de operare Windows XP si Windows .NET Server Family accepta atat arhitectura x86, cat si IA64, incarcatoarele acestora au fost modificate astfel incat sa caute tocmai asamblaje administrate.
Pe sistemele de operare Windows XP si Windows .NET Server Family, atunci cand este invocat un asamblaj administrat (de regula, prin intermediul metodei CreateProcess sau LoadLibrary), incarcatorul OS1 detecteaza faptul ca fisierul contine un cod administrat, analizand intrarea directoare 14 din antetul fisierului PE. (A se vedea image_directory_ entry_C0M_DESCRIPT0R din antetul WinNT.h.) Daca aceasta intrare directoare exista si nu este 0, atunci incarcatorul ignora portiunea de import a fisierului (.idata) si incarca automat biblioteca MSCorEE.dll in spatiul de adresa al procesului. O data incarcata, incarcatorul OS face ca firul procesului sa treaca direct la functia corecta din biblioteca MSCorEE.dll. Functiile de racordare x86 de 6 octeti sunt ignorate pe calculatoarele care ruleaza sistemele de operare Windows XP si Windows .NET Server Family.
O ultima observatie referitoare la fisierele PE: acestea utilizeaza intotdeauna formatul de fisier PE de 32 de biti, nu cel de 64 de biti. Pe sistemele Windows de 64 de biti, incarcatorul OS detecteaza fisierul PE administrat de 32 de biti si stie automat sa creeze un spatiu de adresa de 64 de biti.
Executarea codului din asamblaj
Dupa cum am mentionat mai devreme, modulele administrate pot contine atat metadate, cat si limbaj intermediar (IL). IL este un limbaj masina independent de CPU, creat de compania Microsoft, dupa consultari cu o serie de realizatori externi de limbaje/compilatoare comerciale. Este de un nivel mult mai inalt decat majoritatea limbajelor masina CPU. Limbajul IL intelege tipurile de obiecte si are instructiuni care creeaza si initializeaza obiecte, apeleaza metode virtuale pentru obiecte si manipuleaza direct elementele tablourilor. Are chiar si instructiuni care introduc si detecteaza exceptii pentru tratarea erorilor. IL poate fi considerat un limbaj masina orientat spre obiecte.
De regula, realizatorii de aplicatii vor programa intr-un limbaj de nivel inalt, cum ar fi C# sau Visual Basic. Compilatoarele pentru aceste limbaje de nivel inalt produc cod IL. Totusi, ca orice alt limbaj masina, IL poate fi scris in limbaj de asamblare, iar compania Microsoft pune la dispozitie un program de asamblare IL Assembler, ILAsm.exe. De asemenea, compania Microsoft ofera si un program de dezasamblare IL Disassembler, ILDasm.exe.
Limbajul IL si protejarea proprietatii intelectuale _________
Unele persoane sunt preocupate de faptul ca limbajul IL nu ofera suficienta protectie a proprietatii 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 exacta a ceea ce face codul aplicatiei.
Da, este adevarat ca limbajul IL este de nivel mai inalt decat majoritatea celorlalte limbaje de asamblare si ca, in general, reproducerea tehnologiei codului IL este relativ simpla. Dar, atunci cand se implementeaza un serviciu Web XML sau o aplicatie Web Forms, modulul administrat se afla 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 intelectuala este in perfecta siguranta.
Daca va preocupa modulele administrate pe care le distribuiti, puteti obtine un program utilitar de eclipsare de la o terta partida. Aceste utilitare "amesteca' numele tuturor simbolurilor private din metadatele modului administrat. Va fi dificil pentru cineva sa "descurce' numele si sa inteleaga scopul fiecarei metode. Observati ca aceste programe de eclipsare pot oferi doar o mica protectie, din moment ce codul IL trebuie sa fie intr-o oarecare masura disponibil, pentru ca rularea CLR sa il poata prelucra.
Daca nu credeti ca un program de eclipsare ofera un tip adecvat de protectie a proprietatii intelectuale, puteti avea in vedere implementarea algoritmilor mai sensibili intr-un modul oarecare neadministrat, care va contine instructiuni native CPU, in loc de cod IL si metadate. Apoi puteti folosi caracteristicile de interoperabilitate ale componentei CLR pentru a comunica intre portiunile administrate si cele neadministrate din aplicatia dumneavoastra. Desigur ca aceasta presupune ca nu va ingrijoreaza posibilitatea de reproducere a tehnologiei prin interpretarea instructiunilor native CPU din codul neadministrat.
Retineti ca orice limbaj de nivel inalt va expune, probabil, doar un subset din facilitatile oferite de componenta CLR. Totusi, utilizarea limbajului de asamblare IL permite realizatorului sa acceseze toate facilitatile CLR. Astfel, in cazul in care limbajul de programare ales nu contine o facilitate pe care componenta CLR o ofera si de care doriti sa profitati, atunci puteti alege sa scrieti respectiva portiune de cod in limbajul de asamblare IL - sau eventual un alt limbaj de programare care pune la dispozitie caracteristica CLR cautata.
Singura modalitate prin care puteti afla ce facilitati ofera componenta CLR este de a citi documentatia specifica acesteia. in aceasta carte, vom incerca sa ne concentram asupra caracteristicilor CLR si a modului in care acestea sunt revelate sau nu in limbajul C#.
Presupun ca majoritatea celorlalte carti si articole vor prezenta componenta CLR intr-o perspectiva legata de limbaj, iar majoritatea realizatorilor de aplicatii vor ajunge la concluzia ca rularea CLR ofera doar ceea ce expune limbajul de programare ales de realizator. Atata vreme cat limbajul de programare ales permite realizarea a ceea ce intentionati, aceasta perspectiva nebuloasa nu este prea rea.
Un alt aspect important care trebuie retinut despre limbajul IL se refera la faptul ca nu este legat de vreo anumita platforma CPU. Aceasta inseamna ca un modul administrat care contine cod IL poate fi rulat pe orice platforma CPU, atata vreme cat sistemul de operare rulat pe aceasta gazduieste o versiune a componentei CLR. Desi versiunea CLR initiala este rulata doar pe platforme Windows de 32 de biti, dezvoltarea unei aplicatii care sa utilizeze cod IL confera realizatorului o mai mare independenta de arhitectura CPU aflata la baza.
Standardizarea platformei .NET Framework
In octombrie 2000, compania Microsoft (impreuna cu Intel si Hewlett-Packard, toate ca sponsori) a propus asociatiei ECMA (European Computer Manufacturer's Association) un subset larg al platformei .NET Framework, cu scopul de a o standardiza. ECMA a acceptat aceasta propunere si a creat un comitet tehnic (TC39), care sa supravegheze procesul de standardizare. Comitetul tehnic are urmatoarea structura si sarcina:
Technical Group 1 trebuie sa dezvolte un standard pentru un limbaj de scriptare dinamic (ECMAScript). Implementarea Microsoft a standardului ECMAScript este JScript;
Technical Group 2 trebuie sa realizeze o versiune standardizata a limbajului de programare C#;
Technical Group 3 trebuie sa realizeze o infrastructura Common Language Infrastructure1 (CLI), pe baza unui subset de functionalitati oferite de componenta CLR a platformei .NET Framework si bibliotecii de clase. Mai exact, CLI va defini un format de fisiere, un sistem de tipuri comun, un sistem de metadate extensibil, un limbaj intermediar (IL) si accesul la platforma aflata la baza (P/Invoke). in plus, CLI va defini o biblioteca intermediara de clase de baza (pentru a permite mici dispozitive hardware), conceputa pentru a fi utilizata de mai multe limbaje de programare.
O data ce standardizarea va fi incheiata, aceste standarde vor fi introduse in standardul ISO/IEC JTC 1 (Information Technology). Atunci, comitetul tehnic va analiza si directiile viitoare ale infrastructurii CLI, limbajului C# si standardului ECMAScript si va sustine propuneri de tehnologii complementare sau suplimentare. Pentru mai multe informatii despre ECMA, consultati siturile Web aflate la adresele: https:// www.ECMA.ch si https://MSDN.Microsoft.eom/.NET/ECMA.
O data cu standardizarea infrastructurii CLI, a limbajului C# si standardului ECMAScript, compania Microsoft nu va "poseda' nici una dintre aceste tehnologii. Microsoft va fi pur si simplu una dintre multele (speram) companii care produc implementari ale acestor tehnologii. Desigur ca cei de la Microsoft spera ca implementarea lor va fi cea mai buna, in termeni de performante si caracteristici realizate la cererea utilizatorilor. Aceasta va ajuta la vanzarea sistemului de operare Windows, deoarece cea mai buna implementare Microsoft va fi rulata numai sub Windows. Totusi, alte companii ar putea implementa aceste standarde, intra in competitie cu Microsoft si, posibil, castiga.
Desi unitatile CPU din zilele noastre nu pot executa direct instructiunile IL, este posibil ca cele viitoare sa aiba aceasta capacitate. Pentru a executa o metoda, este necesar ca instructiunile IL corespunzatoare sa fie transformate in instructiuni native CPU. Aceasta este sarcina compilatorului JIT (just-in-time2) al componentei CLR.
in Figura 1-4 se ilustreaza ceea ce are loc atunci cand este apelata pentru prima data o metoda.
Chiar inainte de executia metodei Main, componenta CLR detecteaza toate tipurile la care se face referire in codul acesteia. Aceasta face ca CLR sa aloce o structura de date interna, care este folosita pentru a administra accesul la tipul la care se face referire. in Figura 1-4, metoda Main se refera la un singur tip, Console, ceea ce face ca rularea CLR sa aloce o singura structura interna. Structura de date interna contine o intrare pentru fiecare metoda definita de catre tip. Fiecare intrare contine adresa la care poate fi gasita implementarea metodei. La initializarea acestei structuri, componenta CLR atribuie fiecare intrare unei functii interne nedocumentate, continuta in componenta CLR insasi. Am numit aceasta functie JITCompiler.
Atunci cand metoda Main face prima apelare a metodei WriteLine, este apelata functia JITCompiler.
Functia JITCompiler este responsabila de compilarea codului IL al unei metode in instructiuni native CPU. Deoarece codul IL este compilat "just in time', aceasta componenta CLR este numita in mod frecvent JITer sau compilator JIT.
Atunci cand este apelata, functia JITCompiler stie ce metoda este apelata si ce tip o defineste. Apoi functia JITCompiler cauta metadatele asamblajului de definitie pentru codul IL al metodei apelate. in etapa urmatoare, functia JITCompiler verifica si compileaza codul IL in instructiuni native CPU. Acestea sunt salvate intr-un bloc de memorie alocat dinamic. Apoi, functia JITCompiler merge inapoi la structura de date interna a tipului si inlocuieste adresa metodei apelate cu adresa blocului de memorie care contine instructiunile native CPU. In final, functia JITCompiler trece la codul din blocul de memorie. Acest cod reprezinta implementarea metodei WriteLine (versiunea care are un parametru de tip string). Atunci cand acest cod efectueaza returnarea, se revine la codul din metoda Main, care continua executia in mod normal.
Figura 1-4 Apelarea unei metode pentru prima data
Acum metoda Main apeleaza a doua oara metoda WriteLine. De aceasta data, codul pentru metoda WriteLine a fost deja verificat si compilat. Astfel, apelarea merge direct la blocul de memorie, sarind cu totul peste functia JITCompiler. Dupa executia metodei WriteLine, se revine la metoda Main. In Figura 1-5 se arata care este situatia atunci cand metoda WriteLine este apelata a doua oara.
Impactul negativ asupra performantelor apare doar prima data cand este apelata metoda. Toate apelarile ulterioare ale acesteia sunt executate la viteza maxima a codului nativ: verificarea si compilarea in cod nativ nu sunt efectuate din nou.
Compilatorul JIT stocheaza instructiunile native CPU in memoria dinamica. Aceasta inseamna ca, la terminarea aplicatiei, codul compilat este inlaturat. Astfel, daca aplicatia va fi rulata din nou in viitor sau daca sunt rulate simultan doua instante ale acesteia (in doua procese diferite ale sistemului de operare), atunci compilatorul JIT va trebui sa efectueze din nou compilarea codului IL in instructiuni native.
Figura 1-5 Apelarea unei metode a doua oara
Pentru majoritatea aplicatiilor, impactul asupra performantelor datorat compilarii JIT nu este semnificativ. Majoritatea aplicatiilor au tendinta de a apela in mod repetat aceleasi metode. Aceste metode vor avea un impact negativ asupra performantelor numai o data, pentru o executie a aplicatiei. De asemenea, este mai probabil sa fie necesar un timp mai mare pentru executia din interiorul metodei decat pentru apelarea acesteia.
De asemenea, trebuie avut in vedere faptul ca in compilatorul JIT al componentei CLR se efectueaza optimizarea codului nativ, tot la fel cum face si compilatorul back-end C++ neadministrat. Din nou, s-ar putea sa fie necesar un timp mai indelungat pentru a realiza codul optimizat, dar acesta va fi executat cu performante mult mai bune decat daca nu ar fi fost optimizat.
Realizatorii de aplicatii care au un fond de cunostinte C sau C++, fara a fi la curent cu administrarea, probabil ca se gandesc la implicatiile tuturor acestor consideratii in ceea ce priveste performantele. in fond, codul neadministrat este compilat pentru o anumita platforma CPU si, atunci cand este invocat, el poate fi executat pur si simplu. in acest mediu administrat, compilarea codului se realizeaza in doua faze. Mai intai, compilatorul parcurge codul sursa, facand cat mai mult posibil pentru a realiza codul IL. Dar, pentru a executa codul, insusi codul IL trebuie compilat in momentul executiei in instructiuni native CPU, ceea ce necesita o alocare de memorie mai mare si timp CPU suplimentar pentru realizare.
Puteti sa ma credeti ca, atunci cand am abordat componenta CLR, pornind de la un fond de cunostinte C/C++, am fost destul de sceptic si preocupat de aceasta suprasarcina suplimentara. Adevarul este ca acest al doilea stadiu al compilarii, care apare in timpul executiei, afecteaza performantele si aloca memorie dinamica. Dar cei de la Microsoft s-au straduit mult pentru a reduce la minimum aceasta suprasarcina.
Daca si dumneavoastra sunteti sceptic, atunci va trebui sa construiti cateva aplicatii si sa testati singur performantele. in plus, va trebui sa rulati cateva aplicatii administrate, care nu sunt uzuale - realizate de Microsoft sau de altcineva - si sa le masurati performantele. Cred ca veti fi surprins de cat de bune sunt, in realitate, performantele.
De fapt, probabil ca vi se pare greu de crezut, dar multe persoane (inclusiv eu) sunt de parere ca aplicatiile administrate le-ar putea depasi efectiv pe cele neadministrate, in ceea ce priveste performantele. Exista mai multe ratiuni in favoarea acestei afirmatii. De exemplu, in timpul executiei, atunci cand compilatorul JIT efectueaza compilarea codului IL in cod nativ, el stie mai multe despre mediul de executie decat ar putea sti un compilator neadministrat. Mai jos sunt enumerate cateva modalitati prin care codul administrat ar putea avea performante superioare fata de cel neadministrat:
Un compilator JIT poate detecta ca aplicatia este rulata pe un calculator Pentium 4, producand un cod nativ care sa profite de orice instructiuni speciale ar putea fi oferite de acesta. De obicei, aplicatiile neadministrate sunt compilate pentru o unitate CPU de nivelul "celui-mai-mic-numitor-comun', evitandu-se utilizarea instructiunilor speciale, care ar putea oferi o crestere a performantelor aplicatiei pentru noile unitati CPU.
Un compilator JIT poate detecta ca un anumit test returneaza intotdeauna valoarea de adevar false pe calculatorul pe care este rulat. De exemplu, vom considera o metoda cu codul urmator:
if (numberOfCPUs > 1)
Acest cod ar putea determina compilatorul JIT sa nu genereze instructiuni CPU, in cazul in care calculatorul gazda are o singura unitate CPU. in acest caz, codul nativ este reglat pentru calculatorul gazda: este mai mic si este executat mai repede.
■ Componenta CLR ar putea schita executia codului, recompiland codul IL in cod nativ, in timpul rularii aplicatiei. Codul recompilat ar putea fi reorganizat, pentru a reduce ipotezele incorecte din ramificatii, in functie de tiparele de executie observate.
Acestea sunt numai cateva dintre motivele pentru care este de asteptat ca viitoarele coduri administrate sa fie executate mai bine decat cele neadministrate, din zilele noastre. Dupa cum am spus, performantele sunt, la ora actuala, destul de bune si se vor imbunatati, pe masura ce va trece timpul.
Daca experimentele dumneavoastra demonstreaza ca performantele aplicatiilor obtinute cu compilatorul JIT nu sunt de un nivel adecvat, ati putea profita de avantajele oferite de instrumentul Ngen.exe, care este livrat impreuna cu setul SDK .NET Framework. Acest instrument realizeaza compilarea intregului cod IL dintr-un asamblaj in cod nativ, pe care il salveaza intr-un fisier de pe disc. in timpul executiei, atunci cand este incarcat un asamblaj, componenta CLR verifica automat daca exista si o versiune precompilata a asamblajului si, in acest caz, incarca acest cod precompilat, astfel incat nu mai este necesara nici o compilare in timpul executiei.
Codul IL si verificarea
Limbajul IL se bazeaza pe stive, ceea ce inseamna ca toate instructiunile sale "imping' operanzii intr-o stiva de executie si "extrag' rezultatele din aceasta. Deoarece limbajul IL nu ofera instructiuni de manipulare a registrelor, realizatorii de compilatoare pot produce mai usor codul IL; nu mai este necesar sa se mai gandeasca la administrarea registrelor si sunt necesare mai putine instructiuni IL (din moment ce nu exista nici una pentru manipularea registrelor).
De asemenea, instructiunile IL nu depind de tip. De exemplu, limbajul IL pune la dispozitie o instructiune add, care efectueaza sumarea ultimilor doi operanzi introdusi in stiva; nu exista instructiuni add separate pentru 32 de biti si 64 de biti. Atunci cand este executata instructiunea add, aceasta determina tipurile operanzilor din stiva si efectueaza operatia corespunzatoare.
Dupa parerea mea, cel mai mare beneficiu al limbajului IL consta in faptul ca nu abstractizeaza unitatea CPU aflata la baza. Cel mai mare avantaj este robustetea aplicatiilor, in timpul compilarii codului IL in instructiuni native CPU, componenta CLR efectueaza un proces numit verificare. in timpul verificarii, se examineaza codul IL, de nivel inalt, si se verifica daca tot ceea ce face acesta este "sigur'. De exemplu, se verifica sa nu se citeasca din memorie fara ca, in prealabil, sa se fi scris in aceasta, ca fiecare metoda sa fie apelata cu numarul corect de parametri si ca fiecare parametru sa fie de tipul corect, ca valoarea de returnare a fiecarei metode sa fie utilizata adecvat, ca fiecare metoda sa aiba o instructiune de returnare si asa mai departe.
Metadatele modului administrat cuprind toate informatiile despre metoda si tip utilizate in procesul de verificare. in cazul in care codul IL este considerat a fi "nesigur', este introdusa o exceptie System.Security.Verif ierException, care impiedica executarea metodei.
Codul dumneavoastra este sigur?
Implicit, compilatoarele Microsoft C# si Visual Basic produc coduri sigure. Codul sigur poate fi verificat ca avand aceasta calitate. Totusi, este posibila utilizarea cuvantului cheie unsafe1 in 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.
Pentru a garanta ca toate metodele modulului administrat contin cod IL care poate fi verificat ca fiind sigur, se poate utiliza programul utilitar PEVerify (PEVerify.exe), livrat impreuna cu pachetul .NET Framework SDK. Atunci cand cei de la Microsoft testeaza compilatoarele C# si Visual Basic, ei ruleaza modulul rezultant in programul PEVerify, pentru a garanta ca intotdeauna compilatorul produce un cod sigur, care poate fi verificat.
Daca programul PEVerify detecteaza un cod care nu este sigur, atunci cei de la Microsoft remediaza compilatorul.
Puteti incerca rularea programului PEVerify pe propriile dumneavoastra module, inainte de a le impacheta si livra. Daca programul PEVerify detecteaza o problema, atunci in compilator exista o hiba, iar dumneavoastra ar trebui s-o anuntati companiei Microsoft (sau celei care a produs compilatorul pe care il utilizati). Daca programul PEVerify nu detecteaza un cod care nu poate fi verificat, atunci stiti ca acesta va putea fi rulat fara a introduce o exceptie Verif ierException pe calculatorul utilizatorului final.
Trebuie sa stiti ca verificarea necesita accesul la metadatele continute in orice asamblaje independente. Astfel, atunci cand se utilizeaza programul PEVerify pentru a verifica un asamblaj, acesta trebuie sa poata localiza si incarca toate asamblajele referentiate. Deoarece programul PEVerify utilizeaza componenta CLR pentru a localiza asamblajele dependente, amplasarea acestora este detectata folosind aceleasi reguli de asociere si probare care ar fi utilizate in mod normal la executia asamblajului respectiv. (Vom analiza aceste reguli de asociere si probare in capitolele 2 si 3.)
Observati ca un administrator poate opta pentru dezactivarea verificarii (folosind instrumentul administrativ Microsoft .NET Framework Configuration). Atunci cand verificarea este dezactivata, compilatorul JIT va compila un limbaj IL neverificabil in instructiuni native CPU; dar administratorul isi asuma intreaga responsabilitate pentru comportamentul codului.
in sistemul de operare Windows, fiecare proces are propriul sau spatiu de adrese virtual. Sunt necesare spatii de adrese separate, deoarece nu se poate avea incredere in codul aplicatiei. Este cu totul posibil (si, din pacate, foarte obisnuit) ca o aplicatie sa citeasca si sa scrie intr-o adresa de memorie care nu este valabila. Prin plasarea fiecarui proces Windows intr-un spatiu de adresa separat, se obtine o mai mare robustete: un proces nu poate afecta in sens negativ un alt proces.
Prin verificarea codului administrat, stim ca acesta nu acceseaza necorespunzator memoria care nu trebuie si ca nu poate afecta in sens negativ codul altei aplicatii. Aceasta inseamna ca putem rula mai multe aplicatii administrate, in cadrul unui singur spatiu de adrese virtual.
Deoarece procesele Windows necesita o multime de resurse ale sistemului de operare, multitudinea acestora poate afecta performantele si limita resursele disponibile. Reducerea numarului de procese prin rularea mai multor aplicatii in cadrul unui singur proces OS poate duce la imbunatatirea performantelor si necesita mai putine resurse, totodata fiind la fel de robust. Acesta este un alt avantaj al codului administrat, fata de cel neadministrat.
De fapt, componenta CLR ofera capacitatea de a executa mai multe aplicatii administrate in cadrul unui singur proces OS. Fiecare aplicatie administrata se numeste AppDomain. Implicit, fiecare fisier EXE administrat va fi rulat in propriul sau spatiu de adrese separat, care are o singura aplicatie AppDomain. Totusi, un proces care gazduieste componenta CLR (cum ar fi serviciile Internet Information Services, IIS, sau o versiune viitoare a limbajului SQL Server) poate hotari sa ruleze aplicatiile AppDomain in cadrul unui singur proces OS. O parte din Capitolul 20 este dedicata unei analize a aplicatiilor AppDomain.
Biblioteca de clase .NET Framework
in cadrul platformei .NET Framework este inclus un set de asamblaje .NET Framework Class Library (FCL), care contine cateva mii de definitii de tipuri, fiecare tip oferind o anumita functionalitate. Pe scurt, componenta CLR si biblioteca FCL permit realizatorilor de aplicatii sa construiasca urmatoarele feluri de aplicatii:
Servicii Web XML Metode care pot fi accesate foarte usor prin Internet, serviciile XML Web reprezinta, desigur, elementul esential al initiativei .NET a companiei Microsoft.
Web Forms Sunt aplicatii bazate pe limbajul HTML (situri Web). De regula, aplicatiile Web Forms vor efectua interogari de baze de date si apelari ale serviciilor Web, vor combina si filtra informatiile returnate, pe care le vor prezenta apoi in cadrul unui browser, folosind o interfata cu utilizatorul bogata, bazata pe HTML. Aplicatiile Web Forms ofera un mediu de dezvoltare in stil Visual Basic 6 si Visual InterDev pentru aplicatiile Web scrise in orice limbaj din componenta CLR.
Windows Forms Sunt aplicatii Windows GUI bogate. in loc de a utiliza o pagina Web Forms pentru a crea interfata UI a aplicatiei, se poate folosi functionalitatea mai puternica, cu performante mai ridicate, oferita de suprafata de lucru Windows. Aplicatiile Windows Forms pot profita de controalele, meniurile si evenimentele declansate de mouse si tastatura si pot trata direct cu sistemul de operare aflat la baza. Ca si aplicatiile Web Forms, si acestea efectueaza interogari ale bazelor de date si apelari ale serviciilor XML Web. Aplicatiile Windows Forms ofera un mediu de dezvoltare asemanator cu Visual Basic 6 pentru aplicatiile GUI scrise in orice limbaj CLR.
Aplicatii Windows de consola Pentru aplicatiile cu cerinte UI foarte simple, o aplicatie de consola reprezinta o modalitate rapida si facila de a construi o aplicatie. Compilatoarele, utilitarele si instrumentele sunt implementate, de regula, ca aplicatii de consola.
Servicii Windows Da, folosind platforma .NET Framework, este posibil sa se construiasca aplicatii de serviciu, controlabile prin intermediul facilitatii Windows Service Control Manager (SCM).
Biblioteca de componente Platforma .NET Framework permite construirea de componente (tipuri) autonome, care pot fi cu usurinta incorporate in oricare dintre tipurile de aplicatii mentionate mai sus.
Deoarece biblioteca FCL contine efectiv mii de tipuri, realizatorului de aplicatii ii este prezentat un set de tipuri inrudite in cadrul unui singur spatiu de nume. De exemplu, spatiul de nume System (cu care veti ajunge cat se poate de familiarizat) contine tipul de baza Object, din care deriva, in ultima instanta, toate celelalte tipuri. in plus, spatiul de nume System contine tipuri pentru numere intregi, caractere, siruri, tratarea exceptiilor si operatii I/O de la consola, ca si un set de tipuri utilitare care efectueaza in siguranta transformari intre tipurile de date, formateaza tipurile de date, genereaza numere aleatorii si efectueaza diverse functii matematice. Toate aplicatiile vor utiliza tipuri din spatiul de nume System.
Pentru a accesa oricare dintre caracteristicile platformei, este necesar sa stim ce spatiu de nume contine tipurile care ofera facilitatile cautate. Daca dorim sa personalizam comportamentul oricaruia dintre tipuri, putem pur si simplu deriva propriul tip din tipul FCL dorit. Modul in care platforma .NET Framework prezinta realizatorilor de software o paradigma de programare coerenta este prin natura orientata spre obiecte a acesteia. De asemenea, realizatorii isi pot crea cu usurinta propriile spatii de nume, care sa contina propriile tipuri. Spatiile de nume si tipurile se imbina impecabil in paradigma programarii. Comparativ cu paradigma programarii Win32, aceasta noua abordare simplifica substantial dezvoltarea de software.
Majoritatea spatiilor de nume din biblioteca FCL prezinta tipuri care pot fi utilizate pentru orice fel de aplicatie. in Tabelul 1-2 sunt prezentate cateva dintre spatiile de nume generale, impreuna cu o scurta descriere a utilizarii tipurilor din cadrul acestora.
Tabelul
1-2 Cateva spatii de
nume generale din biblioteca FCL
Spatiul de nume Descrierea continutului
System Toate tipurile de baza, folosite de fiecare aplicatie.
System.Collections Tipurile pentru administrarea colectiilor de obiecte;
cuprinde tipurile de colectii uzuale, cum ar fi stivele, cozile, tabelele hash si asa mai departe.
System. Diagnostics Tipurile care ajuta la instrumentarea si depanarea
aplicatiilor.
System.Drawing Tipurile pentru manipularea graficelor 2-D; sunt utilizate,
de regula, pentru aplicatiile Windows Forms si pentru crearea de imagini care vor aparea in pagina Web Forms.
System.EnterpriseServices Tipurile pentru gestionarea tranzactiilor, componentelor
plasate in cozi, crearea de rezervoare de obiecte, activarea JIT, securitatea si alte caracteristici, care fac ca utilizarea codului administrat sa fie mai eficienta pe server.
System.Globalization Tipurile pentru suportul National Language Support (NLS),
cum ar fi compararile de siruri, formatarile si calendarele.
System. 10 Tipurile pentru realizarea operatiilor I/O cu derulare
continua, parcurgerea directoarelor si fisierelor.
System.Management Tipurile utilizate pentru gestionarea altor calculatoare din
cadrul intreprinderii, prin intermediul facilitatii Windows Management Instrumentation (WMI).
System.NET Tipurile care permit comunicatiile prin retea.
System.Reflection Tipurile care permit inspectarea metadatelor si asocierea
ulterioara cu tipuri si membrii acestora.
Spatiul de nume Descrierea continutului
System.Resources Tipurile pentru manipularea resurselor de date externe.
System.
Runtime. InteropSer Tipurile care
permit codului administrat sa acceseze
vices facilitatile
platformei OS, cum ar fi componentele COM
si functiile din bibliotecile DLL din Win32.
System. Runtime. Remotlng Tipurile care permit accesarea de la distanta a tipurilor.
System.Runtime.Serializat Tipurile care permit ca instantele obiectelor sa fie
ion persistente si sa poata fi
regenerate dintr-un flux.
System.Security Tipurile utilizate pentru protejarea datelor si resurselor.
System.Text Tipurile necesare pentru a lucra cu text in diverse
codificari, cum ar fi ASCII sau Unicode.
System.Threading Tipurile utilizate pentru operatiile asincrone si
sincronizarea accesului la resurse.
System.Xml Tipurile utilizate pentru prelucrarea schemelor si datelor
XML.
Aceasta carte are ca subiect componenta CLR si tipurile generale care interactioneaza cu aceasta (care reprezinta majoritatea spatiilor de nume enumerate in Tabelul 1-2). Astfel, continutul acestei carti este util pentru toti programatorii .NET Framework, indiferent de tipul de aplicatie pe care o realizeaza.
In afara de spatiile de nume generale, componenta CLR pune la dispozitie si spatii de nume ale caror tipuri sunt utilizate pentru construirea de tipuri de aplicatii specifice. in Tabelul 1-3 sunt enumerate cateva dintre spatiile de nume specifice aplicatiilor din biblioteca FCL.
Tabelul 1-3 Cateva spatii de nume specifice aplicatiilor, din biblioteca FCL
Spatiul de nume Tipul de aplicatie
System.Web.Services Tipurile utilizate pentru a construi servicii XML Web.
System.Web.UI Tipurile utilizate pentru a construi aplicatii Web Forms.
System.Windows.Forms Tipurile utilizate pentru a construi aplicatii Windows GUI.
System.ServiceProcess Tipurile utilizate pentru a construi un serviciu Windows,
care poate fi controlat de facilitatea SCM.
Este de asteptat sa apara multe carti bune, care sa explice cum se construiesc tipuri specifice de aplicatii (cum ar fi serviciile Windows, aplicatiile Web Forms si Windows Forms). Aceste carti va vor oferi un excelent punct de plecare pentru a va ajuta la construirea aplicatiilor dumneavoastra. Sunt inclinat sa cred ca aceste carti specifice aplicatiilor va vor ajuta sa invatati de sus in jos, deoarece ele se concentreaza asupra tipului de aplicatie, nu asupra platformei de dezvoltare. In aceasta carte vom oferi informatii care va vor ajuta sa invatati de jos in sus. Dupa parcurgerea acestei carti si a uneia specifice aplicatiilor, veti putea construi cu usurinta si eficient orice tip de aplicatie .NET Framework doriti.
Specificatia Common Type System
Pana acum trebuie sa fi devenit evident ca, in totalitate, componenta CLR se ocupa de tipuri. Tipurile ofera functionalitate aplicatiilor si componentelor acestora. Tipurile reprezinta mecanismul prin care codul scris intr-un limbaj de programare poate comunica cu cel scris in alt limbaj de programare. Deoarece tipurile reprezinta baza componentei CLR, compania Microsoft a creat o specificatie formala - Common Type System1 (CTS) - care descrie modul in care sunt definite si se comporta tipurile.
Specificatia CTS stabileste ca un tip poate contine zero sau mai multi membri. in Partea a IlI-a ne vom ocupa detaliat de acesti membri. Pentru moment, vom oferi doar o scurta prezentare a lor:
campul O variabila de date, care face parte din starea obiectului. Campurile sunt identificate prin nume si tip;
metoda O functie care efectueaza o operatie asupra obiectului, adeseori modificand starea acestuia. Metodele au un nume, o semnatura si modificatori. Semnatura arata conventia de apelare, numarul de parametri (si secventa lor), tipurile de parametri si tipul de valoare returnata de catre metoda;
proprietatea Pentru apelant, acest membru arata ca un camp. Dar pentru imple mentatorul tipului, arata ca o metoda (sau doua). Proprietatile permit unui imple mentator sa valideze parametrii de intrare si starea obiectului, inainte de a accesa va loarea si/sau calcula o valoare numai atunci cand este necesar. De asemenea, ele permit unui utilizator al tipului respectiv sa foloseasca o sintaxa simplificata. in sfarsit, proprietatile permit crearea de "campuri' numai pentru citire sau numai pentru scriere;
evenimentul Un eveniment permite existenta un mecanism de notificare intre un obiect si alte obiecte interesate. De exemplu, un buton ar putea reprezenta un eveniment care sa anunte alte obiecte atunci cand se efectueaza clic pe el.
De asemenea, specificatia CTS arata regulile pentru vizibilitatea tipurilor si pentru accesul la membrii unui tip. De exemplu, prin marcarea unui tip capublic (numit public), acesta este exportat, facandu-1 vizibil si accesibil pentru orice asamblaj. Pe de alta parte, prin marcarea unui tip ca assembly (numit internai in C#), acesta este facut vizibil si accesibil numai pentru codul din acelasi asamblaj. Astfel, specificatia CTS stabileste regulile prin care asamblajele formeaza o limita de vizibilitate pentru un tip, iar componenta CLR intareste regulile de vizibilitate.
Indiferent daca un tip este vizibil pentru un apelant, el reuseste sa controleze daca apelantul are acces la membrii sai. in lista urmatoare sunt prezentate optiunile valabile pentru controlul accesului la o metoda sau un camp:
Private Metoda poate fi apelata numai de catre alte metode din acelasi tip de clasa.
Family Metoda poate fi apelata de catre tipurile derivate, indiferent daca ele se afla in acelasi asamblaj sau nu. De remarcat ca multe limbaje de programare (cum ar fi C++ si C#) se refera la optiunea family ca protected.
Family and assembly Metoda poate fi apelata de catre
tipurile derivate, dar numai
daca
tipul derivat este definit in acelasi asamblaj. Multe limbaje de
programare (cum
ar fi C# si Visual Basic) nu ofera acest control al accesului.
Desigur ca limbajul IL
Assembly il pune la dispozitie;
Assembly Metoda poate fi apelata de catre orice cod din acelasi
asamblaj. Multe
limbaje de programare
se refera la optiunea assembly ca internai.
Family or assembly Metoda poate fi apelata de catre
tipurile derivate din orice
asamblaj.
De asemenea, metoda poate fi apelata de catre orice tipuri din
acelasi asamblaj,
in
limbajul C# cyptmne.2. family or assembly este considerata ca
protected internai.
Public Metoda poate fi apelata de catre orice cod, din orice asamblaj.
in plus, specificatia CTS defineste regulile care guverneaza mostenirea tipurilor, functiile virtuale, timpul de viata al obiectului si asa mai departe. Aceste reguli au fost concepute pentru a cuprinde semantica ce poate fi exprimata in limbajele de programare din epoca moderna. De fapt, nici nu va fi necesar sa invatati regulile CTS per se, deoarece limbajul pe care il veti alege va dezvalui propria sa sintaxa si regulile referitoare la tipuri, in acelasi mod cu care sunteti familiarizat acum, si va potrivi sintaxa specifica limbajului dupa "limbajul' componentei CLR, atunci cand va emite modulul administrat.
Atunci cand am inceput sa lucrez prima data cu componenta CLR, mi-am dat seama repede ca cel mai bine este sa consideram limbajul si comportamentul codului ca doua lucruri separate si diferite. Folosind limbajul C++, putem defini propriile tipuri, cu propriii membri.
Desigur ca am fi putut utiliza si limbajul C# sau Visual Basic pentru a defini acelasi tip, cu aceiasi membri. Cu siguranta ca sintaxa folosita pentru a defini acest tip este diferita, depinzand de limbajul ales, dar comportamentul tipului va fi absolut identic, indiferent de limbaj, deoarece specificatia CTS a componentei CLR este cea care defineste comportamentul tipului.
Pentru a ajuta la clarificarea acestei idei, vom da un exemplu. Specificatia CTS accepta numai mostenirea unica. Astfel, daca limbajul C++ accepta tipuri care mostenesc mai multe tipuri de baza, specificatia CTS nu poate accepta si opera cu astfel de tipuri. Pentru a ajuta realizatorul de aplicatii, compilatorul Visual C++ raporteaza o eroare, daca detecteaza ca se incearca crearea unui cod administrat, care sa includa un tip care mosteneste de la mai multe tipuri de baza.
Mai exista inca o regula a specificatiei CTS. Toate tipurile trebuie (in cele din urma) sa mosteneasca un tip predefinit: System.Object. Dupa cum puteti vedea, Object este numele unui tip definit in spatiul de nume System. Tipul Object este radacina tuturor celorlalte tipuri si, prin urmare, garanteaza ca fiecare aparitie a tipului are un set minim de comportamente. Mai exact, tipul System.Object permite sa se efectueze urmatoarele:
compararea egalitatii a doua instante;
obtinerea codului hash corespunzator instantei respective;
interogarea adevaratului tip al unei instante;
efectuarea unei copii superficiale (dupa biti) a instantei;
obtinerea unei reprezentari sub forma de sir a starii curente a obiectului.
Specificatia Common Language Specification
Specificatia COM permite obiectelor create in diverse limbaje de programare sa comunice unele cu altele. Pe de alta parte, componenta CLR integreaza acum toate limbajele si permite ca obiectele create intr-un limbaj sa fie tratate in mod egal de catre codul scris intr-un limbaj complet diferit. Aceasta integrare este posibila datorita setului de tipuri standard al componentei CLR, informatiilor cu autodescriere (metadatelor) si mediului de executie comun.
Desi aceasta integrare a limbajelor reprezinta un scop extraordinar, adevarul este ca limbajele de programare sunt foarte diferite unele de altele. De exemplu, unele limbaje nu trateaza simbolurile ca fiind dependente de tipul de litere sau nu permit tipuri de numere intregi fara semn, supraincarcarea operatorilor sau metode care sa accepte un numar variabil de parametri.
|
Daca intentionati sa creati tipuri care sa fie usor accesibile din alte limbaje de programare, atunci este necesar sa utilizati numai caracteristicile din limbajul de programare care este sigur ca sunt disponibile si in toate celelalte limbaje. Pentru a va ajuta, compania Microsoft a definit o specificatie numita Common Language Specification1 (CLS), care prezinta detaliat pentru producatorii de compilatoare setul minim de caracteristici pe care trebuie sa le accepte produsele lor, in cazul in care compilatoarele au ca tinta componenta CLR.
Specificatia CLR/CTS accepta mult mai multe caracteristici decat subsetul definit de catre specificatia CLS, astfel incat, daca nu tineti la operabilitatea dintre limbaje, atunci puteti realiza tipuri foarte bogate, limitate doar de catre setul de caracteristici al limbajului respectiv. Mai exact, specificatia CTS defineste regulile pe care trebuie sa le respecte tipurile si metodele vizibile extern, pentru a fi accesibile din orice limbaj de programare compatibil CLR. De remarcat ca regulile CLS nu se aplica pentru codurile care sunt accesibile numai in cadrul asamblajului de definitie. in Figura 1-6 este prezentat un rezumat al ideilor exprimate in acest paragraf.
Figura 1-6 Limbajele ofera o submultime din specificatia CLR/CTS si includ specificatia CLS (dar limbajele nu reprezinta neaparat aceeasi multime). |
Dupa cum se poate observa in Figura 1-6, specificatia CLR/CTS ofera un set de caracteristici. Unele limbaje expun o submultime cuprinzatoare din specificatia CLR/CTS. De exemplu, un programator care doreste sa scrie codul in limbajul de asamblare IL poate utiliza toate caracteristicile oferite de specificatia CLR/CTS.
Majoritatea celorlalte limbaje, cum ar fi C#, Visual Basic si Fortran, ofera programatorului un subset al caracteristicilor CLR/CTS. Specificatia CLS defineste setul minim de caracteristici pe care trebuie sa le accepte toate limbajele.
In cazul in care proiectati un tip intr-un limbaj si intentionati ca acesta sa fie utilizat de un alt limbaj, nu trebuie sa utilizati vreuna dintre caracteristicile din afara specificatiei CLS. Aceasta ar insemna ca s-ar putea ca membrii tipului dumneavoastra sa nu fie accesibili pentru programatorii care scriu coduri in alte limbaje de programare.
in codul de mai jos este definit un tip compatibil cu specificatia CLS, in limbajul C#. Totusi, tipul are cateva constructii care nu sunt compatibile cu specificatia CLS, ceea ce determina compilatorul C# sa emita mesaje de eroare. using System;
//Cere compilatorului sa verifice compatibilitatea CLS. [assembly:CLSCompliant(true)]
//Erorile care urmeaza apar deoarece clasa
este de tip public.
public class App
//Eroare: identificatorul 'App.abcO' difera //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()
In acest cod, atributul [assembly:CLSCompliant(true)] este aplicat asamblajului. Acest atribut ii cere compilatorului sa se asigure ca orice tip expus public nu are vreo constructie care sa-1 impiedice de a fi accesat din alte limbaje de programare. Atunci cand acest cod este compilat, compilatorul C# emite doua erori. Prima este raportata deoarece metoda Abc returneaza un numar intreg fara semn; Visual Basic si alte cateva limbaje de programare nu pot manipula valori intregi fara semn. Cea de-a doua eroare apare pentru ca acest tip expune doua metode publice care difera doar prin tipul de litere: Abc si abc. Visual Basic si alte cateva limbaje nu pot apela ambele metode.
Interesant este ca, daca stergem optiunea public de dinaintea clasei ' class App' si efectuam din nou compilarea, ambele erori vor disparea. Motivul este ca tipul Abc va fi stabilit implicit ca internai si, prin urmare, nu va mai fi expus in afara asamblajului. Pentru o lista completa a regulilor specificatiei CLS, consultati sectiunea "Cross-Language Interope-rability1 ', din documentatia .NET Framework SDK.
Vom distila regulile CLS in ceva foarte simplu. in componenta CLR, fiecare membru al unui tip este fie un camp (date), fie o metoda (comportament). Aceasta inseamna ca fiecare limbaj de programare trebuie sa poata accesa campuri si sa apeleze metode. Anumite campuri si metode sunt utilizate in moduri speciale si comune. Pentru a facilita programarea, limbajele ofera de regula abstractizari suplimentare, care usureaza codificarea acestor structuri de programare uzuale. De exemplu, limbajele etaleaza concepte cum ar fi enumerarile, tablourile, proprietatile, indexatorii, delegatii, evenimentele, constructorii, destructorii, supraincarcarile de operatori, operatorii de conversie si asa mai departe. Atunci cand un compilator intalneste in codul sursa una dintre aceste constructii, el trebuie sa o traduca in campuri si metode, astfel incat componenta CLR si alte limbaje de programare sa o poata accesa.
Vom considera urmatoarea definitie a unui tip, care contine un constructor, un destructor, cativa operatori supraincarcati, o proprietate, un indexator si un eveniment. Codul prezentat este doar pentru a permite compilarea; nu reprezinta o modalitate corecta de implementare a unui tip.
using System;
class Test
//Destructor
-Test()
//Supraincarcarea unor operatori.
public static Boolean operator == (Test t1, Test t2)
public static Boolean operator != (Test t1, Test t2)
//O supraincarcare de operator.
public static Test operator + (Test t1, Test t2)
// 0 proprietate.
public String Aproperty
set
//Un indexator.
public String this[Int32 x]
set
//Un eveniment.
event EventHandler AnEvent;
Atunci cand compilatorul compileaza acest cod, rezultatul este un tip care are un numar de campuri si metode definite in cadrul sau. Puteti observa cu usurinta aceasta, utilizand instrumentul IL Disassembler (ILDasm.exe) din cadrul setului .NET Framework SDK, pentru a analiza modulul administrat rezultant, care este prezentat in Figura 1-7.
in Tabelul 1-4 se arata cum s-a realizat corespondenta dintre constructiile limbajului de programare si campurile si metodele echivalente din specificatia CLR.
Figura 1-7 Cu ajutorul instrumentului ILDasm sunt afisate campurile si metodele tipului Test (obtinute din metadate).
Tabelul 1-4 Campurile si metodele tipului Test (obtinute din metadate).
Membrul tipului |
Tipul de membru |
Constructia echivalenta din limbajul |
de programare |
||
AnEvent |
Camp |
Eveniment; numele campului este AnEvent, |
iar tipul sau este System.EventHandler. |
||
.ctor |
Metoda |
Constructor. |
Finalize |
Metoda |
Destructor. |
add_AnEvent |
Metoda |
Metoda accesorie add a evenimentului. |
get_AProperty |
Metoda |
Metoda accesorie get a proprietatii. |
get_Item |
Metoda |
Metoda accesorie get a indexatorului. |
op_Addition |
Metoda |
Operatorul +. |
op_Equality |
Metoda |
Operatorul ==. |
op_Inequality |
Metoda |
Operatorul !=. |
remove_AnEvent |
Metoda |
Metoda accesorie remove a evenimentului. |
set_AProperty |
Metoda |
Metoda accesorie set a proprietatii. |
set_Item |
Metoda |
Metoda accesorie set a indexatorului. |
Nodurile suplimentare de sub tipul Test, care nu sunt mentionate in Tabelul 1-4 - .class, .custom, AnEvent, AProperty si Item - identifica metadate suplimentare despre tip. Aceste noduri nu corespund campurilor sau metodelor; ele nu fac decat sa ofere o serie de informatii suplimentare despre tipul la care poate avea acces componenta CLR, limbajele de programare sau instrumentele. De exemplu, un instrument poate vedea ca tipul Test pune la dispozitie un eveniment numit AnEvent, care este expus prin intermediul celor doua metode, add_AnEvent si remove_AnEvent.
Interoperabilitatea cu codul neadministrat
Platforma .NET Framework ofera o tona de avantaje, fata de alte platforme de dezvoltare. Totusi, foarte putine companii isi pot permite sa reproiecteze si sa reimplementeze toate codurile existente. Compania Microsoft isi da seama de aceasta si a construit componenta CLR astfel incat sa ofere mecanisme care permit ca o aplicatie sa fie formata atat din parti administrate, cat si din parti neadministrate. Mai exact, componenta CLR accepta trei ipoteze de interoperabilitate:
Codul administrat poate apela o functie neadministrata dintr-o biblioteca DLL.
Codul administrat poate apela cu usurinta functiile continute intr-o biblioteca DLL, folosind un mecanism numit P/Invoke (de la Platform Invoke1 ). in fond, multe dintre tipurile definite intern in biblioteca FCL apeleaza functii exportate din fisierele Kernel32.dll, User32.dll si asa mai departe. Multe limbaje de programare vor prezenta un mecanism care faciliteaza apelarea functiilor neadministrate continute in fisierele DLL de catre codul administrat. De exemplu, o aplicatie C# sau Visual Basic poate apela functia CreateSemaphore, exportata din fisierul Kernel32.dll.
Codul administrat poate folosi o
componenta COM existenta (server). Multe companii au
implementat deja un numar de componente COM neadministrate. Prin utilizarea bibliotecii de tipuri
din aceste componente, poate fi creat un asamblaj administrat, care sa
descrie componenta COM. Codul administrat poate accesa tipul din asamblajul administrat, la fel ca orice alt
tip administrat. Pentru mai multe informatii, folositi
instrumentul Tlblmp.exe, care
este livrat impreuna cu setul .NET|
Framework SDK. Uneori, este posibil sa nu existe o biblioteca de
tipuri sau este necesar un control mai mare
asupra a ceea ce produce programul Tlblmp.exe. in aceste situatii, se
poate construi manual in codul sursa un tip, pe care componenta CLR
il poate utiliza pentru a realiza interoperabilitatea adecvata. De
exemplu, se pot folosi componentele DirectX
COM dintr-o aplicatie C# sau Visual Basic.
Codul neadministrat poate utiliza un tip administrat (server). O mare cantitate de cod neadministrat existent necesita furnizarea unei componente COM, pentru a functiona corect. Este mult mai usor sa se implementeze aceste componente folosind un cod administrat, astfel incat se pot evita toate codurile referitoare la numerotarea referintelor si interfete. De exemplu, s-ar putea crea un control ActiveX sau o extensie shell in limbajul C# sau Visual Basic. Pentru mai multe informatii, folositi instrumentele TlbExp.exe si RegAsm.exe, care sunt livrate impreuna cu setul .NET Framework SDK.
In afara de aceste trei situatii, compilatorul Microsoft Visual C++ (versiunea 13) accepta un nou comutator de la linia de comanda, /cir. Acest comutator ii cere compilatorului sa emita cod IL, in loc de instructiuni native x86. Daca aveti o cantitate mare de cod C++ existent, puteti sa-1 recompilati folosind acest nou comutator de compilare. Pentru a fi executat, noul cod va necesita componenta CLR, iar acum codul poate fi modificat in timp, astfel incat sa se profite de avantajele caracteristicilor specifice componentei CLR.
Cu ajutorul comutatorului /cir nu se poate efectua compilarea in limbajul IL pentru metodele care contin un limbaj de asamblare inline (prin intermediul cuvantului cheie asm), accepta un numar de argumente variabil, apelari setjmp sau care contin rutine intrinsece (cum ar fi enable, disable, _ReturnAddress si _AddressOfReturnAddress).
Pentru o lista completa a constructiilor pe care compilatorul C++ nu le poate compila in limbajul IL, consultati documentatia compilatorului Visual C++. Atunci cand compilatorul nu poate efectua compilarea metodei in limbajul IL, el o realizeaza in x86, astfel incat aplicatia este totusi rulata.
Retineti ca, desi codul IL produs este administrat, datele nu sunt; adica obiectele de tip date nu sunt alocate din memoria heap administrata si nu se realizeaza colectarea gunoaielor pentru ele. De fapt, tipurile de date nu au metadate produse pentru ele, iar numele metodelor tipurilor sunt denaturate.
In
urmatorul cod C se apeleaza functia printf din biblioteca de
rulare C standard si metoda System.ConsoleWriteLine. Tipul
System.Console este definit in cadrul bibliotecii FCL. Astfel, in codul
C/C++ se pot utiliza bibliotecile disponibile pentru limbajele C/C++, ca si tipurile administrate.
#include <stdio.h> //Pentru printf
#using <mscorlib.dll> //Pentru tipurile administrate definite in
//acest
asamblaj
using namespace System; //Acceseaza cu usurinta tipurile din
//spatiul de nume System
//Implementeaza o functie main C/C++ normala
void main()
Compilarea acestui cod este cat se poate de usoara. Daca acest cod s-ar afla intr-un fisier MgdCApp.cpp, l-am compila folosind in promptul de comanda urmatoarea linie:
ci /cir MgdCApp.cpp
Rezultatul este un fisier de asamblare MgdCApp.exe. Daca rulam fisierul MgdCApp.exe, vom obtine urmatoarea iesire:
C:>MgdCApp
Afisat de printf.
Afisat de Console::WriteLine.
Daca folosim utilitarul ILDasm.exe pentru a analiza acest fisier, vom obtine iesirea prezentata in Figura 1-8.
in Figura 1-8 se observa ca programul utilitar ILDasm arata toate functiile si campurile globale definite in cadrul asamblajului. Este evident ca o multime de materiale au fost generate automat de catre compilator. Daca se efectueaza de doua 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
//end of method 'Global Functions' : :main
Ceea ce se vede aici nu arata prea frumos, deoarece compilatorul genereaza o cantitate mare de cod special, pentru a face ca toate acestea sa functioneze. Totusi, din acest cod IL se poate vedea ca este apelata atat metoda printf, cat si Console.WriteLine.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 3273
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved