Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП.doc
Скачиваний:
23
Добавлен:
08.11.2018
Размер:
1.4 Mб
Скачать
  1. События

Рассмотрим взаимодействие двух объектов в программе. Обычный вариант взаимодействия – вызов метода. В этом случае один объект вызывает заранее известный метод другого объекта (рис. 14).

Но возможен и другой вариант, когда объекту необходимо, чтобы какой-либо другой объект извещал его о чем-то, точнее, о том, что произошло некое событие. При этом изменения в исходный код класса объекта, который будет извещать о происходящем событии, вноситься не должны – допустим, это библиотечный класс, или класс, созданный другим разработчиком. Рассмотрим, как это может происходить: первым шагом объект, который желает получать уведомления должен сообщить об этом тому объекту, который эти уведомления посылает. После этого может быть начата посылка уведомлений. Что может представлять из себя посылка уведомлений: единственно возможный вариант – это вызов метода. То есть, когда объект сообщает, что хочет получать уведомления о том, что произошло событие, он должен передать адрес метода, который должен быть вызван при возникновении события (рис. 15). Такой метод называют обработчиком события.

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

Процедурный тип

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

Примеры объявления процедурных типов:

type //тип указатель на функцию без параметров, возвращающую //значение типа integer TIntegerFunction = function: integer;

//тип указатель на процедуру без параметров TProcedure = procedure;

//тип указатель на процедуру с единственным параметром //типа string TStrProc = procedure(const S: string);

//тип указатель на функцию получающую параметр типа double //и возвращающую значение типа double TMathFunc = function(X: double): double;

Можно объявить переменные этих типов:

Var F: TintegerFunction; Proc: TProcedure; SP: TStrProc; M: TMathFunc;

Можно объявлять переменную, не создавая специальный тип:

Var M: function(X: double): double;

Можно передавать значение процедурного типа в качестве параметра подпрограммы:

//процедура получает единственный параметр типа указатель //на функцию, тип описан выше procedure FuncProc(P: TIntegerFunction);

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

Для примера объявим две функции: функцию, которая вычисляет сумму двух чисел Sum, и вычисляющую разность двух чисел Sub. Обе функции получают два параметра типа integer и возвращают значение типа integer:

function Sum(X,Y: integer): integer; function Sub(X,Y: integer): integer;

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

type TArithmetic: function(X,Y: integer): integer;

Заведем переменную соответствующего типа:

Var F: TArithmetic;

Этой переменной можно присваивать как значение указатель на функцию Sum, так и на функцию Sub:

if (SomeCondition) then F := Sum else F := Sub;

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

A := Sum(10,20); //будет вызвана функция Sum A := Sub(10,20); //будет вызвана функция Sub A := F(10,20); //будет вызвана функция Sum или Sub в зависи- //мости от того, какое значение было присвоено пременной F

В двух первых случаях функции вызываются через их имена, в третьем – через указатель на функцию.

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

Type TSomeIntegerFunction = function: integer; var F: TSomeIntegerFunction;

Function IntegerFunction1: integer; Begin result := 256; End;

Function IntegerFunction2: integer; Begin result := 16; End;

procedure FuncProc(P: TSomeIntegerFunction); var Test: integer; begin Test := P; ... end;

procedure Test; begin F := IntegerFunction1; FuncProc(F); //при выполнении FuncProc переменной Test будет присвоено //значение 256 FuncProc(IntegerFunction2); //при выполнении FuncProc переменной Test будет присвоено //значение 16 End;

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

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

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

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

Если необходимо описать тип указатель на метод, при его описании используется служебное слово of object:

Type //указатель на метод, представляющий собой процедуру //без параметров TMethod = procedure of object; //указатель на метод, представляющий собой процедуру //с единственным параметром типа TObject TNotifyEvent = procedure(Sender: TObject) of object;

В действительности тип указатель на метод представляет собой пару указателей – указатель на сам метод и указатель на объект, к которому он относится.