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

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

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

Добавлен: 11.12.2025

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

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

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

В оригинальном издании книги эти главы были помещены на прила­ гаемый к книге компакт-диск. В русском издании книги их решено опубликовать в виде отдельной, дополнительной части.

Глава 18

Эти исключительные исключения

> Обработка ошибок с помощью кодов ошибки

УИспользование механизма исключений вместо кодов ошибки

}Создание собственного класса исключения

}Перекрытие ключевых методов в классе исключения

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

и докладывала об ошибке.

Здесь говорится об ошибках времени выполнения, а не времени компиляции, с кото­ рыми С# разберется сам при сборке вашей программы.

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

Промолчать о том, что произошла ошибка времени выполнения — это всегда наи­ худшее решение, применимое только в том случае, если вы не намерены отлаживать программу и вас не интересует результат ее работы...

В приведенной далее демонстрационной программе FactorialWithEr- r o r показано, что может произойти, если не выявить ошибку. Эта програм­ ма вычисляет и выводит значение факториала для ряда значений.

Факториал числа N равен N*(N-l)*(N-2)*... *1. Например, факториал 4 равен 4*3*2*1 = 24. Функция вычисления факториала работает только для поло­ жительных целых чисел. Это банальный программистский пример для ил­ люстрации ситуации, когда требуется обработка ошибок.

//FactorialWithError - пример функции вычисления

//факториала, в которой отсутствует проверка ошибок


using System;

namespace FactorialWithError

{

//MyMathFunctions - набор созданных мною математических

//функций

public class MyMathFunctions

{

//Factorial - возвращает факториал переданного

//аргумента

public static double Factorial(double dValue)

{

//Начинаем со значения аккумулятора, равного 1 double dFactorial = 1.0;

//Цикл со счетчиком nValue, уменьшающимся до 1, с

//умножением на каждой итерации значения аккумулятора

//на величину счетчика

do

{

dFactorial *= dValue; dValue -= 1.0;

} while(dValue > 1);

// Возвращаем вычисленное значение return dFactorial;

}

}

public class Program

{

public static void Main(string[] args)

{

//Вызов функции вычисления факториала в

//цикле от 6 до -6

for (int i = 6; i > -6; I--)

{

// Вывод результата на каждой итерации Console.WriteLine("i = { о } , факториал = {l}",

i, MyMathFunctions.Factorial(i));

}

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

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

Console.Read();

}

}

}

Функция Factorial () начинается с инициализации переменной-аккумулятора значе­ нием 1. Затем функция входит в цикл, в котором на каждой итерации выполняется умноже­ ние на последовательно уменьшающееся значение счетчика nValue, пока nValue не дос­ тигнет 1. Накопленное в аккумуляторе значение возвращается вызывающей функции.

Алгоритм Factorial () выглядит корректно— пока вы не начнете вызывать эту функцию. Функция Main () также содержит цикл, в котором вычисляются значения фак­ ториала для ряда убывающих значений. Однако вместо того чтобы остановиться на значе­ нии394 1, функция Main () продолжает вычисления для отрицательныхЧасть VII. Дополнительныезначе й — д -6.главы


Врезультате на экране получается следующее:

i= 6, факториал = 72 0

i = 5, факториал = 12 0

i = 4,

 

факториал = 24

i=3,

факториал =

6

i = 2,

 

факториал = 2

1=1,

факториал =

1

| = 0 ,

факториал =

0

i = -1,

факториал = -1

i = -2,

факториал = -2

i = -3,

факториал = -3

i = -4,

факториал = -4

i - -5, факториал = -5

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

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

Если попытаться изменить цикл внутри Factorial () и записать его как do{ . . . }while (dValue ! =0), то программу при передаче отрицательного

значения просто ждет крах. Поэтому никогда не пишите такой оператор срав­ нений — while (dValue ! =0), поскольку ошибка приближения может в лю­ бом случае привести к неверному результату проверки на равенство 0.

В особенности при работе с числами с плавающей точкой избегайте условий наподобие dValue ! =0, в которых требуется точное сравнение для выхода из цикла. Используйте менее строгое условие, как, например, dValue>l. Не­ большая ошибка приближения— такая как dValue = 0.00001— может привести к бесконечному циклу. Об ошибках приближения рассказывается в главе 3, "Объявление переменных-значений".

Возврат индикатора ошибки

Несмотря на свою простоту, функция Factorial О требует проверки ошибочной ситуации: факториал отрицательного числа не определен. Функция Factorial () должна включать проверку этого условия.

