Construcția try...catch...finally
Uneori, în timpul execuției programului apar erori care sunt greu de prevăzut sau chiar imposibil de anticipat. De exemplu, la trimiterea unui fișier prin rețea, conexiunea de rețea se poate întrerupe brusc. Aceste situații se numesc excepții.
Limbajul C# oferă dezvoltatorilor posibilitatea de a trata astfel de situații. Pentru acest lucru, în C# există construcția try...catch...finally.
try
{
}
catch
{
}
finally
{
}
Când se utilizează blocul try...catch...finally, toate instrucțiunile din blocul try sunt executate mai întâi. Dacă în acest bloc nu apar excepții, după execuția lui se execută blocul finally. Apoi construcția try...catch...finally își încheie execuția.
Dacă în blocul try apare o excepție, ordinea obișnuită de execuție se oprește, iar mediul CLR începe să caute un bloc catch care poate trata excepția respectivă. Dacă blocul catch corespunzător este găsit, acesta se execută, iar după finalizarea lui se execută blocul finally.
Dacă blocul catch corespunzător nu este găsit, programul se închide brusc când apare excepția.
Să considerăm următorul exemplu:
int x = 5;
int y = x / 0;
Console.WriteLine($"Rezultatul: {y}");
Console.WriteLine("Sfârșitul programului");
În acest caz, are loc împărțirea unui număr la 0, ceea ce va genera o excepție. La rularea aplicației în modul de depanare, vom vedea în Visual Studio o fereastră care informează despre excepție:

În această fereastră vedem că a apărut o excepție de tip System.DivideByZeroException, adică o încercare de împărțire la zero.
Pentru a evita astfel de închideri bruște ale programului, ar trebui să folosim construcția try...catch...finally pentru tratarea excepțiilor. Astfel, vom rescrie exemplul în felul următor:
try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Rezultatul: {y}");
}
catch
{
Console.WriteLine("A apărut o excepție!");
}
finally
{
Console.WriteLine("Bloc finally");
}
Console.WriteLine("Sfârșitul programului");
În acest caz, din nou va apărea o excepție în blocul try, deoarece încercăm să împărțim la zero. Ajungând la linia:
int y = x / 0;
Execuția programului se va opri. CLR va găsi blocul catch și va transfera controlul către acest bloc.
După blocul catch, se va executa blocul finally.
A apărut o excepție!
Bloc finally
Sfârșitul programului
Astfel, programul nu va executa împărțirea la zero și, prin urmare, nu va afișa rezultatul acestei împărțiri, dar acum nu se va închide brusc, iar excepția va fi tratată în blocul catch.
Trebuie menționat că în această construcție blocul try este obligatoriu. Dacă avem blocul catch, putem omite blocul finally:
try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Rezultatul: {y}");
}
catch
{
Console.WriteLine("A apărut o excepție!");
}
Și, invers, dacă avem blocul finally, putem omite blocul catch și să nu tratăm excepția:
try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Rezultatul: {y}");
}
finally
{
Console.WriteLine("Bloc finally");
}
Cu toate acestea, deși din punct de vedere sintactic această construcție este corectă, deoarece CLR nu va putea găsi un bloc catch corespunzător, excepția nu va fi tratată, iar programul se va închide brusc.
Tratarea excepțiilor și construcțiile condiționale
Unele situații excepționale pot fi prevăzute de către dezvoltator. De exemplu, să presupunem că în program există o metodă care primește un șir de caractere, îl convertește într-un număr și calculează pătratul acestuia:
Square("12"); // Pătratul numărului 12: 144
Square("ab"); // !Excepție
void Square(string data)
{
int x = int.Parse(data);
Console.WriteLine($"Pătratul numărului {x}: {x * x}");
}
Dacă utilizatorul transmite metodei un șir de caractere care nu este un număr, programul va genera o eroare. Pe de o parte, aceasta este exact situația în care se poate aplica blocul try...catch pentru a trata eroarea posibilă. Cu toate acestea, ar fi mult mai optim să verificăm posibilitatea conversiei:
Square("12"); // Pătratul numărului 12: 144
Square("ab"); // Intrare incorectă
void Square(string data)
{
if (int.TryParse(data, out var x))
{
Console.WriteLine($"Pătratul numărului {x}: {x * x}");
}
else
{
Console.WriteLine("Intrare incorectă");
}
}
Metoda int.TryParse() returnează true dacă conversia poate fi realizată și false dacă nu poate fi realizată. Dacă conversia este posibilă, variabila x va conține numărul introdus. Astfel, fără a folosi try...catch, putem trata o situație excepțională posibilă.
Din punct de vedere al performanței, utilizarea blocurilor try...catch este mai costisitoare decât utilizarea construcțiilor condiționale. Prin urmare, dacă este posibil, este mai bine să folosiți construcții condiționale pentru a verifica situațiile excepționale în locul blocurilor try...catch.