Кратко съдържание



страница35/73
Дата21.07.2018
Размер8.57 Mb.
1   ...   31   32   33   34   35   36   37   38   ...   73

Изключенията в .NET Framework


Изключенията в .NET са класическа имплементация на изключенията от ООП, макар че притежават и допълнителни възможности, произтичащи най-вече от предимствата на управлявания код.

В .NET Framework управлението на грешките се осъществява предимно чрез изключения. Всички операции от стандартната библиотека на .NET (Framework Class Library) сигнализират за грешки пос­редством хвърляне (throw, raise) на изключение. .NET програмистите трябва да се съобразя­ват с изключенията, които биха могли да възникнат и да предвидят код за тяхната обработка в някой от извикващите методи.

Изключение може да възникне поради грешка в нашия код или в код който извикваме (примерно библиотечни функции), при изчерпване на ресурс на операцион­ната система, при неочаквано поведение в .NET средата (примерно невъзмож­ност за верификация на даден код) и в много други ситуации.

В повечето случаи едно приложение е възможно да се върне към нормал­ната си работа след обработка на възникнало изключение, но има и ситуации в които това е невъзможно. Такъв е случаят при възникване на някои runtime изключения. Пример за подобна изключителна ситуация е, когато една програма изчерпа наличната работна памет. Тогава CLR хвър­ля изключение, което сигнализира за настъпилия проблем, но програмата не може да продължи нормалната си работа и единствено може да запише състоянието на данните, с които работи (за да минимизира загубите), и след това да прекрати изпълнението си.

