14. Исключительные ситуации
Нарушение в работе программы (не корректное действие) называется исключением. Исключение останавливает текущий поток программы и если никакие меры не предпринимаются, программа просто прекращает выполнение.
Причиной исключений могут быть ошибки в программе (например, деление числа на ноль) или неожиданный ввод (например, выбор несуществующего файла, или вместо цифры для выполнения арифметических действий – буква).
Задачей пользователя является предоставление программе возможности устранить проблемы, не приводя к сбою.
Обработку исключений может выполнять
– системный обработчик исключений или
– написанный пользователем программный код, который выводит сообщение.
Для обработки исключений используются глобальные и локальные обработчики исключений.
Глобальные обработчики – стандартные предусмотрены операционной системой и вызываются автоматически, если отладчик подключен. Обработчик завершает выполнение программы, выдавая информацию о возникновении исключения. Он не всегда понятен пользователю, не говоря уже о досрочном завершении программы.
|
|
Локальные обработчики создает сам пользователь в виде сообщений. Программист может перехватить исключительные ситуации и сделать их обработку, после чего продолжится нормальный ход вычислений приложения.
Обработка локальных исключений в C# реализована с помощью ключевых слов, — try, catch и finally — с помощью которых программа обнаруживает исключения, устраняет их и продолжает выполнение. Они способствуют повышению надежности приложений.
14.1. Виды исключений:
Исключения имеют типы, являющиеся производными от System.Exception. Некоторые исключения, генерируемые при компиляции:
Тип_Исключения | Описание |
ArithmeticException | Основной класс исключений, происходящих при выполнении арифметических операций, таких как DivideByZeroException и OverflowException. |
ArrayTypeMismatchException | Создается, когда тип сохраняемого значения несовместим с фактическим типом массива. |
DivideByZeroException | Создается при попытке разделить целое число на ноль. |
IndexOutOfRangeException | Создается при попытке индексирования массива, если индекс меньше нуля или выходит за границы массива. |
InvalidCastException | Создается, когда неверно выполнено динамическое приведение типов. |
NullReferenceException | Создается при попытке ссылки на объект, значение которого равно null. |
OutOfMemoryException | Создается при неудаче попытки выделения памяти с помощью оператора new. Это означает, что память, доступная для среды выполнения, уже исчерпана. |
OverflowException | Создается при переполнении арифметической операции. |
StackOverflowException | Создается, когда стековая память переполнена. |
14.2.Существуют два типа блоков:
|
|
I. Try … Catch // попробовать исключить
II. Try … Catch … Finally // попробовать исключить в финал
I. Try, Catch
Ключевые слова try и catch используются вместе как составной оператор. Для таких блоков характерна вложенность. Если предполагается, что блок кода может вызвать исключение, используют ключевое слово try и catch (параметр), чтобы сохранить код, который будет выполнен при возникновении исключения.
Блок, которому предшествует ключевое слово try, называется охраняемым (контролируемым) блоком или try-блоком. Блок, которому предшествует конструкция catch (параметр), называется блоком перехватчиком исключения или catch-блоком.
При отсутствии блоков try и catch произойдет сбой программы.
Синтаксис инструкции:
try
{
// Блок кода, подлежащий проверки на наличие ошибок
}
catch (<Тип_Исключения1>)
{
// Код обработчика исключения (<Тип_Исключения1>)
}
catch (<Тип_Исключения2>)
{
// Код обработчика исключения (<Тип_Исключения2>)
}…..
С try -блоком может быть связана не одна, а несколько catch -инструкций. Какая из них будет выполнена, определит тип исключения. Будет выполнена та catch -инструкция, тип исключения которой совпадает с типом сгенерированного исключения (а все остальные будут проигнорированы).
Если исключение не сгенерируется, то try -блок завершается нормально, и все его catch -инструкции игнорируются. Выполнение программы продолжается с первой инструкции, которая стоит после последней инструкции catch. Таким образом, catch -инструкция выполняется только в случае, если сгенерировано соответствующее исключение.
Пример1, в котором в результате деления на ноль создается исключение, которое затем перехватывается. В программе генерируется исключение типа DivideByZeroException – деление на 0
int x = 0, y = 0; // знаменатель равен 0
try
{
x = 10 / y; // Проверяемая инструкция, возможно деление на 0
Console.WriteLine("Ответ x= "+x);
}
catch (DivideByZeroException) // Обработчик исключения
{
Console.WriteLine("Попытка деления на 0.");
}
Блоки try включают операторы программы, которые могут вызвать исключительную ситуацию, например y=0.
Теперь заменим y=0 на y=2:
{
int x = 0, y = 2; // знаменатель равен, например 2 (отличен от 0)
try
{
x = 10 / y; // Проверяемая инструкция, возможно деление на 0
Console.WriteLine("Ответ x= "+x);
}
catch (DivideByZeroException) // Обработчик исключения
{
Console.WriteLine("Попытка деления на 0.");
}
}
Пример2, в котором происходит индексация массива и попытка индексировать массив за пределами его границ, вызывает ошибку нарушения диапазона.
static void Main(string[] args)
{
int[] N = new int[4]; // массив для 4-х элементов
try
{
Console.WriteLine("Перед генерированием исключения.");
// связанное с попаданием индекса вне диапазона
for (int i = 0; i < 10; i++) //в цикле индексируется массив от 0 до 9
{
N[i] = i;
Console.WriteLine("N[{0}]:{1}", i, N[i]);
}
Console.WriteLine("Этот текст не отображается");
}
catch (IndexOutOfRangeException)
{
//Перехватываем исключение.
Console.WriteLine("Индекс вне диапазона");
}
Console.WriteLine("После catch-инструкции");
Console.ReadLine();
}
В программе намеренно генерируется исключение типа IndexOutOfRangeException, а затем это исключение перехватывается.
В программе объявляется массив для 4-х элементов, а в цикле делается попытка индексировать этот массива от 0 до 9. Как только значение индекса устанавливается равным четырем, генерируется исключение типа IndexOutOfRangeException.
Ключевые аспекты обработки исключений:
1. проверяемый код содержится внутри try-блока,
2. при возникновении исключения выполнение try-блока прекращается, а само исключение перехватывается catch инструкцией,
3. catch инструкция не вызывается, а ей передается управление программой.
4. после выполнения catch инструкции программа продолжится со следующей инструкции.
|
|
Если try-блоком исключение не сгенерировано, ни одна из catch инструкций не выполняется и управление программой будет передано инструкции, следующей за catch инструкций.
Если заменить в цикле for (int i = 0; i < 10; i++) верхний индекс на
for (int i = 0; i < N.Length; i++), то границы индексирования массива не нарушаются. Поэтому исключение не генерируется и catch-блок не выполняется.
Если не создать локального обработчика исключения, то C# система динамического управления перехватит исключение, сообщит об ошибке и завершит программу.
С try-блоком можно связать несколько catch инструкций. Все catch инструкции должны перехватывать исключения различного типа.
Пример3. Программа перехватывает как ошибку нарушения границ массива, так и ошибку деления на нуль.
static void Main(string[] args)
{
int[] N = {4,8,16,32,64,128,256,512}; // массив из 8 элементов
int[] D = {2, 0, 4, 4, 0, 8}; // массив из 6 элементов
for (int i = 0; i < N.Length; i++) //в цикле по всем элементам
try
{
Console.WriteLine(N[i] + " / "+ D[i] + " = " + N[i]/D[i]);
}
catch (DivideByZeroException)
{
//Перехватываем исключение.
Console.WriteLine("На нуль делить нельзя!");
}
catch (IndexOutOfRangeException)
{
//Перехватываем исключение.
Console.WriteLine("Нет соответствующего элемента.");
}
Console.ReadLine();
}
Каждая catch инструкция реагирует только на собственный тип исключения.
II. Try, Catch, Finally
Иногда возникает потребность определить программный блок, который должен выполняться по выходу из try/catch блока. Исключение может вызвать ошибку, которая является причиной преждевременного возврата из текущего метода. Удобный путь выхода из этого – блок finally.
Код, содержащийся в блоке finally, выполняется всегда, вне зависимости от возникновения исключения. Чтобы гарантировать возвращение ресурсов, например, убедиться, что файл закрыт, или освободить память от локальных переменных.
Синтаксис инструкции:
class ProgramTryCatchFinally
{
static void Main()
{
try
{
// Блок кода, предназначенный для обработки ошибок
}
catch (<Тип_Исключения1>)
{
// Обработчик для исключения (<Тип_Исключения1>).
}
catch (<Тип_Исключения2>)
{
// Обработчик для исключения (<Тип_Исключения2>).
|
|
}
…..
finally
{
// Код завершения обработки исключений.
}
Не зависимо от итога выполнения try/catch блоков, блок finally выполняется обязательно.
Задача. Используя закон Ома рассчитать величину тока. Проект оконное приложение. Сначала создается интерфейс будущего приложения, а затем обработчик события щелчка по кнопке «Вычислить».
Листинг программы
private void button1_Click(object sender, EventArgs e)
{
try
{
int U = int.Parse(textBox1.Text); // преобразование строкового
int R = int.Parse(textBox2.Text); // отображения в целое
double I = (double)U / R;
label3.Text = I.ToString()+ " A"; // преобразование числа в строку
}
catch
{
label3.Text = "Нельзя вводить буквы, символы и 0";
}
Форматы ввода/вывода под Оконное приложение
int а = int.Parse(textBox1.Text); //преобразование текста, введенного
// в textBox1 в целое число
double b = double.Parse(textBox1.Text); //преобразование текста, введенного
// в textBox1 в вещественное число
label3.Text = S.ToString()+ " руб."; // преобразование числа S в строку
label1.Text =String.Format(“{0:f 3}”,x); // преобразование числа x в строку c форматным выводом символов в компонент label1 через его свойство Text,
где 0 – индекс переменной, f – мантисса, 3 – количество символов после зпт.
label1.Text =String.Format(“{0,5:f 2}”,x); // преобразование числа x в строку c форматным выводом символов в компонент label1 через его свойство Text,
где 0 – индекс переменной, 5 – количество выводимых символов, f – мантисса, 2 – количество символов после зпт.