Обработка ошибок


Содержание
Илон Маск рекомендует:  Iis о сертификатах

Обработка ошибок: Защитное программирование

Введение

В любой программе, сложнее чем «Hello, world» , неизбежно будут происходить ошибки и различные сбои. Поэтому если вы хотите писать надежный и стабильный код, то обязаны заботиться обо всем этом. Конечно, в большинстве случаев нет смысла становиться параноиком и проверять абсолютно все. С другой стороны, во многом это зависит от области применения приложения, которое вы разрабатываете. Если это система контроля банковских переводов или управления полетом спутника, то единственная ошибка в коде может обойтись очень дорого. В этом случае вам на помощь приходит «защитное программирование». Одно из лучших описаний этой методики, на мой взгляд, приводится в следующих книгах:

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

Примеры кода я буду приводить на C++, однако это не должно стать для вас серьезной помехой, если вы пишите на Java, C# или каком-нибудь другом языке со схожим синтаксисом, поскольку многие техники обработки ошибок являются универсальными.

Реклама

Коды ошибок

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

Двойная ответственность

Вот как может выглядеть подобная функция:

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

Такой способ применяется довольно часто, но он имеет некоторые очевидные недостатки. Например, когда мы инициализируем переменную userID значением, которое возвращает функция registerUser() , то на самом деле мы врем. Ведь эта переменная хранит идентификатор пользователя ИЛИ код ошибки, то есть должна называться userIDOrErrorCode . Имя получилось длинным и пользоваться им будет неудобно. Но сразу становится понятно, что эта переменная имеет два назначения (выглядит подозрительно, не правда ли?). Однако даже это не является главным недостатком. В какой-то момент требования к типу идентификатора могут измениться. Например, мы можем решить, что лучше использовать беззнаковое целое число:

Обратите внимание, что в первую очередь я добавил определение typedef для типа UserID . Теперь мы сможем менять фактический тип идентификатора всего в одной строке. Для сравнения, когда мы явно пользовались типом int , то в зависимости от объема кода подобная тривиальная операция могла бы занять несколько часов. Поэтому старайтесь не повторяться (см. Принцип DRY в действии).

Кроме того, теперь мы явно указываем, что функция registerUser() возвращает именно идентификатор UserID , а не идентификатор ИЛИ код ошибки. Иначе мы вновь обманываем тех, кто будет вызывать нашу функцию.

Указатель для ошибки

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

Этой функцией мы можем пользоваться следующим образом:

Выглядит неплохо. Но у вас мог возникнуть вопрос: «А какое значение будет у переменной userID , если ok == false «? Можно предположить, что это будет 0 , то есть функция registerUser() должна вернуть некое нейтральное значение. Например, в случае, когда ожидается возврат класса или структуры, это может быть Null -объект или объект с функцией-членом на подобии isValid() , которая возвращает false , если экземпляр класса находится в некорректном состоянии. Кстати, если бы registerUser() была не обычной функцией, а являлась членом класса, то мы бы могли вообще не возвращать никаких признаков ошибки, но добавить в сам класс что-то вроде getLastError() .

Вот это поворот

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

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

Однако, как вы можете видеть, принципиально ничего не изменилось. Нам все равно приходится инициализировать идентификатор userID значением по умолчанию, но теперь это происходит не в самой функции, а до ее вызова. Если мы не хотим, чтобы переменная userID имела смысл без явной инициализации (хотя всегда есть возможность добавить в иерархию классов Null -объект), то можно воспользоваться альтернативным решением:

На этот раз registerUser() принимает не просто указатель на UserID , а двойной указатель. То есть память для идентификатора пользователя выделяется внутри функции registerUser() . А чтобы быть последовательными, освобождать память для userID мы будем не оператором delete , а с помощью функции releaseMemory() , которую сами должны предусмотреть.

Подобным образом часто пишутся C-библиотеки, которые представляют собой обертку над ООП-кодом на C++. Это делается для получения высокой совместимости библиотек, что особенно актуально для Windows. Вы не можете подключить библиотеку на C++ к проекту, если версии компиляторов, с которой работаете вы, и с которой была собрана библиотека, отличаются. Поэтому если вы хотите сохранить реализацию своей библиотеки закрытой, то оказываетесь ограничены использованием низкоуровневых типов данных, которые есть в C. Но чтобы не отбрасывать свои наработки и не переписывать весь код заново на чистом C, используется показанная выше методика. При этом в пользовательском коде вся работа ведется не с самими экземплярами классов, а с указателями на них. А для выделения и освобождения памяти используются специально предусмотренные функции, которые предоставляет библиотека. Более того, определений классов в заголовочных файлах библиотеки нет вовсе (для простоты я сделал допущение, что мы можем создать экземпляр класса User , хотя его тоже не было бы). Вам доступно лишь объявление, которого достаточно для определения указателя на класс. Из-за этого все вызовы функций-членов должны осуществляться также с помощью C-функций, которые ожидают получить указатель на объект, а входные и выходные аргументы должны быть представлены с помощью примитивных типов, на подобии int или char . Впрочем, это было небольшое отступление от нашей темы. Однако если у вас есть желание узнать об этом вопросе поподробнее, то пишите свои пожелания в комментариях и я подготовлю соответствующую полноценную заметку.

Кто там?

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

Чтобы избежать проблем с областью видимости при объявлении перечисления ResultCode , мы создали пространство имен RegistrationLib . Использование namespace -ов вообще является хорошей практикой при написании кода, который вы планируете кому-то передавать. Теперь registerUser() не просто говорит о том, что что-то случилось, но и указывает причину. Конечно, в качестве примера я взял простейший набор возможных типов ошибок, которые могли бы случиться. К тому же, они не так детальны, как хотелось бы. Проблем с заполнением структуры User может быть немало, а уж при работе с базами еще больше. Однако я думаю, что суть вы уловили.

Чуть выше

До этого момента мы говорили о различных способах возврата ошибки, когда она уже произошла. Но мы же не можем откладывать момент более тесного знакомства с ней вечно. Поэтому приступим. В коде верхнего уровня нам предстоит сделать выбор, что же делать дальше, и какую стратегию проверки кодов возвращаемых значений использовать. На самом деле, когда мы проектировали нашу функцию registerUser() , то при ее реализации возникнут те же вопросы. Более того, если она будет основана на применении разных библиотек с разными механизмами возврата ошибок, то это еще серьезнее может осложнить нам жизнь.

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

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

Получившийся код выглядит несколько проще. Это достигается за счет уменьшения вложенности проверок. Однако обратите внимание, что перед вызовом registerUser() и sendUserIDToRemoteServer() нам приходится проверять результат выполнения предыдущих действий. Такая необходимость появляется по той причине, что если на n -ом шаге что-то случилось, то все оставшиеся шаги уже не имеют смысла. Фактически после ошибки на каком-то из шагов функция onUserDataReady() должна завершаться. Для этого мы бы могли попробовать сделать что-то подобное:

Конечно, код стал еще легче, но теперь мы имеем утечку памяти, ведь возврат из функции при завершении обработки ошибок происходит раньше вызова releaseMemory() . В этом случае у нас есть несколько вариантов. Первый из них заключается в том, чтобы перед каждым return вставить вызов releaseMemory() . Но так мы получим крайне неудачную конструкцию с дублированием кода. Поэтому такой способ сразу отбрасываем. Другой подход многие бы не одобрили, но он вполне того стоит в нашей ситуации. Его суть заключается в использовании goto :

Обычно goto не рекомендуют применять. И это вполне оправдано. Ведь при неумелом использовании код быстро становится хаотичным и запутанным (спагетти-код). Но если вы знаете, что делаете, то почему бы нет? Представленный выше фрагмент программы является хорошим примером, что из каждого правила есть исключения.

Однако есть еще более надежный и качественный вариант. Он заключается в использовании принципа RAII. Наиболее известным примером реализации этого механизма является Умный указатель. Более подробно об этой идиоме я расскажу во второй части, а сейчас посмотрим, как будет выглядеть код функции onUserDataReady() на основе RAII в нашем случае:

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

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

Реклама

Заключение

Наше знакомство с базовыми принципами обработки ошибок закончено. Мы успели рассмотреть основные способы возврата кодов ошибок; коротко вспомнили принцип создания C-оберток над ООП кодом C++ для создания переносимых библиотек; а также разобрались с возможными вариантами реализации функции-обработчика, которая должна проверять коды ошибок. Встретимся во второй части.

Обработка ошибок выполнения

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

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

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

При программировании имеются два подхода:

Предотвращение ошибочных ситуаций.

Обработка ошибки с помощью специальной процедуры.

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

Существует ли файл, который требуется открыть?

Находится ли курсор в требуемой для выполнения макроса позиции?

Перехват используется в том случае, когда предотвратить возникновение ошибочных ситуаций невозможно. Полный список перехватываемых ошибок приводится в разделе системы справочной информации Visual Basic «Перехватываемые ошибки» (Trappable Errors). Ниже описываются только некоторые из них.

3 Инструкция Return без Gosub6 Переполнение7 Не хватает памяти9 Индекс выходит за пределы допустимого диапазона11 Деление на 018 Произошло прерывание, вызванное пользователем35 Процедура Sub, Function или Property не определена53 Файл не найден61 Переполнение диска71 Диск не готов91 Не задана объектная переменная блока With97 Невозможен вызов процедуры Friend для объекта, не являющегося экземпляром определяющего класса335 Невозможен доступ к системному реестру368 Истек срок данного системного файла. Программе требуется файл более новой версии402 Сначала необходимо закрыть самую верхнюю модальную форму422 Свойство не найдено440 Ошибка программирования объектов448 Именованный аргумент не найден482 Ошибка принтера31032 Невозможно создать внедренный объект

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

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

Система перехвата ошибок включает следующие компоненты:

Инструкция On Error устанавливает перехват ошибки. Она сообщает процедуре, что делать, если произошла ошибка, обычно передавая управление подпрограмме обработки ошибки.

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

Объект Err содержит информацию о возникшей ошибке. Подобно любому объекту он имеет свои свойства, включая номер и описание ошибки.

Инструкция Resume позволяет процедуре продолжить операции после обработки ошибки.

Инструкция Exit позволяет выйти из процедуры, не выполняя оставшийся код. Данная инструкция используется очень часто, поскольку если ошибок не возникло, то не следует исполнять подпрограмму обработки ошибки.

Общие сведения о перехвате ошибок.

Общее правило перехвата ошибок можно пояснить на примере следующей процедуры:

Sub MyProcedure ()On Error GoTo MyErrorHandler’…’ Обычные действия, при выполнении каждого из’ которых может произойти ошибка’…Exit Sub ‘ Выход для обхода подпрограммы обработки ошибкиMyErrorHandler:’…’ Подпрограмма обработки ошибки’…ResumeEnd Sub

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

Инструкция On Error имеет три формы:

Инструкция On Error GoTo метка позволяет передать управление подпрограмме обработки ошибки, которая идентифицируется меткой.

Илон Маск рекомендует:  Скорость разработки сайтов, не теряйте деньги за простой

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

Для определение типа ошибки и выполнения, соответствующих ей действий имеется возможность получить значение свойства Number объекта Err.

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

В любом случае инструкция On Error устанавливает обработку ошибок только в той процедуре, в которой она указана.

Инструкцию On Error можно указывать произвольное число раз, изменяя способ обработки ошибок.

Sub MySub ()’…On Error GoTo MyHandler’…OnError Resume Next’…On Error GoTo 0’…MyHandler:’…ResumeEnd Sub

Метка, которая помечает начало подпрограммы обработки ошибок, является параметром инструкции GoTo. Меткой является любое допустимое имя, оно располагается в самом начале строки, а сразу за ней ставится (:).

Обратите внимание, что в инструкции GoTo двоеточие не используется, т.к. оно не является частью имени. Двоеточие только идентифицирует метку.

Инструкция Resume указывается в конце подпрограммы обработки ошибок после выполнения всех требуемых действий. Она возобновляет исполнение процедуры, в которой возникла ошибка.

Инструкцию Resume можно использовать только в подпрограмме обработки ошибки. В противном случае возникает ошибка.

Инструкция Resume имеет три формы:

Инструкция Resume Next передает управление инструкции, которая следует за той, в которой возникла ошибка. Предполагается, что подпрограмма обработки устранила последствия ошибки.

Инструкция Resume или Resume 0 передает управление инструкции, в которой возникла ошибка, и VBA производит попытку выполнять эту строку снова. Предполагается, что подпрограмма обработки устранила причины, которые вызвали ошибку, и теперь инструкцию можно выполнить без ошибки.

Инструкция Resume метка передает управление инструкции, идентифицированной указанной меткой.

Инструкция Resume является необязательной. Если требуется завершить выполнение процедуры после обработки ошибки, то можно опустить эту инструкцию.

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

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

Инструкция Exit имеет пять форм, указывающих на блок, из которого требуется выйти:

Exit SubExit FunctionExit DoExit ForExit Property

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

Объект Err и подпрограмма обработки ошибки.

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

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

Объект Err имеет шесть свойств:

Свойство Number — номер возникшей ошибки.

Свойство Source — имя проекта Visual Basic, в котором произошла ошибка.

Свойство Description — строка, соответствующая номеру ошибки. Некоторые ошибки, включая заданные пользователем ошибки, не имеют описания и тогда строка имеет значение «Ошибка, определяемая приложением» или «Ошибка, определяемая объектом».

Свойство HelpFile — полное имя файла справки Visual Basic, включая диск и путь.

Свойство HelpContext — идентификационный номер в справке Visual Basic, соответствующий возникшей ошибке.

Свойство LastDLLError содержит код системной ошибки для последнего вызова библиотеки динамической компоновки. Используется только в 32-разрядных системах Microsoft Windows и доступно только для чтения.

Обычно, чтобы произвести определенные действия в зависимости от типа возникшей ошибки, требуется просто проверить значение свойства Number. Для этого используется любая логическая инструкция, например, блок If..Else..End If или Select Case..End Select. Блок Select Case..End Select удобнее, т.к. в него проще добавить дополнительное условие.

Заданным по умолчанию свойством объекта Err является свойство Number, поэтому нижеприведенные инструкции эквивалентны:

Select Case Err.Number

Select Case Err

Использование полного синтаксиса упрощает понимание программы. Сокращенный синтаксис совместим только в одну сторону с предыдущими версиями Visual Basic и WordBasic, в которых вместо объекта Err применялась функция Err. Объект Err имеет два метода:

Метод Raise позволяет генерировать ошибку во время выполнения программы. Данный метод используется для проверки подпрограммы обработки ошибки, которую требуется перехватить. Кроме того, с его помощью можно создать для приложения собственные ошибки, указав их номер и описание.

При задании собственных ошибок необходимо сложить номер ошибки с константой vbObjectError. Таким образом, можно гарантировать, что номер не совпадает с номером стандартной ошибки Visual Basic. Приведем пример, в котором создается новая ошибка:

Err.Raise vbObjectError + 1, «MyProject.MyObject», «Служащего с таким именем в данном отделе не имеется»

Метод Clear сбрасывает значения всех свойств объекта Err. Данный метод используется после того, как обработана ошибка и необходимо продолжить выполнение.

В ранних версиях Visual Basic и других языках Basic для создания ошибок использовалась инструкция Error. В целях обеспечения инструкция Error имеется и в новом Visual Basic, однако, во всех вновь создаваемых программах рекомендуется применять объект Err.

Вернуться на главную страницу. или ЗАКАЗАТЬ РАБОТУ

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

C#: обработка ошибок

Исключения, их обработка, и некоторые другие моменты, связанные с ошибками в приложении на C#.

Исключения (Exceptions) и инструкция try

Инструкция try отмечает блок кода как объект для обработки ошибок или очистки. После блока try обязательно должен идти либо блок catch , либо блок finally , либо они оба. Блок catch выполняется, когда внутри блока try возникает ошибка. Блок finally выполняется после того, как прекращает выполнять блок try (или, если присутствует, блок catch ), независимо от того, выполнился ли он до конца или был прерван ошибкой, что позволяет выполнить так называемый код очистки.

Блок catch имеет доступ к объекту исключения ( Exception ), который содержит информацию об ошибке. Блок catch позволяет обработать исключительную ситуацию и как-либо скорректировать ошибку или выбросить новое исключение. Повторное выбрасывание исключения в блоке catch обычно применяется с целью логирования ошибок или чтобы выбросить новое, более специфическое исключение.

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

В целом конструкция try выглядит следующим образом:

Например, следующий код выбросит ошибку DivideByZeroException (поскольку делить на ноль нельзя) и наша программа завершить досрочно:

Чтобы этого избежать можно использовать конструкцию try :

Обработка исключений довольно ресурсоёмкая операция, поэтому на практике для таких случаев как в примере ее лучше не использовать (лучше непосредственно перед делением проверить делить на равенство нулю).

Когда выбрасывается исключение, CLR проверяет выброшено ли оно непосредственно внутри блока try , который может обработать данное исключение. Если да, выполнение переходит в соответствующий блок catch . Если блок catch успешно завершается, выполнение переходит к следующей после блока try инструкции (если имеется блок finally , то сначала выполняется он). Если же исключение выброшено не внутри блока try или конструкция try не содержит соответствующего блока catch , выполнение переходит в точку вызова метода (при этом сначала выполняется блок finally ), и проверка повторяется снова.

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

Оговорка catch

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

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

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

Можно обработать несколько типов исключений с помощью нескольких оговорок catch:

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

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

Более того, в оговорке catch можно опустить и переменную и тип исключения — такая оговрка будет перехватывать все исключения:

Блок finally

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

Блок finally выполняется в следующих случаях:

  • после завершения блока catch
  • если выполнение блока try прервано jump-инструкциями: return , goto и т.д.
  • после выполнения блока try полностью, если исключений так и не было выброшено

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

В пример для закрытия файла вызывается метод Dispose . Использование этого метода внутри блока finally является стандартной практикой. C# даже позволяет заменить всю конструкцию инструкцией using .

Инструкция using

Многие классы инкапсулируют неуправляемые ресурсы, такие как дескриптор файла, соединение с базой данных и т.д. Эти классы реализуют интерфейс System.IDisposable , который содержит единственный метод без параметров Dispose , освобождающий соответствующие машинные ресурсы. Инструкция using предусматривает удобный синтаксис вызова метода Dispose для объектов реализующих IDisposable внутри блока finally :

Что эквивалентно следующей конструкции:

Выбрасывание исключений

Исключение может быть выброшено автоматически во время выполнения программы либо явно в коде программы с помощью ключевого слова throw :

Также исключение может быть выброшено повторно внутри блока catch :

Такой подход позволяет заносить ошибки в лог без их дальнейшего поглощения. Также это позволяет уклониться от обработки неожиданных исключений.

Если throw заменить на throw ex , то пример по прежнему будет работать, но свойство исключения StackTrace не будет отражать исходную ошибку.

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

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

Основные свойства System.Exception

К наиболее важным свойствам класса System.Exception можно отнести:

  • StackTrace — строка, представляющая все методы, которые были вызваны, начиная с того, в котором было выброшено исключение, и заканчивая тем, в котором содержится блок catch , перехвативший исключение;
  • Message — строка с описанием ошибки;
  • InnerException — содержит ссылку на объект Exeption , который вызвал текущее исключение (например, при повторном выбрасывании исключения).

Основные типы исключений

Следующие типы исключений являются наиболее распространенными в среде CLR и .NET Framework. Их можно выбрасывать непосредственно или использовать как базовые классы для пользовательских типов исключений.

  • System.ArgumentException — выбрасывается при вызове функции с неправильным аргументом.
  • System.ArgumentNullException — производный от ArgumentException класс, выбрасывается если один из аргументов функции неожиданно равен null .
  • System.ArgumentOutOfRangeException — производный от ArgumentException класс, выбрасывается когда аргумент функции имеет слишком большое или слишком маленькое значение для данного типа (обычно касается числовых типов). Например, такое исключение будет выброшено если попытаться передать отрицательное число в функцию, которая ожидает только положительные числа.
  • System.InvalidOperationException — выбрасывается когда состояние объекта является неподходящим для нормального выполнения метода, например, при попытке прочесть не открытый файл.
  • System.NotSupportedException — выбрасывается, когда запрошенный функционал не поддерживается, например, если попытаться вызвать метод Add для коллекции доступной только для чтения (свойство коллекции IsReadOnly возвращает true ).
  • System.NotImplementedException — выбрасывается, когда запрошенный функционал еще не реализован.
  • System.ObjectDisposedException — выбрасывается при попытке вызвать метод объекта, который уже был уничтожен (disposed).

Директивы препроцессора

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

Промисы: обработка ошибок

Цепочки промисов отлично подходят для перехвата ошибок. Если промис завершается с ошибкой, то управление переходит в ближайший обработчик ошибок. На практике это очень удобно.

Например, в представленном ниже примере для fetch указана неправильная ссылка (сайт не существует), и .catch перехватывает ошибку:

Как видно, .catch не обязательно должен быть сразу после ошибки, он может быть далее, после одного или даже нескольких .then

Или, может быть, с сервером всё в порядке, но в ответе мы получим некорректный JSON. Самый лёгкий путь перехватить все ошибки – это добавить .catch в конец цепочки:

Если все в порядке, то такой .catch вообще не выполнится. Но если любой из промисов будет отклонён (проблемы с сетью или некорректная json-строка, или что угодно другое), то ошибка будет перехвачена.

Неявный try…catch

Вокруг функции промиса и обработчиков находится «невидимый try..catch «. Если происходит исключение, то оно перехватывается, и промис считается отклонённым с этой ошибкой.

Например, этот код:

…Работает так же, как и этот:

«Невидимый try..catch » вокруг промиса автоматически перехватывает ошибку и превращает её в отклонённый промис.

Это работает не только в функции промиса, но и в обработчиках. Если мы бросим ошибку ( throw ) из обработчика ( .then ), то промис будет считаться отклонённым, и управление перейдёт к ближайшему обработчику ошибок.

Это происходит для всех ошибок, не только для тех, которые вызваны оператором throw . Например, программная ошибка:

Финальный .catch перехватывает как промисы, в которых вызван reject , так и случайные ошибки в обработчиках.

Пробрасывание ошибок

Как мы уже заметили, .catch ведёт себя как try..catch . Мы можем иметь столько обработчиков .then , сколько мы хотим, и затем использовать один .catch в конце, чтобы перехватить ошибки из всех обработчиков.

В обычном try..catch мы можем проанализировать ошибку и повторно пробросить дальше, если не можем её обработать. То же самое возможно для промисов.

Если мы пробросим ( throw ) ошибку внутри блока .catch , то управление перейдёт к следующему ближайшему обработчику ошибок. А если мы обработаем ошибку и завершим работу обработчика нормально, то продолжит работу ближайший успешный обработчик .then .

В примере ниже .catch успешно обрабатывает ошибку:

Здесь блок .catch завершается нормально. Поэтому вызывается следующий успешный обработчик .then .

В примере ниже мы видим другую ситуацию с блоком .catch . Обработчик (*) перехватывает ошибку и не может обработать её (например, он знает как обработать только URIError ), поэтому ошибка пробрасывается далее:

Управление переходит от первого блока .catch (*) к следующему (**) , вниз по цепочке.

Необработанные ошибки

Что произойдёт, если ошибка не будет обработана? Например, мы просто забыли добавить .catch в конец цепочки, как здесь:

В случае ошибки выполнение должно перейти к ближайшему обработчику ошибок. Но в примере выше нет никакого обработчика. Поэтому ошибка как бы «застревает», её некому обработать.

На практике, как и при обычных необработанных ошибках в коде, это означает, что что-то пошло сильно не так.

Что происходит, когда обычная ошибка не перехвачена try..catch ? Скрипт умирает с сообщением в консоли. Похожее происходит и в случае необработанной ошибки промиса.

JavaScript-движок отслеживает такие ситуации и генерирует в этом случае глобальную ошибку. Вы можете увидеть её в консоли, если запустите пример выше.

В браузере мы можем поймать такие ошибки, используя событие unhandledrejection :

Это событие является частью стандарта HTML.

Если происходит ошибка, и отсутствует её обработчик, то генерируется событие unhandledrejection , и соответствующий объект event содержит информацию об ошибке.

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

Правая Скобка ]

Энциклопедия веб разработчика. Все что интересно HTML, CSS, PHP, MySQL и не только !

Обработка ошибок

Обработка ошибок — это процесс обнаружения, перехвата ошибок, вызванных работой программы, а затем принятия соответствующих мер для их устранения.
Определение пользовательской функции обработки ошибок
Можно написать свою собственную функцию для обработки любой ошибки. PHP предоставляет вам структуру для определения функции обработки ошибок. Эта функция должна иметь возможность обрабатывать минимум два параметра (уровень ошибки и сообщение об ошибке), но может принимать до пяти параметров (опционально: файл, номер строки и контекст ошибки)
Синтаксис

Параметр и описание
1 error_level Обязательный параметр — указывает уровень отчета об ошибке. Значение должно быть числом.
2 error_message Обязательный параметр — указывает сообщение об ошибке для пользовательской ошибки
3 error_file
Необязательный, указывает имя файла, в котором произошла ошибка
4 error_line
Необязательный, указывает номер строки, в которой произошла ошибка
5 error_context
Необязательный, указывает массив, содержащий все переменные и их значения, используемые при возникновении ошибки

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

Описание Значение
1 .E_ERROR
Неустранимая ошибка во время выполнения. Выполнение скрипта останавливается
1
2 E_WARNING
Нефатальные ошибки во время выполнения. Выполнение скрипта не останавливается
2
3 E_PARSE
Ошибки анализа времени компиляции. Ошибки анализа должны генерироваться только парсером.
4
4 E_NOTICE
Уведомления о времени выполнения. Скрипт нашел что-то, что может быть ошибкой, но может также произойти при обычном сценарии
8
5 E_CORE_ERROR
Неустранимые ошибки, возникающие во время первоначального запуска PHP.
16
6 E_CORE_WARNING
Нефатальные ошибки во время выполнения. Это происходит во время первоначального запуска PHP.
32
7 E_USER_ERROR
Неустранимая пользовательская ошибка. Она похожа на E_ERROR программиста.
256
8 E_USER_WARNING
Не фатальное пользовательское предупреждение. Оно похоже на E_WARNING, устанавливается программистом с использованием функции PHP trigger_error ()
512
9 E_USER_NOTICE
Пользовательское уведомление. Оно похоже на набор E_NOTICE, задается программистом с использованием функции PHP trigger_error ()
1024
10 E_STRICT
Уведомления о времени выполнения скрипта.
2048
11 E_RECOVERABLE_ERROR
Ошибка похожая на E_ERROR, но может быть захвачена определяемым пользователем дескриптором (см. Также set_error_handler ())
4096
12 E_ALL
Все ошибки и предупреждения, кроме уровня E_STRICT (E_STRICT будет частью E_ALL с PHP 6.0)
8191

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

  • Try — функция, использующая исключение, должна быть в блоке «try». Если исключение не запускается, код будет продолжен как обычно. Однако, если исключение инициируется, код «выбрасывается».
  • Throw — это то, как вы вызываете исключение. Каждый «бросок» должен иметь хотя бы один «улов».
  • Catch. Блок «catch» извлекает исключение и создает объект, содержащий информацию об исключении.

Когда генерируется исключение, код, следующий за оператором, не будет выполнен, а PHP попытается найти первый соответствующий блок catch. Если исключение не поймано, PHP Fatal Error будет выпущена с помощью «Uncaught Exception …»

  • Исключение может быть брошено, и поймано («catched») в PHP. Код может быть окружен блоком try.
  • Каждая попытка должна иметь по крайней мере один соответствующий блок catch. Для перехвата различных классов исключений можно использовать несколько блоков catch.
  • Исключения могут создаваться (или повторно создаваться) в блоке catch.

Есть функции, которые могут быть использованы с исключением класса.

  • метод GetMessage() − сообщение об исключении
  • getCode() − код исключения
  • getFile () — исходное имя файла
  • getLine () — исходная строка
  • getTrace () — n массив backtrace ()
  • getTraceAsString() отформатирована строке трассировки

Создание Пользовательского Обработчика Исключений
Можно определить собственный собственный обработчик исключений используя следующую функцию для установки пользовательской функции обработчика исключений:

Урок №108. Обработка ошибок, cerr и exit()

Обновл. 9 Июн 2020 |

При написании программ возникновение ошибок почти неизбежно. Ошибки делятся на две категории: синтаксические и семантические.

Синтаксические ошибки

Синтаксическая ошибка возникает при нарушении правил грамматики языка C++. Например:

если 7 не равно 8, то пишем «not equal»;

Хотя этот стейтмент нам (людям) понятен, для компьютера он будет неразборчив. В соответствии с правилами грамматики C++, корректно будет:

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

Семантические ошибки

Семантическая ошибка (или ещё «смысловая») возникает, когда код синтаксически правильный, но выполняет не то, что нужно программисту. Например:

Возможно, программист хотел, чтобы вывелось 0 1 2 3 , но, на самом деле, выведется 0 1 2 3 4 .

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

Они могут возникать несколькими способами. Одной из наиболее распространённых семантических ошибок является логическая ошибка. Логическая ошибка возникает, когда программист неправильно программирует логику выполнения кода. Например, фрагмент кода выше имеет логическую ошибку. Вот ещё один пример:

Что произойдёт, если x будет равен 4 ? Условие выполнится как true, а программа выведет x is greater than 4 . Логические ошибки иногда бывает довольно-таки трудно обнаружить.

Другой распространённой семантической ошибкой является ложное предположение. Ложное предположение возникает, когда программист предполагает, что что-то будет истинным или ложным, а оказывается наоборот. Например:

Заметили потенциальную проблему здесь? Предполагается, что пользователь введёт значение между 0 и длиной строки Hello, world! . Если же пользователь введёт отрицательное число или число побольше, то index окажется за пределами диапазона массива. В этом случае, поскольку мы просто выводим значение по индексу, результатом будет вывод мусора (при условии, что пользователь введёт число вне диапазона). Но в других случаях ложное предположение может привести и к изменениям значений переменных, и к сбою в программе.

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

Определение ложных предположений

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

При вызове функции, когда caller может передать некорректные или семантически бессмысленные аргументы.

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

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

Поэтому, придерживаясь безопасного программирования, нужно следовать следующим трём правилам:

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

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

Проверяйте данные ввода на соответствие ожидаемому типу данных и его диапазону.

Рассмотрим примеры проблем:

Проблема №1: При вызове функции caller может передать некорректные или семантически бессмысленные аргументы:

Обработка ошибок

Поискав по рунету материал на тему обработки ошибок в VBA, не увидал на первых двух страницах результатов поиска чего-то, что мне понравилось. Может плохо смотрел, но решил написать на эту тему свою статью.

Простите, но — немного словоблудия :)

Ошибки в программе


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

Вы обращаетесь к объекту по имени, а объекта с таким именем в коллекции нет

Вы хотите выделить ячеку на одном листе, а этот лист в данный момент не является активным (типичнейшая ошибка новичков в Excel VBA)

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

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

Вы пытаетесь присвоить переменной значение, которое оно не может хранить. Например, переменной типа Long нельзя присвоить строковую константу или переменной типа Integer присвоить знанчение превышающее число 32767 .

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

Continue (продолжить) — этот пункт во время возникновения ошибки всегда не активен. Он активен, когда по ходу выполнения программы вы использовали оператор Stop . Кстати это очень полезный оператор для отладки программы.

End (завершить) — завершение исполнения программы

Debug (отладка) — переход в режим отладки, в котором можно посмотреть, на каком операторе возникла ошибка, что содержат переменные, можно даже перетащить жёлтую полоску, подсвечивающую текущий оператор, назад, и модифицировать знанчение переменных через окно Immediate window (впрочем это экзотика). В общем случае кнопка Debug позволяет посмотреть, где случилась ошибка и попытаться понять почему так случилось.

Если вы — автор программы, в которой случилась ошибка, то вы, должно быть, в начале будете рады увидеть подобное окно, ибо только так вы сможете отловить основные ошибки, скрытые в вашем коде. Однако, если эту ошибку видит пользователь, то для него это, мягко говоря, безрадостное и малопонятное зрелище. Ещё хуже, если за эту программу вам заплатили деньги. Поэтому в среде худо-бедно профессиональных программистов принято предусматривать обработку ошибок в своих программах.

Почему вообще в коде возникают ошибки?

Много ошибок во время написания кода возникает по невнимательности или не совсем адекватного понимания того, что делаешь. Таких ошибок, как правило, очень много, особенно у начинающих программистов, но эти ошибки довольно легко отловить и исправить, так как, пока вы их не исправите, ничего не работает. Ну, например, вы должны извлечь данные из 5-го столбца, а вы извлекаете из 6-го, а их там банально нет. Ясно, что вы это очень быстро заметите.

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

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

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

Задачи механизмов обработки ошибок

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

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

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

Файл примера

Скачать

Код без обработки ошибок

Вот простой пример с потолка. Если вызвать Example_00 , то она прекрасно отработает без ошибок и вернёт это:

В функцию GetCalories передаётся строка с блюдом, а она должна вернуть его калорийность, сверившись с таблицей в A1:B7 .

Давайте поищем слабые места в этом коде. Первое, что должно прийти в голову — если мы ищем, то, что произойдёт, если мы не найдём? А произойдёт, конечно же, ошибка. Её инициирует метод Match .

Ещё одно слабое место этой подпрограммы: функция возвращает вещественный тип Double , и даже, если поиск оказался удачным, то в Cells(intRow, 2) может случайно находиться текстовая строка, а потому, когда вы числовому типу попытаетесь присвоить строковый тип, также произойдёт ошибка. И, если вы второй ошибки сможете избежать за счёт дополнительного оператора if с проверкой через IsNumber (), то избежать первой ошибки таким способом нельзя. Что же делать? А вот тут на сцену выходят операторы обработки ошибок.

Есть 2 подхода к обработке ошибок: автономный подход и выносной . Эти термины я придумал только что, чтобы проще было их обсуждать.

