КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Interface
Begin МЕТОДЫ Методы служат для обработки информации, содержащейся в полях. Доступ к полям может осуществляться без дополнительных (формальных) параметров. Формальные параметры у методов обычно служат для обмена информацией с другими классами. В классе метод только объявляется. Описывается он в разделе реализации модуля (секция Implementation). При описании указывается имя класса-владельца и через точку имя метода. Например, Procedure TMyClass.AMethod(Param:AType); Begin end;. Внутри begin... end можно вызывать любые методы предков с указанием имени предка. Ближайший предок, имеющий такое же имя, как у данного метода, может вызываться с помощью зарезервированного слова inherited. Методы могут синтаксически оформляться следующим способом: • Procedure; • Function; • Constructor - вид Procedure, служащий для построения объекта, инициализации полей и правильного вызова так называемых виртуальных методов; • Destructor - вид Procedure, служащий для освобождения памяти, т.е. При построении объекта автоматически объявляется дополнительная переменная Self. Тип этой переменной совпадает с классом, породившим данный объект. Переменная Self является локальной и определена только внутри данного экземпляра класса. При необходимости внутри любого метода можно воспользоваться этой переменной. ВИДЫ МЕТОДОВ Методы бывают разных видов. По умолчанию методы являются статическими (Static) Методы в разных классах могут иметь одинаковые имена. При вызове методов обязательно указывается через точку имя объекта, вызвавшего данный метод. Например, пусть класс AClassType породил объект AnObject. Если вызывается метод AStaticMethod, то вызов записывается так: AnObject.AStaticMethod;. Если в классе AClassType непосредственно такого метода нет, то вызывается ближайший по линии наследования метод с заданным именем. Недостаток статических методов заключается в следующем. Пусть метод AStaticMethod вызывает внутри себя другой метод, например Second-Method. Пусть по линии наследования определено несколько методов с именем SecondMethod. В данном случае AStaticMethod вызовет ближайший по программному описанию SecondMethod, независимо от того объекта, который вызвал метод AStaticMethod.Таким образом, при использовании статических методов нельзя конкретно указать, какой из имеющихся по линии наследования нескольких методов с одним именем требуется вызвать. МЕТОДЫ VIRTUAL И ПОЛИМОРФИЗМ Особенности виртуальных (Virtual) методов рассмотрим на примере. Пусть объявлен класс AClass. Этот класс имеет потомка, который, в свою очередь, также имеет потомка, например AClass -> BClass -> CClass. Пусть AClass содержит метод Drag, который служит для рисования некоторой геометрической фигуры. При этом внутри метода Drag вызывается метод Show для рисования заданной фигуры. Пусть метод show реализован в каждом из трех указанных классов для рисования точки (класс AClass), окружности (BClass) и квадрата (CClass). Метод Drag содержится только в AClass. Так как нужная фигура вызывается для рисования из метода Show, нет необходимости дублировать его в других классах. Пусть заданы три объекта: Var A:AClass; В:Bclass; С:CClass. Пусть с помощью соответствующих конструкторов эти объекты созданы. Пусть вначале все три метода Show объявлены как статические. Запишем вызов на рисование геометрической фигуры: с. Drag;. Рассмотрим, какая фигура изобразится в данном случае. По линии наследования от CClass, вызвавшего Drag, в классе AClass найдется метод Drag. При вызове происходит обращение к Show. В данном случае вызовется ближайший к Drag метод, находящийся в классе AClass, т.е. изобразится точка. Вызвать метод Show из класса BClass для рисования окружности или вызвать Show из CClass и нарисовать квадрат с помощью статических методов нельзя. Итак, в данном случае ставится следующая задача. Вызов C.Drag; должен нарисовать квадрат, B.Drag; - окружность, соответственно A.Drag; -точку, т.е. метод Show для С.Drag должен вызываться из класса CClass, для B.Drag- из класса BClass, а для A. Drag- из класса AClass. Такая задача решается с помощью виртуальных методов show. Тогда вызов show будет определяться конкретным фактическим объектом. Виртуальные методы определяются с помощью ключевого слова Virtual. По линии наследования все реализации одного и того же виртуального метода должны иметь одинаковые заголовки. С помощью зарезервированного слова Virtual определяется самая первая реализация виртуального метода, все последующие реализации переопределяются с помощью ключевого слова Override. Если заданный метод появляется далее по линии наследования без ключевого слова Override, то свойство виртуальности теряется для данного класса и всех его потомков. Статические методы определяются на этапе компиляции, а виртуальные - при выполнении программы. Для реализации вызовов виртуальных методов в зависимости от фактического объекта строится так называемая таблица виртуальных методов. Построение этой таблицы возлагается на соответствующий конструктор. Свойство одного и того же метода вести себя по-разному называется полиморфизмом (многоформным). Например, метод Drag, указанный в приведенном выше примере, может построить точку, окружность или квадрат. Для реализации полиморфизма используются виртуальные методы. Итак, наряду с инкапсуляцией и наследованием, третьей особенностью типа класс по сравнению с другими типами является полиморфизм. МЕТОДЫ DYNAMIC Механизм Dynamic доступа к другим методам подобен механизму Virtual. В обоих случаях адрес нужной процедуры или функции определяется фактическим объектом. Отличие заключается в том, что в данном случае строится таблица динамических методов. Обращение к этой таблице происходит медленнее, чем к таблице виртуальных методов, зато объем программы получается меньше. Методы Dynamic целесообразно использовать, когда класс имеет множество потомков, а число переопределяемых методов небольшое. МЕТОДЫ MESSAGE Методы Message - обработки сообщений - представляют собой особую форму динамических методов. Обработчики сообщений всегда являются процедурами. Для ускорения поиска в таблице динамических методов после ключевого слова Message записывается константа целого типа, являющаяся индексом нужного метода. В обработчике сообщений имеется один параметр Var. Методы Message - это одно из звеньев взаимодействия компонентов Delphi с Windows. Объявление обработчика сообщений выглядит приблизительно так: Procedure Handle_WM_Paint(var Msglnfo); Message WM_Paint;. Для поиска нужного адреса в таблице динамических методов в данном примере записана константа WM_Paint. Многие константы для Message зарезервированы за конкретными сообщениями Windows и выбирать их произвольно нельзя. Указанная константа поиска данного сообщения Windows может быть переопределена в каком-либо предке по линии наследования для данного объекта. Если константа Message не найдена, то вызывается метод обработки сообщений, заданный по умолчанию. МЕТОДЫ ABSTRACT Обычно методы создаются для выполнения каких-то конкретных действий. Если по какой-либо причине это выполнить не удается, метод в классе может быть зарезервирован с обязательным переопределением его в классах потомков. Такой метод помечается ключевым словом Abstract. Если абстрактный метод не переопределен, то вызов такого метода приводит к вызову специальной процедуры Abstract, которая генерирует исключительную ситуацию. Абстрактным не может быть статический метод, так как статические методы нельзя переопределять. Например, Type TmyParent=class Procedure AMethod;virtual;abstract; End; TmyClass=class(TmyParent) Procedure AMethod;override; End; МЕТОДЫ OVERRIDE Как указано выше, зарезервированным словом Override помечаются переопределенные виртуальные или динамические методы (пример выше). МЕТОДЫ CLASS Исходное назначение методов - определять поведение экземпляров объектов какого-либо класса. В некоторых случаях необходимо иметь ситуацию, когда поведение, задаваемое для метода, не должно зависеть от реально существующего объекта. Такая ситуация возникает с методом, который, например, должен возвращать имя класса. В этом случае метод помечается ключевым словом Class. В отличие от других команд, таких, как Dynamic, Virtual и т.д., слово Class ставится перед заголовком метода, например Class Procedure AMethod;. ПРИМЕР ПРИЛОЖЕНИЯ 14 Рассмотрим пример по использованию конструкторов и деструкторов в объектах. Кроме того, посмотрим, как можно использовать указатели на методы и методы class. Пусть на форме динамически строится совокупность объектов типа TButton. Далее пусть эти объекты последовательно разрушаются с выдачей соответствующих сообщений о числе объектов. Рассматривать этот пример будем последовательно, вводя все новые и новые конструкции и проверяя их непосредственно в среде Delphi. ДИНАМИЧЕСКОЕ СОЗДАНИЕ КОМПОНЕНТОВ Пусть имеется форма, у которой есть только обработчик события Mouse Down, с помощью которого можно динамически строить кнопки, нажимая на левую клавишу мыши. Закрепляется каждая кнопка на форме в позиции координат точки, в которой была нажата клавиша мыши. Вот код данного обработчика: procedure TForm1.FormMouseDown(Sender:TObject; Button: TMouseButton; Shift: TShiftState; X, T: Integer); Var Btn:Tbutton; Btn:=TButton.Create(self) Btn.Parent:=self; Btn.Left:=x; Btn.Top:=y; Btn.Width:=Width+50; Btn.Caption:=Format('Кнопка x,y= %d,%d',[x,y]); end;. В Delphi ключевое слово self используется, например, когда нужно явно обратиться к текущей форме в одном из ее методов. Динамическое создание компонентов является тем случаем, когда требуется передать конструктору create параметр Owner (указать владельца создаваемого объекта), а затем присвоить то же значение свойству Parent (указать родителя). В обоих случаях нужно передавать имя текущей формы (не всегда параметры Owner и Parent совпадают, как в данном случае). Таким образом, обработчик выше будет корректно работать, если параметр self заменить на Form (пусть такое имя имеет переменная типа Form). Следует отметить, что код, приведенный выше, обычно записывается с использованием оператора with: procedure TForm1.FormMouseDown(Sender: TObject/Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin with TButton.Create(self) do begin Parent:=self; Left:=x; Top:=y; Width:=Width+50; Caption:=Format('Кнопка x,y= %d,%d,[x,y]); end; end;. Ниже приводится объявление модуля, используемого в данном случае: unit priml4; uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Menus; Type TForm1 = class(TForm) procedure FormMouseDown (Sender: TObject;Button: TMouseButton;Shift: TShiftState; X, Y: Integer); end; var Form1: TForm1;. Вариант решения примера представлен на рис 34.
Рис.34 ИСПОЛЬЗОВАНИЕ КЛАССА СО СЧЕТЧИКОМ ОБЪЕКТОВ Пусть в примере 14 будем строить кнопки некоторого собственного типа, унаследованного от типа TButton. Построим для этих целей класс, в котором предусмотрим счетчик создаваемых объектов. Для хранения числа создаваемых объектов понадобится переменная целого типа. Объявим ее в секции implementation: var CountBtns: integer = 0;. Эта переменная содержит общую информацию о форме и представляет данные модуля, поэтому она не включена как поле в класс. Для доступа к такой информации понадобится метод класса. Теперь представим объявление нашего класса: type TMyButton=class(TButton) public Constructor Create{AOwner:TComponent); override; Destructor Destroy; override; class function GetCount:integer; end;. Увеличение счетчика объектов предусмотрим при вызове конструктора, уменьшение - при вызове деструктора. Для построения объектов и их разрушения, естественно, будем использовать стандартные методы. Ниже приводятся описания деструктора и конструктора: constructor TMyButton.Create (AOwner: TComponent); begin inherited; Inc(CountBtns); end; destructor TMyButton.Destroy; begin dec(CountBtns); inherited; end;.
Так как переменная countBtns объявлена в секции implementation, обратиться к ней извне модуля нельзя. Только метод класса позволяет нам прочитать ее текущее значение: class function TMyButton.GetCount: integer; begin Result:=CountBtns; end;. Теперь можно создавать объекты нашего нового типа TMyButton, немного изменив приведенный выше код обработчика FormMouseDown: procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin with TMyButton.Create(self) do begin Parent:—self; Left:=x; Top:=y; Width:=Width+60; Caption:=Format{'%d кнопка x,y= %d,%d',[GetCount,x,y]); end;. Класс-функция GetCount может вызываться, в отличие от обычных методов, как из объекта (в обработчике FormMouseDown), так и из класса. Например, добавим таймер (компонент TTimer) на форму и обеспечим при срабатывании таймера изменение заголовка формы с помощью следующего обработчика события OnTimerr procedure TForm1.Timer1Timer(Sender: TObject); begin Caption:=Format('Кнопок на форме %d',[TMyButton.GetCount]); end;. В результате решения получим, например, следующий вариант (рис. 35). ОТСЛЕЖИВАНИЕ РАЗРУШЕНИЯ ОБЪЕКТОВ Проследим за разрушением объектов на форме. Очевидно, это можно увидеть в секциях initialization и finalization, т.е. после того, как форма начнет разрушаться. initialization MessageBox(0,PChar(Format('На форме %d кнопок', [TMyButton.GetCount])),'Инициализация',mb_ok); finalization MessageBox(0,PChar(Format('На форме %d кнопок', [TMyButton.GetCount])),’Финиш’,mb_ok);. В данном случае использовалась функция Windows API MessageBox, так как при разрушении формы доступ в секции finalization ко многим функциям Delphi невозможен. Запуская данную программу на выполнение, получим два сообщения до построения и после разрушения формы. СОБЫТИЯ В приложениях и компонентах событие возникает как результат послания операционной системой сообщения, что произошло некоторое действие, которое контролирует метод послания уведомления о событии. Этот метод проверяет, назначил ли пользователь соответствующий обработчик событий. Если событие произошло и обработчик назначен, то метод послания уведомления о событии вызывает соответствующий обработчик. Методы послания уведомления о событии можно переопределять, используя стандартные методы, или создавать свои собственные. Рассмотрим вариант переопределения методов послания уведомления о событии. Такие методы реализуются в разделе Protected. Поэтому потомки тех встроенных классов, в которых реализуются такие методы, могут делать доступными их для пользователя. Например, рассмотрим, как переопределяется метод послания уведомления о событии Click для кнопки, одновременно изменяя ее заголовок на строку, в которую записано текущее время. TtimeButton=class(Tbutton) Protected Procedure Click; override; End; ……………………….. Procedure TtimeButton.Click; Inherited Click; Caption:=DateTimeToStr(Now); End;.
Методы послания уведомления о событии являются указателями на метод, которые вызываются в случае, когда компонент получает сообщение от Windows о том, что произошло некоторое событие, воздействующее на данный компонент. Обработчики событий представляют собой свойства, которые выполняют чтение и запись в указатели на метод. Например, рассмотрим фрагмент, взятый из класса TControl, содержащегося в исходном коде VCL. Имена обработчиков событий по соглашению содержат префикс On. Вначале объявляется событие; TNotifyEvent = procedure (Sender:TObject) of object;. Далее объявляется класс, содержащий поля данного типа, и обработчики событий, работающие с этими полями. Tcontrol=class(TComponent) Private FOnClick: TNotifyEvent; …………………. Ptotected Property OnClick: TnotifyEvent read FOnClick write FOnClick; …………………………………… end;. При создании собственных обработчиков необходимо научиться строит методы посылки уведомления о событии. Эти методы должны уметь получать и обрабатывать сообщения Windows, что возможно сделать с помощью методов вида Message. УКАЗАТЕЛИ НА МЕТОДЫ Этот тип данных представляет собой ссылку (указатель) на метод. Указатель на метод содержит два адреса: одна ссылка на адрес кода метода, другая - на адрес экземпляра объекта - представляет собой скрытый параметр self. Адрес self представляет собой в данном случае адрес расположения данных, принадлежащих конкретному объекту. Указатели на методы реализуют один из принципов компонентной технологии - делегирование. Если кто-то написал класс, у которого есть поля в виде указателей на методы, например, Туре TNotifyEvent=procedure(Sender:Tobject) of object; TMyButton=class OnClick: TNotifyEvent; End; ,то можно менять поведение построенных (даже скомпилированных) объектов этого типа, просто присваивая этим указателям новые методы. Например, пусть объявлено: туре TForm1=class(TForm) Procedure OnButton1Click(Sender:Tobject); Button1:MyButton; End;. Теперь при построении компонента на форме можно делегировать обработчик OnButton1click из TForm1 в MyButton путем следующего присваивания: MyButton.OnClick:=Form1.OnButton1click;. ПРИМЕР ПРИЛОЖЕНИЯ 15 Продолжим рассмотрение примера 14. Попытаемся не только динамически создавать новые объекты, но и разрушать их также динамически. Выберем, что уничтожение очередного объекта будет наступать, как только пользователь нажмет на клавишу Backspace (#8). Для реализации этой идеи понадобятся указатели на методы. В данном случае динамически созданный объект для своего уничтожения должен отслеживать нажатие клавиши #8. Для этих целей может служить событие OnKeyPress. Этому событию надо делегировать собственное событие. Прежде всего, необходимо убедиться, что оно объявлено как указатель на метод, иначе делегирование невозможно. В справочной системе Delphi можно найти следующее объявление: TKeyPressEvent=procedure(Sender:Tobject;var key:char) of object; Property OnKeyPress: TKeyPressEvent;. Таким образом, для делегирования необходимо создать свою процедуру. В классе TForm1 добавим собственный обработчик: Procedure MyButtonKeyPress(Sender:Tobject; var key:Char);. Кроме того, в класс TForm1 необходимо добавить переменную для записи того динамически построенного метода, который подлежит удалению, так как может удаляться не только текущий объект, но и предыдущий (если новые объекты не создаются). Добавим такое объявление в секцию Private ToDestroy:TMyButton;. Теперь определим новую процедуру обработки события OnKeyPress: Procedure TForm1.MyButtonKeyPress(Sender: TObject; var Key: Char); begin if key=#8 then ToDestroy:=Sender as TButton; end;. Эта процедура не уничтожает, а запоминает подлежащий уничтожению объект. Чтобы она работала, необходимо при создании кнопки делегировать наш обработчик, записывая OnKeyPress:=MyButtonKeyPress;. Допишем еще одну строку для установки фокуса на текущий объект SetFocus;, т.е. после удаления очередного объекта необходимо переместить фокус ввода на предыдущий объект. Удаление кнопки реализуем в обработчике onTimer. Полный текст программы приводится ниже. unit priml5; interface uses Windows, Messages, SysUtils,Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TMyButton=class(TButton) public Constructor Create (AOwner:TCoraponent); override; Destructor Destroy; override; class function GetCount:integer; end; TForm1 = class(TForm) Timer1: TTimer; procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, У: Integer); procedure Timer1Timer(Sender: TObject}; private ToDestroy:TMyButton; Procedure MyButtonKeyPress{Sender:Tobject; var key:Char); end; var Form1: TForm1; implementation var CountBtns: integer =0; {$R *.dfm} Constructor TMyButton.Create(AOwner: TComponent); begin inherited; Inc(CountBtns); end; Destructor TMyButton.Destroy; begin .dec(CountBtns); inherited; end; class function TMyButton.GetCount: integer; begin Result:=CountBtns; end; procedure TForm1.MyButtonKeyPress(Sender: TObject; var Key: Char); begin if key=#8 then ToDestroy:=Sender as TMyButton; end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Begin with TMyButton.Create(self) do begin Parent:=self; Left:=x; Top:=у; Width:=Width+60; Caption:=Format('%d кнопка x,y= %d,%d', [GetCount,x, y]); OnKeyPress:=MyButtonKeyPress; SetFocus; end; end; procedure TForm1.Timer1Timer(Sender: TObject); begin if Assigned(ToDestroy) then begin SelectNext{ToDestroy,false,true); ToDestroy.Free; ToDestroy:=nil; end; Caption: =Format {'Кнопок %d'., [TMyButton. GetCount]); end; end. Вариант решения получим таким же, как на рис. 35. Однако в данном варианте, нажимая неоднократно на клавишу Backspace, можно удалить все кнопки, построенные на форме. Все переменные типа класс (например, ToDestroy) по сути, являются указателями, поэтому для проверки, существует ли тот или иной объект, применяется функция Assigned (обработчик Timer1Timer), которая проверяет, равна переменная значению "пустой указатель" (т.е. nil) или нет. ТИПЫ ССЫЛКИ НА КЛАСС Такой программный элемент, как тип обычной переменной, представляющий собой правила обращения с теми или иными переменными, существует только во время компиляции программы. Эти правила не могут измениться во время выполнения программы. Типы ссылки на класс, которые синтаксически объявляются как class of TMyClass, позволяют нарушить указанные правила. С помощью этих типов можно обращаться к объектным типам во время выполнения программы. Для некоторого заданного типа объектный тип - это значение, которое можно записать в переменную типа ссылки на класс. Во время выполнения программы в разных ее участках переменной данного типа можно присваивать в качестве значения различные объектные типы и, таким образом, строить нужные объекты, вызывая или иной виртуальный конструктор. СВОЙСТВА Свойства представляют интерфейс с внутренними полями данных того или иного объекта. Внутренние поля обычно объявляются в разделе Private. Их имена, как правило, начинаются с буквы F, например поле FColor. Соответственно свойство, с помощью которого осуществляется доступ к заданному полю FColor, имеет имя Color. Свойства могут объявляться с различной степенью доступа. Если требуется, чтобы они отображались в окне инспектора объектов, их объявляют в разделе Published. Свойства могут создаваться с помощью различных типов данных, а именно: • Simple - простые свойства; • Enumerated - перечисляемые; • Set - множества; • Object - объектного типа; • Array - индексированные свойства. Разные типы свойств по-разному отображаются в инспекторе объектов и имеют свои собственные редакторы для изменения значений свойств. Далее рассмотрим синтаксис записи различных видов свойств. СВОЙСТВА SIMPLE Простые свойства включают числовые, символьные и строковые типы данных. Наиболее часто используются свойства типа integer и string. Например, свойства Width, Height имеют тип integer, свойство Caption имеет тип string. Объявим для демонстрации синтаксиса записи простых свойств следующий класс: Туре TSimple=class(TCustomControl) Private PString: string; Published Property StringProp:string read FString write FString; end; Как следует из этого примера, свойства имеют тип поля, в котором хранится значение данного свойства. Кроме того, для свойства необходимо указать
способ чтения информации из соответствующего поля и способ записи данных в поле. Если отсутствует или способ чтения, или способ записи данных, то соответствующее свойство имеет доступ или только по чтению, или только по записи. СВОЙСТВА ENUMERATED Свойства enumerated определяются типом перечень и логическим типом Boolean. Обычно все возможные значения данного типа свойств некоторым способом помечаются, например, как в объявлениях ниже: туре TEnumProp=(epZero, epOne, epTwo, epThree); TEnum=class(TCustomControl) Private FEnumProp: TEnumProp; Published Property EnumProp:TEnumProp read FEnumProp write FEnumProp; end;. СВОЙСТВА SET Элементы свойства Set (множество) в инспекторе объектов заключаются в квадратные скобки. Для развертывания в инспекторе объектов элементов базового множества, включающего в себя все возможные варианты, имеется знак "+", расположенный слева от наименования свойства. Включить или не включить тот или иной элемент из базового множества в рабочее множество выбирают путем указания true или false. Рабочее множество определяет значение данного свойства. Рассмотрим пример объявления свойства Set. При этом используется тип TEnumProp, объявленный выше. TSetProp=set of TEnumProp TSetClass=class(TCustomControl) Private FSetProp:TSetProp; Published Property SetProp:TsetProp read FSetProp write FSetProp; End;. СВОЙСТВА OBJECT Свойства Object в инспекторе объектов помечаются или знаком "+" или кнопкой с многоточием. Например, свойство Font имеет объектный тип. Дня того чтобы привести пример объявления свойства объектного типа, несводимо предварительно сформировать объект какого-либо класса и вы- . 129 брать предка для этого класса. На практике в большинстве случаев в качестве предка выбирается встроенный класс TPersistent. Объявим класс, который будет определять далее в примере тип свойства. Туре T0bjectDop=class{TPersistent) Private FMyInt:integer; Public Property MyProp:integer read FMyInt write FMyInt; End;. В данном случае нужно решить одну проблему. Объявленный выше класс (TObjectDop) должен войти как составная часть в класс, который должен будет содержать свойство заданного объектного типа TObjectDop. Соответственно потребуется задавать этому свойству значение, а это означает, что потребуется объект типа TObjectDop, для которого необходимо обеспечить выделение памяти и освобождение памяти. Стандартных методов выделения и освобождения памяти в данном случае недостаточно. Для выделения памяти создадим конструктор Create, а для высвобождения памяти - деструктор Destroy.
туре TObjectProp=class(TCustomControl) Private FObjectProp: TObjectDop; Public Constructor Create(AOwner:TComponent);override; Destructor Destroy;override; Published Property ObjectProp: TObjectDop read FObjectProp write FObjectProp; End;. Конструктор и деструктор объявлены с командой Override. Это означает, что в данном классе переопределяются имеющиеся по линии наследования стандартные виртуальные конструктор Create и деструктор Destroy. Определим эти методы. Constructor TObjectProp.Create(AOwner:TComponent); begin Inherited Create(AOwner); FObjectProp:= TObjectProp.Create; end; Destructor TObjectProp.Destroy; Begin FObjectProp.Free; Inherited Destroy; End;. Необходимо обратить внимание на то, в какой последовательности вызываются стандартные методы с помощью процедуры inherited. СВОЙСТВА ARRAY Свойства Array позволяют создать очень похожие на массивы индексированные свойства, которые отличаются от обычных массивов по двум основным аспектам: • свойства типа Array могут индексироваться строковым значением; • свойства типа Array могут получать доступ только к одному элементу В окне Object Inspector эти свойства помечаются кнопкой с многоточием (…). Для изменения значений свойств данного вида вызывается специальный редактор. Ниже приводится пример объявления индексированного свойства. Туре Str7=string[7]; TMas=array [1..3] of str7; TMasClass=class(TCustomControl) Private FMasProp: TMas; Function GetMasInt(pIndex:integer):string; Function GetMasStr(pIndex:string):integer; Public Constructor Create(AOwner:TComponent);override; Property MasPropInt[Index:integer]:string read GetMasInt; Property MasPropStr[Index:string]:4integer read GetMasStr; end;. В данном случае переопределяется конструктор create, который необходим не для выделения памяти, а для задания значений элементам индексированного свойства. Поэтому необходимо будет исключить вызов стандартного конструктора с помощью команды inherited. Далее определим конструктор и две функции для чтения значений элементов индексированного свойства, которое в данном случае имеет доступ к соответствующему полю только по чтению. Constructor TMasClass.Create(AOwner:TComponent); begin FMasProp[1]:='one'; FMasProp[2]:='two'; FMasProp[3]:='three'; End; TMasClass.GetMasInt{pIndex:integer):string; begin result: = 'unknown'; if pIndex in [1..3] then result:= FMasProp[pIndex]; end; Function TMasClass.GetMasStr(pIndex:string):integer; var x:integer; begin result:=-l; for x:=l to 3 do if Uppercase(BMasProp[x])=Uppercase(pIndex) then begin result:=x; exit; end; end;. Здесь первая функция может читать значение свойства при заданном индексе, а вторая может использоваться для поиска индекса по заданному значению. ЗАДАНИЕ НАЧАЛЬНЫХ ЗНАЧЕНИЙ СВОЙСТВАМ Когда создаются свойства, они первоначально получают нулевые или неопределенные (в зависимости от типа свойства) значения. Если необходимо задать свойствам какие-либо значения по умолчанию, то используются следующие шесть команд: Default, NoDefault, Default для свойств типа массив, Stored, index, Dispid. Например, Property HumberProp:integer read Fnumber write FNumber default 5; Property EnumProp: TEnumProp Read FenumProp write FEnumProp default epOne;. Для свойства типа массив команда Default указывается после точки с запятой без использования какого-либо значения. Массив, если он задан при объявлении класса, представляет собой начальные значения элементов индексированного свойства. Property ArrayPropInt[Index: integer]: string read GetArrayPropInt; Default;. Команда NoDefault применяется обычно для унаследованных свойств при указании, что родительское значение не действует. Команда stored используется для того, чтобы указать, сохранять ли текущее значение, например, Property NumProl:integer read Fnuml write FNuml Stored true; Property NumPro2:integer read FNum2 write FNum2 Stored false; Property NumPro3:integer read Fnum3 write Fnum3 Stored Fun3; - В первом случае указано, что необходимо сохранять текущее значение свойства, во втором - не сохранять, в третьем - сохранять, если функция Fun3 возвращает true. Команда Index позволяет обеспечить гибкость при объявлении методов доступа Read и write к полям. Рассмотрим это на примере. Пусть два свойства intProp1 и intProp2 для доступа к полям PIntProp1 и FIntProp2 используют одни и те же подпрограммы: Property IntProp1:integer Index 1 read GetIntProp write SetIntProp; Property IntProp2:integer Index 2 read GetIntProp write SetIntProp;. Для указания того, что свойство IntProp1 работает с полем FIntProp1, a intProp2 - с полем FintProp2, используется команда index. В данном случае число после слова Index указывает, какое поле следует использовать, например, подпрограммы GetIntProp и SetIntProp можно определить так: Function <имя класса>.GetIntProp (pIndex:integer):integer; begin case pIndex of 1:result:=FIntProp1; 2:result:=FIntProp2; end; end; Procedure <иия класса>.SetIntProp (pIndex:integer; pValue:integer); begin case pIndex of 1: FIntProp1:= pValue; 2: FIntProp2:= pValue; end; end;. Команда Dispid применяется при работе с объектами OLE. ПРИМЕР ПРИЛОЖЕНИЯ 16 Пусть требуется построить на форме три геометрические фигуры: круг, квадрат, эллипс. Необходимо обеспечить возможность изменения цвета каждой фигуры. Пусть будет по два цветовых варианта каждой фигуры (рис. 36). Для решения задачи построим четыре класса. Первый класс назовем TDrawArea. Включим в него следующие действия: инициализация всех по-
Рис. 36 Программа приводится ниже. unit PRIM16;
Дата добавления: 2014-12-29; Просмотров: 464; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |