// Вызвать исключение выхода за пределы диапазона аргумента.

  myCar.Accelerate(-10);

}

catch(Exception e)

{

  // Обработать все остальные исключения?

  Console.WriteLine(e.Message);

}

catch (CarIsDeadException e)

{

  Console.WriteLine(e.Message);

}

catch (ArgumentOutOfRangeException e)

{

  Console.WriteLine(e.Message);

}

Console.ReadLine;

Представленная выше логика обработки исключений приводит к возникновению ошибок на этапе компиляции. Проблема в том, что первый блок catch способен обрабатывать любые исключения, производные от System.Exception (с учетом отношения "является"), в том числе CarIsDeadException и ArgumentOutOfRangeException. Следовательно, два последних блока catch в принципе недостижимы!

Запомните эмпирическое правило: блоки catch должны быть структурированы так, чтобы первый catch перехватывал наиболее специфическое исключение (т.е. производный тип, расположенный ниже всех в цепочке наследования типов исключений), а последний catch — самое общее исключение (т.е. базовый класс имеющейся цепочки наследования: System.Exception в данном случае).

Таким образом, если вы хотите определить блок catch, который будет обрабатывать любые исключения помимо CarIsDeadException и ArgumentOutOfRangeException, то можно было бы написать следующий код:

// Этот код скомпилируется без проблем.

Console.WriteLine("***** Handling Multiple Exceptions *****\n");

Car myCar = new Car("Rusty", 90);

try

{

  // Вызвать исключение выхода за пределы диапазона аргумента.

  myCar.Accelerate(-10);

}

catch (CarIsDeadException e)

{

  Console.WriteLine(e.Message);

}

catch (ArgumentOutOfRangeException e)

{

  Console.WriteLine(e.Message);

}

// Этот блок будет перехватывать все остальные исключения.

// помимо CarIsDeadException и ArgumentOutOfRangeException

catch (Exception e)

{

  Console.WriteLine(e.Message);

}

Console.ReadLine;

На заметку! Везде, где только возможно, отдавайте предпочтение перехвату специфичных классов исключений, а не общего класса System.Exception. Хотя может показаться, что это упрощает жизнь в краткосрочной перспективе (поскольку охватывает все исключения, которые пока не беспокоят), в долгосрочной перспективе могут возникать странные аварийные отказы во время выполнения, т.к. в коде не была предусмотрена непосредственная обработка более серьезной ошибки. Не забывайте, что финальный блок catch, который работает с System.Exception, на самом деле имеет тенденцию быть чрезвычайно общим.

<p id="AutBody_Root313">Общие операторы catch</p>

В языке C# также поддерживается "общий" контекст catch, который не получает явно объект исключения, сгенерированный заданным членом:

// Общий оператор catch.

Console.WriteLine("***** Handling Multiple Exceptions *****\n");

Car myCar = new Car("Rusty", 90);

try

{

  myCar.Accelerate(90);

}

catch

{

  Console.WriteLine("Something bad happened...");

                  // Произошло что-то плохое...

}

Console.ReadLine;

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

<p id="AutBody_Root314">Повторная генерация исключений</p>

Внутри логики блока try перехваченное исключение разрешено повторно сгенерировать для передачи вверх по стеку вызовов предшествующему вызывающему коду. Для этого просто используется ключевое слово throw в блоке catch. В итоге исключение передается вверх по цепочке вызовов, что может оказаться полезным, если блок catch способен обработать текущую ошибку только частично:

// Передача ответственности.

...

try

{

Перейти на страницу:

Похожие книги