Главная » Delphi » Получение информации о типах RTTI

0

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

Листинг 10.3. Главная форма проекта ClassInfo.dpr

unit MainFrm;

interface uses

Windows, Messages, SysUtils, Classes, QGraphics, QControls, QForms, QDialogs, QStdCtrls, QExtCtrls;

type

TMainForm = class(TForm) lbSampClasses: TListBox; lbPropList: TListBox; lbBaseClassInfo: TListBox;

procedure FormCreate(Sender: TObject); procedure lbSampClassesClick(Sender: TObject); procedure FormDblClick(Sender: TObject);

private

{ Закрытые объявления }

public

{ Открытые объявления }

end;

var

MainForm: TMainForm;

implementation uses TypInfo;{$R *.XFM}

function CreateAClass(const AClassName: string): TObject;

{ Этот метод показывает, как создать класс по его имени. Не

забудьте зарегистрировать данный класс с помощью метода Register-

Classe(), как это сделано в методе инициализации данного модуля. }

var

C : TFormClass;

SomeObject: TObject;

begin

C := TFormClass(FindClass(AClassName));

SomeObject := C.Create(nil);

Result := SomeObject;

end;procedure GetBaseClassInfo(AClass: TObject; AStrings: TStrings);

{ Этот метод позволяет получить некоторые основные данные RTTI

объекта и возвращает эту информацию параметру AStrings. }

var

ClassTypeInfo: PTypeInfo;

ClassTypeData: PTypeData;

EnumName: String;

begin

ClassTypeInfo := AClass.ClassInfo;

ClassTypeData := GetTypeData(ClassTypeInfo);

with AStrings do begin

Add(Format(‘Class Name:           %s’, [ClassTypeInfo.Name]));

EnumName := GetEnumName(TypeInfo(TTypeKind),

Integer(ClassTypeInfo.Kind));

Add(Format(‘Kind:                 %s’, [EnumName]));

Add(Format(‘Size:                 %d’, [AClass.InstanceSize]));

Add(Format(‘Defined in:           %s.pas’,

[ClassTypeData.UnitName]));

Add(Format(‘Num Properties: %d’, [ClassTypeData.PropCount]));

end;

end;

procedure GetClassAncestry(AClass: TObject; AStrings: TStrings);

{ Этот метод определяет предков данного объекта и возвращает имена

классов в параметр AStrings. }

var

AncestorClass: TClass;

begin

AncestorClass := AClass.ClassParent;

{ Перебирает все родительские классы, начиная с Sender и до

конца иерархии наследования. }

AStrings.Add(‘Class Ancestry’);

while AncestorClass <> nil do begin

AStrings.Add(Format(‘          %s’,[AncestorClass.ClassName]));

AncestorClass := AncestorClass.ClassParent;

end;

end;

procedure GetClassProperties(AClass: TObject; AStrings: TStrings);

{ Этот метод позволяет получить имена свойств и методов данного

объекта и вернуть эту информацию параметру AStrings. }

var

PropList: PPropList;

ClassTypeInfo: PTypeInfo;

ClassTypeData: PTypeData;

i: integer;

NumProps: Integer;

begin

ClassTypeInfo := AClass.ClassInfo;

ClassTypeData := GetTypeData(ClassTypeInfo);

if ClassTypeData.PropCount <> 0 then begin

// Резервирует память, необходимую для хранения указателей на

// структуры TPropInfo, исходя из количества свойств.

GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);

try

// Заполняет список PropList указателями

// на структуры TPropInfo.

GetPropInfos(AClass.ClassInfo, PropList);

for i := 0 to ClassTypeData.PropCount – 1 do

// Отфильтровываются свойства, являющиеся событиями

// (свойства, хранящие указатели на методы).

if not (PropList[i]^.PropType^.Kind = tkMethod) then

AStrings.Add(Format(‘%s: %s’, [PropList[i]^.Name,

PropList[i]^.PropType^.Name]));

// Получить свойства, являющиеся событиями

// (свойства, хранящие указатели на методы).

NumProps := GetPropList(AClass.ClassInfo, [tkMethod],

PropList);

if NumProps <> 0 then begin

AStrings.Add(”);

AStrings.Add(‘             EVENTS   ================ ‘);

AStrings.Add(”);

end;

// Параметр AStrings заполняется именами событий.

for i := 0 to NumProps – 1 do

AStrings.Add(Format(‘%s: %s’, [PropList[i]^.Name,

PropList[i]^.PropType^.Name]));

finally

FreeMem(PropList,

SizeOf(PPropInfo) * ClassTypeData.PropCount);

end;

end;

end;

procedure TMainForm.FormCreate(Sender: TObject);

begin

// Добавить в список несколько классов.

lbSampClasses.Items.Add(‘TButton’);

lbSampClasses.Items.Add(‘TForm’);lbSampClasses.Items.Add(‘TListBox’); lbSampClasses.Items.Add(‘TPaintBox’); lbSampClasses.Items.Add(‘TFindDialog’); lbSampClasses.Items.Add(‘TOpenDialog’); lbSampClasses.Items.Add(‘TTimer’); lbSampClasses.Items.Add(‘TComponent’); lbSampClasses.Items.Add(‘TGraphicControl’);

end;

procedure TMainForm.lbSampClassesClick(Sender: TObject);

var

SomeComp: TObject;

begin

lbBaseClassInfo.Items.Clear;

lbPropList.Items.Clear;

// Создать экземпляр выбранного класса.

SomeComp :=

CreateAClass(lbSampClasses.Items[lbSampClasses.ItemIndex]);

try

GetBaseClassInfo(SomeComp, lbBaseClassInfo.Items);

GetClassAncestry(SomeComp, lbBaseClassInfo.Items);

GetClassProperties(SomeComp, lbPropList.Items);

finally

SomeComp.Free;

end;

end;

procedure TMainForm.FormDblClick(Sender: TObject);

begin

ShowMessage(‘hello’);

end;

initialization begin

RegisterClasses([TButton, TForm, TListBox, TPaintBox, TFindDialog, TOpenDialog, TTimer, TComponent, TGraphicControl]);

end;

end.НА ЗАМЕТКУ

Пример, демонстрирующий применение RTTI для CLX, находится на прилагаемом CD

в каталоге CLX для настоящей главы.

В этой  главной форме содержится три  списка.  Список  lbSampClasses содержит имена  классов  нескольких объектов, информацию о типах  которых необходимо полу чить.  При  выборе объекта из списка  lbSampClasses в список  lbBaseClassInfo за носится основная информация о размере и происхождении этого  объекта, а в список lbPropList — сведения о свойствах выбранного объекта.Для получения информации о классе используются три вспомогательные процеду

ры.

•    GetBaseClassInfo() —  заполняет  список  основной  информацией  об  объек

те — типе,  размере, модуле, в котором он определен, и количестве свойств.

•  GetClassAncestry() — заполняет список именами объектов предков.

•  GetClassProperties() —  заполняет список  свойствами данного класса  и их типами.

Каждая  процедура использует в качестве параметров экземпляр объекта и список строк.

При  выборе пользователем одного  из классов в списке  lbSampClasses его процеду ра обработки события OnClick (lbSampClassesClick()) вызывает вспомогательную функцию CreateAClass(), которая создает  экземпляр класса,  заданного именем типа класса. Затем  этот  экземпляр объекта передается по назначению, чтобы  в нужный  спи сок было занесено значение соответствующего свойства TListBox.Items.

CОВЕТ

Функция CreateAClass() способна создать класс по его имени. Но, как уже было ска- зано, передаваемый этой функции в качестве параметра класс необходимо зарегист- рировать с помощью процедуры RegisterClasses().

Получение информации о типах объектов

Процедура GetBaseClassInfo() передает значение, возвращаемое функцией TOb- ject.ClassInfo(), функции GetTypeData(), определенной в модуле TypeInfo.pas. Эта процедура возвращает указатель  на структуру TTypeData, построенную для класса, структура PTypeInfo которого была передана данной процедуре (см. листинг 10.2). Про цедура GetBaseClassInfo() просто указывает на различные поля структур TTypeInfo и TTypeData для расширения списка  AStrings. Обратите внимание на использование функции GetEnumName() для получения строки по имени  перечислимого типа.  Это так же функция RTTI,  определенная в файле TypInfo.pas. О RTTI  перечислимых типов рассказывается в следующем разделе.

CОВЕТ

Используйте функцию GetTypeData(), определенную в файле TypInfo.pas, чтобы получить указатель на структуру TTypeInfo данного класса. Результат вызова функ- ции TObject.ClassInfo() следует передать функции GetTypeData().CОВЕТ

Чтобы получить имя перечислимого типа, представленного в виде строки, можно вос- пользоваться функцией GetEnumName(). Функция GetEnumValue() возвращает зна- чение перечислимого типа по заданному имени.

Получение информации о происхождении объекта

Процедура GetClassAncestry() заполняет список  строк  именами базовых классов данного объекта. Это довольно простая операция, использующая процедуру  ClassPar- ent() данного объекта и возвращающая указатель  на тип TClass базового класса  или nil, если достигнута вершина иерархии наследования (класс прародитель). Процедура GetClassAncestry() “проходит” по всей  иерархии наследования и добавляет имена базовых  классов (предков) в соответствующий список строк.

Получение информации о типах свойств объекта

Если объект обладает свойствами, то значение TTypeData.PropCount соответст вует их общему количеству. Существует  несколько подходов для получения информа ции о свойствах данного класса, но здесь приводится лишь два из них.

Процедура GetClassProperties() начинается так же, как и предыдущих два ме тода, — с передачи результата функции ClassInfo() в функцию GetTypeData() для получения  указателя   на  структуру  TTypeData класса.   Затем,  исходя   из  значения ClassTypeData.PropCount, выделяется память  для переменной PropList, опреде ленной как указатель  на массив  PPropList, который, в свою  очередь, определен в модуле TypeInfo.pas:

type

PPropList = ^TPropList;

TPropList = array[0..16379] of PPropInfo;

Массив TPropList хранит указатели  на данные  TPropInfo каждого  свойства. Тип

TPropInfo определен в модуле TypeInfo.pas следующим образом:

PPropInfo = ^TPropInfo; TPropInfo = packed record

PropType: PPTypeInfo; GetProc: Pointer; SetProc: Pointer; StoredProc: Pointer; Index: Integer; Default: Longint; NameIndex: SmallInt; Name: ShortString;

end;

Поле TPropInfo и является информацией о типе свойства.

Процедура GetClassProperties() использует  функцию  GetPropInfos() для

заполнения массива  указателями на информацию RTTI  обо  всех  свойствах данного

объекта. Затем, перебирая в цикле  все  элементы массива,  считывает имена  и типы свойств  из соответствующих данных  RTTI. Обратите внимание на следующую строку:

if not (PropList[i]^.PropType^.Kind = tkMethod) then

Таким  образом отфильтровываются свойства, являющиеся событиями (указатели на методы). Эти свойства будут добавлены в соответствующий список  позднее, попут но продемонстрировав альтернативный метод  получения информации RTTI  о свой стве.  Чтобы получить массив  TPropList для свойств конкретного типа,  в заключи тельной  части   метода   GetClassProperties() используется   функция   GetProp-List(). В данном  случае представляют интерес лишь свойства типа tkMethod. Функ ция  GetPropList() также  определена в модуле TypeInfo.pas (обратите внимание на комментарии в исходном коде).

CОВЕТ

Используйте функцию GetPropInfos(), если хотите получить указатель на информа- цию RTTI обо всех свойствах данного объекта. Для получения информации о свойст- вах определенного типа используйте функцию GetPropList().

На рис. 10.3 изображена главная форма с информацией о типах времени выполнения.

Рис. 10.3. Главная форма с информацией о типах

Проверка наличия у объекта определенного свойства

Ранее  была поставлена задача  проверки существования определенного свойства у данного объекта. Тогда речь  шла о свойстве DataSource. Используя функции, опре деленные в модуле TypInfo.pas, можно  написать функцию проверки того,  предна значен ли элемент управления для работы с данными:

function IsDataAware(AComponent: TComponent): Boolean;

var

PropInfo: PPropInfo;

begin

// Искать свойство по имени DataSource

PropInfo := GetPropInfo(AComponent.ClassInfo, ‘DataSource’);

Result := PropInfo <> nil;

// Двойная проверка, чтобы удостовериться в

// происхождении от класса TDataSource.if Result then

if not ((PropInfo^.Proptype^.Kind = tkClass) and (GetTypeData(

PropInfo^.PropType^).ClassType.InheritsFrom(TDataSource)))

then

Result := False;

end;

Здесь   используется  функция  GetPropInfo(),  возвращающая  указатель   TPro- pInfo на данное  свойство. Эта функция возвращает значение nil, если  свойство не существует. Для дополнительной проверки необходимо удостовериться, что свойство DataSource действительно происходит от класса TDataSource.

Эту функцию можно усовершенствовать так, чтобы она проверяла наличие свой

ства с любым заданным именем:

function HasProperty(AComponent: TComponent; APropertyName: String): Boolean;

var

PropInfo: PPropInfo;

begin

PropInfo := GetPropInfo(AComponent.ClassInfo, APropertyName);

Result := PropInfo <> nil;

end;

Обратите внимание, что такой  подход применим только  к тем свойствам, которые объявлены как published. Для непубликуемых свойств  данные  RTTI отсутствуют.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии