Опубликован: 19.08.2004 | Уровень: для всех | Доступ: платный | ВУЗ: Национальный исследовательский ядерный университет «МИФИ»

Лекция 12: Событийно управляемое программирование в .NET

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

FileStream s = null;
try {
    s = new FileStream(
        curName, FileMode.Open);
    ...

    catch (FileNotFoundException e){
        Console.WriteLine(
        "file {0} not found",
        e.FileName);
    }
    catch (IOException){
        Console.WriteLine(
        "some IO exception
        occurred");
    }
    catch {
        Console.WriteLine(
        "some unknown error
        occurred");
    }
    finally if (s != null)
        s.Close();
}

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

По результатам анализа приведенного фрагмента программы на языке C# рассмотрим основные характеристики обработки исключительных ситуаций с учетом особенностей среды вычислений Microsoft .NET.

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

Таким образом, при полном разборе случаев разработчиком программного обеспечения одно из условий обнаружения catch всегда удовлетворяется (естественно, в том случае, если список условий не пуст).

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

Далее, заметим, что тип конкретизации исключительной ситуации exception должен быть выводим из базового класса системы типизации Microsoft .NET под названием System.Exception.

Наконец, важным свойством оператора является тот факт, что при отсутствии явного указания параметра exception подразумевается обработка событий в соответствии с умолчаниями, принятыми для полей и методов базового класса System.Exception системы типизации Microsoft .NET.

Для иллюстрации особенностей механизма обработки исключительных ситуаций в среде вычислений Microsoft .NET приведем фрагмент содержательного описания системного базового класса System.Exception системы типизации .NET, оформив его для наглядности в виде таблицы 23.1.

Как видно из приведенной таблицы, класс System.Exception представляет собой описание многочисленных объектов и методов, которые контролируют стандартный ход обработки исключительных ситуаций, возникающих в программных реализациях (в том числе и разработанных на языке C#). При этом каждая исключительная ситуация характеризуется уникальным диагностическим сообщением.

Таблица 23.1. Некоторые свойства и методы класса System.Exception.
Имя свойства или метода Функции свойства или метода
Свойства:  
e.Message сообщение об ошибке в виде строки
e.StackTrace просмотр стека вызова методов как строки
e.Source приложение или объект, которые вызвали исключительную ситуацию
e.TargetSite метод объекта, который вызвал исключительную ситуацию
...  
Методы:  
e.ToString() возвращает имя исключительной ситуации
...  

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

Исходя из вида изученного оператора try языка программирования C#, а также из стандартных свойств и методов обработки исключений класса System.Exception среды вычислений .NET, можно предположить, что существуют явные и неявные источники возникновения исключительных ситуаций.

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

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

throw new FunnyException(10);
class FunnyException : ApplicationException
{
    public int errorCode;
    public FunnyException(int x){
    errorCode = x; 
    }
}

Как видно из приведенного фрагмента программы, оператор throw реализует явный вызов экземпляра класса FunnyException с генерацией кода ошибки.

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

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

Приведем для сведения небольшой фрагмент сложной структуры иерархии стандартных исключительных ситуаций среды вычислений Microsoft .NET:

Exception
    SystemException
        ArithmeticException
          DivideByZeroException
          OverflowException
            ...
        NullReferenceException
        IndexOutOfRangeException
        InvalidCastException
        ...

Заметим, что существует еще более общий уровень иерархии исключений, чем SystemException, а именно, уровень Exception.

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

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

Продолжим анализ иерархии исключительных ситуаций среды вычислений .NET.

ApplicationException
...// специализированные исключения
    ...
    IOException
        FileNotFoundException
        DirectoryNotFoundException
        ...
    WebException
    ...

Отдельный подкласс исключений составляют исключения, встречающиеся в приложениях.

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

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

Рассмотрим особенности реализации механизма реакции на исключительные ситуации в языке программирования C# и среде Microsoft .NET.

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

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

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

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

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

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

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

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

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

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

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

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

Для более подробного самостоятельного ознакомления с тематикой лекции рекомендуется следующий список источников: [ 24, 28, 36, 66-69, 79-81].