Всички изключения в .NET Framework са обекти, наследници на класа System.Exception, който ще разгледаме в детайли след малко. Всъщност, съществуват и изключения, които не отговарят на това изискване, но те са нестандартни и възникват рядко. Тези изключения не са съвместими със CLS (Common Language Specification) и не могат да се предизвикат от .NET езиците (C#, VB.NET и т. н.), но могат да възникнат при изпълнение на неуправляван код.

Изключенията носят в себе си информация за настъпилите грешки или необи­чайни ситуации. Тази информация може да се извлича от тях и е много полезна за идентифицирането на настъпилия проблем. В .NET Framework изключенията пазят в себе си името на класа и метода, в който е възникнал проблемът, а ако асемблито е компилирано с дебъг информа­ция, изключенията пазят и името на файла и номера на реда от сорс кода, където е възникнал проблемът.

Когато възникне изключение, изпълнението на програмата спира. CLR средата запазва състоянието на стека и търси блока от кода, отгово­рен за прихващане и обработка на възникналото изключение. Ако не го намери в границите на текущия метод, го търси в извикващия го метод. Ако и в него не го намери, го търси в неговия извикващ и т. н. Ако никой от извикващите методи не прихване изключението, то се прихваща от CLR, който показва на потребителя информация за възникналия проблем.

Изключенията улесняват писането и поддръжката на надежден програмен код, като дават възможност за обработката на проблемните ситуации на много нива. В .NET Framework се позволява хвърляне и прихващане на изключения дори извън границите на текущия процес.


Прихващане на изключения


Работата с изключения включва две основни операции – прихващане на изключения и предизвикване (хвърляне) на изключения. Нека разгледаме първо прихващането на изключения в езика C#.

Програмна конструкция try-catch


В C# изключенията се прихващат с програмната конструкция try-catch:

try

{

// Do some work that can raise an exception



}

catch (SomeExceptionClass)

{

// Handle the caught exception



}

Кодът, който може да предизвика изключение, се поставя в try блока, а кодът, отговорен за обработка му – в catch блока.

Catch блокът може да посочи т. нар. филтър за прихващане на изключе­ния или да го пропусне. Филтърът представлява име на клас, поставен в скобки като параметър на catch оператора. В горния пример филтърът задава прихващане на изключения от класа SomeExceptionClass и всички класове, негови наследници. Ако филтърът бъде пропуснат, се прихващат всички изключения, независимо от типа им:



try

{

// Do some work that can raise an exception



}

catch


{

// Any exception is caught here

}


Изразът catch може да присъства няколко пъти съответно за различните типове изключения, които трябва да бъдат прихванати, например:

try

{

// Do some work that can raise an exception



}

catch (SomeExceptionClass)

{

// Handle the SomeExceptionClass and its descendants



}

catch (OtherExceptionClass)

{

// Handle the OtherExceptionClass and its descendants



}

Как CLR търси обработчик за изключенията?


Когато възникне изключение, CLR търси "най-близкия" catch блок, който може да обработи типа на възникналото изклю­чение. Първо се претърсва try-catch блокът от текущия метод, към който принадлежи изпълнява­ният в момента код (ако има такъв блок). Последовател­но се обхождат асоции­раните с него catch блокове, докато се намери този, чийто филтър съот­ветства на типа на възникналото изключение.

Ако това претърсване пропадне, се извършва същото претърсване за следва­щия try-catch блок, ограждащ текущия (ако има такъв). Този блок може да се намира в текущия метод, в извикващия го метод или в някой от методите, които са извикали него. Ако търсенето отново пропадне, се търси следващия try-catch блок и се проверяват неговите филтри дали улавят възникналото изключение. Търсенето продължава докато се наме­ри първият подходящ обработчик на възникналото изключение или се установи, че няма изобщо такъв.

Търсенето може да обходи целия стек на извикване на методите и да не успее да намери catch блок, който да обработи изключението. В такъв случай изключението се обработва от CLR (появява се съобщение за грешка).

Прихващане на изключения – пример


Нека разгледаме един прост пример:

static void Main()

{

string s = Console.ReadLine();


try

{

Int32.Parse(s);



Console.WriteLine("You entered valid Int32 number {0}", s);

}

catch (FormatException)



{

Console.WriteLine("Invalid integer number!");

}

catch (OverflowException)



{

Console.WriteLine("Number too big to fit in Int32!");

}

}


В този пример програма очаква да се въведе цяло число. Ако потребите­лят въведе нещо различно, ще възникне изключение.

Извикването на метода Int32.Parse(s) може да предизвика различни изключения и затова е поставено в try блок, към който са асоциирани няколко catch блока.

Ако вместо число се подаде някаква произволна комбинация от символи, при извикването на метода Int32.Parse(s) ще възникне изключението System.FormatException, което ще бъде прихванато и обработено от първия catch блок.

Ако потребителят въведе число, по-голямо от максималната стойност за типа System.Int32, при извикването на Int32.Parse(s) ще възникне System.OverflowException, чиято обработка се извършва от втория catch блок.

Всеки catch блок е подобен на метод който приема точно един аргумент от определен тип изключение. Този аргумент може да бъде зададен само с типа на изключението, както е в по-горния пример, а може да се зададе и променлива:

catch (OverflowException ex)

{

// Handle the caught exception



}

Тук посредством от променливата еx, която е инстанция на класа System.OverflowException, можем да извлечем допълнителна информа­ция за възникналото изключение.

Прихващане на изключения на нива – пример


Нека сега разгледаме един по-сложен пример за прихващане на изключе­ния – прихващане на изключения на няколко нива:

static void Main()

{

try



{

int result = Calc(100000, 100000, 1);

Console.WriteLine(result);

}

catch (ArithmeticException)



{

Console.WriteLine("Calculation failed!");

}

}
static int Calc(int a, int b, int c)



{

int result;

try

{

checked



{

result = a*b/c;

}

}

catch (DivideByZeroException)



{

result = -1;

}

return result;



}

В този пример изключенията се прихващат на 2 нива – в try-catch блок в метода Calc(…) и в try-catch блок в метода Main(), извикващ Calc(…).

Ако методът Calc(…) бъде извикан с параметри (0, 0, 0), ще се получи деление на 0 и изключението DivideByZeroException ще бъде прихванато и обработено в try-catch блока на Calc(…) метода и съответно ще се получи стойност -1.

Ако, обаче, методът Calc(…) бъде извикан с параметри (100000, 100000, 1), ще се получи препълване на типа int, което в checked блок ще предизвика ArithmeticOverflowException. Това изключение няма да бъде хванато от catch филтъра в Calc(…) метода и CLR ще провери следващия catch филтър. Това е try-catch блокът в метода Main(), от който е извикан методът Calc(…). CLR ще открие в него е подходящ обработчик за изключението (catch филтърът за класа ArithmeticException, на който класът ArithmeticOverflowException е наследник) и ще го изпълни. Резултатът ще е отпе­чатване на съобщението "Calculcation failed!".

Възможно е по някаква причина в Calc(…) метода да възникне изключение, което не е наследник на ArithmeticException (например OutOfMemoryException). В такъв случай то няма да бъде прихванато от никой от catch филтрите и ще се обработи от CLR.


1   ...   31   32   33   34   35   36   37   38   ...   73


База данных защищена авторским правом ©obuch.info 2016
отнасят до администрацията

    Начална страница