Но что должна делать функция Factorial ( ) , столкнувшись с ошибкой? Лучшее, что она может сделать в такой ситуации — это сообщить об ошибке вызывающей функции в надежде на то, что источник ошибки знает, почему она произошла и как с ней справиться.

Классический способ указать на происшедшую ошибку в функции — это возвратить значение, которое функция не в состоянии вернуть при безошибочной работе. Например, значение факториала не может быть отрицательным. Таким образом, факториал может возвращать значение -1, если ему передается отрицательный аргумент, -2 для нецелого аргумента и так далее — для каждой ошибки некоторое соответствующее ей число. Та­ кие числа называются кодами ошибки. Вызывающая функция может проверить, не вер­ нула ли вызываемая функция отрицательное значение, и если д а — то вызывающая функция будет знать о том, что произошла ошибка. Значение возвращаемого кода ошиб­ ки позволяет определить ее природу.

Глава

18.

Эти исключительные исключения

395


jjjdWfeK Указанные изменения внесены в код демонстрационной программы Facto­

//FactorialErrorReturn - создание функции вычисления

//факториала, которая возвращает код ошибки, если что-то

//идет не так

using System;

namespace FactorialErrorReturn

{

//MyMathFunctions - набор созданных мною математических

//функций

public class MyMathFunctions

{

//Следующие коды ошибок представляют некорректные

//значения

public

const

int NEGATIVE_NUMBER =

-1;

public

const

int NON_INTEGER_VALUE =

-2;

//Factorial - возвращает факториал переданного

//аргумента

public static double Factorial(double dValue)

{

// Проверка: отрицательные значения запрещены if (dValue < 0)

{

return NEGATIVE NUMBER;

}

// Проверка: передано ли целое значение аргумента int nValue = (int)dValue;

if (nValue != dValue)

{

return NON INTEGER VALUE;

}

//Тесты пройдены, начинаем со значения аккумулятора,

//равного 1

double dFactorial = 1.0;

//Цикл со счетчиком nValue, уменьшающимся до 1, с

//умножением на каждой итерации значения аккумулятора

//на величину счетчика

do

{

dFactorial *= dValue; dValue -= 1.0;

} while(dValue > 1);

// Возвращаем вычисленное значение return dFactorial;

}

}

public class Program

{

public static void Main(stririg[] args)

396 {

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

//Вызов функции вычисления факториала в

//цикле от 6 до -6


for (int i = 6; i > -6; i--)

{

double dFactorial = MyMathFunctions.Factorial(i); if (dFactorial == MyMathFunctions.NEGATIVE NUMBER)

{

Console.WriteLine

("Factorial() получила отрицательный параметр"); break;

}

if (dFactorial == MyMathFunctions.NON INTEGER VALUE)

{' "

Console.WriteLine

("Factorial() получила нецелый параметр"); break;

}

// Вывод результата на каждой итерации Console.WriteLine("i = { о } , факториал = {l}",

i, MyMathFunctions.Factorial(i));

}

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

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

Console.Read ();

Теперь перед началом вычислений функция Factorial () выполняет ряд проверок. Первая проверка — не отрицателен ли переданный функции аргумент. Обратите внима­ ние, что значение 0 разрешено, поскольку приводит к разумному результату7. Если про­ верка не пройдена, функция тут же возвращает код ошибки. Затем выполняется второй тест, проверяющий, равен ли переданный аргумент своей целочисленной версии. Если да — дробная часть аргумента равна 0.

Функция Main () проверяет результат, возвращаемый функцией Factorial ( ) , на предмет обнаружения ошибок. Однако значения наподобие -1 и -2 мало информативны для программиста, так что класс MyMathFunctions определяет пару целочисленных

констант. Константа NEGATIVE_NUMBER равна -1, a NON_INTEGER_VALUE 2. Это

ничего не меняет, но делает программу, в особенности функцию Main ( ) , существенно более удобочитаемой.

Обычно по соглашению для имен констант используются строчные буквы, а слова в имени разделены символами подчеркивания.

Обращение к этим константам выполняется посредством имени класса, как

MyMathClass. NEGATIVE_NUMBER. Константные переменные автоматиче­

ски являются статическими, что делает их свойствами класса, разделяемыми всеми объектами. Другие варианты работы с константами описаны во врезке "Немного о константах".

7 Этот "разумный" результат некорректен, так как в математике принято, что факториал 0 ра­

вен 1. — Примеч. ред.

Глава

18.

Эти исключительные исключения

397