Assigned — Функция Delphi


Delphi-Help

Assigned

Assigned

Описание

Delphi функция Assigned проверяет является ли указатель nil. Если не nil, то возвращает True; если nil, то False.
Использование нулевого указателя в Delphi закончится исключением.
Возможна проверка указателей, объектов, методов (Pointer, TObject, Method)
При обращении к методам лучше использовать Assigned, чем Nil.

Пример кода

var
myPtr : PChar;

begin
// Переменные Pointer по умолчанию не установлены на nil
if Assigned (myPtr)
then ShowMessage(‘myPtr не nil’)
else ShowMessage(‘myPtr — nil’);

// Так что мы должны установить их на nil, чтобы быть уверенными, что они неопределены
myPtr := Nil;
if Assigned (myPtr)
then ShowMessage(‘myPtr всё ещё не nil’)
else ShowMessage(‘myPtr — nil’);
end;

Иллюстрированный самоучитель по Delphi 7 для профессионалов

Класс TPersistent

«Persistent» в переводе с английского означает «устойчивый», «постоянный». Что же такого постоянного в одноименном классе? Ответ таков: виртуальный метод procedure Assign(Source: TPersistent);.

Этот важнейший метод осуществляет копирование содержимого одного объекта (source) в другой (self, т. е. в объект, вызвавший метод Assign). При этом объект-получатель остается самим собой, чего нельзя достигнуть, используя простое присваивание переменных объектного типа:

Ведь в этом случае указатель на одну область адресного пространства, содержащую экземпляр класса (объект), замещается указателем на другую область адресного пространства, содержащую другой объект.

Метод Assign позволяет продублировать объект – присвоить одному объекту значения всех свойств другого. При этом объекты не обязательно должны быть одного и того же класса; более того, они не обязательно должны находиться в отношениях «родитель-потомок». Данный метод тем и хорош, что позволяет полиморфное присвоение. Конструкция:

Позволяет скопировать содержимое картинки Picture в папку обмена Windows (объект clipboard). Какова здесь логика? Известно, что в папку обмена можно поместить растровую картинку, текст, метафайл, мультимедийные данные и т. п. Метод Assign класса TClipboard переписан таким образом, чтобы обеспечить присвоение (т. е. реальное перемещение в папку обмена) всех этих данных.

Для обеспечения взаимодействия потомков класса TPersistent со средой разработки предназначен метод:

Он возвращает имя объекта для передачи его в Инспектор объектов.

Для взаимодействия с потоками при загрузке и сохранении компонентов предназначен следующий метод:

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

Assigned — Функция Delphi

Необходимо присвоить свойства одного объекта другому.

type
TSome > public
a:byte;
b:word;
c:boolean;
end;

var
SC1,SC2:TSomeClass;
begin
SC1:=TSomeClass.Create;
SC2:=TSomeClass.Create;
SC1.a:=1;
SC1.b:=2;
SC1.c:=true;
SC2.Assign(SC1); // Здесь возникает ошибка
SC1.Free;
SC2.Free;
end;

Еще-бы! У TPersistent метод Assign считай что пуст (точнее — генерит эксепшн — см. сорцы). Так что метод Assign тебе придется написать самому. Эт те не С++ с конструктором корпирования :)

Надо перекрыть метод
procedure AssignTo(Dest: TPersistent); virtual;
В котором расскажешь классу что надо делать.

> Alx2 © (03.09.02 15:10)
Почитал help. Все-таки лучше перекрыть именно assign

Ну естественно будет ошибка, ведь Delphi не знает как копировать поля. Поэтому метод Assign и есть виртуальный. Ты его должен переопределить:

type
TSome >public
a:byte;
b:word;
c:boolean;
procedure Assign(Source: TPersistent); override;
end;
.
implementation

procedure TSomeClass.Assign(Source: TPersistent);
begin
inherited Assign(Source);
a:=(Source as TSomeClass).a;
.
end;

Прошу прощения inherited не нужен

type
TSome >public
a:byte;
b:word;
c:boolean;
procedure Assign(Source: TPersistent); override;
end;
.
implementation

procedure TSomeClass.Assign(Source: TPersistent);
begin
a:=(Source as TSomeClass).a;
.
end;

А все свойства (у меня их много) в цикле перебрать нельзя?

