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

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

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

Добавлен: 11.12.2025

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

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

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

Немного о константах

Предпочтительный способ записи констант почти всегда использует следую» щий, более гибкий по сравнению с применением const, подход.

public static readonly int NEGATIVE_NUMBER = -1;

Значение const вычисляется во время компиляции и может быть инициализировано только числом или строкой. Статическая переменная только для чтения вычисляется во время выполнения программы и может быть инициализирована объектом любого вида, Используйте const только там, где производительность программы сверхкритична.

Еще один способ определения констант — в данном случае группы связанных кон- стант—посредством ключевого слова enum, как описано в главе 15, "Обобщенное программирование". Типы ошибок для MyMathClass могут быть определены еледующим образом:

enum MathErrors

NegativeNumber,

NonlntegerValue

Функция Factorial () может возвращать значение MathErrors, и вы можете проверить его в своей программе следующим образом (как можно часто увидеть в классах

.NET Framework):

MathErrors meResult = MyMathFunctions.Factorial(6); if(meResult == MathErrors.NegativeNumber) ...

Теперь функция Factorial () сообщает об ошибках функции Main ( ) , которая вы- водит соответствующее сообщение на экран и завершает на этом свою работу:

i

= 6,

факториал =

720

i

= 5,

факториал =

12 0

i = 4, факториал = 24

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

 

i = 2, факториал = 2

 

1 = 1 ,

факториал =

1

 

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

 

Factorial() получила

отрицательный параметр

Нажмите

<Enter> для

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

(Здесь я предпочел прекращать работу при обнаружении ошибки.) Указание о про­ исшедшей ошибке посредством возвращаемого функцией значения повсеместно исполь­ зуется еще со времен FORTRAN. Зачем же менять этот механизм?

Чем плохи коды ошибок

Что же не так с кодами ошибок? Они были достаточно хороши даже для FORTRAN! Да, но в те времена компьютеры были ламповыми. Увы, но коды ошибок приводят к ря­ ду проблем.

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

398

Часть

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


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

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

В целом числе не удается разместить большое количество информации. Так, рассматриваемая функция Factorial О возвращает -1, если ее аргумент от­ рицателен. Локализовать ошибку было бы проще, если бы был известен сам аргумент, но в возвращаемом функцией типе для него просто нет места.

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

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

//Вызов SomeFuncO, проверка кода ошибки, его обработка и

//возврат из функции

errRtn = SomeFunc () ;

if (errRtn == SF_ERRORl)

(

Console .WriteLine ("Ошибка типа 1 при вызове SomeFuncO"); return MY ERROR 1;

if (errRtn == SF ERROR2)

Console .WriteLine ("Ошибка типа 2 при вызове SomeFuncO"); return My_ERROR 2;

}

// Вызов другой функции, проверка кода ошибки и так далее..

.errRtn = SomeOtherFuncО; if (errRtn == SOF_ERRORl)

{

Console.WriteLine("Ошибка типа 1 при вызове " + "SomeOtherFunc () ") ;

return MY ERROR 3;

)

if (errRtn == SOF ERROR2)

{

Console.WriteLine("Ошибка типа 2 при вызове " +

Глава 18. Эти исключительные исключения

399


"SomeOtherFunc()");

return MY_ERR0R_4;

}

Такой механизм имеет ряд проблем.

В нем очень много повторов. Дублирование кода обычно очень неприятно по-| пахивает...

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

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

К счастью, описанный далее механизм исключений решает указанные проблемы.

В С# для перехвата и обработки ошибок используется совершенно иной механизм, называемый исключениями. Он основан на ключевых словах try, catch, throw и fi­ nally. Набросать схему его работы можно следующим образом. Функция пытается (try) пробраться через кусок кода. Если в нем обнаружена проблема, она бросает (throw) индикатор ошибки, который функции могут поймать (catch), и независимо от того, что именно произошло, в конце (finally) выполнить специальный блок кода, как показано в следующем наброске исходного текста:

public class MyClass

{

public void SomeFunction()

{

// Настройка для перехвата ошибки try

{

//Вызов функции или выполнение каких-то иных

//действий, которые могут генерировать исключение SomeOtherFunction();

//. . . Какие-то иные действия . . .

}

catch(Exception е)

{

//Сюда управление передается в случае, когда в блоке

//try сгенерировано исключение — в самом ли блоке, в

//функции, которая в нем вызывается, в функции,

//которая вызывается функцией, вызванной в try-блоке

//и так далее — словом, где угодно. Объект Exception

//описывает ошибку

'Далее будет использоваться выражение "генерирует исключение". — Примеч. ред.

400

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


}

finally

{

//Выполнение всех завершающих действий: закрытие

//файлов, освобождение ресурсов и т.п. Этот блок

//выполняется независимо от того, было ли

//сгенерировано исключение.

}

}

public

void

SomeOtherFunction()

 

 

{

 

 

 

 

 

 

 

I I

. . .

Ошибка произошла

где-то в теле

функции . . .

11

. . . Ж "пузырек" исключения "всплывает" вверх по

//

всей

цепочке вызовов,

пока

не будет

перехвачен в

//

блоке

 

catch

 

 

 

throw

new

Exception("Описание

ошибки");

I I

. . .

Продолжение функции . . .

 

Функция Some Function () помещает некоторую часть кода в блок, помеченный ключевым словом try. Любая функция, вызываемая в этом блоке (или функция, вызы­ ваемая функцией, вызываемой в этом блоке — и так далее...), рассматривается как вы­ званная в данном try-блоке.

Непосредственно за блоком try следует ключевое слово catch с блоком, которому передается управление в случае, если где-то в try-блоке произошла ошибка. Аргумент catch-блока — объект класса Exception (или некоторого подкласса Exception).

Однако catch-блок не обязан иметь аргументы: пустой catch перехватывает все

исключения, как и catch(Exception): catch

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

catch (MyException)

{

// Действия, которые не требуют обращения к объекту // исключения

}

Блок finally— если таковой имеется в вашем исходном тексте — выполняется даже в случае перехвата исключения, не говоря уже о том, что он выполняется при нор­ мальной работе. Обычно он предназначается для "уборки" — закрытия открытых фай­ лов, освобождения ресурсов и т.п.

В отличие от исключений С++, в которых аргументом catch может быть объ­ ект произвольного типа, исключения С# требуют, чтобы он был объектом клас­ са Exception или производного от него.

Итак, где-то в дебрях кода на неизвестно каком уровне вызовов в функции SomeO-

t h e r F u n c t i o n ( )

случилась ошибка... Функция сообщает об этом, генерируя исклю-

Глава 18. Эти

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

401