ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3209

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

чение в виде объекта Exception и передает его с помощью оператора throw вверх цепочке вызовов в первый же блок, который в состоянии перехватить его и обработан!

Иногда обработчик try/catch располагается в той же функции, в которой нерировано исключение. Первая же функция, владеющая достаточным количес вом контекстуальной информации для выполнения действий по его обработке может перехватить и обработать его; если данная функция не в состоянии этого сделать, исключение передается дальше вверх по цепочке вызовов.

В демонстрационной программе FactorialException приведены ключевые элементы механизма исключений.

//FactorialException - создание функции вычисления

//факториала, которая сообщает о некорректном аргументе с

//использованием исключений

using System;

namespace FactorialException

{

//MyMathFunctions - набор созданных мною математических

//функций

public class MyMathFunctions

{

//Factorial - возвращает факториал переданного

//аргумента

public static double Factorial(int nValue)

{

// Проверка: отрицательные значения запрещены if (nValue < 0)

{

// Сообщение об отрицательном аргументе string s = String.Format(

"Отрицательный аргумент в вызове Factorial { о } " , nValue);

throw new Exception(s); // Генерация исключения...

}

//начинаем со значения аккумулятора,

//равного 1

double dFactorial = 1.0;

//Цикл со счетчиком nValue, уменьшающимся до 1, с

//умножением на каждой итерации значения аккумулятора

//на величину счетчика

do

{

dFactorial *= nValue,- } while(--nValue > 1);

// Возвращаем вычисленное значение return dFactorial;

402

Часть VII. Дополнительные главн


public class Program

{

public static void Main(string[] args)

{

try // Исключения от функции Factorial() "всплывут" до // этого блока

{

//Вызов функции вычисления факториала в

//цикле от 6 до -6

for (int i = 6; i > -6; i--)

{

// Вычисление факториала

double dFactorial = MyMathFunctions.Factorial(i); // Вывод результата на каждой итерации Console.WriteLine("i = { о } , факториал = {l}",

i, MyMathFunctions.Factorial(i));

catch(Exception e) // ... перехват исключения

{

Console.WriteLine("Ошибка:");

Console.WriteLine(e.ToString());

}

// Ожидаем подтверждения пользователя Console.WriteLine("Нажмите <Enter> для " +

"завершения программы...");

Console.Read();

Эта "исключительная" версия функции Main () практически полностью находится в try-блоке.

Всегда помещайте содержимое функции Main () в try-блок, поскольку функ­ ция Main () — начальная и конечная точка программы. Любое исключение, не перехваченное где-то в другом месте, будет передано функции Main ( ) . Это последняя возможность перехватить исключение перед тем, как оно попадет прямо в Windows, где это сообщение об ошибке будет гораздо сложнее интер­ претировать.

Блок catch в конце функции Main () перехватывает объект Exception и использует его метод ToString () для вывода информации об ошибке, содержащейся в этом объекте в виде строки.

Более консервативное свойство Exception.Message возвращает более удо­ бочитаемую, но менее информативную информацию по сравнению с предос­ тавляемой методом е . ToString ().

Эта версия функции Factorial () включает ту же проверку на отрицательность пе­ реданного аргумента, что и предыдущая (для экономии места в ней опущена проверка того, что аргумент — целое число). Если аргумент отрицателен, функция Factorial ()

403


форматирует сообщение об ошибке с описанием ситуации, включая само отрицательное значение, вызвавшее ошибку. Затем функция Factorial () вносит информацию в вновь создаваемый объект типа Exception, который передается с помощью механизма исключений вызывающей функции.

Вывод этой программы выглядит следующим образом:

i

=

6,

факториал

=

720

i

=

5,

факториал

=

120

i

=

4,

факториал

=

24

i

=

3,

факториал

=

6

i

=

2,

факториал

=

2

i

=

1,

факториал

=

1

i = 0, факториал = 0 Ошибка:

System.Exception: Отрицательный аргумент в вызове Factorial -1

at

Factorial(Int32 nValue) in

 

 

с:\c#programs\Factorial\Program.cs:line

21

at

FactorialException.Program.Main(String[]

args) in

 

с:\c#programs\Factorial\Program.cs:line

49

Нажмите <Enter> для завершения программы...

В первых нескольких строках выводятся корректно вычисленные факториалы число от 6 до 09. Попытка вычислить факториал -1 приводит к генерации исключения.

В первой строке сообщения об ошибке выводится информация, сгенерированная в функции Factorial ( ) . Эта строка описывает природу ошибки, включая вызвавшее

неприятности значение аргумента

 

1.

 

В оставшейся части вывода выполняется трассировка стека. В первой строке указы вается, в какой функции сгенерировано исключение. В данном случае это было сделано в функции Factorial (int) — а именно в 21 строке исходного файла Program.cs Функция Factorial () была вызвана из функции Main (string [] ) в строке 49 того же файла. На этом трассировка файла прекращается, поскольку функция Main () содер­ жит блок, перехвативший и обработавший указанное исключение.

Трассировка стека доступна в одном из окон отладчика Visual Studio.

Вы должны согласиться, что это весьма впечатляюще. Сообщение об ошибке описы­ вает случившееся и позволяет указать аргумент, приведший к ней. Трассировка стека полностью отслеживает, где именно и в результате какой последовательности вызовов произошла ошибка. При такой диагностике поиск ошибки и ее причины не должен со­ ставить никакого труда.

Получение подробной информации, такой как трассировка стека, удобно в про цессе разработки и отладки, но при выходе программы к конечным пользовате­ лям вам наверняка захочется, чтобы она выдавала более простые и понятные со­ общения. (При этом все равно останется возможность записи трассировки стека в журнальный файл, чтобы вы могли владеть всей необходимой информацией, когда пользователь обратится к вам за помощью.)

9 Еще раз напомним читателю, что в математике принято считать, что 0! = 1. — Примеч. ред.

404

Часть VII. Дополнительные главы


Стандартный класс исключения Exception, предоставляемый библиотекой С#, в состоянии предоставить вам достаточное количество информации. Вы можете запро­ сить объект исключения о том, где он был сгенерирован, какая строка была передана ему генерирующей функцией. Однако в ряде случаев стандартного класса Exception быва­ ет недостаточно. У вас может оказаться слишком много информации, чтобы разместить ее в одной строке. Например, функция приложения может захотеть передать вызвавший проблемы объект для последующего анализа. Изучение этого объекта может быть полез­ ным вплоть до полного восстановления после происшедшей ошибки.

Локально определенный класс может наследовать класс Exception так же, как и любой другой класс. Однако пользовательский класс исключения должен наследовать не непосредственно класс Exception, а класс ApplicationException, являющийся подклассом Exception, как показано в следующем фрагменте исходного текста:

//CustomException - добавление ссылки на MyClass к

//стандартному классу исключения

public class CustomException : ApplicationException

{

private MyClass myobject; // Хранит ссылку на вызвавший // проблемы объект

CustomException(string sMsg, MyClass mo) : base(sMsg)

{

myobject = mo;

'}

//Предоставляет доступ к объекту, сохраненному в объекте

//исключения

public MyClass MyCustomObject{ get {return myobject;}}

}

Класс CustomException представляет собой самодельный класс для сообщения об ошибке в любой программе, работающей с классом MyClass. Этот подкласс класса Ap­ plicationException содержит такую же строку, как и исходный класс, но добавляет к ней ссылку на объект MyClass, вызвавший проблемы. Это позволяет произвести де­ тальное исследование случившегося.

В приведенном далее примере выполняется перехват исключения CustomExcep­ tion и используется информация об объекте MyClass:

public class Program

{

public void SomeFunction()

{

try

{

//... действия перед вызовом демонстрационной функции SomeOtherFunctionO ;

//... продолжение работы ...

}

catch(MyException me)

 

[лава{ 18. ЭТИ исключительные исключения

405


//Здесь у вас имеется доступ к методам Exception и

//ApplicationException

string s = me.ToStringО;

// Но у вас есть еще и доступ к методам, уникальным

//для вашего класса исключения MyClass mo = me.MyCustomObject;

//Например, вы можете запросить у объекта MyClass его

//собственное описание

string s = mo.GetDescription();

}

}

public void SomeOtherFunctionO

{

 

 

 

// Создание myobject

 

 

MyClass myobject

= new M y C l a s s 0 ;

 

// ... сообщение об ошибке с участием myobject ..

throw new MyException("Ошибка в объекте

MyClass",

 

myobj ect) ;

 

// ... Остальная

часть функции ...

 

}

 

 

 

}

 

 

 

В этом фрагменте кода

функция

SomeFunction ()

вызывает функцию SomeO-|

therFunction () из охватывающего

блока try. Функция SomeOtherFunction()

создает и использует объект myobject. Где-то в функции SomeOtherFunction() программа проверки ошибок подготавливает исключение к генерации для сообщения о происшедшей ошибке. Вместо создания простого объекта типа Exception или Ap­ plicationException, функция SomeFunction О применяет разработанный ваш тип MyExcept ion, пригодный не только для передачи текстового сообщения об ошиб­ ке, но и ссылки на вызвавший ее объект.

Блок catch в функции Main () указывает, что он предназначен для перехвата объ­ ектов MyExcept ion. После того как такой объект перехвачен, код приложения в со­ стоянии применить все методы Exception, как, например, метод ToString ( ) . Одна­ ко в этом catch-блоке может использоваться и другая информация, к примеру, вызов методов объекта MyClass, ссылка на который передана в объекте исключения.

Фрагмент кода в предыдущем разделе продемонстрировал генерацию и перехват ло­ кально определенного объекта исключения MyExcept ion. Рассмотрим еще раз конст­ рукцию catch из этого примера:

public void SomeFunction()

{

try

{

SomeOtherFunctionO ;

}

catch(MyException me)

{

}

}

406

Часть VII. Дополнительные главы

Главе