2 MaximatorV (03.09.02 16:23) — Свойства перебрать можно, только вот «a:byte» и т.п. — это не свойства а просто переменные. Попробуй объединить их в записи, тогда их можно копировать «скопом» :)

>MaximatorV (03.09.02 16:23)
Побьют меня ногами за такой совет, но, если очень уж лень один раз нарисовать вручную их присваивание, то можно попробовать взять кусок памяти с адреса первого поля твоего объекта по адрес последнего поля и все, что между этими адресами находится, залить в соответствующий адресный интервал другого объекта с помощью move, например.

Published свойства можно.

procedure CopyPublishedProperties(Src, Dest: TObject);
var i: Integer;
Cls: TClass;

PropCnt: Integer;
PropList: PPropList;
PropInfo: PPropInfo;

function IsField(PropProc: Pointer): Boolean;
type ba = array[0..3]of Byte;
begin
Result := ba(PropProc)[3] > $FE;
end;

begin
if not Dest.InheritsFrom(Src.ClassType) then
raise Exception.CreateFmt(«Cannot copy properties from %s to %s», [Src.ClassName, Dest.ClassName]);

PropCnt := GetTypeData(Src.ClassInfo)^.PropCount;
GetMem(PropList, PropCnt*SizeOf(Pointer));

try
GetPropInfos(Src.ClassInfo, PropList);

for i := 0 to PropCnt-1 do
begin
PropInfo := PropList^[i];
if (PropInfo^.Name <> «Name») then

// Какие-то св-ва, помимо Name, возможно, тоже не
// стоит копировать

begin
if PropInfo^.PropType^^.Kind = tkMethod then
SetMethodProp(Dest, PropInfo,
GetMethodProp(Src, PropInfo))
else if PropInfo^.PropType^^.Kind = tkClass then
begin
<
Cls := GetTypeData(PropInfo^.PropType^).ClassType;
if (not Cls.InheritsFrom(TComponent)) and
(Cls.InheritsFrom(TPersistent)) and
IsField(PropInfo^.SetProc)
>
if PropInfo^.Name = «Constraints»
then
// По непонятной причине свойство
// property Constraints: TSizeConstraints read FConstraints write FConstraints;
// производит запись прямо в поле, а не через Set-метод + Assign
// Поэтому приходится делать обходной маневр.
//
// Не исключено, что имеются подобные глюки и с другими (более редкими) св-вами.

TPersistent(GetObjectProp(Dest, PropInfo)).Assign
(TPersistent(GetObjectProp(Src, PropInfo)))
else
begin
SetObjectProp(Dest, PropInfo,
GetObjectProp(Src, PropInfo));
end;
end
else
SetPropValue(Dest, PropInfo^.Name, GetPropValue(Src, PropInfo^.Name));
end;
end;
finally
FreeMem(PropList, PropCnt*SizeOf(Pointer));
end;

// if (Src is TControl) then
// TControl(Dest).Parent := TControl(Src).Parent;
end;

2 valery_f (03.09.02 16:34)

А свойства можно перебрать в цикле, основываясь на RTTI, как я догадываюсь?

Delphi assign generic value in function

Since Delphi (sadly) doesn’t support nullable types I wanted to try to make my own implementation of them. This is what I’ve written so far:

It seems that I cannot assing the FValue with a generic value taken from the function. Is there a way to do this?

I wanted do an easy implementation of nullables. I need a setValue function because I need to assign the FHasValue to true or false (so I know if the value is «nullable» or not). In the main form I’d call the code like this:

Есть ли в Builder аналог функции Assigned изDelphi

В Delphi есть функция Assigned(myObjPointer), которая возвращает true в случае, если объект по адресу «myObjPointer» существует и не удален.

Как аналогичное сделать средствами C++, либо средствами C++ Builder?

Я неправильно понимал смысл функции Assigned.
Оказывается, она тупо проверяет является ли переданый указатель пустым.

Вот и весь ответ на мой ламерский вопрос

да, но если просто удалить myPointer

то он не будет равен 0, а останется неизменным, а память, куда он указывает будет удалена. т.е. он будет указывать на мусор, поэтому всегда после delete делай myPointer = 0;

Разработка компонентов в среде Delphi

Все компоненты Delphi являются частью иерархии, которая называется Visual Component Library (VCL). Общим предком всех компонентов является класс TComponent (рис. 9.1.1), в котором собран минимальный набор общих для всех компонентов Delphi свойств.

Свойство ComponentState содержит набор значений, указывающих на текущее состояние компонента. Приведем некоторые значения свойства:

csDesigning компонент находится в режиме проектирования
sDestroyingKOMnoHeHT сейчас будет разрушен;
csLoading компонент загружается из файла формы;
csReading компонент считывает значения из файла формы;
csWriting компонент записывает значения своих свойств в поток;
csUpdating компонент вносит изменения, чтобы отразить изменения в родительской форме.

Класс TComponent вводит концепцию принадлежности. Каждый компонент имеет свойство Owner (владелец), ссылающееся на другой компонент как на своего владельца. В свою очередь, компоненту могут принадлежать другие компоненты, ссылки на которые хранятся в свойстве Components. Конструктор ком­понента принимает один параметр, который используется для задания владельца компонента. Если передаваемый владелец су­ществует, то новый компонент добавляется к списку Components владельца. Свойство Components обеспечивает автоматическое разрушение компонентов, принадлежащих владельцу. Свойст­во ComponentCount показывает количество принадлежащих компонентов, a Componentlndex — номер компонента в массиве Components.

В классе TComponent определено большое количество мето­дов. Наибольший интерес представляет метод Notification. Он вызывается всегда, когда компонент вставляется или удаляется из списка Components владельца. Владелец посылает уведомле­ние каждому члену списка Components. Этот метод переопределя­ется в порождаемых классах для того, чтобы обеспечить действи­тельность ссылок компонента на другие компоненты. Например, при удалении компонента Tablel с формы свойство DataSet компонента DataSourcel, равное Tablel, устанавливается в Nil.

Илон Маск рекомендует:  Что такое код ifx_copy_blob

Процесс разработки компонента включает пять этапов:

создание модуля компонента;

добавление в новый компонент свойств, методов и событий;

регистрацию компонента в среде Delphi;

На рис. 9.1.1 изображены базовые классы, формирующие структуру VCL. В самом верху расположен TObject, который является предком для всех классов в Object Pascal. От него про­исходит TPersistent, обеспечивающий методы, необходимые для создания потоковых объектов. Потоковый объект — объект, ко­торый может запоминаться в потоке. Поток представляет собой объект, способный хранить двоичные данные (файлы). Поскольку Delphi реализует файлы форм, используя потоки, то TComponent порождается от TPersistent, предоставляя всем компонентам способность сохраняться в файле формы.

Класс TComponent представляет собой вершину иерархии компонентов и является первым из четырех базовых классов, используемых для создания новых компонентов. Прямые по­томки TComponent — невизуальные компоненты.

Вершину иерархии визуальных компонентов представляет класс TControl.

Класс TControl вводит понятие родительских элементов управ­ления (parent control). Свойство Parent является окном, кото­рое содержит элемент управления. Например, если компонент Panel 1 содержит Button 1, то свойство Parent компонента Button 1 равно Panel 1.

Свойство ControlStyle определяет различные стили, приме­нимые только к визуальным компонентам, например:

csAcceptControls элемент управления становится родителем любых элементов управления, помещенных на него во время проектирования. Применим только к оконным элементам управления;
csCaptureMouse элемент управления перехватываетсобытия мыши;
сsFrames элемент управления имеет рамку;
csSetCaption свойства Caption и Text элемента управления (если не заданы явно) устанавливаются так, чтобы совпадать со свойством Name;
csOpaque элемент управления скрывает все элементы позади себя.

В классе TControl определено большинство свойств, использу­емых визуальными компонентами: свойства позиционирования (Align, Left, Top, Height, Width), свойства клиентской области (ClientHeight, ClientWidth), свойства внешнего вида (Color, Enabled, Font, ShowHint, Visible), строковые свойства (Caption, Name, Text, Hint), свойства мыши (Cursor, DragCursor, DragKind, DragMode).

Кроме того, класс TControl реализует методы диспетчеризации событий.

Все визуальные компоненты подразделяют на графические элементы управления и оконные элементы управления. Каж­дый тип представляет свою иерархию классов, происходящую соответственно от TGraphicControl и TWinControl. Главная раз­ница между этими типами компонент состоит в том, что графи­ческие компоненты не поддерживают идентификатор окна, и, соответственно, не могут принять фокус ввода.

Оконные компоненты далее разбиваются на две категории. Прямые потомки TWinControl являются оболочками вокруг су­ществующих элементов управления, реализованных в Windows (например, TEdit, TButton, и др.) и, следовательно, знают, как себя рисовать.

Для компонентов, которые требуют идентификатора окна, но не инкапсулируют базовых элементов Windows, которые бы обеспечивали возможность перерисовывать себя, имеется класс TCustomControl.

9.1.2. Класс TGraphicControl

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

По умолчанию объекты TGraphicControl не имеют собствен­ного визуального отображения, но для наследников обеспечи­ваются виртуальный метод Paint (вызывается всегда, когда элемент управления должен быть нарисован) и свойство Canvas (используется как «поверхность» для рисования).


Класс TWinControl используется как базовый для создания компонентов, инкапсулирующих соответствующие оконные эле­менты управления Windows, которые сами себя рисуют.

Класс TWinControl обеспечивает свойство Handle, являюще­еся ссылкой на идентификатор окна базового элемента управ­ления. Кроме этого свойства класс реализует свойства, методы и события, поддерживающие клавиатурные события и измене­ния фокуса:

свойства фокуса TabStop, TabOrder;
свойства внешнего вида Ctl3D, Showing;
методы фокуса CanFocus, Focused;
методы выравнивания AlignControl, EnableAlign, Re Align;
оконные методы CreateWnd, CreateParam, RecreateWnd, CreateWindowHandle, DestroyWnd;
события фокуса OnEnter, OnExit;
события клавиатуры OnKeyDown, OnKeyPress, OnKeyUp.

Создание любого потомка этого класса начинается с вызова ме­тода CreateWnd, который вначале вызывает CreateParams для инициализации записи параметров создания окна, а затем вызыва­ет CreateWindowHandle для создания реального идентификатора окна, использующего запись параметров. Затем CreateWnd настра­ивает размеры окна и устанавливает шрифт элемента управления.

9.1.4. Класс TCustomControl

Класс TCustomControl представляет собой комбинацию клас­сов TWinControl и TGraphicControl. Являясь прямым потомком класса TWinControl, TCustomControl наследует способность управления идентификатором окна и всеми сопутствующими возможностями. Кроме этого, как и класс TGraphicControl, класс TCustomControl обеспечивает потомков виртуальным ме­тодом Paint, ассоциированным со свойством Canvas.

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

создание Windows-элемента управления (TWinControl);

создание графического элемента управления (TGraphic-Control);

создание нового элемента управления (TCustomControl); О создание невизуального компонента (TComponent).

9.2. Создание модуля компонента и тестового приложения

Определившись с выбором компонента, можно приступить к созданию модуля компонента. Для этого необходимо выпол­нить следующие шаги.

Выполните команду File/ New. / Component или Component/ New Component.

В диалоговом окне New Component (рис. 9.2.1.) установите основные параметры создания компонента: Ancestor type (имя класса-предка), Class Name (имя класса компонента), Palette Page (вкладка палитры, на которой должен отображаться ком­понент) и Unit file name (имя модуля компонента).

После щелчка на кнопке ОК будет сгенерирован каркас но­вого класса.

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

Упражнение 9.2.1. Разработайте новый компонент, который объединяет компоненты TEdit и TLabel. Компонент Label рас­полагается выше поля редактирования (TEdit). При перемеще­нии поля редактирования TLabel следует за ним. При удалении поля редактирования TLabel также удаляется.

В качестве предка класса нового компонента используем TEdit.

Выполните команду Component/ New component. Установите следующие значения параметров окна: Ancestor type TEdit

Class Name TLabelEdit

Palette Page Test

Unit file name . \LabelEdit\LabelEdit.pas

Щелкните на кнопке ОК, автоматически будет сгенерирован следующий код:

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type

В модуле описан каркас нового класса и написана процедура регистрации компонента (Register), которая помещает его на страницу Test. Сохраните файл модуля компонента.

Разработка тестового приложения

Создайте новый проект. Сохраните его файлы в папке . \LabelEdit: файл модуля — под именем Main.pas, файл про­екта — Test Application, dpr.

Добавьте имя модуля разрабатываемого компонента в раздел Uses формы тестового приложения:

В общедоступный раздел класса TForml добавьте поле

В обработчике события OnCreate формы динамически со­здайте новый компонент:

procedure TForml.FormCreate(Sender: TObject);

Сохраните файлы проекта.

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

