procedure WMSize(var message:tWMSize);message WM_Size;
{начальный вариант синтаксиса}
end;
tMyControl2=
class(tMyControl1)
procedure Resize(var Info);message WM_Size; {так тоже можно}
end;
В модуле Messages описаны обработчики практически всех необходимых сообщений Windows. Каждый обработчик имеет идентификатор — целочисленную константу. Например, WM_Size — идентификатор сообщения об изменении размера экранной формы, WM_Move — идентификатор сообщения о ее перемещении и т. д.
В обработчиках сообщений можно вызвать прародительский метод с использованием только одного слова inherited, без указания его имени: он находится автоматически по индексу.
Самый общий обработчик — метод DefaultHandler, описанный в классе tObject. При обработке сообщения обычно надо вызвать в конце обработчик для прародителя:
procedure MyMsgHandler(var message);message WM_command;
begin
MyProcessing; {некая процедура обработки сообщения}
inherited; {в отличии от конструктора, вызов в самом конце}
end;
При получении объектом от операционной системы Windows сообщения описанного типа (идентификация сообщений происходит автоматически по индексам) вызывается соответствующий обработчик. В описанном выше примере это MyMsgHandler.
Однако обычно нет необходимости обрабатывать сообщения Windows, так как имеется механизм делегирования событий, о котором сейчас пойдет речь.
При необходимости обработки сообщений в обработчике события (см. следующий параграф) надо вызвать метод application. processMessages.
4.15. События и их делегирование. Обработка событий.
События — это свойства процедурного типа. Их название принято начинать с префикса "on". Для описания события некого типа (как описать новый тип события будет рассказано далее) в объекте надо описать поле того же типа, что и свойство. Реально это поле служит указателем на процедуру обработчика события. Кроме того, надо описать собственно свойство:
fOnMyEvent:tMyEvent;
property OnMyEvent:tMyEvent
read fOnMyEvent
write fOnMyEvent;
Тут поле fOnMyEvent является ссылкой на процедуру, с помощью которой осуществляется обработка события. Это поле доступно по чтению и записи! Присваивание свойству значения — это присваивание полю fOnMyEvent указателя на метод, который будет вызываться при наступлении события. Чтение — это метод. Эти методы называются обработчиками событий. Если никакой обработчик не присвоен полю-указателю, то там хранится значение notAssigned.
Пример:
присваивание
application. onActivate:=MyMethod; {MyMethod — имя некой процедуры – обработчика события}
означает, что при запуске приложения будет сначала выполнена процедура MyMethod.
Возможность такого рода действий связана с тем, что указание имени процедуры, функции, массива, объекта в Delphi — это указание соответствующего адреса, а он в принципе может быть переприсвоен. Таким образом, события описываются как свойства, доступные по записи, и при выполнении условия совместимости по типам их обработчики могут быть переприсвоены (говорят, что события делегированы).
Общим для описания типа "событие" является первый параметр Sender типа tObject и ключевая фраза of object после описания типа. Ниже приведен пример задания и использования определенного пользователем типа события tMyEvent:
type
{tNotifyEvent=procedure(Sender:tObject)of object;}
{предопределенное простейшее событие}
tMyEvent=procedure(Sender:tObject;var aMyVar:tMyVar) of object;
tObj1=
class
fOnMyEvent:tMyEvent;
property onMyEvent:tMyEvent
read fOnMyEvent
write fOnMyEvent;
end;
tObj2=
class
procedure MyEventProcessing1(Sender:tObject;
var aMyVar:tMyVar);
procedure MyEventProcessing2(Sender:tObject;
var aMyVar:tMyVar);
end;
...
var aObj1:tObj1;
aObj2:tObj2;
myKey:Boolean;
begin
aObj1:=tObj1.Create;
aObj2:=tObj2.Create;
...
if MyKey
then
aObj1.onMyEvent:=aObj2.MyEventProcessing1
else
aObj1.onMyEvent:=aObj2.MyEventProcessing2;
...
end.
При делегировании можно присваивать методы других классов! Даже не являющихся потомком и прародителем! Но при этом должно быть полное соответствие списков параметров обработчиков событий со списком параметров процедурного типа события.
4.16. Методы класса и указатели на класс.
Некоторые методы бывает нужно вызывать без создания экземпляра класса (например, для получения информации о имени класса, размере экземпляра класса и т. п.). Такие методы называются методами класса - в C++ и Java они называются статистическими методами (не путать со статистическими методами в смысле TP и Delphi!). Методы класса имеют перед словами procedure или function ключевое слово class (за исключением конструкторов, которые также являются методами класса, но для которых синтаксис, как мы знаем, другой):
type
tMyObj=
class
class function MyGetSize:String;
end;
var aMyObj:tMyObj;
begin
...
Label1.caption:=tMyObj. MyGetSize; {вызов метода класса tMyObj}
aMyObj:=tMyObj. Create; {вызов другого метода класса tMyObj}
Label2.caption:=aMyObj. MyGetSize; {вызов метода класса с
экземпляром класса}
...
end;
Возможно описание указателя на класс с помощью ключевой фразы class of. Это иногда требуется в программах, использующих сложную иерархию объектов, для унифицированного динамического создания, копирования и уничтожения объектов, принадлежащих разным классам. В модуле SysUtils указатель на tObject описан так:
type tObject=class;
tClass=class of tObject;
Имеются следующие важнейшие стандартные функции классов, определенные в tObject:
· class function ClassName:string — возвращает имя класса, вызвавшего объект;
· class function ClassParent:tClass — возвращает указатель на класс прародителя;
· function ClassType:tClass — возвращает указатель на класс вызвавшего объекта;
· class function ClassInfo:Pointer — возвращает указатель на структуру (запись), содержащую информацию о классе.
Также имеется ряд других стандартных функций классов.
Пример использования указателя на класс:
tMyObj=
class
...
end;
tMyObjRef=class of tMyObj;
tChildObj=
class(tMyObj)
...
end; {/class}
var
aMyObjRef:tMyObjRef;
aMyObj:tMyObj;
aChildObj:tChildObj;
После такого объявления можно проводить присваивания такого рода:
if...
then
aMyObjRef:=tMyObj; {при выполнении условия aMyObjRef
стало типом tMyObj}
else
aMyObjRef:=tChildObj; {иначе - типом tChildObj}
aMyObj:=aMyObjRef. Create; {создали объект, тип которого
определяется aMyObjRef}
Без указателей на класс пришлось бы писать вызовы конструктора Create внутри оператора if по отдельности для tMyObj и tChildObj.
Указатели на класс удобно использовать совместно с булевским оператором is, возвращающим true в случае совместимости по присваиванию объекта, указанного перед is, с экземпляром класса, упомянутого после is:
if aMyObj is tAnyObj then...;
Например, если aMyObj – кнопка типа tBatton, а в качестве tAnyObj написано tWinControl, оператор is возвратит true. Если же aMyObj – метка типа tLabel, он возвратит false, так как tLabel является потомком tControl, но не tWinControl.
Существует также специальный вариант приведения типов для объектов при помощи оператора as:
aObj1:=aObj2 as tObj1; {aObj2 вызывается в точности как экземпляр его
класса-прародителя tObj1}
aMyVar:=(aMyObj as tMyRecordObj).Data; {тут Data - свойство;
если это обычное поле, приведение не имеет смысла}
Понятно, что такое приведение возможно только к типу класса-прародителя.
Этот тип приведения очень часто используется в обработчиках событий. Например, если мы хотим изменить координату “x” элемента типа tImage, перетаскиваемого “мышью”, то в обработчике onDragOver элемента, над которым мы его перетаскиваем, надо написать
If Sender is tImage then( Sander as tImage).x=x;
При этом написать Sender. x=x нельзя, так как типом Sender является tObject, а в нем поле x не определено.
4.17. Дополнительные возможности Object Pascal.
4.17.4. Функции
а) могут возвращать значения определенных пользователем типов, включая сложные;
б) внутри каждой функции предопределена доступная по чтению и записи локальная переменная Result, имеющая тот же тип, что и функция. Ее значение до первого присваивания не определено. После завершения процедуры-функции эта функция возвращает значение, присвоенное переменной Result. Использование переменной Result является более наглядным и современным, чем обычный синтаксис языка PASCAL для функций, где значение возвращается через доступную только по записи локальную переменную с именем, совпадающим с именем функции.
4.17.5. Оператор CASE.
Значения вариантов (в том числе диапазоны) не могут пересекаться. Расположение вариантов в порядке возрастания позволяет компилятору оптимизировать код.
4.17.6. Открытые массивы.
Открытым массивом называется массив, используемый как формальный параметр процедуры, длина которого задается не в месте задания процедуры, а при ее вызове. При этом при ее вызове могут использоваться массивы разной длины.
Замечание: если нужен массив переменной длины, для этих целей используют список (мы уже знаем, как это делать). Правда, работа с массивом требует меньше памяти и идет быстрее, чем со списком. В Delphi начиная с версии 2.0 одномерные открытые массивы можно использовать без описания соответствующего типа. С версии 4.0 поддерживаются двумерные открытые массивы. Имеются предопределенные функции Low и High, в качестве параметра которых подставляется имя массива. При этом Low возвращает номер первого элемента, а High — последнего. Надо отметить, что в Delphi (как и в С++, JavaScript и Java) все номера начинаются с 0, а не с 1.
Пример:
program ArcDemo; {MyData - открытый массив}
function Mean(MyData:array of Real):Real;
var i:Integer;
begin
Result:=0;
for i:=Low(MyData)to High(MyData)do {цикл от первого до
последнего элемента}
Result:=Result+MyData[i];
Result:=Result/(High(MyData)-Low(MyData)+1);
end;
var X:Real;
begin
X:=mean([1,9,9,7]); {задаем массив путем перечисления элементов}
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |


