Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 1
Exceptii. Garbage Collection.
Automatic Memory Management - Garbage Collection
Probleme tratate
Exceptii
• Definirea unei exceptii.
• Exceptii sistem. Clasa Exception.
• Blocurile try, catch, finally.
Garbage Collection.
• Alocare memorie si initializare resurse.
• Finalizare – Destructor in C#. Directiva using.
• Generatii.
• Clasa GC.
• Pattern-ul Dispose.
• Interfata IDisposable.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 2
Exceptii
Ce este o exceptie?
Termenul eroare indica ca programatorul a facut ceva gresit.
Exceptiile nu indica neaparat o eroare in cod.
Exceptiile nu conduc neaparat la oprirea executiei unei aplicatii.
O exceptie poate fi definita ca fiind violarea unei presupuneri implicite asupra unei
interfete (J. Richter).
O linie de cod poate sa nu se execute corect din diverse motive: depasire aritmetica,
depasire stiva, memorie insuficienta, indici in afara intervalului, etc.
O aplicatie ce si-ar propune sa verifice toate (sau aproape toate) posibilitatile ce pot
aparea in executarea unei linii de cod si-ar pierde din claritate, ar fi foarte greu de
intretinut, iar mare parte din timp procesor s-ar consuma cu aceste verificari.
Realizarea unei astfel de aplicatii este aproape imposibila.
Folosind exceptiile nu este nevoie sa scriem cod pentru a detecta toate aceste posibile
caderi. In plus codul se executa mai rapid.
Trebuie spus de la inceput ca exceptiile genereaza alte probleme care trebuiesc rezolvate.
Dintre avantajele introducerii exceptiilor amintim :
• Abilitatea de a mentine cod clar si de a intelege logica aplicatiei mai usor.
• Posibilitatea de a detecta si localiza bug-uri in cod.
• Exceptia include un sir de caractere ce o descrie.
• Sirul de caractere contine o cale catre apelul ce a declansat anomalia. Informatiile
se gasesc pe stiva.
Cand apare o “cadere”, CLR scaneaza stiva firului cautand codul ce ar putea trata
exceptia. Daca nu exista un asemenea cod, primim o notificare “unhandled exception”.
Folosirea incorecta a tratarii execeptiilor poate crea neplaceri mai mari decat nefolosirea
lor.
Evolutia manipularii exceptiilor
Win32 API si COM nu folosesc exceptiile pentru a notifica utilizatorul despre
problema aparuta. Multe functii returneaza FALSE sau NULL sau o valoare de tip
HRESULT pentru a indica ca ceva nu s-a executat corect. De altfel nu s-a standardizat
tratarea erorilor in Win32 API. La inceput C si C++ nu suportau exceptiile.
Metoda veche de raportare a erorilor se limita la un numar pe 32 de biti. In marea
majoritate a cazurilor mesajele nu sunt clare si nu ofera suficienta informatie pentru a
detecta ce anume s-a intimplat.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 3
Exceptiile nu pot fi ignorate. Daca aplicatia nu trateaza exceptia, CLR termina aplicatia.
Daca o metoda genereaza o exceptie, aceasta inseamna ca nu lucreaza asa cum ar trebui.
Toate metodele definite de tipurile din cadrul de lucru .NET genereaza exceptii pentru a
indica ca anumite presupuneri au fost incalcate. Nu se returneaza nici o valoare de stare.
Mecanismul manipularii exceptiilor
Mecanismul de manipulare al exceptiilor cadrului de lucru .NET este construit folosind
“Structured Exception Handling” (SEH).
Urmatorul cod C# arata o folosire standard a mecanismului de tratare al exceptiilor.
void Metoda() {
try {
// aici scriem codul ce vrem sa-l protejam (supus verificarii) }
catch (InvalidCastException) { // Aici este codul ce se va executa daca apare exceptia // InvalidCastException sau orice exceptie a unui // tip derivat din InvalidCastException. } catch (NullReferenceException) { // Aici este codul ce se va executa daca apare exceptia // NullReferenceException sau orice exceptie a unui // tip derivat din NullReferenceException. } catch (Exception e) { // In interiorul acestui bloc scriem codul care se executa // pentru o exceptie CLS-compliant (conforma cu CLS). // In mod normal se rearunca exceptia.
throw; }
catch { // Aici este codul ce se va executa pentru orice tip de exceptie // In mod normal se rearunca exceptia.
throw; } finally {
// Acest cod este garantat ca se executa totdeauna, dar // binenteles exista cateva exceptii de la // aceasta presupunere. // Codul de aici se executa indiferent daca a aparut // sau nu o exceptie. // Este asa numitul cod de curatare, mai precis
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 4
// de terminare // a unor operatii incepute in bocul try.
}
//
// In continuare este codul ce se executa cand nu // a aparut nici o exceptie.
}
In mod normal in cod avem un bloc try, unul sau mai multe blocuri catch si optional un
bloc finally.
Blocul try
In general un bloc try contine cod ce are nevoie de operatii de terminare corecta
(curatare) sau operatii de tratare a exceptiilor.
� Codul pentru terminare corecta a blocului try (de ex. inchiderea unui fisier)
trebuie plasat in blocul finally.
� Codul de tratare al exceptiilor trebuie plasat in unul sau mai multe blocuri catch.
� Fiecare bloc catch identifica un anumit tip de exceptie.
Putem crea cate un bloc catch pentru orice tip de eveniment pentru care avem
posibilitatea de a corecta exceptia aparuta sau a obtine mai multe informatii despre
aceasta. Aceste informatii pot ajuta pentru depistarea unui bug sau neindeplinirea unor
conditii de sistem.
Un bloc try trebuie sa fie asociat cu cel putin un bloc catch sau finally.
Blocul catch
Blocul catch contine cod ce se executa cand apare o exceptie. Daca codul din
blocul try nu cauzeaza o exceptie, atunci codul din blocul catch nu se executa. Daca
exista blocul finally atunci codul se executa indiferent daca s-a aruncat sau nu o exceptie.
Expresia dintre paranteze de la blocul catch se numeste “filtru exceptie”.
Exceptia filtru este un tip ce reprezinta o situatie exceptionala pe care dezvoltatorul a
anticipat-o si poate sa o trateze intr-un anume fel (poate iesi din aceasta situatie).
In C#, tipul din “filtru” trebuie sa fie System.Exception sau un tip derivat din
System.Exception.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 5
� Blocurile catch sunt tratate in mod secvential, de sus in jos, deci blocurile
catch pentru tratarea exceptiilor specifice trebuie plasate cat mai aproape
de blocul try.
Daca o exceptie este generata de codul din blocul try (sau orice metoda apelata
din cadrul acestuia), CLR cauta blocurile catch al caror filtru se potriveste cu exceptia
aruncata. Daca nu exista un asemenea bloc catch, atunci CLR cauta in stiva un bloc catch
al carui filtru se potriveste. Daca cautarea s-a terminat si nu s-a gasit un asemenea bloc
atunci CLR opreste aplicatia si afiseaza mesajul “Unhandled exception”.
CLR pastreaza o arborescenta a tuturor metodelor apelate din blocul try.
Cand s-a localizat un bloc catch ce trateaza excepia (filtrul se potriveste), CLR executa
codul din toate blocurile finally, plecand de la acela al blocului try ce a generat exceptia
si oprindu-se la acela al blocului try ce contine blocul catch ce trateaza exceptia.
Codul din ultimul bloc finally se va executa dupa ce s-a executat codul din blocul catch.
In C#, un filtru catch poate specifica o variabila de tip exceptie. Aceasta variabila se
refera la un obiect derivat din System.Exception ce a aruncat aceasta exceptie. Codul din
blocul catch poate referi aceasta variabila pentru a obtine informatii suplimentare despre
exceptie.
La sfarsitul ultimului bloc catch avem urmatoarele posibilitati:
1. “regenerarea” aceleasi exceptii;
2. generarea unei alte exceptii;
3. sa lasam ca firul sa termine cautarea aici.
In primele doua situatii, CLR va cauta in continuare pe stiva un bloc catch ce poate trata
exceptia.
In ultima situatie se va executa codul din blocul finally si apoi se continua in secventa cu
codul ce urmeaza acestui bloc. Daca nu exista un bloc finally, se continua executia cu
codul ce urmeaza ultimului bloc catch.
Blocul finally
Un bloc finally contine cod ce este garantat ( ?!) ca se va executa.
Exemplu:
void ReadData(String pathname) {
FileStream fs = null; try {
fs = new FileStream(pathname, FileMode.Open); // Procesare date din fisier
} catch (OverflowException) {
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 6
// Cod de tratare a exceptiei OverflowException // sau a oricarui tip de exceptie derivat din OverflowException } finally {
// Ne asiguram sa inchidem fisierul if (fs != null) fs.Close();
} }
Indiferent de cum se executa codul din blocul try, codul din blocul finally se va executa.
Blocul finally trebuie sa apara dupa toate blocurile catch asociate cu blocul try.
Nu se genereaza exceptii in blocul finally. Riscul este de a construi o aplicatie ce nu se
mai termina.
Exceptii generate de aplicatie. Exceptii generate de CLR.
Trebuie sa facem distinctie intre exceptiile generate de aplicatie si cele generate de CLR.
Comportarile aplicatiei sunt diferite functie de cine a generat exceptia.
CLR arunca exceptii, de ex. System.StackOverflowException, sau
System.OutOfMemoryException, sau System.ExecutionEngineException.
Codul din blocul finally este posibil sa nu se poata executa in cazul exceptiilor de mai
sus.
OutOfMemoryException = este generata aceasta exceptie cand construim un nou
obiect si nu exista memorie suficienta. In acest caz procesul se termina imediat, codul din
blocul finally se executa. Daca exceptia este aruncata de CLR atunci procesul se termina
imediat si aplicatia nu mai este in stare sa prinda aceasta exceptie si blocul finally nu se
mai executa.
StackOverflowException = CLR arunca aceasta exceptie cand un fir a utilizat tot
spatiul pentru stiva. Aplicatia poate prinde aceasta exceptie dar codul din blocul finally
nu se va executa pentru ca si asta are nevoie de spatiu pe stiva.
Fiecare bloc catch ce prinde aceasta exceptie trebuie sa o « arunce » in continuare
(rethrow) pentru a da sansa CLR-ului sa termine procesul.
Daca exceptia apare in CLR, aplicatia nu poate prinde aceasta exceptie si blocul finally
nu se executa. In acest caz CLR termina procesul sau lanseaza un debugger.
ExecutionEngineException = CLR arunca aceasta exceptie cand detecteaza ca
structurile sale de date interne sunt corupte sau are anumite bug-uri. CLR-ul termina
procesul sau va lansa un debugger pentru proces. Nu se executa nici un bloc catch si nici
finally.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 7
Clasa System.Exception Modelul de tratare al exceptiilor este bazat pe reprezentarea exceptiilor ca obiecte si
separarea codului aplicatiei de codul de tratatre al exceptiilor.
Un bloc catch ce trateaza System.Exception trebuie sa fie ultimul bloc pentru try.
Ordinea blocurilor catch ce trateaza o exceptie va fi urmatoarea :
• intai exceptia pentru tipul derivat
• apoi exceptia pentru clasa de baza (blocurile catch se trateaza secvential, iar
cautarea se opreste dupa primul bloc catch ce trateaza exceptia).
Exemplu (cod incorect – al doilea bloc catch nu va fi « executat » niciodata pentru ca
System.Web.HttpException este derivat din System.Exception).
try { // Code which can cause a web exception or arithmetic exception. } catch (System.Exception ex) { MessageBox.Show ( "An exception occurred." ); } catch (System.Web.HttpException ex) { MessageBox.Show ( "A web exception occurred." ); }
Acelasi exemplu, dar de aceasta data corect:
try { // Code which can cause a web exception or arithmetic exception. } catch (System.Web.HttpException ex) { MessageBox.Show ( "A web exception occurred." ); } catch(System.Exception ex) { MessageBox.Show ( "An exception occurred." ); }
In clasa Exception exista doua categorii de exceptii:
• clase exceptii predefinite de CLR, derivate din System.Exception.
• Clase exceptii definite de utilizator la nivel de aplicatie si derivate din
ApplicationException.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 8
Tipurile de exceptii ce sunt derivate din System.Exception se numesc CLS-compliant
(conforme cu CLS).
Tipul System.Exception este descris in continuare.
Proprietati :
Message - Read-only String – Un mesaj ce descrie exceptia generata.
Source - Read/write String – Contine numele assembly-ului ce a generat exceptia.
StackTrace - Read-only String – Contine numele si signatura metodei apelate ce a dus
la generarea exceptiei.
TargetSite - Read-only MethodBase – Contine metoda ce a generat exceptia.
HelpLink - Read-only String – Contine un URL la documentatia exceptiei.
InnerException - Read-only Exception – Indica exceptia anterioara daca exceptia
curenta a fost generata in timp ce manipula o alta exceptie.
Metoda GetBaseException returneaza originea exceptiei.
HResult - Read/write Int32 – Proprietate protected, folosita cand exista cod managed si
unmanaged COM.
ApplicationException:
� ApplicationException este generata de un cod al utilizatorului si nu
de CLR. In mod normal exceptiile create de utilizator ar trebui derivate
din acest tip.
� ApplicationException diferentiaza exceptiile definite de utilizator de
cele definite de sistem.
� ApplicationException nu furnizeaza informatii despre cauza
exceptiei. In mod normal nu se genereaza o asemenea exceptie de catre
aplicatie, dar in cazul cand se face acest lucru se va pasa constructorului
clasei un mesaj ce va descrie cauza aparitiei exceptiei.
Pentru mai multe detalii consultati documentatia Microsoft.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 9
Clase exceptii folosite in mod frecvent
System.ArithmeticException – exceptii ce apar in timpul operatiilor aritmetice,
cum ar fi System.DivideByZeroException and System.OverflowException.
System.ArrayTypeMismatchException - ArrayTypeMismatchException este
generata cand un obiect incompatibil se incearca a fi memorat intr-un Array.
System.DivideByZeroException – incercare de impartire prin zero.
System.IndexOutOfRangeException - IndexOutOfRangeException este generata
cand se incearca sa se acceseze un tablou folosind un index mai mic ca zero sau in afara
intervalului.
System.InvalidCastException – aruncata cand o conversie implicita de la tipul de
baza sau interfata la tipul derivat nu se executa corect in momentul executiei.
System.NullReferenceException – generata cand se foloseste o referinta null la un
obiect pentru a-l accesa.
System.OutOfMemoryException - OutOfMemoryException este generata cand
operatia 'new' (crearea unui nou obiect) nu se executa cu succes pentru ca nu exista
suficienta memorie.
System.OverflowException - OverflowException este generata cand apare depasire
la executia unei operatii aritmetice.
System.StackOverflowException - StackOverflowException este generata cand
cand stiva de executie este prea mica pentru a executa toate apelurile initiate (probabil
bucla infinita).
ArgumentException – exceptie generata cand un argument furnizat unei metode este
invalid.
Metodele definite de tipurile din FCL sunt presupuse ca arunca exceptii derivate din
System.Exception.
Exemplu:
using System; public class MyClass {} public class ArgExceptionExample { public static void Main() {
MyClass my = new MyClass(); string s = "sometext";
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 10
try { int i = s.CompareTo(my); } catch (Exception e) { Console.WriteLine("Error: {0}",e.ToString()); } } }
Rezultatul este:
Error: System.ArgumentException: Object must be of type String.
at System.String.CompareTo(Object value)
at ArgExceptionExample.Main()
Definirea claselor de exceptii
Prin conventie numele unui tip exceptie ar trebui sa se termine cu Exception.
Metodele ar trebui sa-si verifice argumentele inainte de a le folosi.
Clasele din FCL ce pot fi folosite pentru acest lucru sunt:
• ArgumentNullException,
• ArgumentOutOfRangeException
• DuplicateWaitObjectException.
Nu se va scrie cod ce va arunca o exceptie de tipul Exception,
ApplicationException, sau SystemException.
Exemplu:
class SomeType {
public void SomeMethod(Object o) {
if (!((o is ICloneable) && (o is IComparable))) throw new LipsaInterfataException(...); // urmeaza cod
} }
Aici avem tipul LipsaInterfataException. Cum il definim?
Singura regula de care trebuie sa tinem seama este cea referitoare la efectele colaterale.
Daca derivam aceasta exceptie din ArgumentException, atunci codul existent ce
capteaza aceasta exceptie va capta si noua exceptie.
Cand definim acest tip trebuie sa vedem cu cine se aseamana dintre exceptiile existente,
asemanare in sensul metodei de tratare a exceptiei.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 11
Daca derivam tipul din Exception nu exista prea multe informatii despre exceptia
aruncata.
Cel mai indicat este sa ne gandim la efectele colaterale decat la cele directe cand derivam
un tip exceptie.
Clasa de baza Exception defineste trei constructori:
1. unul implicit ce seteaza toate campurile si proprietatile la valori implicite;
2. un ctor cu un parametru de tip String ce creaza o instanta a tipului si seteaza un
mesaj specific.
3. un ctor cu doi parametri: un String si o instanta a unui tip derivat din
Exception ce seteaza un mesaj si o exceptie interna.
Daca adaugam campuri la un tip exceptie acestia trebuiesc initializati in constructor.
Exemplu
Generarea si tratarea unei exceptii ce face referinta la “inner Exception”.
using System; public class MyAppException: ApplicationException { public MyAppException (String message) : base (message) {} public MyAppException (String message, Exception inner) : base(message,inner) {} }
public class ExceptExample {
public void ThrowInner () { throw new MyAppException("ExceptExample inner exception"); } public void CatchInner() { try { this.ThrowInner(); } catch (Exception e) { throw new MyAppException("Error caused by trying ThrowInner.",e); }
} } public class Test { public static void Main() { ExceptExample testInstance = new ExceptExample();
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 12
try { testInstance.CatchInner(); } catch(Exception e) { Console.WriteLine ("In Main catch block. Caught: {0}", e.Message); Console.WriteLine ("Inner Exception is {0}",e.InnerException); }
} }
Rezultatul este: In Main catch block. Caught: Error caused by trying ThrowInner.
Inner Exception is MyAppException: ExceptExample inner exception
at ExceptExample.ThrowInner()
at ExceptExample.CatchInner()
Exemplu (2) de definire a unei exceptii :
using System; using System.IO; public class CustomException : ApplicationException { object extraInfo;
public CustomException(string message, object extra) : base(message)
{ extraInfo = extra; } public override string ToString() { return "CustomException: " + extraInfo.ToString() + "" + base.Message + "" + base.StackTrace; } } // Test class CustomExceptionGenerator { static void Main() { try { Metoda(); }
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 13
catch (CustomException ce) { Console.WriteLine(ce.ToString()); } Console.ReadLine(); } public static void Metoda() { // Daca aici adaugam urmatorul cod: //
// int i, j,k ; // i = 10 ; // j = 0 ; // k = i/j ; // // atunci apare « Unhandled Exception » // si apoi exceptia creata.
// throw new CustomException("Ceva e gresit aici...", "ExtraInfo");
} }
Rezultatul este :
CustomException: ExtraInfo
Ceva e gresit aici ...
at CustomExceptionGenerator.SomeMethod() in c:\Documents and Settings\info\My
Documents\SharpDevelop Projects\p5_exception1\Main.cs:line 49
at CustomExceptionGenerator.Main() in c:\Documents and Settings\info\My Docum
ents\SharpDevelop Projects\p5_exception1\Main.cs:line 36
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 14
Garbage Collection
Probleme:
� Creare obiecte.
� Gestiune memorie heap.
� Eliberare memorie heap (distrugere obiecte).
Etapele necesare pentru a accesa o resursa:
1. Creare resursa (operatorul new). 2. Initializare memorie pentru a seta starea initiala a obiectului (operatiile se
realizeaza in constructor).
3. Utilizare resursa prin accesarea membrilor tipului respectiv.
4. Eliberare resursa (eliberarea zonei de memorie ocupata de resursa).
Managementul automat al memoriei incearca sa rezolve problemele legate de accesarea
obiectelor inexistente (bug de programare) precum si mentinerea memoriei heap intr-o
stare consistenta (pierderi de memorie la nivel de aplicatie).
Tipurile preconstruite, cum ar fi Int32, Point, Rectangle, String, ArrayList si
SerializationInfo, reprezinta resurse ce nu au nevoie de cod special pentru a fi eliberate
din memorie.
Tipurile ce reprezinta (sau wrap) resurse unmanaged cum ar fi fisiere, socket-uri,
mutexuri, bitmap, etc., au nevoie de executia unui cod cand obiectul este distrus.
Resursele unmanaged nu sunt gestionate de garbage collector.
Alocare memorie si initializare resurse
Cerinta principala a CLR-ului este ca toate resursele sa fie alocate intr-o memorie heap
numita managead heap. Din aceasta memorie « managed heap », dezvoltatorul nu va
elibera obiectele, acestea vor fi dealocate automat cand aplicatia nu mai are nevoie de ele.
Cand stie managed heap ca un obiect nu mai este necesar in aplicatie si trebuie eliberat?
Exista mai multi algoritmi pentru a realiza acest lucru, fiecare lucrand bine intr-un anumit
mediu.
Cand un proces este initializat, CLR rezerva o zona contigua de memorie (adrese) care
initial nu contine nici un obiect. Aceasta zona (heap) este gestionata cu ajutorul unui
pointer, numit in cazul de fata, NextObjPtr.
Acest pointer indica adresa urmatorului bloc liber in heap, deci unde se va aloca
urmatorul obiect nou creat.
Initial, NextObjPtr contine adresa de inceput a zonei rezervate pentru heap.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 15
Operatorul new este folosit pentru alocarea unui obiect in heap, iar in IL (limbaj
intermediar) ii corespunde instructiunea newobj.
CLR-ul executa urmatorele lucruri cand se creaza un nou obiect:
1. Calculeaza numarul de octeti necesari pentru noul tip si toate tipurile sale de
baza ;
2. Fiecare obiect mentine doua campuri speciale (32 biti lungime): un camp ce
mentine un pointer la o tabela de pointeri la metode si un SyncBlockIndex. Pe
un sistem pe 32 biti se mai adauga in plus 8 octeti pentru fiecare obiect, iar pe un
sistem 64-biti se adauga 16 octeti pentru fiecare obiect.
3. Verifica daca exista memorie suficienta in heap. Daca exista memorie, obiectul
este alocat la adresa data de NextObjPtr.
4. Se apeleaza constructorul obiectului (valoarea lui this este egala cu valoarea lui
NextObjPtr) si se obtine adresa unde este memorat obiectul (pointerul this).
5. Se modifica valoarea lui NextObjPtr, astfel incat acesta sa puncteze la
urmatoarea zona de memorie, libera.
Dupa cum se observa din figura, intre obiecte nu exista spatiu de memorie nefolosit. Una
din caracteristicile « managed heap » este si aceasta de a memora obiectele unul dupa
altul fara zone libere de memorie intre obiecte (zona contigua de obiecte). Din punctul de
vedere al « memoriei gestionate » (managed heap) aceasta presupune ca spatiul de adrese
de memorare este infinit. Din cauza ca acest lucru nu este posibil, s-a implementat un
mecanism, ce tine sub control aceasta presupunere, numit : garbage collector (GC).
In principiu acest mecanism asigura intretinerea zonei heap astfel incat obiectele ce nu
mai sunt necesare aplicatiei sunt eliminate si memoria este compactata.
Ideea generala pentru GC : este apelat in momentul cand se cere alocarea unui nou obiect
in heap managed si nu mai exista loc in heap. In realitate mecanismul este foarte
sofisticat si supus mereu schimbarii. Acesta poate fi implementat ca un fir de executie ce
se executa in background, fir ce are o prioritate scazuta. Prioritatea firului poate creste in
momentele critice, cand este absolut necesara compactarea memoriei. Obiectele din heap
au asociate anumite atribute folosite de algoritmul implementat in GC. De exemplu
obiectele sunt impartite in « clase » (generatii) pentru a face diferenta intre obiectele
vechi si cele noi. De asemenea obiectele pastreaza o stare ce poate indica faptul ca
acestea nu mai sunt necesare (ar fi trebuit sa se execute destructorul) dar ele inca persista
in aceasta zona.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 16
Algoritmul Garbage Collection. Idee generala.
GC verifica daca exista in heap obiecte ce nu mai sunt utilizate de aplicatie, iar in caz
afirmativ obiectele sunt eliberate si memoria compactata. In cazul cand nu mai exista
memorie heap disponibila se genereaza exceptia OutOfMemoryException.
Fiecare aplicatie are o multime de “radacini” (puncte de intrare) in heap. Un punct de
intrare contine un pointer la un tip referinta.
Acest pointer poate referi un obiect din heap sau poate fi null.
Toate variabilele globale si locale, variabilele statice, de tip referinta sunt
considerate puncte de intrare (radacini) in heap, de asemenea variabilele parametru de tip
referinta de pe stiva unui fir sunt considerate puncte de intrare in heap.
In interiorul unei metode, un registru CPU ce se refera la un obiect de tip
referinta, este considerat punct de intrare.
Compilatorul JIT creaza tabele interne in momentul cand compileaza o metoda
(cod IL), iar punctele de intrare in aceste tabele contin adrese sau registri CPU si
constituie puncte de intrare in heap.
Folosind aceste tabele si alte informatii pentru obiectele din heap, GC poate
determina daca o anumita zona din heap contine un obiect valid sau nu. De asemenea GC
are acces la stiva firului si o poate examina pentru a determina daca exista metode ce fac
referire la obiecte din heap.
GC presupune ca toate obiectele din heap sunt invalide (adica trebuiesc eliminate).
Se construieste un graf al tuturor punctelor de intrare in heap pentru obiectele valide.
Ceea ce nu se gaseste in acest graf se considera ca sunt obiecte ce trebuiesc eliminate. In
continuare GC parcurge liniar memoria heap si va elimina obiectele ce nu sunt in graf
(algoritmul prezentat este foarte simplificat) compactand in acelasi timp memoria heap.
Compactarea memoriei heap de catre GC are ca efect modificarea tabelei ce contine
puncte de intrare in heap, in caz contrar am avea referinte la obiecte ce nu mai sunt la
adresa corecta (daca un obiect contine un pointer la alt obiect se va modifica si valoarea
acestui pointer). In final NextObjPtr va puncta la prima zona de meorie libera
(memorie organizata in mod liniar).
Din punctul de vedere al programatorului, exista anumite avantaje. Acesta nu mai trebuie
sa scrie cod pentru a gestiona timpul de viata al obiectului.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 17
Urmatorul cod (MSDN) arata cum sunt alocate si gestionate obiectele in heap.
class App { static void Main() {
// ArrayList object created in heap, a is now a root. ArrayList a = new ArrayList(); // Create 10000 objects in the heap. for (Int32 x = 0; x < 10000; x++) {
a.Add(new Object()); // Object created in heap } // Right now, a is a root (on the thread’s stack). So a is // reachable and the 10000 objects it refers to are reachable. Console.WriteLine(a.Length); // After a.Length returns, a isn’t referred to in the code // and is no longer a root. // If another thread were to start a garbage collection // before the result of a.Length were passed to WriteLine, // the 10000 objects would have their memory reclaimed. Console.WriteLine("End of method"); }
}
CLR foloseste metadata pentru a determina membrii unui obiect ce se refera la alte
obiecte.
Finalizare
Orice tip ce incapsuleaza (wrap) o resursa negestionata (unmanaged), cum ar fi fisier,
obiect nucleu mutex, socket, etc., trebuie sa suporte finalizarea, adica sa ofere resursei
eliberarea corecta cand aceasta va fi supusa procesului de colectare in vederea eliminarii
(garbage collection).
Pentru aceasta tipul implementeaza o metoda numita Finalize.
Cand GC determina ca un obiect trebuie eliminat din memorie, acesta apeleaza metoda
Finalize a obiectului, daca aceasta exista - in sensul ca fost scris cod pentru aceasta
metoda.
Observatie
Metoda Finalize() este definita in clasa Object astfel: protected virtual void Finalize().
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 18
In metoda Finalize se va apela metoda corespunzatoare pentru “eliberarea resursei”,
pentru fisiere sau obiecte nucleu ( de exemplu aceasta metoda poate avea numele
CloseHandle).
Pentru tipurile ce folosesc resurse unmanaged este obligatoriu de a defini aceasta metoda
Finalize, in caz contrar resursa nu este eliberata corect din memorie, si vor exista
“pierderi” de memorie ce vor fi rezolvate de catre SO la terminarea procesului (vezi ceva
asemanator la obiecte nucleu).
In MSDN
The Finalize method is used to perform cleanup operations on unmanaged resources held by the current object before the current object is destroyed. The method is protected and therefore
is accessible only through this class or through a derived class.
This method is automatically called after an object becomes inaccessible, unless the object has
been exempted from finalization by a call to GC.SuppressFinalize. During shutdown of an
application domain, Finalize is automatically called on objects that are not exempt from
finalization, even those that are still accessible. Finalize is automatically called only once on a given instance, unless the object is re-registered using a mechanism such
as GC.ReRegisterForFinalize and GC.SuppressFinalize has not been subsequently called.
Every implementation of Finalize in a derived type must call its base type's implementation
of Finalize. This is the only case in which application code is allowed to call Finalize.
Urmatoarul exemplu (pseudo cod) arata cum ar trebui scris codul din metoda Finalize.
Acesta cod nu se compileaza. In realitate nu putem scrie o asemenea metoda in C#.
public sealed class OSHandle {
// Acest camp pastreaza un handle Win32 la resursa unmanaged. private IntPtr handle; // Ctor initializeaza handle. public OSHandle(IntPtr handle) {
this.handle = handle; } // Metoda Finalize apelata de GC in momentul cand // se cere eleiberarea memoriei folosite de obiect.
protected override void Finalize() {
try {
CloseHandle(handle); } catch() { Console.WriteLine(“Nu putem elibera resursa!!!”);
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 19
} finally {
base.Finalize(); }
}
public IntPtr ToHandle() {
return handle; }
[System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle);
}
Explicatii :
Cand obiectul OSHandle este supus procesului de curatare (obiectul in acest moment nu
mai este valid pentru aplicatie, dar pentru GC este inca valid), GC va apela metoda
Finalize a acestui obiect.
In blocul finally se apeleaza metoda de baza Finalize, adica cea din System.Object,
aceasta asigurand ca acest cod se va executa chiar daca a aparut o exceptie (nu in CLR !).
Pentru a inlatura eventualele codari eronate in metoda Finalize (netratarea exceptiilor
si neapelarea metodei System.Object.Finalize) s-a definit o sintaxa speciala pentru
aceasta metoda. Sintaxa pentru destructor.
Sa urmarim codul urmator care este aproape identic cu cel anterior.
public sealed class OSHandle {
private IntPtr handle; public OSHandle(IntPtr handle) {
this.handle = handle; }
// Metoda Finalize apelata de GC in momentul cand // se cere eleiberarea memoriei folosite de obiect. ~OSHandle() {
CloseHandle(handle); }
// cod ...
[System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle);
}
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 20
Observatii
Aici apare o confuzie in utilizarea acestui destructor pentru OSHandle.
Daca se examineaza codul IL, atunci acesta apare ca cel descris in primul exemplu.
Explicatie cod :
Pentru a crea o instanta a obiectului OSHandle, vom apela o functie Win32 ce returneaza
un handle la o resursa unmanaged, de ex. CreateFile, CreateMutex,
CreateSemaphore, CreateEvent, etc. Apoi folosim operatorul new din C# pentru a
construi o instanta pentru OSHandle, pasand ca parametru in ctor acest handle. Cand
acest obiect va fi tratat de GC, GC va « vedea » ca exista o metoda Finalize
implementata si o va apela, ceea ce va permite eliberarea resursei nucleu (vezi si
gestionarea timpului de viata al unui obiect nucleu, se va apela CloseHandle()).
CLR nu suporta distrugerea determinista ( !) a unui obiect (nu putem scrie cod pentru a
distruge obiectul). Finalize va fi apelata numai de GC, iar cand GC se executa nu stim
si nu putem programa acest lucru.
Nu se recomanda folosirea metodei Finalize cand se dezvolta un tip. Timpul procesor
pentru GC va creste caci trebuie sa apeleze Finalize pe obiectele pe care le va distruge
si pe toate obiectele la care face referire un alt obiect ce are metoda Finalize
implementata.
De exemplu, pentru un tablou cu 10000 de obiecte pe fiecare obiect se va apela metoda
Finalize cand acesta va fi in atentia GC.
Pentru resursele unmanaged este obligatorie folosirea metodei Finalize.
Intern se construieste o lista cu obiectele ce implementeaza metoda Finalize. GC va
trebui sa consulte si aceasta lista.
Observatie :
Nu trebuie scris cod in Finalize ce acceseaza obiecte interne ale obiectului curent,
membri pentru asemenea obiecte.
De exemplu avem un obiect, numit Extern, ce contine un pointer (referinta) la un alt
obiect, numit Intern. Deoarece GC nu apeleaza intr-o ordine determinista metoda
Finalize, este posibil sa se apeleze Finalize pe obiectul Intern in timp ce obiectul
extern este in « viata ». Nu avem nici un control asupra ordinii executiei metodelor
Finalize.
In acest moment pointerul (referinta) din obiectul Extern la obiectul Intern este invalid.
Codul din Finalize trebuie sa se execute foarte rapid (asemanator ca la sectiuni critice).
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 21
Eliberarea acestor obiecte se face cu metoda Dispose().
1. Nu trebuie scris cod pentru sincronizare in cadrul acestei metode.
2. In Finalize trebuiesc tratate exceptiile.
3. In Finalize nu trebuie sa facem referire la obiecte managed sau metode statice
managed, este posibil ca aceste obiecte sa nu mai existe sau metodele statice sa
faca referire la obiecte ce nu mai exista.
Urmatorul exemplu (Tom Archer : Inside C#...) emite un sunet cand GC executa o
colectie.
public sealed class GCBeep {
~GCBeep() {
// We’re being finalized, beep.
MessageBeep(-1);
// If the AppDomain isn’t unloading, // create a new object // that will get finalized at the next collection.
if (!AppDomain.CurrentDomain.IsFinalizingForUnload())
new GCBeep(); }
[System.Runtime.InteropServices.DllImport("User32.dll")] private extern static Boolean MessageBeep(Int32 uType);
}
class TestApp {
static void Main() {
// Constructing a single GCBeep object causes // a beep to occur every time a // garbage collection starts. new GCBeep();
// Construct a lot of 100-byte objects.
for (Int32 x = 0; x < 10000; x++) {
Console.WriteLine(x); Byte[] b = new Byte[100];
} }
}
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 22
Cauzele ce produc apelul metodei Finalize
1. Generatia 0 este plina. Acest eveniment este calea cea mai obisnuita de a apela
metoda Finalize, pentru ca acesta apare cand aplicatia se executa si are nevoie
sa aloce noi obiecte.
2. Codul apeleaza explicit metoda statica Collect din System.GC. Codul poate
cere in mod explicit CLR-ului sa execute o colectie. MS nu recomanda apelul
acestei metode.
3. CLR descarca un AppDomain. Cand se intimpla acest lucru, CLR considera ca
nu exista intrari in heap in cadrul AppDomain si apeleaza Finalize pentru
obiectele ce au fost create in AppDomain.
4. CLR se inchide (shuting down). Cand un proces se termina OK, se incearca
terminarea corecta a CLR-ului. In acest moment CLR considera ca nu exista
puncte de intrare in heap in cadrul procesului si apeleaza metoda Finalize
pentru obiectele din heap managed.
CLR foloseste un fir dedicat pentru a apela metodele Finalize. Pentru primele trei
evenimente metoda Finalize intra intr-o bucla infinita, firul este blocat si nu se mai
apeleaza nici o metoda Finalize. Este un dezastru.
Pentru evenimentul 4, fiecare metoda Finalize consuma aproximativ 2 secunde pentru
executie. Daca nu se intimpla asa, CLR omoara procesul si nu se mai apeleaza alte
metode Finalize. De asemenea, daca CLR consuma mai mult de 40 secunde pentru a
apela o metoda Finalize a unui obiect, CLR omoara procesul.
Valorile scrise mai sus pot fi schimbate de MS in cadrul procesului de imbunatatire al
algoritmului pentru GC.
Codul din Finalize poate construi alte obiecte; daca se intimpla asta in timp ce CLR se
termina (shutdown) CLR va continua sa colecteze obiecte si sa apeleze metodele lor
Finalize pina cand nu mai exista nici un obiect sau au trecut 40 de secunde.
Reluam exemplul GCBeep. Explicatie.
Daca un obiect GCBeep este pe cale sa se finalizeze din cauza evenimentului 1 sau 2, un
nou obiect GCBeep este construit. Este corect pentru ca aplicatia continua sa ruleze,
presupunind ca vor fi mai multe colectii in viitor.
Daca un obiect GCBeep este pe cale sa se finalizeze din cauza evenimentului 3 sau 4, un
nou obiect nu va putea fi construit pentru ca acest obiect ar trebui sa fie creat in timp ce
se descarca AppDomain sau CLR a fost oprit. Daca aceste noi obiecte au fost create,
CLR ar trebui sa poata apela Finalize pentru aceste obiecte.
Pentru a preveni constructia acestor noi obiecte GCBeep, metoda Finalize din GCBeep
apeleaza metoda AppDomain.IsFinalizingForUnload. Aceasta metoda returneaza
TRUE daca metoda Finalize a obiectului este apelata pentru ca AppDomain se
descarca din memorie. Solutia de mai sus este buna numai pentru AppDomain.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 23
Pentru CLR, MS a adaugat proprietatea read-only HasShutdownStarted la clasa
System.Environment.
Metoda Finalize din GCBeep ar trebui sa citeasca aceasta proprietate si daca este TRUE
sa nu mai permita crearea de noi obiecte GCBeep.
Finalizare – probleme interne
Sa ne reamintim ca obiectele ce au implementata metoda Finalize sunt pastrate intr-o
lista separata, lista de finalizare, gestionata de GC.
Obiectele ce nu implementeaza propria metoda Finalize nu sunt trecute in lista de
finalizare, chiar daca sunt derivate din System.Object ce are metoda Finalize.
Sunt doua actiuni importante ce trebuiesc facute : alocare memorie pentru obiectul creat,
inscrierea obiectului in aceasta lista. In fapt se considera actiuni elementare care fiecare la
rindul ei este critica. Putem distinge astfel de operatii: alocare memorie, completare lista
finalizare, construire obiect (apelare constructor).
Ordinea acestor operatii (conform documentatiei MS) este : alocare memorie, completare
lista finalizare, apelare constructor.
Daca obiectul nu este construit din diverse motive, atunci sistemul trebuie sa fie capabil
sa faca rollback pe operatiile anterioare. Cel mai important este eliminarea obiectului din
lista de finalizare. In rest ramane treaba GC sa rezolve problema cu memoria alocata si
nefolosita.
Sa consideram exemplul din figura de mai sus.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 24
Cand se executa GC, obiectele B, E, G, H, I, si J nu mai constituie puncte valide de
intrare in heap (radacini) si deci vor fi supuse procesului de colectare si eliminare.
Pentru fiecare obiect se cauta daca exista in lista de finalizare, iar in caz afirmativ este
eliminata intrarea din aceasta lista si trecuta intr-o alta lista F-reachable si asta inseamna
ca pentru obiectele de aici se va apela metoda Finalize.
Dupa colectie managed heap arata astfel :
Un fir dedicat din CLR, va apela metodele Finalize.
Un fir dedicat este folosit pentru a preveni eventualele probleme legate de sincronizare.
Cand coada F este vida, acest fir este in asteptare. El va fi lansat numai pe evenimente ce
spun ca sunt obiecte in coada F.
Obiectele ce au intrari in coada F nu sunt supuse procesului de eliminare, ele constituie
inca radacini valide pentru heap managed.
Memoria heap managed dupa etapa a doua a colectarii si eliminarii arata astfel:
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 25
Clasa GC
public static class GC
Metodele din aceasta clasa influenteaza modul cum este eliberat un obiect managed din
memoria heap.
GC nu recunoaste referinte la un obiect din cod unmanaged.
Metode (MSDN)
Name Description
Collect () Forces an immediate garbage collection of all
generations.
Collect(Int32) Forces an immediate garbage collection from
generation zero through a specified generation.
Collect(Int32, GCCollectionMode)
Forces a garbage collection from generation zero
through a specified generation, at a time specified
by a GCCollectionMode value.
GetGeneration(Object) Returns the current generation number of the
specified object.
GetTotalMemory
Retrieves the number of bytes currently thought
to be allocated. A parameter indicates whether
this method can wait a short interval before
returning, to allow the system to collect garbage
and finalize objects.
KeepAlive
References the specified object, which makes it
ineligible for garbage collection from the start of
the current routine to the point where this method
is called.
ReRegisterForFinalize Requests that the system call the finalizer for the
specified object for which SuppressFinalize has
previously been called.
SuppressFinalize Requests that the system not call the finalizer for
the specified object.
WaitForFullGCComplete(Int32)
Returns, in a specified time-out period, the status
of a registered notification for determining
whether a full garbage collection by common
language the runtime has completed.
WaitForPendingFinalizers Suspends the current thread until the thread that
is processing the queue of finalizers has emptied
that queue.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 26
Proprietati
Name Description
MaxGeneration Gets the maximum number of generations that the system currently
supports.
Paternul Dispose: Fortarea unui obiect sa se stearga
Interfata IDisposable
Metoda Finalize se foloseste in special pentru resurse unmanaged.
Metoda nu poate fi apelata direct.
Tipurile ce ofera posibilitatea de a fi in mod determinist “dispose” sau inchise
implementeaza ceea ce se numeste “dispose pattern”.
Pattern-ul Dispose – defineste conventiile la care un dezvoltator ar trebui sa adere in
momentul cand defineste un tip ce doreste sa poata fi sters in mod implicit.
Pentru un tip ce implementeaza pattern-ul dispose, utilizatorul va putea elimina obiectul
exact atunci cand nu mai are nevoie de el.
Un tip poate contine atat Finalize cat si Dispose de exemplu : clasa
System.IO.BinaryWriter intra in aceasta categorie.
Exemplu (MSDN):
Crearea unei clase ce implementeaza interfata IDisposable si metoda
IDisposable.Dispose. Trebuie eliberata o resursa unmanaged.
using System; using System.ComponentModel; // The following example demonstrates how to create // a resource class that implements the IDisposable interface // and the IDisposable.Dispose method. public class DisposeExample { // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class MyResource: IDisposable { // Pointer to an external unmanaged resource. private IntPtr handle; // Other managed resource this class uses. private Component component = new Component();
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 27
// Track whether Dispose has been called. private bool disposed = false; // The class constructor. public MyResource(IntPtr handle) { this.handle = handle; }
// Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // 1. // If disposing equals true, the method has been // called directly or indirectly by a user's code. // Managed and unmanaged resources can be disposed. // 2. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should // not reference other objects. // Only unmanaged resources can be disposed.
private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. component.Dispose(); } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. CloseHandle(handle); handle = IntPtr.Zero; // Note disposing has been done. disposed = true;
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 28
} }
// Use interop to call the method necessary // to clean up the unmanaged resource. [System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } } public static void Main() { // Insert code here to create // and use the MyResource object. } }
Exemplu din MSDN.
In acest caz nu avem de a face cu o resursa unmanaged.
using System; using System.IO; // This class shows how to use a disposable resource. // The resource is first initialized and passed to // the constructor, but it could also be // initialized in the constructor. // The lifetime of the resource does not // exceed the lifetime of this instance. // This type does not need a finalizer because it does not // directly create a native resource like a file handle // or memory in the unmanaged heap. public class DisposableResource : IDisposable { private Stream _resource; private bool _disposed;
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 29
// The stream passed to the constructor // must be readable and not null. public DisposableResource(Stream stream) { if (stream == null) throw new ArgumentNullException("Stream in null."); if (!stream.CanRead) throw new ArgumentException( "Stream must be readable."); _resource = stream; _disposed = false; } // Demonstrates using the resource. // It must not be already disposed. public void DoSomethingWithResource() { if (_disposed) throw new ObjectDisposedException( "Resource was disposed."); // Show the number of bytes. int numBytes = (int) _resource.Length; Console.WriteLine("Number of bytes: {0}", numBytes.ToString()); } public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // If you need thread safety, use a lock around these // operations, as well as in your methods // that use the resource. if (!_disposed) { if (disposing) { if (_resource != null) _resource.Dispose(); Console.WriteLine("Object disposed."); } // Indicate that the instance has been disposed.
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 30
_resource = null; _disposed = true; } } } class Program { static void Main() { try { // Initialize a Stream resource to pass // to the DisposableResource class. Console.Write("Enter filename and its path: "); string fileSpec = Console.ReadLine(); FileStream fs = File.OpenRead(fileSpec); DisposableResource TestObj = new DisposableResource(fs); // Use the resource. TestObj.DoSomethingWithResource(); // Dispose the resource. TestObj.Dispose(); } catch (FileNotFoundException e) { Console.WriteLine(e.Message); } } }
Exceptii
Facultatea de Informatica Iasi – Universitatea Al I. Cuza – Iasi 03.11.2014
Ioan Asiminoaei 31
Folosirea instructiunii using
Exemplu
using System; using System.IO; class App {
static void Main() {
// Create the bytes to write to the temporary file. Byte[] bytesToWrite = new Byte[] { 1, 2, 3, 4, 5 }; // Create the temporary file. using (FileStream fs = new FileStream("Temp.dat", FileMode.Create)) {
// Write the bytes to the temporary file. fs.Write(bytesToWrite, 0, bytesToWrite.Length);
} // Delete the temporary file. File.Delete("Temp.dat"); // This always works now.
} }
In using se initializeaza obiectul si se salveaza o referinta la acesta intr-o variabila. Cu
aceasta referinta apelam metode din obiect.
La intilnirea instructiunii using, compilatorul construieste blocurile try si finally, iar in
blocul finally se face cast la IDisposable pentru a apela metoda Dispose.
Instructiunea using lucreaza numai cu tipuri ce implementeaza interfata IDisposable.