Национальный исследовательский ядерный университет «МИФИ»
Опубликован: 19.08.2004 | Доступ: свободный | Студентов: 6054 / 400 | Оценка: 4.16 / 3.86 | Длительность: 10:50:00
Специальности: Программист

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

Аннотация: В данной лекции будут рассмотрены вопросы, относящиеся к идеологии, математическому основанию и обзору возможностей событийно управляемого проектирования и реализации программных систем - одного из важнейших аспектов современного объектно-ориентированного программирования.
Ключевые слова: среда вычислений, событие, соотнесение, предметная область, объект, индивидуализирующая функция, функция, истина, ложь, аппликация, произвольное, двухуровневая концептуализация, оценивающее отображение, возможный индивид, действительный индивид, состояние, определение, обработка событий, интерфейс, ПО, инициация, меню, активация событий, означивание, конкретизация, механизм делегатов, функция обратного вызова, callback function, делегат, безопасность кода, контроль соответствия типов, переменная-делегат, присваивание, greeting, шаг концептуализации, значение, аналогия, теория вычислений, домен, программирование, исключительная ситуация, объект первого рода, First, class object, формальная система, ламбда-исчисление, комбинаторная логика, обобщение, форма Бэкуса-Наура, БНФ, выход, receiver, динамический метод, абстрактный метод, abstract, виртуальный метод, virtual, замещенный метод, override, экземпляр, new, динамическая, вывод, параметр, управление событиями, класс, try, catch, блок операторов, finally, закрытие файла, net, условия обнаружения, список, синтаксис, exception, система типизации, системный базовый класс, диагностическое сообщение, стек вызовов, исключение, операции, время выполнения, компилятор, деление на нуль, переполнение, анализ, подкласс, файл, поиск, программа, дамп, алгоритм, ООП, процент, концептуализация, основание, верифицируемость

Ключевым элементом для понимания природы взаимодействия объектов программы в среде вычислений является понятие события.

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

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

В соответствии со схемой двухуровневой концептуализации, оценивающее отображение ||.|| переводит индивид h- языка (в частности, языка программирования) в h. Затем возможный (потенциальный) индивид h из семейства возможных индивидов H переводится событием i из семейства соотнесений Asg в действительный индивид h(i) из семейства действительных индивидов Ui. Аналогично, следующий шаг преобразований, управляемый событием j из семейства Asg, переводит h(i) в состояние h(i)(j) (см. рис. 23.1).

Схема двухуровневой концептуализации объектов данных.

Рис. 23.1. Схема двухуровневой концептуализации объектов данных.

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

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

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

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

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

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

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

В целях реализации механизма событий в языке программирования C# предусмотрен так называемый механизм делегатов.

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

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

В качестве интуитивного определения понятия делегата отметим, что под делегатом понимается метод, который ведет себя как тип.

В качестве иллюстрации приведем описание типа- и переменной-делегата на языке С#:

delegate void Notifier (string sender);    
// обычное описание метода с
// ключевым словом delegate

Notifier greetings;
// описание переменной-делегата

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

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

void SayHello(string sender) {
    Console.WriteLine(
        "Hello from " + sender);
}
greetings = new Notifier(SayHello);

Как видно из приведенного примера, на основе ранее описанного типа-делегата Notifier в соотнесении с вновь описанным методом SayHello осуществляется присваивание значения переменной greetings. Фактически данный пример изображает первый шаг концептуализации.

Проиллюстрируем далее порядок вызова переменной-делегата следующим фрагментом программы на языке C#:

greetings("John");
// активирует SayHello("John") =>
    "Hello from John"

Как мы видим, означивание переменной-делегата greetings активизирует соотнесенный с ней на предыдущем шаге метод SayHello в соотнесении "John" с генерацией состояния "Hello from John". Фактически данный пример изображает второй шаг концептуализации.

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

void SayGoodBye(string sender) {
    Console.WriteLine(
    "Good bye from " + sender);
}
greetings = new Notifier(SayGoodBye);
greetings("John");
//SayGoodBye("John") =>
    "Good bye from John"

Как видно из приведенного примера, переменная-делегат greetings типа Notifier может принимать в качестве значения любой из методов SayHello и SayGoodBye.

В данном случае, в отличие от предыдущего примера, значением, вычисляемым в ходе выполнения метода SayGoodBye, соотнесенного с переменной-делегатом greetings, является конкретизация "Good bye from John", полученная на основе означивания SayGoodBye("John").

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

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

Прежде всего, следует отметить то обстоятельство, что переменные-делегаты могут иметь пустое значение null, что соответствует отсутствию назначенного им метода. Здесь проявляется аналогия с теорией вычислений Д. Скотта в том отношении, что любой домен непременно имеет неопределенное значение. Эта параллель наводит на предположение, что теория вычислений Д. Скотта способна адекватно формализовать событийно управляемое программирование. Оказывается, что это предположение весьма важно, а его исследование - продуктивно.

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

Еще одной особенностью переменных-делегатов является их принадлежность к классу объектов первого рода (first class object). Согласно правилам языка программирования C#, делегаты можно хранить в структурах данных, передавать как параметры и т.д.

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