Вы уже знаете, что свойства, как и поля класса, являются атрибутом объекта, причем с чтением и установкой значения свойства могут быть связаны некоторые события.
Однако у них есть существенные отличия, о которые необходимо сказать несколько слов.
Коренное отличие свойств от полей объекта заключается в том, что когда в инспекторе объектов изменяется значение свойства, то оно заносится в файл формы. Файл формы в проекте представляет собой просто текстовый файл с расширением DFM специального формата (в более ранних версиях Delphi использовался файл ресурсов Windows), в котором определены значения свойств. Просмотреть файл можно с помощью любой программы, способной работать с текстовыми файлами, но изменять их содержимое настоятельно не рекомендуется или, в крайнем случае, делать это крайне осторожно, так как некорректная модификация содержимого файла формы может привести к разрушению ее логической целостности.
Для того чтобы увидеть файл формы в текстовом виде с помощью Delphi необходимо в редакторе формы правой кнопкой мыши вызвать всплывающее меню и выбрать пункт меню «View as Text». Тот же эффект имеет нажатие сочетания клавиш <Alt+F12>.
Пример 2.1. Пример файла формы
object Form1: TForm1 Left = 280 Top = 110 Width = 696 Height = 480 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 144 Top = 56 Width = 75 Height = 25 Caption = 'Button1' TabOrder = 0 end end
Реальная необходимость в ручном редактировании файла формы возникает крайне редко, но если уж возникает, то без него не обойтись. Например, вы разместили на форме компонент некоторого класса (к примеру, кнопку класса TSpeedButton), подписали ее, настроили ее положение, определили обработчики событий. Затем вы пришли к выводу, что форма будет модальной, а кнопка – возвращать значение ModalResult, а у TSpeedButton такого свойства нет. Что же делать? Выход один – сменить класс кнопки, для этого нужно кнопку TSpeedButton удалить и из палитры компонентов поставить новую класса TButton, у которой есть свойство ModalResult.
Но кнопка у нас так хорошо настроена и нам очень не хочется настраивать ее заново. Как бы сменить ее тип? Очень просто – открываем файл формы, находим декларацию кнопки и вручную меняем тип. Главное при этом не ошибиться в имени класса, иначе мы просто потеряем компонент!
Может возникнуть вопрос: «А куда же помещается файл формы после компиляции программы в исполняемый EXE-файл?» Он помещается в ресурс типа RCDATA и используется программой для инициализации объекта формы при ее создании.
Перейдем непосредственно к синтаксису объявления свойств.
Свойство объявляется с использованием ключевого слова property. Далее следует название свойства, уникальное для данного класса, и после двоеточия - тип. Затем с ключевыми словами read и write указываются методы (или непосредственно поля) чтения и записи соответственно. Если свойство только для чтения, то указывается только метод чтения после слова read.
При огромном желании можно объявить и свойство только для записи, но пользы от него будет мало, так как мы не сможем прочитать значение свойства. В действительности же в библиотеке VCL нет ни одного свойство «только для записи».
При указании полей/методов чтения/записи должны соблюдаться следующие правила:
Если в классе объявляется новое свойство, то поля и методы доступа должны быть объявлены ранее (как правило, в разделе private).
Если свойство существует в классе-предке, то оно должно быть видимо классом-наследником. Это значит, что если наследник объявляется в другом модуле, то свойство не должно быть private. При этом если необходимо просто повысить видимость свойства, то его тип не указывается.
Поле для хранения значения свойства должно быть с ним одного типа.
Метод доступа не может быть динамическим (dynamic). Если же он является виртуальным (virtual), то он не должен быть перегружаемым (overload), то есть изменять число и типы формальных параметров.
Метод чтения должен быть функцией, возвращающей результат того же типа, что и само свойство.
Метод записи должен быть процедурой, принимающей один-единственный параметр, того же типа, что и само свойство. Исключение из этого правила составляют свойства-массивы индексированные свойства, которые будут рассмотрены позже.
После указания методов чтения/записи могут (но не обязаны) указываться спецификаторы памяти stored, default, и nodefault. Они не играют никакой роли в готовой программе, а предназначены для указания необходимости сохранения значения опубликованного (published) свойства в файле формы.
Директива stored явно указывает, сохранять ли значение свойства в файле формы или нет. За директивой stored должны следовать либо константы True или False, либо имя логического (Boolean) поля, либо метода-функции без параметров, возвращающего значения типа Boolean.
Директивы default и nodefault управляют значениями свойств по умолчанию. За директивой default должна следовать константа того же типа, что и свойство. Например:
Обращаю ваше внимание на то, что директивы default не достаточно для того, чтобы присвоить свойствам начальные значения, она только указывает, сохранять ли значение свойства в файле формы или нет. Начальные значения свойств должны устанавливаться в конструкторе класса. Есть еще одно полезное следствие из использования директивы default – в инспекторе объектов значения свойств, отличные от значений по умолчанию выделяются жирным шрифтом.
Если значение свойства равно значению по умолчанию и параметр stored равен False, то оно не сохраняется в файле формы, а если отличается - сохраняется.
Убедимся в этом. Просмотрим файл формы, как это было показано ранее. В файле перечислены значения явно не всех свойств формы, а лишь тех, которые либо не имеют значений по умолчанию, либо значение свойство отличается от значения по умолчанию. Изменим значения какого-либо свойства, которое в Инспекторе объектов не выделено шрифтом. Например, AlphaBlend, управляющего полупрозрачностью формы. Еще раз посмотрим на файл формы и убедимся, что теперь значение свойства занесено в файл формы.
Из этого простого эксперимента следует важный вывод - следует по возможности всегда указывать значение свойств по умолчанию с помощью директивы default – это уменьшит не только размеры файла формы, но и ресурса RCDATA, а значит и готового приложения.
Директива nodefault используется для указания того, что свойство не имеет значения по умолчанию, любое значение свойства всегда сохраняется в файле формы. Если ни одна из директив не указано, то считается, что свойство не имеет значения по умолчанию – его значение всегда будет сохраняться в файле формы.
К сожалению, директивы default и nodefault работают не со всеми типами свойств. Они действуют только по отношению к порядковым типам и множествам, нижняя и верхняя границы которых находятся в интервале 0..31. Для вещественных типов, указателей и строковых типов значение по умолчанию может быть 0, nil и ‘’ (пустая строка) соответственно.
Если есть правило, то должно быть и исключение из него. Так, директива default при описании свойства-массива имеет другое значение - указывает свойство по умолчанию.
Перейдем к рассмотрению особенностей объявления и работы со свойствами различного типа.