9.3. Добавление свойств, методов и событий

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

Добавление свойства происходит в три этапа.

1. Создание внутреннего поля класса для хранения значения свойства.

2. Описание и разработка методов доступа к значению свойства.

3. Описание свойства.

В классе TControl свойства Caption/Text, Parent и Hint опре­деляются так:

TControl = class (TComponent)

function IsCaptionStored: Boolean;

function IsHintStored: Boolean;

procedure SetText(const Value: TCaption);

property Caption: TCaption read GetText write SetText stored IsCaptionStored;

property Text: TCaption read GetText write SetText;

property Parent: TWinControl read FParent write SetParent;

property Hint: string read FHint write FHint stored IsHintStored;

Объявление свойства имеет следующий синтаксис: property : тип определители;

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

Каждое объявление свойства должно определять тип свойства, для этого используется символ двоеточие после имени свойства.

Для указания метода, который будет использоваться для осуществления выборки значения свойства, используется ди­ректива read. Метод должен быть функцией, чей возвращае­мый тип является тем же самым, что и тип свойства.

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

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

Илон Маск рекомендует:  Шаблон ввода данных

При обращении к значению свойства происходит перена­правление на соответствующий метод. Например, оператор s : =Editl. Text; автоматически будет преобразован в оператор s : =Editl. GetText; а оператор Editl. Text: =’ Test’ — в опе­ратор Editl.Text(‘Test’).

Описание свойства должно содержать определитель read или write или сразу оба. Если описание свойства включает в себя только определитель read, то оно является свойством только для чтения. В свою очередь, свойство, чье описание включает в себя только определитель write, является свойством только для записи. При присвоении свойству, определенному с директивой только для чтения, какого-либо значения или при использова­нии в выражении свойства с директивой только для записи все­гда возникает ошибка.

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

Когда программист использует Инспектор объектов для измене­ния свойств формы или свойств компонентов, то результирующие изменения заносятся в файл формы. Файлы форм представляют собой файлы ресурсов Windows, и когда приложение запускается, то описание формы подгружается из этого файла. Для определения того, что должно сохраняться в файле формы, служат специфика­торы памяти — необязательные директивы stored, default и node-fault. Эти директивы влияют на информацию о типе во время вы­полнения, генерируемую для свойств published.

Директива stored управляет тем, будет или нет свойство дейст­вительно запоминаться в файле формы. За директивой stored дол­жны следовать либо константы True или False, либо имя поля, имеющего тип Boolean, либо имя метода, у которого нет парамет­ров, и возвращающего значение типа Boolean. Например,

property Hint: string read FHint write FHint stored IsHintStored;

Если свойство не содержит директиву stored, то оно рассмат­ривается как содержащее ее с параметром True.

Директивы default и nodefault управляют значениями свой­ства по умолчанию. За директивой default должна следовать константа того же типа, что и свойство, например:

property Tag: Longint read FTag write FTag default 0 ;

Чтобы перекрыть наследуемое значение default без указания нового значения, используется директива nodefault. Директи­вы default и nodefault работают только с порядковыми типами и множествами, нижняя и верхняя границы которых лежат в промежутке от 0 до 31. Если такое свойство описано без дирек­тив default и nodefault, то оно рассматривается как с директи­вой nodefault. Для вещественных типов, указателей и строк значение после директивы default может быть только О, NIL и

(пустая строка) соответственно.

Когда Delphi сохраняет компонент, то просматриваются спе­цификаторы памяти published свойств компонента. Если значе­ние текущего свойства отличается от default значения (или ди­ректива default отсутствует) и параметр stored равен True, то значение свойства сохраняется, иначе свойство не сохраняется.

Спецификаторы памяти не поддерживаются свойствами-мас­сивами, а директива default при описании свойства-массива имеет другое назначение.

Простые свойства — это числовые, строковые и символьные свойства. Они могут непосредственно редактироваться в Инс­пекторе объектов и не требуют специальных методов доступа.

Рассмотрим создание простого свойства Color, описанного в классе TContol (модуль controls.pas):

TControl = class (TComponent)

function IsColorStored: Boolean;

procedure SetColor(Value: TColor);

property Color: TColor read FColor write SetColor stored IsColorStored default clWindow;

function TControl.IsColorStored: Boolean;

Result := not ParentColor;

procedure TControl.SetColor (Value: TColor);

if FColor <> Value then

Perform(CM_COLORCHANGED, 0, 0) ;

9.3.2. Свойства перечислимого типа

Определенные пользователем перечислимые и логические свойства можно редактировать в окне инспектора объектов, вы­бирая подходящее значение свойства в раскрывающемся списке. Рассмотрим создание свойства перечислимого типа на при­мере компонента Shape (модуль extctrls.pas).

TShapeType = (stRectangle, stSquare, stRoundRect, stRoundSquare, stEllipse, stCircle);

procedure SetShape(Value: TShapeType);

property Shape: TShapeType read FShape write SetShape

if FShape <> Value then

9.3.3. Свойства типа множества


Свойство типа множества при редактировании в окне Инспек­тора объектов выглядит так же, как множество, определенное синтаксисом языка Pascal. Простейший способ его отредактиро­вать — развернуть свойство в Инспекторе объектов, в результате каждый его элемент станет отдельным логическим значением.

При создании свойства типа множества нужно создать соот­ветствующий тип, описать методы доступа, после чего описать само свойство. В модуле Controls.pas свойсво Align описано сле­дующим образом:

TAlign = (alNone, alTop, alBottom, alLeft, alRight, alClient);

TAlignSet = set of TAlign; TControl = class(TComponent)

procedure SetAlign(Value: TAlign);

property Align: TAlign read FAlign write SetAlign default alNone;

procedure TControl.SetAlign(Value: TAlign);

var OldAlign: TAlign;

if FAlign <> Value then

if not (csLoading in ComponentState) and

(not (csDesigning in ComponentState) or (Parent <> NIL))

if ((OldAlign in [alTop, alBottom])=(Value in [alRight, alLeft])) and not (OldAlign in [alNone, alClient]) and not (Value in [alNone, alClient]) then SetBounds(Left, Top, Height, Width)

в соответствии со значением свойства Align >

Свойства могут являться объектами или другими компонен­тами. Например, у компонента Shape есть свойства-объекты Brush и Реп. Когда свойство является объектом, то оно может быть развернуто в окне инспектора так, чтобы его собственные свойства также могли быть модифицированы. Свойства-объек­ты должны быть потомками класса TPersistent, чтобы их свой­ства, объявленные в разделе published, могли быть записаны в поток данных и отображены в инспекторе объектов.

Для определения объектного свойства компонента необходимо сначала определить объект, который будет использоваться в каче­стве типа свойства. В модуле graphics.pas описан класс TBrush:

procedure GetData(var BrushData: TBrushData);

procedure SetData(const BrushData: TBrushData);

function GetBitmap: TBitmap;

procedure SetBitmap(Value: TBitmap);

function GetColor: TColor;

procedure SetColor(Value: TColor);

function GetHandle: HBrush.;

procedure SetHandle(Value: HBrush);

function GetStyle: TBrushStyle;

procedure SetStyle(Value: TBrushStyle);

constructor Create; destructor Destroy; override;

procedure Assign(Source: TPersistent); override;

property Bitmap: TBitmap read GetBitmap write SetBitmap;

property Handle: HBrush read GetHandle write SetHandle;

property Color: TColor read GetColor write SetColor

property Style: TBrushStyle read GetStyle write SetStyle

Метод Assign предназначен для копирования значения свойств экземпляра TBrush:

Функции Delphi модуля System

Модуль System

Предлагаем список функций модуля System, используемого в среде разработки Delphi.

Abs Предназначена для получения абсолютной величины числа
Addr Возвращает адрес переменной, функции или процедуры
ArcTan Арктангенс числа, возвращается в радианах
Assigned Осуществляет проверку функциональности указателей, объектов, методов
BeginThread Начинает отдельный поток выполнения кода
Chr Конвертирует целое число в символ
Concat Соединяет несколько строк в одну
Copy Создает копию части строки или части массива
Cos Косинус числа
Eof Возвращает true, если позиция курсора находится в конце файла открытого с помощью Reset
Eoln Возвращает true, если позиция курсора находится в конце строки
Exp Выдаёт экспоненту числа
FilePos
FileSetDate Установка даты и времени последнего изменения файла
FileSize Выдает размер открытого файла в записях
GetLastError Выдаёт код ошибки последнего неудачного Windows API вызова.
GetMem Получает указанное число байтов памяти.
Hi Возвращает байт старшего разряда от типа Integer.
High Возвращает самое высокое значение типа или переменной
Int Целая часть числа с плавающей точкой
IOResult Содержит возвращаемый код последней операции ввода/вывода
IsMultiThread Возвращает True, если код выполняет множество потоков
Length Возвращает число элементов в массиве или строке
Ln Выдает натуральный логарифм числа
Lo Возвращает младший байт целого числа (2-байтового)
Low Возвращает самое низкое значение типа или переменной
Odd Провеяет, является ли целое число нечетным
Ord Порядковое значение целого числа, символа или перечисления
ParamCount Выдает число параметров переданной текущей программе
ParamStr Возвращается один из параметров используемых для запуска текущей программы
Pi Математическая константа
Pos Находит позицию одной строки в другой
Pred Уменьшает порядковую переменную
Random Генерирует случайное целое число или число с плавающей запятой
Round Округление чисел с плавающей запятой до целого числа
RunError Заканчивает программу с диалогом ошибки
SeekEof Пропускает символы, пока не встретится конец файла
SeekEoln Пропускает символы, пока не встретится конец текущей строки или файла
Sin Синус числа
SizeOf Возвращает занимаемый размер типа или переменной в байтах
Slice Создает часть массива с параметром «Открытый Массив»
Sqr Возвращает квадрат числа
Sqrt Возвращает квадратный корень числа
StringOfChar Создает строку из одного символа, повторенного много раз
StringReplace Заменяет одну или несколько подстрок, найденных в заданной строке
StringToWideChar Преобразует обычную строку в WideChar-буфер с завершающим 0
Trunc Целая часть числа с плавающей запятой
UpCase Преобразует значение Char к верхнему регистру
WideCharToString Копирует строку WideChar, заканчивающуюся нулём, в нормальную строку
Append Открывает текстовый файл, для добавления записей в файл (добавляет в конец файла)
Assign Назначает дескриптор файла на бинарный или текстовый файл
AssignFile Связывает дескриптор файла с бинарным или текстовым файлом
BlockRead Читает блок записей данных из нетипизированного двоичного файла
BlockWrite Записывает блок записей данных в нетипизированный двоичный файл
Break Выполняет выход из одного цикла
ChDir Выбор диска и директории ( папки ), в которой будет производиться работа
Close Закрывает открытый файл
CloseFile Закрывает открытый файл
Continue Заставляет перейти к следующей итерации цикла
Dec Декремент порядковой переменной
Delete Удаляет часть символов из строки
Dispose Очищает память на которую указывает указатель
EndThread Заканчивает поток с кодом завершения
Erase Стирает файл
Exclude Исключает значение из переменной набора (множества)
Exit Осуществляет выход из функции или процедуры
Frac Дробная часть числа с плавающей запятой
FillChar Заполняет раздел памяти значением байта или символа-заполнителя
Flush Сбрасывает буферизованные данные текстового файла в файл
GetDir Получает текущий каталог (диск плюс путь) для указанного диска.
Halt Заканчивает программу с дополнительным диалогом.
Inc Увеличивает порядковую переменную
Include Включает значение во множество переменных
Insert Вставляет строку в другую строку
MkDir Создаёт каталог
Move Копирует байты данных из источника в место назначения
New Создаёт новую переменную типа указатель
Randomize Устанавливает генератор случайного числа на следующее значение
Read Позволяет прочитать данные из двоичного или текстового файла
ReadLn Позволяет прочитать полную строку данных из текстового файла
ReallocMem Позволяет изменить размер существующего блока памяти
Reset Открывает текстовый файл для чтения, или двоичный файл для чтения/записи
ReWrite Открывает текстовый или двоичный файл для записи
RmDir Удаление каталога
Seek Перемещает указатель в двоичном файле в новую позицию
SetLength Изменяет размер строки или размер динамического массива
SetString Копирует символы из буфера в строку
Str Конвертирует целое число или число с плавающей точкой в строку
Truncate Уменьшает размер файла — удаляя все данные после текущей позиции
WriteLn Записывает законченную строку данных в текстовый файл

Илон Маск рекомендует:  transform-style в CSS

Почему я не должен использовать «if Assigned()» перед доступом к объектам?

Этот вопрос является продолжением конкретного комментария от людей о stackoverflow, который я видел несколько раз. Я, вместе с разработчиком, который научил меня Delphi, чтобы все было в безопасности, всегда ставил чек if assigned() перед освобождением объектов и перед другими делами. Тем не менее, теперь мне говорят, что я не должен добавлять эту проверку. Я хотел бы знать, есть ли какая-либо разница в том, как приложение компилируется/запускается, если я это сделаю, или если оно вообще не повлияет на результат.

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

Теперь скажем, что я представляю новый пользовательский объект списка TMyList из TMyListItem . Для каждого элемента в этом списке, конечно, мне нужно создать/бесплатно каждый объект объекта. Существует несколько различных способов создания элемента, а также несколько различных способов уничтожения элемента (добавление/удаление является наиболее распространенным). Я уверен, что это очень хорошая практика, чтобы поставить эту защиту здесь.

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

ИЗМЕНИТЬ

Вот пример, чтобы попытаться объяснить вам, почему у меня есть привычка делать это:

Моя точка зрения заключается в том, что if SomeCreatedObject <> nil не совпадает с if Assigned(SomeCreatedObject) , потому что после освобождения SomeCreatedObject он не оценивает значение nil . Поэтому должны быть необходимы обе проверки.

Почему Delphi объектами, доступ даже после вызова . Бесплатно?

В Delphi, почему Назначены() функция по-прежнему возвращают True, после того, как я вызвать деструктор?

ниже пример кода будет писать «sl-прежнему назначен» на консоль.

Однако, я могу позвонить FreeAndNil(sl); и она не будет назначена.

я программирую на Delphi, но это никогда не имело для меня смысл.

кто-то Может объяснить?

я пытался сравнивать VCL операций. FreeAndNil короткий и сладкий и имеет смысл:

Написание своих компонентов для Delphi. Часть 3. Создание собственных событий

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

Для решения данной задачи предназначен механизм событий. В процессе разработки определённым действиям, выполняемым с компонентом, сопоставляются соответствующие события. Самый простой пример – событие OnClick, которое сопоставлено клику мыши по компоненту.

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

Основы создания собственных событий

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

Собственное событие состоит из трёх основных частей:

  • Закрытое поле типаTNotifyEvent;
  • Открытое для чтения и записи свойство этого же типа для назначения обработчика. Рекомендуется объявлять это свойство в секции published, чтобы обработчик можно было создать с помощью Object Inspector;
  • Вызов события при изменении свойства или выполнении какого-либо действия.

Условно события можно разделить на две группы:

  • События при вызове методов (выполнении действий);
  • События при изменении значений свойств.
Пример реализации событий при вызове метода

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

Создадим два собственных события, которые будут возникать при выполнении записи в файл:

  • Событие перед сохранением в файл журнала (OnBeforeWriting);
  • Событие после сохранения в файл журнала (OnAfterWriting).

Создадим закрытые поля для этих событий:

Создадим свойства для назначения обработчиков

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

Функция Assigned проверяет, назначен ли вызываемому событию обработчик (true если назначен).

На этом создание обоих событий завершено. Им можно назначить обработчики и они будут вызываться в соответствующие моменты работы программы.

Пример реализации события при изменении свойства

Рассмотрим тот же самый компонент.

Создадим собственное событие, которое будет срабатывать при изменении свойства FilePath, которое хранит путь к файлу журнала. Это позволит программе более гибко среагировать на «переключение» с одного файла журнала на другой.

Создадим поле описывающее событие и свойство для работы с ним:

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

В коде метода SaveFilePath помимо присвоения значения соответствующему полю выполним вызов вновь созданного события.

После этого в программе можно будет обработать событие при изменении пути к файлу журнала.

«Нестандартные» события

Приведённый выше механизм описания событий является стандартным для всех компонентов Delphi независимо от используемой библиотеки (VCL, FireMonkey).Он доступен для любой поддерживаемой среды.

Однако за универсальность приходится расплачиваться тем, что в событии описанное как TNotifyEvent невозможно обработать более одного аргумента, что затрудняет создание событий для обработки сложной программной логики. Но, у данной проблемы есть простое и изящное решение – описать собственный процедурный тип для события.

Описав свой тип можно создавать события этого типа точно также как и для TNotifyEvent.

Создадим такое «нестандартное событие» предшествующее изменению свойства FilePath компонента рассмотренного выше. При этом помимо объекта вызвавшего событие будем передавать в него новый путь к файлу журнала.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL