Изключенията в .NET Framework са обекти. Класът System.Exception е базов клас за всички изключения в CLR. Той дефинира свойства, общи за всички .NET изключения, които съдържат информация за настъпилата грешка или необичайна ситуация.
Ето и някои често използвани свойства:
-
Message – текстово описание на грешката.
-
StackTrace – текстова визуализация на състоянието на стека в момента на възникване на изключението. Дава информация за това в кой метод в кой файл и на кой ред във файла е възникнало изключението. Имената на файловете и редовете са налични само при компилиране в дебъг режим.
-
InnerException – изключение, което е причина за възникване на текущото изключение (ако има такова). Например имаме метод който чете от файл и после форматира прочетените данни. Ако по време на четенето възникне изключение то може да бъде прихванато и да се хвърли ново изключение от друг, собствено дефиниран тип, като прихванатото изключение се присвои на свойството InnerException. Целта е обработчикът на изключението да получи информация както за възникналия проблем, така и за неговия първопричинител. Чрез свойството InnerException изключенията могат да се свързват във верига, която съдържа последователно всички изключения, които са причинили изключението в нейното начало.
Ще илюстрираме употребата на свойствата с един пример:
using System;
class ExceptionsTest
{
public static void CauseFormatException()
{
string s = "an invalid number";
Int32.Parse(s);
}
static void Main(string[] args)
{
try
{
CauseFormatException();
}
catch (FormatException fe)
{
Console.Error.WriteLine(
"Exception caught: {0}\n{1}",
fe.Message, fe.StackTrace);
}
}
}
|
Свойството StackTrace е изключително полезно при идентифициране на причината за изключението. Резултатът от примера е информация за прихванатото в Main() метода изключение, отпечатана върху стандартния изход за грешки:
Exception caught: Input string was not in a correct format.
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Int32.Parse(String s)
at ExceptionsTest.CauseFormatException() in c:\consoleapplication1\exceptionstest.cs:line 8
at ExceptionsTest.Main(String[] args) in c:\consoleapplication1\exceptionstest.cs:line 15
|
Имената на файловете и номерата на редовете са достъпни само ако сме компилирали с дебъг информация. Ако компилираме по-горния пример в Release режим, ще получим много по-бедна информация от свойството StackTrace:
Exception caught: Input string was not in a correct format.
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at ExceptionsTest.Main(String[] args)
|
Превключването между Debug и Release на компилация става много лесно от лентата с инструменти за компилация във VS.NET:
Йерархия на изключенията
Изключенията са класове и като такива могат да се наследяват и да образуват йерархии. Както вече знаем, всички изключения в .NET Framework наследяват класа System.Exception. Този клас има няколко важни наследника, от които обектната йерархия продължава в няколко посоки. Това се вижда от следната диаграма:
Някои изключения директно наследяват System.Exception, например класовете System.SystemException и System.ApplicationException.
Системните изключения, които се използват от стандартните библиотеки на .NET и вътрешно от CLR, наследяват класа System.SystemException. Ето някои от тях:
-
System.ArithmeticException – грешка при изпълнението на аритметична операция, например деление на 0, препълване на целочислен тип и др.
-
System.ArgumentException – невалиден аргумент при извикване на метод.
-
System.NullReferenceException – опит за достъп до обект, който има стойност null.
-
System.OutOfMemoryException – паметта е свършила.
-
System.StackOverflowException – препълване на стека. Обикновено възниква при настъпване на безкрайна рекурсия.
-
System.IndexOutOfRangeException – опит за излизане от границите на масив.
Изключенията дефинирани от потребителя трябва да наследяват класа System.ApplicationException. Така потребителските програми ще предизвикват изключения само от този тип или негови наследници. Това дава възможност да се разбере дали проблемът е на ниво потребителски код или е свързан със системна грешка. Възможно е потребителски-дефинирано изключение да наследи и директно System.Exception, а не System. ApplicationException, но има много спорове дали това е добра практика. Някои експерти твърдят, че наследяването на ApplicationException усложнява излишно йерархията, докато други смятат, че е по-важно да се разграничават системните от потребителските изключения.
Подредбата на catch блоковете
Както вече знаем, при прихващане на изключения от даден клас се прихващат и изключенията от всички негови наследници. Затова е важна подредбата на catch блоковете. Например конструкцията:
try
{
// Do some works that can raise an exception
}
catch (System.ArithmeticException)
{
// Handle the caught arithmetic exception
}
|
прихваща освен ArithmeticException и изключенията OverflowException и DivideByZeroException. В този пример всичко е наред, но нека разгледаме следния код:
static void Main()
{
string s = Console.ReadLine();
try
{
Int32.Parse(s);
}
catch (Exception) // Трябва да е най-накрая
{
Console.WriteLine("Can not parse the number!");
}
catch (FormatException) // Този код е недостижим
{
Console.WriteLine("Invalid integer number!");
}
catch (OverflowException) // Този код е недостижим
{
Console.WriteLine("The number is too big!");
}
}
|
В този пример има недостижим код, защото първият catch блок ще се изпълнява за всички типове изключения, тъй като той прихваща базовия тип System.Exception. По тази причина по-специфичните блокове след него няма да се изпълнят никога.
|
catch блоковете трябва да са подредени така, че да започват от изключенията най-ниско в йерархията и да продължават с по-общите. Така ще бъдат обработени първо по-специфичните изключения и след това по общите. В противен случай кодът за по-специфичните никога няма да се изпълни.
| Изключения и неуправляван код
Управлявания .NET код може да предизвика само изключения, наследници на System.Exception. Неуправляваният код може да предизвика и други изключения. За прихващане на всички изключения в C# се използва следната конструкция:
try
{
// Do some work that can raise an exception
}
catch
{
// Handle the caught exception
}
|
Използването на тази конструкция е опасно и трябва да се използва само в краен случай, когато е наистина е наложително, защото прихващането на всякакви изключения може да доведе до неочаквани резултати.
Сподели с приятели: |