Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

326

Г л а в а 1 6

 

 

ется за пределы блока контроля (его третий оператор пропускается). Ловушка (обработчик) исключений с заголовком catch (FormatException) настроена на обработку исключений типа FormatException. В теле обработчика два оператора. Первый из них выдает на консоль сообщение, второй передает управление на начало следующей итерации цикла. Цикл не будет завершен, пока пользователь не введет значение x без ошибок. Диалог пользователя с программой может быть, например, таким:

x = 3.5<ENTER>

Ошибка в формате данных! x = 3d5<ENTER>

Ошибка в формате данных! x = 3,5<ENTER>

res = 3,5

Обратите внимание, что в блоке контроля за исключениями оператор Console.WriteLine("res="+x); выполняется только один раз.

Порядок действий по контролю за возникновением исключений и их обработкой следующий. Выполняются операторы блока контроля за исключениями. Если исключений не возникло, то все catch-обработчики пропускаются. При возникновении исключения управление незамедлительно передаётся catch–блокам. Обработчики исключений просматриваются последовательно до тех пор, пока не будет обнаружен обработчик, “настроенный” на переданное исключение. После выполнения операторов этого обработчика выполняется блок завершения (если он есть) и только затем заканчивается исполнение try-оператора. Отметим, что среди блоков обработки выполняется только один или не выполняется ни один.

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

Исключения

327

 

 

16.3. Свойства исключений

Каждое исключение – это объект либо класса System. Exception, либо производного от него. В классе Exception есть свойства, которые можно использовать при обработке исключений. Вот два из них:

●●Message – текстовое описание ситуации, при которой создано исключение;

●●Source – имя объекта или приложения, пославшего исключение.

Прежде чем привести пример использования этих свойств, ещё раз обратимся к предыдущей программе, где использована catch-инструкция для FormatException. Если пользователь введет синтаксически правильное числовое значение, которое выходит из диапазона представимых в системе чисел, то возникает ситуация, при которой будет сгенерировано исключение, отличное от FormatException. Например, в результате ввода:

x = 1e999<ENTER>

произойдет аварийное завершение программы с такой выдачей сообщения о необработанном исключении:

Необработанное исключение: System.OverflowException:

Значение было недопустимо малым или недопустимо большим для Double.

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

catch (OverflowException)

Более общим решением является дополнение программы catch–инструкцией, настроенной на перехват всех исключений типа ArithmeticException, относящихся к обработке арифметических данных.

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

328

Г л а в а 1 6

 

 

try

{

Console.Write("x = ");

x = double.Parse(Console.ReadLine()); Console.WriteLine("res = " + x);

}

catch (FormatException ex)

{

Console.WriteLine("ex.Message=" + ex.Message); Console.WriteLine("ex.Source=" + ex.Source); continue;

}

catch (ArithmeticException ex)

{

Console.WriteLine("ex.Message=" + ex.Message); Console.WriteLine("ex.Source=" + ex.Source); continue;

}

После блока try два обработчика. Второй перехватывает исключения типа ArithmeticException, к которым относится и OverflowException. В каждом из обработчиков выводятся значения свойств Message и Source, а затем управление с помощью операторов continue передается следующей итерации цикла. Результаты выполнения программы могут быть такими:

x = 1e999<ENTER>

ex.Message=Значение было недопустимо малым или недопустимо большим для Double. ex.Source=mscorlib

x = qwer<ENTER>

ex.Message=Входная строка имела неверный формат. ex.Source=mscorlib

x = 4.0<ENTER>

ex.Message=Входная строка имела неверный формат. ex.Source=mscorlib

x = 4,0<ENTER> res = 4

Обратите внимание, что сообщения (значения свойства Message) различны для разных типов исключений. Источник генерации исключений во всех примерах один – базовая библиотека Microsoft (mscorlib).

Исключения

329

 

 

Примечание: При анализе исключения в catch-блоке полезно выводить значение выражения ex.ToString(). В нем содержится информация как о самом исключении, так и о точке его генерации в коде программы.

16.4. Управление программой

спомощью исключений

Вкачестве примера применения механизма исключений не для исправления ошибок ввода, а для управления программой, рассмотрим обработку исключения System.IndexOutRangeException. Это исключение посылается при попытке обратиться

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

Предварительно определим статический метод varyArray(), позволяющий изменять размер одномерного массива по следующим правилам. Параметры метода: ar – ссылка на исходный массив и размер int newSize нового массива, который будет сформирован методом. Если значение параметра newSize меньше длины исходного массива, то в получаемый массив копируются только newSize первых элементов исходного массива. Если newSize больше длины исходного массива, то значения всех элементов исходного массива присваиваются первым newSize элементам формируемого массива. Остальные элементы (как мы знаем) по умолчанию инициализируются нулевыми значениями. Код метода:

static int[] varyArray(int[] ar, int newSize) { int [] temp = new int [newSize];

Array.Copy(ar, temp, newSize<ar.Length?newSize:ar. Length);

return temp;

}

330

Г л а в а 1 6

 

 

Этот закрытый метод будем использовать вначале для организации “роста” массива, а затем для удаления из массива незаполненных при вводе правых элементов.

У приведенного в следующей программе метода с заголовком public static int[] arrayRead() параметров нет. Для него исходными данными служат значения (целые числа), которые пользователь вводит с клавиатуры. Конец ввода – ввод нулевого числа. Текст программы с указанными методами:

//16_04.cs – растущий массив и исключения. using System;

class Method {

//Изменить размер массива до значения newSize: static int[] varyArray(int[] ar, int newSize) {

int [] temp = new int [newSize];

Array.Copy(ar, temp, newSize<ar.Length?newSize:ar. Length);

return temp;

}

//Читать числа в растущий массив:

public static int[] arrayRead() {

int k=0,

 

// Количество прочитанных чисел

dimAr=2,

// Текущий размер массива

x;

// Вводимое число.

int [] row = new int[dimAr];

while (true)

{

do Console.Write(«x = «);

while (!int.TryParse(Console.ReadLine(), out x)); if (x == 0) break;

try {

row[k] = x; k++;

}

catch(IndexOutOfRangeException)

{dimAr *= 2;

row = Method.varyArray(row, dimAr); row[k++] = x;

}

} //end while

row = Method.varyArray(row, k); return row;

}

Исключения

331

 

 

}

class Program { static void Main() {

int [] res = Method.arrayRead(); foreach (int memb in res)

Console.Write(memb+” “);

}

}

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

x = 1<ENTER> x = 2<ENTER> x = 3<ENTER> x = 4<ENTER> x = 5<ENTER> x = 0<ENTER> 1 2 3 4 5

Вметоде arrayRead() объявлены несколько переменных: k

счетчик прочитанных и сохраненных в создаваемом массиве ненулевых чисел; dimAr – текущий размер массива, который вначале равен 2; x – переменная, которой присваивается введенное пользователем числовое значение; row – ссылка на одномерный целочисленный массив, элементам которого присваиваются вводимые числовые значения.

Ввод чисел и сохранение их в массиве, адресованном ссылкой row, выполняется в “бесконечном” цикле с заголовком while(true). В его теле вспомогательный цикл с постусловием (такой цикл мы уже неоднократно применяли в программах) обеспечивает корректный ввод значения x. Если х после ввода оказывается равным нулю, то оператор break; прекращает выполнение “бесконечного” цикла. Для ненулевого значения х делается попытка присваивания row[k]=x. Если k<dimAr, то присваивание выполняется благополучно, значение k увеличивается на 1 и следует переход к новой итерации цикла – читается новое значение х. Как только k достигает значения dimAr (а вначале эта величина равна 2), выполнение row[k]=x становится невозможным и возникает исключение типа IndexOutRangeException. Управление передается за пределы контролируемого блока – оператор k++; не выполняется.

332

Г л а в а 1 6

 

 

Посланное исключение перехватывает соответствующий ему catch-обработчик. В его теле удваивается значение переменной dimAr, и переменной row присваивается ссылка на новый увеличенный вдвое массив, первые элементы которого получают значения уже введенные с клавиатуры. Далее в этот массив заносится значение х, и увеличивается на 1 счетчик обработанных чисел, что обеспечивает оператор row[k++]=x;. Блок try и соответствующий ему обработчик catch целиком размещены в конце цикла, поэтому следует переход к его очередной итерации.

При выходе из цикла (когда х получит нулевое значение) следует обращение к методу varyArray(row, k). При этом k равно реальному количеству значений, присвоенных первым k элементам массива, адресованного ссылкой row. Формируемый этим методом массив будет содержать только реально введенные пользователем ненулевые числа. “Лишних” элементов в хвосте созданного методом arrayRead() массива не будет. Сказанное подтверждают результаты выполнения метода Main(), где оператор foreach выводит значения всех элементов созданного массива.

16.5. Исключения в арифметических выражениях

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

int x=111111, y=111111, z=0;

double a=x/0.0;

//Результат: "бесконечность"

double b=x/0;

//Ошибка компиляции

double c=x/z;

//Исключение DivideByZeroException

double d=x*y;

//Результат: -539247567

Как отмечено в комментариях, все приведенные выражения приводят к возникновению особых ситуаций. В первых трех случаях программист по сообщениям компилятора или по результатам выполнения программы может явно распознать ситуацию. Значением переменной a=x/0.0 является беско-

Исключения

333

 

 

нечно большая величина. В случае b=x/0 компилятор выдает сообщение о попытке целочисленного деления на нулевую константу. Выражение c=x/z не смущает компилятор, но приводит к генерации исключения System.DivideByZeroException на этапе выполнения программы. В случае вычисления x*y происходит целочисленное переполнение, но никаких признаков особой ситуации нет. Правда, переменной d присваивается некоторое отрицательное значение после умножения двух положительных величин. В нашем простом примере этот результат может служить сигналом об особой ситуации. Однако в сложных арифметических выражениях целочисленное переполнение может остаться незамеченным, но приведет к неверному результату.

Для отслеживания таких ошибок в арифметических выражениях следует использовать служебное слово checked. Это слово играет две роли, оно обозначает операцию и вводит специальный блок “наблюдения” за переполнениями при вычислениях выражений:

checked (выражение) checked {операторы}

В первом случае отслеживаются возникновения переполнений в заключенном в скобки выражении. Во втором случае контролируются переполнения во всех операторах блока. В обоих случаях при возникновении переполнения генерируется исключение System.OverflowException.

Наш пример можно дополнить оператором

double е= checked(x*y);

В этом случае переполнение при вычислении выражения x*y не будет игнорироваться, а приведет к генерации названного исключения. Обработка такого исключения может быть организована обычным образом с помощью блока try/catch.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]