Atunci cand programam este aproape imposibil sa nu primim erori: de sintaxa (abatere de la sintaxa limbajului de programare), de logica (greseli in logica programului) sau erori la executie (exceptii).
O exceptie reprezinta o eroare care intervine la runtime, la momentul executiei. In C#, exceptiile se pot trata intr-o maniera structurata si controlata, acest lucru insemanand faptul ca programatorul nu trebuie sa mai verifice manual daca o operatie se executa sau nu cu succes.
C# defineste exceptii standard pentru tipurile de erori obisnuite dintr-un program. De exemplu: impartire la zero, depasirea capacitatii unui vector, memorie insuficienta, etc.
Blocurile Try – Catch – Finally
Cuvintele cheie in C# rezervate pentru tratarea exceptiilor sunt try, catch, finally, throw. Acestea reprezinta un sistem unitar, utilizarea unuia dintre ele implicand si utilizarea altuia.
Intr-un bloc try vom scrie instructiunile care trebuie verificate pentru aparitia erorilor. Daca pe parcursul executiei acestor intructiuni apare o exceptie, aceasta este “aruncata”, lansata (thrown).
Cu ajutorul lui catch programul poate intercepta exceptia si o poate trata in functie de logica programului. Instructiunile din catch se executa doar daca se lanseaza o exceptie.
Instructiunile din finally se vor executa intotdeauna.
O imagine de ansamblu a codului folosit pentru tratarea exceptiilor:
try { //in this block exception may get thrown } catch { //handle exception } finally { //cleanup code, optionally }
Cuvantul cheie try nu poate aparea fara a fi completat de cuvantul cheie catch sau de finally si nici invers. Finally este optional.
Un exemplu simplu de tratare a exceptiei DivideByZeroException si de folosire a blocului try – catch – finally:
try { int x = 0; //raise the exception int y = 4 / x; } catch { //catch the exception Console.WriteLine("X must be greater than zero"); } finally { //this code will be allways executed Console.WriteLine("Program completed"); Console.Read(); }
Pot exista mai multe instructiuni catch asociate unui try. Instructiunea care se va executa se va stabili in functie de tipul exceptiei, celelalte sunt ignorate.
static void Compute(int [] numbers) { try { int secondNumber = numbers[0]; Console.Write(secondNumber); } catch (IndexOutOfRangeException) { Console.WriteLine("Must provide more than an argument"); } catch (NullReferenceException) { Console.WriteLine("Argument is null"); } catch (FormatException) { Console.WriteLine("Argument is not a number"); } catch (Exception) { Console.WriteLine("Another exception occured"); } }
Se adauga instructiunile pentru blocurile catch cat mai specifice, inainte celor generale.
CLR cauta instructiunea catch care va trata exceptia. Daca metoda curenta in care ne aflam cu debug-erul nu contine un bloc catch, atunci CLR va cauta in metoda care a apelat metoda curenta, si asa mai departe conform call stack. Daca nu este gasit nici un catch, atunci CLR va afisa un mesaj cum ca exceptia nu este tratata, unhandled exception message, apoi va opri executia programului.
In general, instructiunea catch nu are un parametru. Totusi, il putem adauga in cazul in care vom avea nevoie de accesul la obiectul care reprezinta exceptia. Este folositor pentru furnizarea informatiilor suplimentare despre eroarea produsa.
catch (Exception exception)
{
Console.WriteLine(exception.Message);
Console.WriteLine(exception.GetType());
}
Fluxul executiei programului continua cu instructiunile aflate dupa blocul catch.
Throw
Pentru a lansa manual o exceptie se foloseste throw.
if (numbers == null) throw new ArgumentNullException("Array is null");
Tipul obiectului trebuie sa fie o clasa derivata din Exception.
Exemplu de folosire a blocului finally
Instructiunile dintr-un bloc finally se executa intotdeauna, chiar daca o exceptie este prinsa sau nu. Acest bloc este folosit pentru a “curata” resursele folosite intr-un bloc try si pentru a asigura executarea unor instructiuni de cod indiferent de modul in care se iese din blocul precedent de try.
static void ReadFile()
{
StreamReader reader = null;
try
{
//open text file for reading
reader = File.OpenText(@"C:\fisier.txt");
//return if the current position is at the end of the stream
if (reader.EndOfStream) return;
//read the content of the stream
Console.WriteLine(reader.ReadToEnd());
}
catch (FileNotFoundException)
{
Console.WriteLine("File not found");
}
finally
{
//cleanup code, close the file
if(reader != null)
reader.Dispose();
}
}
Relansarea unei exceptii
Presupunem urmatoarea clasa. In metoda Divide vom trimite exceptia DivideByZeroException la un nivel mai sus, in metoda parinte.
class Compute
{
public void DivideTwoNumbers(int x, int y)
{
try
{
var result = x / y;
}
catch(DivideByZeroException)
{
// the control goes back to parent method
throw;
}
}
}
In metoda Main :
static void Main(string[] args)
{
Compute comp = new Compute();
try
{
comp.DivideTwoNumbers(5, 13);
}
catch(DivideByZeroException)
{
Console.WriteLine("Exception caught here");
}
}
System.Exception
Dupa cum s-a putut observa in exemplele de mai sus, exceptiile sunt reprezentate prin clase. Toate deriva din clasa de baza Exception. Se disting doua clase prin reprezentarea a doua categorii generale de exceptii: exceptii generate de motorul de executie SystemException si exceptii generate de programele de aplicatie ApplicationException. Ele nu adauga membri noi la clasa de baza, ci doar definesc radacile celor doua ierarhii de clase care reprezinta exceptii. De exemplu, in cazul impartirii la zero se produce o exceptie de tipul DivideByZeroException.
Programatorul isi poate defini propriile clase care reprezinta exceptii prin derivarea clasei ApplicationException.
Pentru mai multe detalii, se poate consulta documentatia de pe msdn.
Un program trebuie sa trateze exceptiile intr-o maniera logica si eleganta si sa isi continue apoi executia. Daca programul nu intercepteaza exceptia, el va fi fortat sa se inchida.
Chiar daca nu este considerata optima, o alta modalitate de raportare a erorilor o reprezinta codurile de retur. Ce parere aveti? Voi ati utilizat-o?
Pentru ca tratarea erorilor constituie un subiect dificil voi continua intr-un viitor articol crearea propriilor clase de exceptii si consecintele exceptiilor neinterceptate.
Pingback/Trackback
Tratarea exceptiilor in C# (II) | by zeltera