|
|
|||||||||||||||||||||||||||||
|
Delphi 2010 RTTI - ОсновыИсточник: delphi2010 Александр Божко
В Delphi 2009 как и в предыдущих версиях, RTTI был ограничен элементами, размещенными в Published секции. У вас был доступ к указателям на свойства, поля и методы объекта. Если потратить немного времени на обучение, то старая версия RTTI превращалась в довольно мощный механизм. Однако, мощь RTTI предыдущих версий смотрится весьма скромно по сравнению с тем, что стало возможным в Delphi 2010. В Delphi 2010 вы можете извлекать RTTI информацию практически отовсюду. Выбор, какая информация должна быть доступна, остается за вами, этим управляет новая директива $RTTI. Поведение по умолчанию определено в System.pas, фрагмент которого приведен ниже, свойства и методы теперь доступны посредствам RTTI как в Published, так и в Public секциях, а поля доступны во всех секциях. Section of System.pas: { RTTI Visibility } type TVisibilityClasses = set of (vcPrivate, vcProtected, vcPublic, vcPublished); const { Эти константы отображают текущие установки, встроенные в компилятор. Обычно для классов они наследуются от TObject. } DefaultMethodRttiVisibility = [vcPublic, vcPublished]; DefaultFieldRttiVisibility = [vcPrivate..vcPublished]; DefaultPropertyRttiVisibility = [vcPublic, vcPublished]; type { Настройки RTTI по-умолчанию} {$RTTI INHERIT METHODS(DefaultMethodRttiVisibility) FIELDS(DefaultFieldRttiVisibility) PROPERTIES(DefaultPropertyRttiVisibility)} Сама по себе RTTI информация не будет представлять ценности до тех пор, пока у вас не будет удобного доступа к ней. Новый модуль RTTI.pas предоставляет простой и элегантный метод доступа к этим данным. Гибкость классов, без головной боли с управлением памятью (Memory Management) стала ключевым моментом для нового проекта. Поскольку RTTI доступ реализован через контекст, вместе с высвобождением контекста высвобождаются все RTTI объекты, созданные в рамках контекста. var c : TRttiContext; begin c := TRttiContext.Create; try // RTTI Access code here finally c.free; end; end; Если вы откроете RTTI.pas, вам следует обратить внимание на то, что TRttiContext не является объектом, это - запись (Record), но не смущайтесь по поводу того, что вы должны вызывать .Create и .Free так же как и для объекта. Это обусловлено необходимостью высвобождать пул RTTI объектов, которые могут быть созданы. Хотя help файл рекомендует не высвобождать их, лично я предпочитаю делать это. Ввиду большого числа возникших вопросов, я дам более детальное разъяснение. TRttiContext предоставляет несколько ключевых методов, которые позволяют вам получать доступ к типам в системе. function GetType(ATypeInfo: Pointer): TRttiType; overload; function GetType(AClass: TClass): TRttiType; overload; function GetTypes: TArray<TRttiType>; function FindType(const AQualifiedName: string): TRttiType; Например, следующий код вернет TRttiType класс, репрезентующий, TButton. var c : TRttiContext; t : TRttiType; begin c := TRttiContext.Create; try // Via a String t := c.FindType('StdCtrls.TButton'); // Via the pTypeInfo Pointer t := c.GetType(TButton.ClassInfo); // Via the class type t := c.GetType(TButton); finally c.Free; end; end; TRttiType содержит много функций, которые позволяют вам запрашивать члены класса на предмет их типа. var c : TRttiContext; t : TRttiType; begin c := TRttiContext.Create; try // Via a String t := c.FindType('StdCtrls.TButton'); // Via the pTypeInfo Pointer t := c.GetType(TButton.ClassInfo); // Via the class type t := c.GetType(TButton); finally c.Free; end; end; Так, например, следующее консольное приложение будет показывать все методы TButton. program Project10; {$APPTYPE CONSOLE} uses StdCtrls, TypInfo, Rtti; var c : TRttiContext; m : TRttiMethod; begin c := TRttiContext.Create; for m in c.GetType(TButton).GetMethods do begin Writeln(m.ToString); end; c.Free; readln; end. Результат. constructor Create(AOwner: TComponent) class destructor Destroy procedure Click ... (Many Lines Removed) ... procedure AfterConstruction procedure BeforeDestruction procedure Dispatch(var Message) procedure DefaultHandler(var Message) class function NewInstance: TObject procedure FreeInstance class destructor Destroy Перейдем на следующий уровень. Приведенный ниже код создает TStringList используя RTTI, вызывая метод Add и получая доступ к свойству Text. Здесь приведен не совсем практичный пример, он создан только для того, что бы показать отдельные возможности. Вы должны обратить внимание на то, что для сохранения значений используется тип TValue. TValue может как сохранять, так и извлекать тип. program Project11; {$APPTYPE CONSOLE} uses StdCtrls, TypInfo, Classes, Rtti; var c : TRttiContext; m : TRttiMethod; t : TRttiInstanceType; SL : TValue; Lines : TValue; begin c := TRttiContext.Create; t := (c.FindType('Classes.TStringList') as TRttiInstanceType); SL := t.GetMethod('Create').Invoke(t.MetaclassType,[]); t.GetMethod('Add').Invoke(SL,['Hello Do you like my hat?']); t.GetMethod('Add').Invoke(SL,['I like that hat, what a party hat!']); Lines := t.GetProperty('Text').GetValue(SL.AsObject); Writeln(Lines.ToString); c.Free; readln; end. Результат. Hello Do you like my hat? Хотя может показаться, что TValue работает подобно variant, TValue не является заменой Variant. Характерно, что если вы назначаете TValue определенный тип, вы должны извлекать его как этот назначенный тип. Например, вы не сможете назначить TМalue integer, а извлечь string, в результате вы получите Invalid Type Cast. Попробуйте пока разобраться с этим, а в следующих статьях мы детальней углубимся в этот вопрос. Ссылки по теме
|
|