Delphi: Углубленное исследование TRTTIType

Источник: delphi2010
Александр Божко

TRttiType обеспечивает нас удобным интерфейсом, который позволяет получать доступ ко всей информации, связанной с типом. В большинстве случаев, как мне кажется, он интуитивно понятен и документирован. Я рекомендую открыть Rtti.pas и посмотреть описание TRttiType. В общих чертах я рассказал про TRttiType в предыдущих статьях.

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

function GetMethods: TArray<TRttiMethod>; overload; virtual;
function GetFields: TArray<TRttiField>; virtual;
function GetProperties: TArray<TRttiProperty>; virtual;

function GetMethod(const AName: string): TRttiMethod; virtual;
function GetMethods(const AName: string): TArray<TRttiMethod>; overload; virtual;
function GetField(const AName: string): TRttiField; virtual;
function GetProperty(const AName: string): TRttiProperty; virtual;

Однако, иногда вам хочется получить доступ, к тому, что было объявлено, лишь в  конкретном типе и не объявлялось в родительских типах. Это можно сделать, используя следующую функциональность:

function GetDeclaredMethods: TArray<TRttiMethod>; virtual;
function GetDeclaredProperties: TArray<TRttiProperty>; virtual;
function GetDeclaredFields: TArray<TRttiField>; virtual;

Если ваш тип унаследован, вы можете получить Основной Тип с помощью вызова свойства BaseType.
Приведенный ниже код как вы можете подниматься по дереву типов для нахождения родительского типа.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI;
type
  TOneObject = Class(TObject)
  end;

  TTwoObject = Class(TOneObject)
  end;

  TThreeObject = Class(TTwoObject)
  end;

var
 c : TRttiContext;
 t : TRttiType;

begin
   c := TRttiContext.Create;
   try
     t := c.GetType(TThreeObject);
     writeln(t.Name);
     while Assigned(t.BaseType) do
     begin
       t := t.BaseType;
       writeln(t.Name);
     end;
   finally
     c.Free
   end;
   readln;
end.

Результат:

TThreeObject
TTwoObject
TOneObject
TObject

Есть три набора свойств, которые дают вам больше информации о данном типе.

property AsInstance: TRttiInstanceType read GetAsInstance;
property IsInstance: Boolean read GetIsInstance;
property AsOrdinal: TRttiOrdinalType read GetAsOrdinal;
property IsOrdinal: Boolean read GetIsOrdinal;
property IsSet: Boolean read GetIsSet;
property AsSet: TRttiSetType read GetAsSet;

Экземпляром Типов являются Классы, и тип TRttiInstanceType предоставляет свойство "MetaclassType", которое возвращает TClass для данного типа. Следующий пример показывает, как использовать этот тип для создания экземпляра объекта.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI;
type
  TOneObject = Class(TObject)
  end;

  TTwoObject = Class(TOneObject)
  end;

  TThreeObject = Class(TTwoObject)
  end;

var
 c : TRttiContext;
 t : TRttiType;
 o : TObject;
begin
   c := TRttiContext.Create;
   try
     t := c.GetType(TThreeObject);
     o := t.AsInstance.MetaclassType.Create;
     Writeln(o.ClassName);
     o.Free;
   finally
     c.Free
   end;
   readln;
end.

Результат

TThreeObject

TRttiOrdinalType ссылается на Ординальные Типы, такие как Integer, Перечисляемые типы и т.д:
Он содержит 3 новых свойства, для того, что бы помочь вам при работе с ординальными типами.

TOrdType = (otSByte, otUByte, otSWord, otUWord, otSLong, otULong);

...

property OrdType: TOrdType read GetOrdType;
property MinValue: Longint read GetMinValue;
property MaxValue: Longint read GetMaxValue;

Следующий пример кода демонстрирует их поведение.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI,TypInfo;
type
  TMyEnum = (enOne,enTwo,enThree);
var
 c : TRttiContext;
 t : TRttiType;
begin
   c := TRttiContext.Create;
   try
     t := c.GetType(TypeInfo(TMyEnum));
     writeln(GetEnumName(TypeInfo(TOrdType),ord(t.AsOrdinal.OrdType)));
     writeln(t.AsOrdinal.MinValue);
     writeln(t.AsOrdinal.MaxValue);
   finally
     c.Free
   end;
   readln;
end.

Результат:

otUByte
0
2

Обратите внимание, я обратился к TypInfo.pas, что бы вызвать GetEnumName(), эта, и связанная с ней функция GetEnumValue() позволяет вам работать с именами перечисляемых типов, вместо ординальных значений.
RTTI информация, которая была доступна в предыдущих версиях Delphi для получения информации о типе, использовала указатель, т.е. "pTypeInfo".
Теперь это хранится в свойстве .Handle TRttiType, я рассказываю об этом, как и о процедурах модуля TypInfo.pas , на тот случай, если вам понадобится обратиться к низкоуровневой функциональности.
TRttiSetType предоставляет одно новое свойство "ElementType", которое позволяет вам получать тип элементов множества. Следующий код демонстрирует это в действии.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI,TypInfo;
type
  TMyEnum = (enOne,enTwo,enThree);
  TMySet = set of TMyEnum;
var
 c : TRttiContext;
 t : TRttiType;
begin
   c := TRttiContext.Create;
   try
     t := c.GetType(TypeInfo(TMySet));
     Writeln('Element Type:');
     writeln(t.AsSet.ElementType.ToString);
   finally
     c.Free
   end;
   readln;
end.

Результат

Element Type:
TMyEnum

Что бы быть последовательным, скажу, что тип TRttiRecordType  наследуется от TRttiType, но он содержит всего одно новое свойство, ManagedFields, которому я не нашел осмысленного применения.

property AsRecord: TRttiRecordType read GetAsRecord;
property IsRecord: Boolean read GetIsRecord;

Но не пропустите свойство IsRecord, оно очень полезно.
Существуют и несколько других наследников, которые позволят вам использовать стандартные "is" и "as" для получения доступа к дополнительной информации, связанной с данным типом.
TRttiInterfaceType
TRttiInt64Type
TRttiMethodType
TRttiClassRefType
TRttiEnumerationType
TRttiStringType
TRttiAnsiStringType
TRttiFloatType
TRttiArrayType
TRttiDynamicArrayType
TRttiPointerType
TrttiProcedureType
Следующий код показывает, как вы можете преобразовать тип TRttiType в TRttiStringType, для того, что бы выяснить с каким строковым типом вы имеете дело.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI,TypInfo;
type
   TmyRecord = record
     UniStr : String;
     AnsiStr : AnsiString;
     WideStr : WideString;
   end;
var
 c : TRttiContext;
 t : TRttiType;
 field : TRttiField;
begin
   c := TRttiContext.Create;
   try
     for field in c.GetType(TypeInfo(TMyRecord)).GetFields do
     begin
       t := field.FieldType;
       writeln('Field:',field.Name);
       writeln('RttiType:',t.ClassName);
       if (t is TRttiStringType) then
         Writeln('String Kind:',GetEnumName(TypeInfo(TRttiStringKind),ord((t as TRttiStringType).StringKind)));
       Writeln;
     end;
   finally
     c.Free
   end;
   readln;
end.

Результат:

Field:UniStr
RttiType:TRttiStringType
String Kind:skUnicodeString

Field:AnsiStr
RttiType:TRttiAnsiStringType
String Kind:skAnsiString

Field:WideStr
RttiType:TRttiStringType
String Kind:skWideString

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=22207