Обработка исключительных ситуаций. Предложения try-catch-finally, throw и класс Error

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

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

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


Такой подход весьма неплох в случае простых алгоритмов. Если же отслеживать нужно несколько потенциальных источников сбоя, то гораздо лучше применить специализированные средства борьбы с ошибками. В ActionScript это конструкция предложений try—catch—finally, предложение throw и класс Error. Все эти элементы описаны в ЕСМА-262, но в ActionScript появились только во Flash MX 2004, еще на один шаг приблизив скриптовый язык Flash к полному соответствию стандартам.

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

try {
statements
} catch (error){
error_statements
} finally {
fin_statements
}

где:
• try. В блоке данного предложения размещается код, выполнение которого может сопровождаться ошибками. Плейер должен попробовать (по-английски try — пробовать) проделать скрипт и, в случае возникновения ошибочной ситуации, перейти к исполнению исправляющего ее кода в блоке предложения catch.


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

Синтаксис предложения throw:

throw expression;

Здесь expression. — выражение, результат вычисления которого должен быть передан предложению catch как указатель на то, какая произошла ошибка. Это может быть строка с именем исключительной ситуации или ее номер. Но чаще для этого применяются объекты специального класса Error (о нем мы поговорим ниже);

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

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


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

В принципе, предложение catch не является в рассматриваемой конструкции try—catch-finally
обязательным. Однако без него она попросту не имеет смысла;

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

В качестве примера использования конструкции try—catch—finally приведем функцию для вычисления десятичных логарифмов:

function lg(arg:Number):Number {
try {
// Проверяем, принадлежит ли переданный аргумент к типу number.


Если нет —
// сигнализируем об ошибочной ситуации
if (typeof arg!= "number") {
throw "Неверный тип";
}
// Проверяем, не является ли значением аргумента NaN
if (isNaN(arg)) {
throw "Неопределенное значение";
}
// Убеждаемся, что аргумент неотрицателен (логарифм от отрицательных чисел
// не определен)
if (arg<0) {
throw "Недопустимое значение";
}
// Если условия ни одной из исключительных ситуаций не выполнились,
// вычисляем логарифм и возвращаем результат
return Math.log(arg)/Math-log(10);
// Прописываем код, который должен быть проделан в случае возникновения
// исключительной ситуаиии
} catch (е) { // Параметр е хранит значение, переданное throw
// В зависимости от того, какая исключительная ситуация возникла, выполняем
// разные действия
switch (e:String) {
case "Неверный тип":
return Math.log(Number(arg))/Math.log(10);
case "Неопределенное значение":
trace("Аргумент не является конечным числом");
return NaN;
саsе "Недопустимое значение":
trace("Аргумент не может быть меньше нуля"];
return NaN;
}
// Код в блоке finally выполнится всегда
} finally {
trace("Попытка вычислить логарифм осуществлена");
}
}

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


Оказывается, код в нем выполняется, даже если работа функции обрывается при помощи предложения return. Таким образом, скрипт, связанный с предложением finally, проделывается всегда.

У предложения throw также имеется интересная особенность. Если с предложением try не связано предложение catch (или же тип переменной catch не совпадает с типом поступившего значения), то результат вычисления переданного throw выражения отправляется в панель Output. Например;

try {
throw "Привет"; // В Output появляется - "Привет"
} finally {
}

Вообще же предложение throw совсем необязательно должно использоваться в коде предложения try. Разместить его можно совершенно п любом исполнимом коде. При его активации будет просмотрена цепочка областей видимости данного кода. Первое обнаруженное в ней предложение catch будет задействовано. Все предложения finally, которые встретятся интерпретатору по мере поиска предложения catch, будутактивированы. Если ни одного предложения catch найдено не будет, результат вычисления связанного с throw выражения будет передан в Output. Пример:

function func():Void {
try {
} finally {
trace("Пока"); // Выводит: Пока
}
throw "Привет";
}
try {
func();
} catch (e:String) {
trace(e); // Выводит: Привет
} finally {
trace("Пока"); // Выводит: Пока

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

Код в предложении try проделывается лишь до первого вызова предложения throw. Даже выполнив код обработки ошибки, интерпретатор не возврашается к проделыванию скрипта предложения try. А это означает, что одновременно можно «отловить» лишь одну ошибку. Впрочем, на практике несколько исключительных ситуаций сочетаются редко, поэтому данная особенность не является серьезным недостатком.

С одним предложением try может быть связано несколько предложений catch. Но для этого должно выполняться обязательное условие: параметры предложений catch должны быть строго типизированы. Иначе при компиляции возникнет ошибка.

Условие строгой типизации параметров при связывании одного предложения try с несколькими предложениями catch на первый взгляд кажется необоснованным. Однако это не так. Благодаря такому условию стала реальной одна замечательная возможность. А именно: код в блоке catch выполняется лишь в том случае, если тип поступившего от throw объекта данных соответствует типу параметра. Это позволяет по-разному реагировать на ошибку в зависимости от результата вычисления связанного с throw выражения — с помощью создания для каждого потенциально возможного типа отдельного предложения catch. Пример:

try {
throw 1;
} catch (a:Number) {
trace("Привет"); // Выводит: Привет
} catch (b:String) {
trace("Пока");
}

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

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

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

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

Объекты класса Error — это чрезвычайно простые структуры. Для них характерны всего два свойства и один метод:

• message. Данное свойство инициализируется при создании объекта и служит для хранения
строки с описанием возникшей исключительной ситуации;

• name. В этом свойстве хранится тип произошедшей ошибки. В обшем случае он эвпадает с
именем класса, к которому относится объект. Соответственно, для тасса Error свойство name будет хранить строку «Error». Практическую важность данное свойство имеет в случае создания собственных классов для описания нештатных ситуаций;

• toString(). Метод преобразует объект ошибки в строку. По сути же, при этом просто возвращается значение свойства message.

Обычно объект ошибки создается непосредственно в выражении предложения throw. В качестве параметра конструктор Error принимает строку, которая должна быть присвоена свойству message.

Например:
// Функция, вычисляющая котангенс
function cot(arg:Number):Number {
try {
if (typeof arg!= "number") {
throw new Error("Неверный тип аргумента");
}
return I/Math.tan (arg);
} catch (err:Error) {
trace(err.message);
}
}

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

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

ЕСМА-262 предусматривает наличие встроенных подклассов класса Error, отвечающих за основные разновидности исключительных ситуаций. При возникновении ошибки создается соответствующий объект и передается коду, обрабатывающему исключительные ситуации для данного алгоритма.

Встроенные подклассы класса Error выполняют сугубо служебные функции. Поэтому они являются недокументированными. Однако иметь представление о данных классах стоит.

Перечислим их:

EvalError (показывает, что глобальная функция eval() используется некорректно), SyntaxError (указывает на синтаксическую ошибку), Type Error (показывает, что операнд имеет тип, отличный от требуемого), ReferenceError (указывает, что ссылка является пустой), URIError (фиксирует ошибку при задании адреса).

Набор свойств у описанных подклассов класса Error такой же, как у него самого. Например:

var err=new SyntaxError("Синтаксическая ошибка");
trace(err.name); // Выводит: SyntaxError
trace(err.message); // Выводит: Синтаксическая ошибка
trace(err.toString()); // Выводит: Синтаксическая ошибка

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

Однако применение специализированных средств обработки ошибок — это хорошая практика.

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

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Action Script на эту тему:
Блок предложений
Предложение return
Предложение with
Предложения var и function
Предложения выражений и пустые предложения

Вернуться в раздел: Action Script / 6. Предложения