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, но я не пытаюсь переписывать документацию, поэтому я оставлю их для самостоятельного исследования.
Здравствуйте. Возможно ли с помощью RTTI клонировать TAction от одной кнопки к другой? И, если не трудно, то покажите как. Весь интернет перерыл, но так и не смог найти ответа. Одна надежда на вас. Буду очень признателен.
Вечер добрый !
Реально захотелось класс переделать под C#.
Класс занимается тем, что вызывает функцию, но преобразовывает параметры.
Писать по новой- лениво, поэтому родилась идея склонировать класс Delphi под C# и не только под него…
Получается, что консольному приложению на Delphi “подсовываю” класс и получаю готовую либу на нескольких языках, которую только и компилируй.
Фокус в том, что в оригинале существует набор параметров, которые идут по- умолчанию.
Например:
Function SetupAccount(Name: String; access: Integer=22): Boolean;
Вот можно откопать это значение для access, чтобы метод стал аналогичен полностью оригиналу ?
Т.е. типы- не проблема, они запросто транслируются в текст на других языках, а вот значения…
Заранее признателен.