Свойства - это специальный механизм классов, регулирующий доступ к полям. Свойства объявляются с помощью зарезервированных СЛОВ property, read И write (слова read И write считаются зарезервированньши только в контексте объявления свойства). Обычно свойство связано с некоторым полем и указывает те методы класса, которые должны использоваться при записи в это поле или при чтении из него. Например:
type
TaClass = class
IntField: Integer; Function GetField: Integer;
Procedure SetField (Value: Integers);
Property IntegerValue: Integer read GetField
write SetField;
end;
В контексте программы свойство ведет себя как обычное поле. Например, мы могли бы написать такие операторы:
var
aClass: TaClass;
Value: Integer;
begin
aClass:= TaClass.Create; { Обязательное обращение к
конструктору перед обращением к полю или свойству!}
aClass.IntegerValue:= 0;
Value:= aClass.IntegerValue;
aClass.Destroy; // Удаление ненужного объекта
end;
Более того, возможен и такой оператор присваивания:
aClass.IntField:= NewValue;
Разница между этим оператором и оператором
aClass.IntegerValue:= NewValue;
заключается в том, что при обращении к свойству автоматически подключается метод setFieid, в котором могут реализовываться специфичные действия. Вспомним использовавшийся нами в учебной программе оператор
|
|
IbOutput.Caption:= 'Строка';
Свойство Caption компонента Label вызывает метод setText, который не только запоминает строку символов во внутренней переменной, но и осуществляет прорисовку метки с новым текстом.
Если нет необходимости в специальных действиях при чтении или записи свойства, вместо имени соответствующего метода можно указывать имя поля:
type
TaClass = class
IntFiled: Integer;
Procedure SetFieid (Value: Integers;
Property IntegerValue:
Integer read IntFiled write SetFieid;
end;
Если необходимо, чтобы свойство было доступно только для чтения или только для записи, следует опустить соответственно часть write или read. Вообще говоря, свойство может и не связываться с полем. Фактически оно описывает один или два метода, которые осуществляют некоторые действия над данными того же типа, что и свойство
Варианты
Вариант (в Delphi 1 он отсутствует) - это тип variant, разработанный специально для тех случаев, когда на этапе компиляции программист не может сказать, какого типа данные будут использоваться в выражении или как параметры вызова подпрограмм. Переменная-вариант занимает в памяти дополнительные 2 байта, в которые помещается информация о действительном типе переменной. Эта информация позволяет компилятору создать код, который будет осуществлять необходимое преобразование типов на этапе прогона программы.
В переменную-вариант можно поместить: <UL
· целое или вещественное число;
· логическое значение;
· строку;
· время и/или дату;
· OLE-объект;
· массив произвольной размерности и длины, содержащий элементы одного из перечисленных выше типов.
|
|
Варианты могут участвовать в целочисленных, вещественных, логических и время-дата выражениях при условии корректности соответствующих преобразований. Например, если варианту v присвоена строка '1.0', то выражение 1+v будет правильным вещественным значением 2,0. Однако если v:= 'текст', выражение 1+v вызовет исключение EVariantError.
В Delphi определены такие константы, указывающие тип помещенных в вариант данных:
Таблица 10.1. Типы возможных значений варианта | ||
Имя | Константа | Смысл |
varEmp.ty | $0000 | Нет данных |
varNull | $0001 | Неизвестный тип параметра |
varSmallInt | $0002 | Целый тип Smallint |
varlnteger | $0003 | Целый тип Integer |
varSingle | $0004 | Вещественный тип Single |
varDouble | $0005 | Вещественный тип Double |
varCurrency | $0006 | Вещественный тип Currency |
varDate | $0007 | Тип дата-время |
varOleStr | $0008 | OLE-строка в кодировке Unicode |
varDispatch | $0009 | Указатель на OLE-объект |
varError | $000А | Код ошибки |
varBoolean | $000В | Тип WordBool |
varVariant | $000С | Тип Variant (только для вариантных массивов) |
varUnknow | $0011 | Неизвестный OLE-объект |
varByte | $0100 | Целый тип Byte |
varString | $0100 | Строковый тип |
varArray | $2000 | Вариантный массив |
varByRef | $4000 | Указатель на данные |
Структура вариантного типа описывается следующим образом:
TVarData = packed record
VType: Word;
Reservedly Reserved2, ReservedS: Word;
case Integer of
varSmallInt: (VSmallInt: Smallint);
varlnteger: (VInteger: Integer);
varSingle: (VSingle: Single);
varDouble: (VDouble: Double);
varCurrency: (VCurrency: Currency);
varDate:(VDate: Double);
varOleStr: (VOleStr: PWideChar);
varDispatch: (VDispatch: Pointer);
varError: (VError: WordBool);
varString: (VString: Pointer);
varArray: (VArray: PVarArray);
varByRef: (VPointer: Pointer);
end;
Как нетрудно убедиться, любая переменная вариантного типа представляет собой 16-байтную запись, содержащую 8-байтную вариантную часть, которая хранит либо собственно данные, либо их адрес (т. е. указатель на динамически размещенные данные). В поле VType в момент создания варианта компилятор помещает, признак отсутствия данных varEmpty. В работающей программе значение этого поля меняется в соответствии с текущим типом данных, размещенных в вариантной части. Замечу, что программа не может получить прямого доступа к полям вариантной записи. Получить тип вариантных данных можно с помощью функции varType (см. ниже), а изменить тип - путем присваивания варианту нового значения.
К чему приводится | Тип данных в варианте | |||||
varEmpty | Целые | Вещественные | Дата-Время | Строковые | Логические | |
К дата- 'У: время | 30.12.1899 00:00:00 | Преобразование в Double | Преобразование в Double | Без преобразования | Преобразование в дату | Преобразование в Double |
К целым | 0 | Преобразование в соответствующий тип | Округление до ближайшего целого | Округление до ближайшего целого | Преобразование в целый тип | 0 для False, иначе-1 (255 для Byte) |
'К дата- 'У: время | 30.12.1899 00:00:00 | Преобразование в Double | Преобразование в Double | Без преобразования | Преобразование в дату | Преобразование в Double |
К строковым | Пустая строка | Преобразование в символьный вид | Преобразование в символьный вид | Преобразование в символьный вид | Без преобразования | '0'для False,'-!' для True |
К логическим | False | False для 0, иначе True | False для 0, иначе True | False для 0, иначе True | False для 'False' и для '0', иначе True | Без преобразования |
Преобразование вариантов к данным других типов
При участии вариантов в выражениях, а также при присваивании их значений переменным других типов тип размещенных в варианте данных преобразуется по следующим правилам:
Здесь
· К целым Отнесены varByte, varSmallInt, varlnteger/ varError;
· К вещественным - varSingle, varDouble/ varCurrency;
· К строковым -var String, varOleStr.
Подпрограммы для работы с вариантами
Для работы с вариантами можно использовать такие подпрограммы:
Таблица 10.2. Подпрограммы для работы с вариантами | |
function VarAsType(const V: Variant; VarType: Integer): Variant; | Преобразует данные варианта V к типу, определяемому параметром VarType |
procedure VarCast(var Dest: Variant; const Source: Variant; Var Type: Integer); | Преобразует данные варианта Source к типу,определяемому параметром VarType, и помещает результат в переменную Dest |
procedure VarClear(var V: Variant); | Освобождает динамическую память, если она была связана с вариантом, и дает ему тип varEmpty |
procedure VarCopy(var Dest: Variant; const Source: Variants; | Копирует параметр Source в вариант Dest |
function VarFrom-DateTime(DateTime: TDateTime):Variant; | Возвращает вариант, содержащий данные DateTime типа дата-время |
function VarIsEmpty(const V: Variant): Boolean; | Возвращает True, если вариант V не содержит данных |
function VarIsNull(const V: Variant): Boolean; | Возвращает True, если вариант V содержит данные неопределенного типа (varNull) ', |
function VarToDateTime(const V:Variant): TDateTime); | Преобразует данные варианта V к типу дата-время |
function VarToStr(const V: Vari ant): String; | Преобразует данные варианта V к строке; |
function VarType(const V: Variant): Integer; | Возвращает тип хранящихся в варианте данных i |
Вариантные массивы
|
|
Значением варианта может быть массив данных, такие варианты называются вариантными массивами. (Не путайте с обычным или динамическим массивом, элементами которого являются варианты!) Значениями элементов вариантного массива могут быть любые допустимые для варианта значения, кроме строк varstring. Значениями элементов вариантного массива могут быть и варианты, а это значит, что в таком массиве могут одновременно храниться данные разных типов (и в том числе строки). Например:
var
V: Variant;
begin
// Создаем одномерный вариантный массив с 5 элементами:
V:= VarArrayCreate([0, 4], varVariant);
// Наполняем его:
V[0]:= 1; //Тип целый
V[1]:= 1234.5678; //Тип вещественный
V[2]:= 'Hello world'; //Строковый тип
V[3]:= True; //Логический тип
//Пятым элементом исходного массива сделаем еще один массив:
V[4]:= VarArrayOf([1, 10, 100, 1000]);
Caption:= V[2]; //Hello world
IbOutput.Caption:= IntToStr(V[4][2]); //200
end;
Все действия с вариантными массивами осуществляются с помощью следующих процедур и функций:
Таблица 10.3. Подпрограммы для работы с вариантными массивами | |
function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; | Создает вариантный массив из элементов типа VarType с количеством и границами измерений, указываемых параметром Bounds |
function VarArrayDimCount(const A: Variant): Integers; | Возвращает количество измерений вариантного массива А или 0, если А не массив |
function VarArrayHighBound(const A: Variant; Dim: Integer): Integer; | Возвращает верхнюю границу индекса вариантного массива А по измерению Dim |
function VarArrayLock(var A: Variant): Pointer; | Блокирует массив (предотвращает его возможные изменения размеров) и возвращает указатель на связанные с ним данные |
function VarArrayLowBound(const A: Variant; Dim: Integer): Integers; | Возвращает нижнюю границу индекса вариантного массива А по измерению Dim |
function VarArrayOf(const Values: array of Variant): Variants; | Создает одномерный вариантный массив по перечню значений, содержащихся в открытом массиве Values. Нижняя граница индексов вариантного массива в этом случае равна 0 |
procedure VarArrayRedim(var A: Variant; HighBound: Integer); | Изменяет верхнюю границу индекса вариантного массива А на величину HighBound. Вызов процедуры игнорируется, если массив был заблокирован функцией VarArrayLock |
function VarArrayRef(const A:Variant): Variants; | Возвращает ссылку на вариантный массив. Используется при обращении к API-функциям |
procedure VarArrayUnlock(var A: Variant) | Отменяет действие функции VarArrayLock |
Пользовательские варианты
|
|
Стандартный вариант может хранить только одно из значений, указанных в табл. 10.2. В версии Delphi 6 появились так называемые пользовательские варианты, которые фактически снимают ограничения на характер значений варианта.
Чтобы познакомиться со свойствами новых вариантов, воспользуемся одним из них - вариантом, способным хранить комплексные числа, преобразовывать их в другие типы и осуществлять над ними нужные действия. Как мы увидим дальше, создание пользовательского варианта может быть весьма трудоемким делом - все зависит от сложности хранимых в нем данных. Мы воспользуемся вариантом, созданным разработчиками Delphi и включенным в модуль VarCmplx.
Создайте такой обработчик bbRunClick:
uses VarCmplx; // Эта ссылка обязательна!
procedure TfmExample.bbRunClick(Sender: TObject);
var
VI, V2: Variants- begin
// Создаем два случайных комплексных числа:
VI:= VarComplexCreate(Trunc(Random*1000)/100,
Trunc(Random*1000)/100);
V2:= VarComplexCreate(Trunc(Random*1000)/100,
Trunc(Random*1000)/100);
with mmOutput.Lines do
begin
// Пустая строка-разделитель
Add (' ');
Add('1-e число: '# 9+V1);
Add('2-е число: '#9+V2);
Add('Сложение'#9+(V1+V2));
Add('Вычитание'#9+(V1-V2));
Add('Умножение'# 9+(VI*V2));
Add('Деление'#9#9+(V1/V2))
end
end;
Небольшой комментарий: сложная конструкция Trunc (Random*1000) /100 понадобилась только для того, чтобы реальные и мнимые части комплексных чисел содержали по три значащих цифры.
Вид экрана работающей программы показан на рис. 10.1. Как видим, новый вариант легко справляется с поддержкой комплексных чисел: функция VarComplexCreate создает вариант, содержащий комплексное число, а дальнейшее поведение варианта -стандартное (он поддерживает математические операции и преобразование к строковому типу). Однако эта легкость обманчива: исходный текст модуля VarCmplx, который, собственно, и придал варианту дополнительные свойства (по умолчанию располагается в файле Source\Rtl\Common\VarCmplx.pas), содержит более 30000 байт..
Ниже показана структура записи TVarData. Два первых байта в этой записи (поле VType) хранят признак значения варианта, остальные 14 могут использоваться для размещения данных.
Рис. 10.1. Демонстрация комплексных вариантов
Создание пользовательского варианта проходит в три этапа.
1. Сначала в записи rvarData размещаются новые данные или ссылка на них.
2. Объявляется класс, который является прямым или косвеннымпотомком специального класса TCustomVariantType. В этомклассе предусматриваются все необходимые методы для реализации свойств варианта: присваивания ему новых значений, преобразования хранящихся значений к другим типам, выполнения необходимых математических действий.
3. Создаются вспомогательные методы для объявления потомков нового класса и определения их типов.
В результате перечисленных шагов вы получаете полноценный вариант, обогащенный новыми свойствами: он может хранить не только те значения, которые перечислены в табл. 10.2, но и любые другие, в том числе свойства и методы! (В этом последнем случае наследником для исполняемого класса нового варианта вместо TCustomVariantType является TInvokeableVariantType или TPublishableVariantType.)
Размещение в варианте новых значений
Для размещения в варианте нового (не предусмотренного стандартным вариантом) значения нужно создать соответствующий класс и поместить в подходящее поле rvarData объект этого класса. Вот как, например, размещаются комплексные данные в модуле VarCmplx:
TComplexVarData = packed record
VType: TVarType;
Reserved1, Reserved2, Reserved3: Word;
VComplex: TComplexData;
Reserved4: Longint;
end;
Такая запись лишь сохраняет 16-байтную структуру TVarData, помещая в поле VComplex ссылку на объект класса TComplexData. Собственно комплексные числа хранятся в полях достаточно сложного класса:
type
TComplexData = class(TPersistent) private
FReal, FImaginary: Double;
end;
В этом классе предусмотрены многочисленные методы, управляющие новыми данными. Так, простой вызов VarComplexCreate приводит к срабатыванию нескольких методов, создающих объект VComplex и наполняющих его поля:
procedure VarComplexCreateInto (var ADest: Variant;
const AComplex: TComplexData);
begin
VarClear(ADest);
TComplexVarData(ADest).VType:= VarComplex;
TComplexVarData(ADest).VComplex:= AComplex;
end;
function VarComplexCreate(const AReal, AImaginary: Double): Variant;
begin
VarComplexCreateInto(Result,
TComplexData.Create(AReal, AImaginary));
end;