Примеры обработки исключений

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

Листинг 1

using System;

namespace ConsoleApplication

{

class OurClass

{

static void Main(string[] args)

{

float num1 = 1, num2 = 2, summarize, multiply, divide=0;

int k=0, l=0, d=0;

Console.Write("Введите первое число num1:");

try { num1 = float.Parse(Console.ReadLine()); }

catch

{

Console.WriteLine("Неправильный формат числа!\n" +

"В качестве значения первого числа будет 1");

}

Console.Write("Введите второе число num2:");

try { num2 = float.Parse(Console.ReadLine()); }

catch

{

Console.WriteLine("Неправильный формат числа!\n" +

"В качестве значения второго числа будет 2");

}

k = (int)num1;

l = (int)num2;

summarize = num1 + num2; multiply = num1 * num2; divide = num1 / num2; ;

Console.WriteLine("num1 + num2 = " + summarize);

Console.WriteLine("num1 * num2 = " + multiply);

Console.WriteLine("num1 / num2 = " + divide);

Console.WriteLine("Целые \nk= " + k + " l= " + l);

 

try { d = k / l; }

catch (DivideByZeroException)

{

Console.WriteLine("d=k/l= ?");

Console.WriteLine("Нельзя делить на нуль! ");

Console.WriteLine("В качестве результата операции деления будет использовано значение 0");

}

Console.WriteLine("d=k/l="+d);

}

}

}

Ниже приведены три варианта результатов работы программы, обрабатывающие различные ситуации. Вариант 1 демонстрирует обработку исключения, возникающего при неправильном вводе числа num2.

Результаты работы программы вариант 1:

Вариант 2 содержит выводимую информацию в случае, когда программа работает так, что не генерируется ни одно исключение и, следовательно, все обработчики исключений программы игнорируются.

Результаты работы программы вариант 2:

Третий вариант запуска программы демонстрирует перехват исключения DivideByZeroException, генерируемого в ответ на попытку деления целого числа на нуль.

Результаты работы программы вариант 3:

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

for (int i = 0; i < n+1; ++i),

где индекс i выходит за правую границу объявленного диапазона, поскольку изменение индекса массива предусмотрено только от 0 до 5. При попытке использования индекса, равного 6, генерируется исключение IndexOutOfRangeException. Для перехвата этого исключения сделаем этот цикл внутренним блоком оператора try, за которым поместим следующую catch-инструкцию:

catch (IndexOutOfRangeException)

{

Console.WriteLine("Выход индекса за пределы диапазона");

}

Данная catch-инструкция перехватывает указанное исключение и выводит необходимое сообщение.

Пример 2. Поиск наибольшего элемента в одномерном массиве.

Листинг 2 – Поиск максимального элемента в одномерном массиве

using System;

namespace ConsoleApplication5

{

class Program

{

static void Main(string[] args)

{

const int n = 6;

int[] a = new int[n] { 3, 12, 5, -9, 8, -4 };

 

Console.WriteLine("Исходный массив:");

for (int i = 0; i < n; ++i)

Console.WriteLine("\t" + a[i]);

Console.WriteLine();

int max = a[0];

try

{

for (int i = 0; i < n+1; ++i)

if (a[i] > max)

max = a[i];

}

catch (IndexOutOfRangeException)

{

Console.WriteLine("Выход индекса за пределы диапазона");

}

Console.WriteLine("Максимальный элемент = " + max);

Console.Read();

}

}

}

Результаты работы программы:

Как же будет работать программа, если перехват данного исключения в ней отсутствует? Ответ один: это исключение все же будет перехвачено, но уже системой исполнения программы языка, которая выдаст сообщение об ошибке и прекратит выполнение программы:

Оператор throw

Операторы try могут многократно вкладываться друг в друга. Исключение, которое возникло во внутреннем блоке try и не было перехвачено соответствующим блоком сatch, передается на верхний уровень, где продолжается поиск подходящего обработчика. Этот процесс называется распространением исключения.

Распространение исключений предоставляет программисту дополнительные возможности. Например, если на внутреннем уровне недостаточно информации для того, чтобы провести полную обработку ошибки, можно выполнить частичную обработку и сгенерировать исключение повторно, чтобы оно было обработано на верхнем уровне.

До сих пор рассматривались исключения, которые генерирует среда выполнения С#, но это может сделать и сам программист. Для генерации исключения используется оператор throw с параметром, определяющим вид исключения. Параметр должен быть объектом, порожденным от стандартного класса System.Ехсерtion. Этот объект используется для передачи информации об исключении его обработчику.

Оператор throw употребляется либо с параметром, либо без него:

throw [ выражение ];

Форма без параметра применяется только внутри блока саtch для повторной генерации исключения. Тип выражения, стоящего после throw, определяет тип исключения, например:

throw new DividеВуZегоЕхсерtion( ) ;

Здесь после слова throw записано выражение, создающее объект стандартного класса «ошибка при делении на 0» с помощью операции new. При генерации исключения выполнение текущего блока прекращается и происходит поиск соответствующего обработчика с передачей ему управления. Обработчик считается найденным, если тип объекта, указанного после throw, либо тот же, что задан в параметре саtch, либо является производным от него.

Рассмотрим пример, в котором использован оператор throw для генерации программой исключения DivideByZeroException.

Листинг 3 – Генерация исключения с помощью оператора throw

using System;

class Demo

{

public static void Main()

{

try

{

Console.WriteLine("Перед генерацией исключения DivideByZeroException");

throw new DivideByZeroException();

}

catch (DivideByZeroException)

{

Console.WriteLine("Произошел перехват исключения");

}

Console.WriteLine("Завершение программы");

}

}

Как видим, в программе вообще отсутствуют какие-либо данные и операции над ними. Тем не менее, в ней есть оператор try, блок которого содержит оператор throw, создающий самостоятельно с помощью ключевого слова new объект исключения DivideByZeroException. Это исключение перехватывается следующим за try оператором саtch.

Результат работы программы:

Если после оператора throw в блоке try попытаться выполнить такую инструкцию:

Console.WriteLine("После генерации исключения ");

то она не будет выполнена, а в списке ошибок появится сообщение «Обнаружен недостижимый код».

Дополнительные сведения

Класс Ехсерtion содержит несколько полезных свойств (табл. 2), с помощью которых можно получить информацию об исключении.

Таблица 2–Свойства класса System.Exception

Свойство Описание
НеlpLink URL файла справки с описанием ошибки
Меssage Текстовое описание ошибки. Устанавливается при создании объекта. Свойство доступно только для чтения
Source Имя объекта или приложения, которое сгенерировало ошибку
StackTrace Последовательность вызовов, которые привели к возникновению ошибки. Свойство доступно только для чтения
InnerException Содержит ссылку на исключение, послужившее причиной генерации текущего исключения
TargetSite Метод, выбросивший исключение

Операторы сhecked и unchecked.В языкеC# есть возможность управлять генерацией исключений, возникающих при переполнении в арифметических вычислениях, когда может быть получен такой результат, который выходит за границы диапазона типа данных, используемого в данных вычислениях. Рассмотрим такой фрагмент программы:

byte i,j;

i=100; j=100; i=i+10*j;

Ясно, что в результате его выполнения новое значение i не может быть представлено в типе byte.

Управление процессом генерации исключений в такой ситуации возможно с помощью ключевых слов:

- сhecked – проверка включена;

- unchecked – проверка отключена и, если возникнет переполнение, то оно будет проигнорировано; в этом случае значение результата будет усечено до размера целого типа.

Данные ключевые слова употребляются как операции, если они используются в выражениях, и как операторы, если они предваряют блок, например:

а = сhecked (i=i+10*j); / / проверка включена для выражения

checked

{ / / проверка включена для блока

i=i+10*j;

}

Если значение проверяемого выражения не представимо в описанным для него типе, то генерируется исключение OverflowException.

Ключевое слово unchecked также может быть использовано в двух формах. Первая форма используется для указания, что следует проигнорировать переполнение в одном выражении, а вторая – то же самое, только по отношению к блоку.

Явное задание статуса контроля переполнения для некоторых программ в подозрительных ситуациях весьма желательно, поскольку установки по умолчанию определяются настройкой соответствующей опции компилятора и окружением исполнения программы и могут отличаться от желаемых.

6 Вопросы для самоконтроля

1) Что такое «исключительная ситуация»?

2) Обработка какого типа исключений поддерживается в С#?

3) Каким образом обычная система реагирует на неправильные действия программы?

4) Каким образом С# позволяет в ряде случаев предотвратить аварийную остановку программы?

5) Дайте пояснения определениюстандартных исключений в С#.

6) Из какого встроенного класса должны быть выведены все классы исключений в С#?

7) Куда помещаются программные инструкции, которые нужно проконтролировать на предмет исключений?

8) С помощью какого блока выброшенное исключение может быть перехвачено программным путем и обработано соответствующим образом?

9) Какие блоки являются ядром обработки исключений?

10) Перечислите наиболее часто используемые исключения, определенные в пространстве имен System.

11) Приведите формат записи try/catch-блоков обработки исключений.

12) В каком случае создается обработчик "глобального перехвата", какой вид он имеет?

13) Приведите пример программы с обработкой некоторых исключений.

14) Для чего используется оператор throw? Приведите пример его использования.