Автономный подход

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

Итак, что тут сделано:

Сразу после объявления функции GetCalories_v1 идёт оператор on error resume next , который в случае возникновения в каком-либо месте ошибки, предписывает VBA просто передавать управление на следующий оператор, идущий после ошибочного.

Мы объявили переменные. Необъявленные переменные получают тип Variant и значение по умолчанию Empty . Объявленные переменные числовых типов инициируются нулём, строковые — пустой строкой, то есть я наперёд знаю, что они содержат, а это хорошо для обработки ошибок.

На вызове метода WorksheetFunction.Match у нас возникает ошибка, так как искомого значения в таблице нет. А это, между прочим, был оператор присваивания ( = ). Прежде, чем левой части оператора присваивания ( intRow ) что-то будет присвоено, необходимо вычислить правую часть оператора присваивания ( WorksheetFunction.Match . ), а поскольку в процессе этого вычисления возникает ошибка, то переменная intRow остаётся такой, какой была! А, как я уже сказал, VBA автоматически её инициализирует нулём до начала исполнения подпрограммы. Получается, что, если в этом операторе возникнет ошибка, то в intRow будет ноль. Если ошибки во время поиска не возникнет, то ноля там не будет ни при каких раскладах, так как строки на листе нумеруются с единицы.

И вот этот ноль мы и контролируем, добавляя оператор If . Если intRow больше нуля, то WorksheetFunction.Match отработала штатно, а если нет — то работу подпрограммы надо прерывать, но об этом чуть позже.

Далее мы помним, что Cells(intRow, 2) может теоретически вернуть строковое значение, которое вызовет ошибку Type missmatch при присвоении переменной типа Double ( GetCalories_v1 ), поэтому мы вставляем дополнительную проверку промежуточной переменной varTemp тому, что она числовая. И если это так, то присваиваем GetCalories_v1 значение из varTemp .

В случае возникновения любой ошибки внутри GetCalories_v1 она просто вернёт ноль. Почему ноль? Потому что переменная GetCalories_v1 тоже инициализируется нулём и об этом не надо заботиться, а в случае ошибки она останется в неприкосновенности.

Соответственно родительский код (в нашем случае его роль играет процедура Example_01 ) должен проверить, а не вернёт ли GetCalories_v1 ноль, и быть готовым к этой ситуации.

А вот теперь тонкий момент, который не все понимают. Почему я использовал промежуточные переменные intRow и varTemp ? Вроде бы есть очевидный ответ — чтобы не вычислять значение выражений с Match и Cells 2 раза. Отчасти это, конечно, так. Но это, в данном случае, не главная причина. Главная причина в том, что такой код

вызовет неправильное поведение программы. Если у нас Match вызовет исключение, то VBA передаст управление на СЛЕДУЮЩИЙ оператор, а следующий оператор в данном случае это то, что идёт после Then — присваивание переменной varTemp значения. Таким образом наша проверка на наличие ошибки сработает с точностью до наоборот, передав управление в ту часть кода, которая должна быть защищена от ситуации, когда Match не нашла строку в таблице. Вот почему важно в операторе If не иметь ничего такого, что могло бы вызвать ошибку.

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

Выносной подход

Данный метод основан на том, что, когда возникает ошибка, то VBA передаёт управление на специальный участок кода — обработчик ошибок, который обычно размещают в конце подпрограммы. Это может выглядеть так:

Обратите внимание, что:

Оператор on error теперь в случае ошибки предписывает передавать управление на метку ErrorHandler , которая объявлена в конце кода процедуры GetCalories_v2

В коде мы никак не заботимся о каких-либо проверках. Возникла ошибка? Иди на метку — там разберутся.

Если ошибки не случилось, то, чтобы программа не стала исполнять строчки, предназначенные для обработки ошибок, перед меткой ErrorHandler обычно ставят оператор Exit Sub или Exit Function (в зависимости от типа подпрограммы).

Принципиальный момент — наличие оператора On Error Resume Next сразу после метки ErrorHandler . Дело в том, что после того, как вы перешли на метку ErrorHandler , очень опасно иметь действующим оператор On Error GoTo ErrorHandler , так как, если у вас в обработчике ошибки случится любая ошибка, то управление будет передано опять на метку и, как нетрудно понять, образуется бесконечный цикл. Поэтому сразу после метки мы возможность возникновения цикла ликвидируем оператором On Error Resume Next .

Что лучше?

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

Автономный подход

Преимущества Недостатки
Есть возможность точно идентифицировать каждую конкретную проблему (если вы её предусмотрели), возникающую во время исполнения, что позволит вам дать самые точные инстркции пользователю для предотвращения появления исключения в будущем. Достаточно трудоёмок, так как подразумевает наличие большого количества проверок в коде. Каждое потенциально опасное действие должно быть снабжено соответствующим оператором If , в котором контролируется значение переменной или код ошибки.
Надо хорошо представлять себе ситуации, где могут возникнуть ошибки, в противном случае ряд ошибок вы просто не заметите на этапе отладки.
Необходимо больше кода, а также требуется опыт и фантазия.
Необходимо больше промежуточных переменных

Выносной подход

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

Средства VBA для обработки ошибок

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

Операторы

On Error

Оператор on error управляет тем, на какой участок вашего кода будет передано управление в случае возникновения ошибки. Данный оператор можно вставить в любое место вашей программы или подпрограммы. Есть 3 варианта:

On error goto label — после того, как этот оператор выполнен, ошибка, возникшая на других операторах программы приведёт к переходу на метку label.

On error resume next — после такого оператора, VBA будет игнорировать возникшую ошибку и передавать управление на следующий оператор, стоящий за тем, в котором возникла ошибка.

On error goto 0 — это режим по-умолчанию. В случае возникновения ошибки данный режим приведёт к появлению на экране стандартного обработчик ошибок VBA с кнопками End и Debug .

Resume

Данный оператор возобновляет выполнение программы. Применяется в выносном методе обработки ошибок.

resume label — возобновление с метки label

resume next — возобновление со следующего оператора

resume или resume 0 — возобновление с оператора, вызвавшего ошибку. Это имеет смысл, если вы устранили ошибку в своём обработчике. На мой взгляд, на практике такое применяется крайне редко.

Goto label

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

Досрочный выход из циклов (Do или For) и досрочный выход из подпрограмм (функции или процедуры). Могут пригодиться при обработке ошибок, но вообще это операторы и без того чрезвычайно полезны.

Объект Err

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

Свойство Err.Number — содержит числовой код ошибки, по которому их различают в программе. Поскольку Number — свойство по умолчанию, то вы можете его опускать, то есть Err и Err.Number — это эквиваленты. Значение ноль говорит о том, что ошибки не произошло.

Err.Description — содержит англоязычное краткое описание ошибки

Err.Source — возвращает имя модуля, в котором возникла ошибка

Err.Clear — сбрасывает последнюю ошибку. Err сбрасывается также при выполнении оператором Resume , Exit (любого типа кроме Do и For) и On Error .

Err.Raise — искусственно вызывает исключение указанного в переданном параметре типа. Можно использовать для тестирования вашей подсистемы обработки ошибок.

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

Обработка ошибок

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

try/except — перехватывает исключения, возбужденные интерпретатором или вашим программным кодом, и выполняет восстановительные операции.

try/finally выполняет заключительные операции независимо от того, возникло исключение или нет.

raise — дает возможность возбудить исключение программно.

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

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

Назначение исключений

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

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

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

Заключительные операции. Как будет показано далее, инструкция try/finally позволяет гарантировать выполнение завершающих операций независимо от наличия исключений.

Необычное управление потоком выполнения. И, наконец, так как исключения – это своего рода оператор «goto», их можно использовать как основу для экзотического управления потоком выполнения программы.

Примеры исключений

Предположим, что у нас имеется следующая функция:

Профессиональная обработка ошибок в Python Alex Grigorovich

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

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

Коды статуса против Исключений

Существует две основных модели обработок ошибок: Коды статуса и Исключения. Коды статуса могут использоваться в любом языке программирования. Исключения требуют поддержки языка/среды исполнения.

Python поддерживает исключения. Python и его стандартная библиотека использует исключения свободно для того, чтобы сообщить о различных состояниях, таких как IO ошибки, ошибки деления на ноль, ошибки пределов индексации, а также некоторые некритичные ситуаций, такие как конец итерации (хотя обычно эти ошибки скрыты). Большинство библиотек придерживаются этого принципа и вызывают исключения.

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

Небольшой пример

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

При вызове h() , мы получаем на выходе:

Исключения Python

Python исключениями являются объекты организованные в классовой иерархии.

Вот иерархия целиком:

Существует несколько специальных исключений, которые являются производными от BaseException , такие как SystemExit , KeyboardInterrupt и GeneratorExit . Еще есть класс Exception , который является базовым классом для StopIteration , StandardError и Warning . Все стандартные ошибки являются производными от StandardError .

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

Вызов исключений

Вызов исключений очень прост. Вы просто используете ключевое слово raise чтобы вызвать объект, который является подклассом Exception . Это может быть экземпляр Exception , одно из стандартных исключений (напр., RuntimeError ), или подкласс Exception , который вы получили. Вот небольшой фрагмент кода, который демонстрирует эти случаи:

Перехват исключений

Вы получили исключение, с условием except , как вы видели в примере. Когда вы получили исключение, у вас есть три варианта:

  • Пропустить (обработать его и продолжить работу).
  • Сделать что-то вроде записи в журнал, но получить повторно то же самое исключение, чтобы продолжить его обработку на более высоком уровне.
  • Вызвать другое исключение вместо текущего.

Пропустить исключение

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

Например, если вы получаете входящий файл, который может быть в различных форматах (JSON, YAML), вы можете попробовать проанализировать его с помощью различных средств. Если анализатор JSON создаёт исключение, которое показывает, что файл имеет некорректный формат JSON, вы пропускаете его и пробуете проанализировать через парсер YAML. Если парсер YAML также не справляется с задачей, тогда вы даёте исключению перейти на следующий уровень.

Обратите внимание, что другие исключения (например, file not found или no read permissions) будут переходить на следующий уровень и не будут обработаны конкретным исключением. Это хорошая тактика в том случае, если вы хотите использовать YAML парсер, когда анализ с помощью JSON парсера не удался.

Если вы хотите обрабатывать все исключения, тогда используйте except Exception . Например:

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

Перезапуск исключения

Чтобы перезапустить исключение, просто напишите raise без аргументов внутри обработчика. Это позволит выполнить некоторую локальную обработку, но также пропустит исключение для обработки на верхние уровни. Здесь, функция invoke_function() выводит тип исключения в консоль и затем повторно вызывает его.

Вызов Различных Исключений

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

Финальное утверждение

Иногда вы хотите убедиться, что код очистки выполняется, даже если где-то по пути возникло исключение. Например, у вас может быть подключение к базе данных, которое требуется закрыть, как только вы закончите. Это неправильный способ сделать это:

Если функция query() вызывает исключение, то вызов close_db_connection() никогда не будет выполнен и подключение останется открытым. Утверждение finally всегда выполняется после всех попыток обработчика. Вот как сделать это правильно:

Вызов open_db_connection() может не вернуть подключение или вызвать исключение. В этом случае нет необходимости закрывать соединение.

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

Диспетчеров Контекста

Контекстные менеджеры обеспечивают еще один механизм обработки ресурсов, таких как файлы или подключения к БД, которые выполняются автоматически, даже если исключения были вызваны. Вместо блоков try-finally, можно использовать определение with . Вот пример с файлом:

Теперь, даже если process() вызывает исключение, этот файл будет закрыт правильно сразу же когда область видимости блока with завершена, независимо от того, было исключение обработано или нет.

Ведение журнала

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

При записи полезно учитывать тип исключения, сообщение и маршрут ошибки. Вся эта информация доступна через объект sys.exc_info , но если вы используете logger.exception() метод в обработчике исключений, Python извлечёт всю необходимую для вас информацию.

Это лучший пример, которую я рекомендую:

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

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

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

Sentry

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

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

Работа с временной ошибкой

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

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

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

Полезные оформители

Два оформителя которые могут помочь в обработке ошибок, это @log_error , который записывает исключение и затем вновь вызывает его и @retry оформитель, который будет повторять вызов функции несколько раз.

Журнал ошибок

Вот пример простой реализации. Оформитель исключает объект logger. Когда он оформляет функцию и функция вызвана, он обработает вызов в блоке try-except, и если там было исключение сделает запись в журнал и наконец повторно вызовет исключение.

Вот пример, как его использовать:

Retrier

Здесь, очень хорошая реализация @retry оформителя.

Заключение

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

Обработка ошибок в коде программ РНР (стр. 1 из 8)

РОССИЙСКИЙ ХИМИКО-ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ

им. Д.И. Менделеева

ОБРАБОТКА ОШИБОК В КОДЕ ПРОГРАММ PHP

Новомосковск 2008
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ

РОССИЙСКИЙ ХИМИКО-ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ

им. Д.И. Менделеева

ОБРАБОТКА ОШИБОК В КОДЕ ПРОГРАММ PHP

Составитель: В. С. Прохоров
Содержание

1. КОНТРОЛЬ ОШИБОК

1.2.1 НЕСЕРЬЕЗНЫЕ ОШИБКИ

1.2.2 СЕРЬЕЗНЫЕ ОШИБКИ

1.2.2.1 Прекращение выполнения программы

1.2.2.2Возврат недопустимого значения

1.2.2.3 Ненормальное состояние программы

1.2.2.4 Вызов функции-обработчика

1.3 ДИРЕКТИВЫ РНР КОНТРОЛЯ ОШИБОК

1.3.1 ДИРЕКТИВА error_reporting

1.3.2 ДИРЕКТИВА display_errors

1.3.3 ДИРЕКТИВА error_log

1.4 УСТАНОВКА РЕЖИМА ВЫВОДА ОШИБОК

1.5 ОПЕРАТОР ОТКЛЮЧЕНИЯ ОШИБОК

1.5.1 ПРИМЕР ИСПОЛЬЗОВАНИЯ ОПЕРАТОРА @

1.5.2 ПРЕДОСТЕРИЖЕНИЯ ПО ПРИМЕНЕНИЮ ОПЕРАТОРА ОТКЛЮЧЕНИЯ ОШИБОК @

2 ПЕРЕХВАТ ОШИБОК. МЕТОД РЕГИСТРАЦИИ ОБРАБОТЧИКА ОШИБОК

2.1 ФУНКЦИЯ set_error_handler

2.2 ФУНКЦИЯ restore_error_handler()

2.3 ПРОБЛЕМЫ С ОПЕРАТОРОМ @

2.4 ГЕНЕРАЦИЯ ОШИБОК

2.5 СТЕК ВЫЗОВОВ ФУНКЦИЙ

2.6 ПРИНУДИТЕЛЬНОЕ ЗАВЕРШЕНИЕ ПРОГРАММЫ

3. ПЕРЕХВАТ ОШИБОК. МЕТОД ИСКЛЮЧЕНИЙ

3.1 БАЗОВЫЙ СИНТАКСИС

3.2 ИНСТРУКЦИЯ throw

3.3 РАСКРУТКА СТЕКА

3.4 ИСКЛЮЧЕНИЯ И ДЕСТРУКТОРЫ

3.5 ИСКЛЮЧЕНИЯ И set_error_handler()

3.6 КЛАССИФИКАЦИЯ И НАСЛЕДОВАНИЕ

3.7 БАЗОВЫЙ КЛАСС Exception

3.8 ИСПОЛЬЗОВАНИЕ ИНТЕРФЕЙСОВ

3.9.1 Неподдерживаемая конструкция try. finally

3.9.2 «Выделение ресурса есть инициализация»

3.9.3 Перехват всех исключений

3.10 ТРАНСФОРМАЦИЯ ОШИБОК

3.10.1 Серьезность «несерьезных» ошибок

3.10.2 Преобразование ошибок в исключения

3.10.3 Код библиотеки PHP_Exceptionizer

3.10.4 Иерархия исключений

3.10.5 Фильтрация по типам ошибок

Имеется мнение: «В любой программе есть хотя бы одна ошибка». На практике «хотя бы одна» означает «много» или даже «очень много».

Фаза «избавления» программы от ошибок (фаза отладки) является наиболее длительной и трудоемкой. Основное времяпровождение программиста (и не только) — это борьба с ошибками.

Одна из самых сильных черт РНР — возможность отображения сообщений об ошибках прямо в браузере. В зависимости от состояния интерпретатора сообщения будут выводиться в браузер или подавляться.

Для успешной борьбы с ошибками нужно научиться управлять настройками РНР, узнать о его тонких местах и о возможностях основных директив. Отдельное внимание следует уделять методикам отладки скриптов, а точнее — обработке сообщений об ошибках и предупреждений, которые могут возникнуть во время работы программы, а также выводу стека вызовов процедур (подобного тому, что существует в языках Java и Perl). Следует с осторожностью использовать оператор отключения предупреждений об ошибках.

Задача обработки ошибок в коде программы — одна из самых важных и популярных при программировании. Для ее успешного решения требуется уточнить понятие термина «ошибка» и определить его роль в программировании, а также изучить различные классификации ошибочных ситуаций. Эта задача может быть эффективно решена при использовании понятия «исключение» и способов применения конструкции try. catch. Использование механизма наследования и классификации исключений может сильно сократить код программы и сделать его универсальным. Существуют коды библиотек, позволяющих обрабатывать многочисленные ошибки и предупреждения, генерируемые функциями РНР, как обыкновенные исключения.

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

1. КОНТРОЛЬ ОШИБОК

Термин «ошибка» имеет три различных значений:

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

2. Внутреннее сообщение об ошибке («внутренняя ошибка»), которую выдает РНР в ответ на различные неверные действия программы (например, открытие несуществующего файла).

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

3. Пользовательское сообщение об ошибке («пользовательская ошибка»), к которой причисляются все сообщения или состояния, генерируемые и обрабатываемые самой программой. Например, в скрипте авторизации ситуация «введен неверный пароль» — ошибка именно такого рода.

1.1 РОЛИ ОШИБОК

Внутреннее сообщение об ошибке означает ошибку, которую нет смысла показывать в браузере пользователя. Это необходимо делать на этапе отладки скрипта, когда в роли пользователя выступает сам разработчик. Такое сообщение лучше всего записывать в файлы журнала для дальнейшего анализа, а в браузер выводить стандартный текст, например: «Произошла внутренняя ошибка, информация о ней будет доступна разработчику скрипта позже». Многие программисты предпочитают также в конце страницы выдавать дополнительные сведения об ошибке, т. е. записывать сообщение и в файл журнала, и выводить на экран. Такая практика в большинстве случаев помогает разработчику «на месте» выяснить, что же произошло.

Для записи сообщений об ошибках в журнал в РНР существуют специальные средства: директивы log errors, error log, а также функция error log () (подробнее см.п.п. 1.3.2, 1.3.3).

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

● внутреннее сообщение: ответ SQL-сервера, дата и время ошибки, номер строки в программе и т. д.;

● пользовательское сообщение: например, текст «Ошибка соединения с SQL-сервером, попробуйте зайти позже».

1.2 ВИДЫ ОШИБОК

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

$f = @fopen(«spoon.txt», «r»);

В этом примере код восстановления — это инструкция if, которая явно обрабатывает ситуацию невозможности открытия файла. Обратите внимание, что используется оператор @ перед fopen(), чтобы не получать диагностическое сообщение от самого РНР — оно не нужно, у нас же собственный обработчик ошибочной ситуации (код восстановления).

В данной терминологии диагностические сообщения, которые выдает РНР, также можно назвать кодом восстановления.

Ошибки по своей «серьезности» можно подразделить на два больших класса:

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

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

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

1.2.1 НЕСЕРЬЕЗНЫЕ ОШИБКИ

Для обработки нефатальных ошибок, после которых не требуется «персональное» восстановление, в РНР имеется инструмент, называемый установкой обработчика ошибок (или перехватом ошибок; подробнее см. п. 2).

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

1.2.2 СЕРЬЕЗНЫЕ ОШИБКИ

Серьезные ошибки в общем случае невозможно обработать с использованием set_error_handler(), потому что в каждом конкретном случае нужно писать «персональный» код восстановления.

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

Главный вопрос при работе с серьезными ошибками — написание кода восстановления. Он должен иметь достаточный контроль над ходом выполнения программы (например, мог выполнять инструкции return или break, а не только лишь завершал программу по exit ()).

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL