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

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

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

Добавлен: 11.12.2025

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

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

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

А если функция SomeOtherFunction () сгенерирует простое исключение Excep­ tion или исключение еще какого-то типа, отличного от MyException? Это будет на­ поминать ситуацию, когда футбольный вратарь ловит баскетбольный мяч — мяч, ловить который он не научен. К счастью, С# позволяет программе определить несколько блоков catch, каждый из которых предназначен для различного типа исключений.

Блоки catch должны в этом случае следовать один за одним, без разрывов, в порядке от наиболее специализированных классов ко все более общим. С# проверяет каждый catch-блок, последовательно сравнивая сгенерированный объект с аргументами catchолоков, как показано в следующем фрагменте исходного текста:

public void SomeFunction ()

(

t r y

SomeOtherFunctionO ;

 

 

catch(MyException me)

//

Наиболее специализированный тип

{

//

исключения

// Здесь перехватываются все объекты MyException

}// Между этими catch-блоками могут находиться блоки с

//другими типами исключений

catch(Exception е)

// Наиболее общий тип исключения

//Все остальные неперехваченные исключения

//перехватываются в этом блоке

Если функция SomeOtherFunction () сгенерирует объект Exception, он минует блок catch (MyException), поскольку Exception не является типом MyExcep­ tion. Он будет перехвачен в следующем блоке — catch (Exception).

Любой класс, наследующий MyException, ЯВЛЯЕТСЯ MyException: class MySpecialException : MyException

{

// ... что-то там ...

}

В этом случае блок для MyException перехватит и объект MySpecialException. (Наследование всех пользовательских исключений от одного базового пользовательского исключения — неплохая мысль. Само базовое исключение наследуйте от ApplicationException.)

Всегда располагайте catch-блоки от наиболее специализированного к наибо­ лее общему. Никогда не размещайте более общий блок первым, как это сделано в приведенном фрагменте исходного текста:

public void SomeFunction ()

(

t r y

{

SomeOtherFunction();

}

Глава 18. Эти исключительные исключения

407


catch(Exception me) // Самый общий блок - это неверно!

{

 

 

 

//

Все объекты

MyException

будут перехвачены здесь

}

 

 

 

catch(MyException

е)

 

{

 

 

 

//

Сюда не доберется ни один объект - все они будут

//

перехвачены

более общим

блоком

}

}

Более общий блок отнимает объекты исключений у более специализированного блощ К счастью, компилятор в состоянии обнаружить такую ошибку и предупредить о ее наличии

Как исключения протекают сквозь пальцы

Что, если С#, пройдя все catch-блоки, так и не найдет подходящего? Или в вы» вающей функции вообще нет catch-блока? Что будет тогда?

Рассмотрим следующую простую цепочку вызовов функций:

//

MyException - демонстрация того, как можно создать

новый

//

класс исключения и как функция может перехватывать

только

//

те исключения, которые может обработать

 

using System; namespace MyException

{

//Вводим некоторый тип MyClass public class MyClass{}

//MyException - - добавляем ссылку на MyClass к

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

public class MyException : ApplicationException

private MyClass myobject;

public MyException(string sMsg, MyClass mo) : base(sMsg)

myobject = mo,-

}

// Дает внешним классам доступ к объекту

public MyClass MyCustomObject{ get {return myobject;}} public class Program

{

// fl - - перехватывает обобщенный объект исключения public void f1(bool bExceptionType)

try

{

f2(bExceptionType);

catch(Exception e)

{

408

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


Console.WriteLine("Перехват обобщенного " + "исключения в fl О ") ;

Console.WriteLine(е.Message);

}

}

// f2 - - готов к перехвату MyException public void f2(bool bExceptionType)

{

try

{

f3(bExceptionType);

}

catch(MyException{ me)

Console.WriteLine("Перехват MyException в f2()"); Console.WriteLine(me.Message);

}

}

// f3 - - He перехватывает никаких исключений public void f3(bool bExceptionType)

{ f4(bExceptionType);

}

// f4 - - генерация одного из двух типов исключений public void f4(bool bExceptionType)

{

// Работаем с некоторым локальным объектом MyClass mc = new MyClass О; if(bExceptionType)

{

//Произошла ошибка — генерируем объект исключения с

//объектом

throw new MyException("Генерация MyException " + "в f4()", mc) ;

}

throw new Exception("Обобщенное исключение в f4 () " );

}

public{ static void Main(string[] args)

// Сначала генерируем обобщенное исключение...

Console.WriteLine("Сначала генерируем " + "обобщенное исключение");

new ProgramO . fl (false) ;

// ... а теперь наше исключение Console.WriteLine("\пГенерируем исключение " +

"MyException"); new ProgramO .fl(true) ;

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

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

Console.Read();

Глава 18. Эти исключительные исключения

409


Функция Main () создает объект Program и тут же использует его для вызова мето да f 1 ( ) , который, в свою очередь, вызывает метод f 2 ( ) , который вызывает метод f 3 ( ) , вызывающий метод f 4 ( ) . Функция f 4 () выполняет сложную проверку ошибки которая выливается в генерацию либо исключения MyException, либо обобщенного исключения Exception, в зависимости от аргумента типа bool. Вначале сгенериро ванное исключение Exception передается в функцию f3 ( ) . Здесь С# не находит catch-блока, и управление передается вверх по цепочке в функцию f 2 ( ) , которая пе рехватывает исключения MyException и его наследников. Этот тип исключения не со ответствует обобщенному исключению Exception, и управление передается еще дальше вверх. Наконец, в функции f 1 () находится catch-блок, соответствующий сге нерированному исключению.

Второй вызов в функции Main () заставляет функцию f 4 () сгенерировать объект MyEx ception, который перехватывается в функции £2 ( ) . Это исключение не пересылается функцию f 1 ( ) , поскольку оно перехватывается и обрабатывается функцией f 2 ().

(Может ли функция Main () в действительности создать объект класса, содержащий объект класса, в котором содержится Main () — т.е. класса Program? Конечно, почему бы и нет? См. последний раздел главы 14, "Интерфейсы и структуры".)

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

Сначала генерируем обобщенное исключение Перехват обобщенного исключения в fl() Обобщенное исключение в f4()

'Генерируем исключение MyException Перехват MyException в f2() Генерация MyException в f4()

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

Функция наподобие f3 ( ) , не содержащая ни одного catch-блока, вовсе не ред кость. Можно сказать даже больше — такие функции встречаются гораздо чаще, чем функции с catch-блоками. Функция не должна перехватывать исключения, если она не готова их обработать. Должна ли некоторая математическая функция ComputeX() в которой вызывается функция Factorial () как часть вычислений, перехватывать исключение, которое может быть сгенерировано функцией Factorial () ? Функция ComputeXO может совершенно не представлять, откуда взялись неверные входные данные для функции Factorial О и что теперь делать. В этом случае функция ComputeX ( ) , конечно же, не должна содержать catch-блока и перехватывать генери­ руемые исключения.

Функция наподобие f 2 () перехватывает только один тип исключений. Она ожида­ ет только один определенный тип ошибки, который умеет обрабатывать. Например MyException может быть исключением, определенным для выдающейся библиотеки классов гениального автора, написанной, понятное дело, мной, и так и называющей­ с я — BrilliantLibrary. Функции, составляющие BrilliantLibrary, генери­ руют и перехватывают только исключения MyException.

Однако функции BrilliantLibrary могут также вызывать функции обычной стандартной библиотеки System. Функции BrilliantLibrary могут не знать, как следует обрабатывать обобщенные исключения System, в особенности если они вызва­ ны некорректными входными данными.

410

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


Если вы не знаете, что делать с исключением — лучше не делайте ничего: по­ звольте разобраться с ним вызывающей функции. Но будьте честны сами с со­ бой: не позволяйте исключению уйти только потому, что вы слишком заняты, чтобы писать обработчик.

Регенерация исключения

В ряде случаев метод не в состоянии полностью обработать ошибку, но не хочет пе­ редавать исключение вызывающей функции, не вложив свои "пять копеек" в его обра­ ботку. В таком случае catch-блок может частично выполнить обработку исключения,

азатем передать его дальше (вообще-то, не слишком привлекательная картина). Рассмотрим, например, метод F (), который открывает файл при входе в метод

снамерением закрыть его при выходе из метода. Где-то в середине работы F () вызы­ вается G (). Исключение, сгенерированное в G (), может привести к тому, что у F () не будет шансов закрыть этот файл, который так и останется открытым до полного за­ вершения программы. Идеальным решением было бы включение в F () catch-блока (или блока finally), который бы закрывал все открытые файлы. F() может пере­ дать исключение дальше после того, как закроет все необходимые файлы и выполнит прочие требуемые действия.

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

public void f 1 ()

{

t r y

{

f 2 () ;

}

// Перехват исключения...

catch(MyException me)

{

//... Частичная обработка исключения ...

Console .WriteLine ("Перехват MyException в fl ()")'';

//... Генерация нового исключения для передачи его

//вверх по цепочке вызовов

throw new Exception(

"Исключение, сгенерированное в fl()");

Генерация нового объекта исключения позволяет классу переформулировать сообщение об ошибке, добавив в него дополнительную информацию. Генера­ ция обобщенного объекта Exception вместо специализированного MyEx­ ception обеспечивает гарантированный перехват этого исключения на уров­ нях выше f 1 ().

Генерация нового исключения имеет тот недостаток, что трассировка стека при этом начинается заново, с точки генерации нового исключения. Источник исходной ошибки оказывается потерян, если только fit) не предпримет специальных мер для его сохранения.

(пава 18. Эти исключительные исключения

411