A case of using delphiXE RTTI to dynamically obtain information at runtime and obtain RttiType information of a TComponent class or TObject class
One, understand RTTI
First look at the official document: http://docwiki.embarcadero.com/RADStudio/Rio/en/Working_with_RTTI The translation is as follows:
Runtime Type Information (RTTI) is a programming mode in which information about types can be obtained at runtime. If RTTI generation is enabled, the generated binary file will contain special metadata that contains information about the type (for example, class ancestry of the class, declared fields, annotated attributes [I add : Contains the fields, attributes, functions, and procedures of the class, including published, public, protected, and private] ). Usingthe functions provided in the System.Rtti unit , you can obtain this information at runtime. The end result is the ability to create an abstract and general framework in which any public RTTI type can be operated.
Note: RTTI is unique to Delphi development tools.
Note: No runtime type information is generated for generic methods.
What I added: Delphi roots RTTI information into the package information after compilation and linking (the binary file generated by the link, which is linked to the .exe executable file (Windows), .jar (Android), .static file.ah (IOS) )) System.PackageInfoTable in the table, the table is read at runtime. Note: On the iOS platform , the link is done statically, so RTTI will not be rooted. However, if you use the GetType method , you can get RTTI
RTTI generated control
Use the following compiler directives to control the generation of runtime type information. You may need to limit the generation of RTTI to reduce the size of the executable file.
Delphi | C ++ |
---|---|
{$ M},{$ TYPEINFO} | __declspec (delphirtti) |
{$ METHODINFO} | Not applicable |
$ RTTI | #pragmaexplicit_rtti |
{$ WEAKLINKRTTI} | Not applicable |
topic
- Get RTTI context Obtaining the RTTI Context
- Querying for Type Information
- Generic type information General Type Information
- Simple type of information Information for Simple Types
- Structured type information Information for Structured Types
- Runtime operations on the type of Run-Time Operations on Types
- Delphi RTTI和C ++ Builder Delphi的RTTI and C++Builder
You can also take a look
- Delphi attributes in Attributes in Delphi
Code example
- Category: RTTI Category
- RTL.AttributesAndRTTI example (many code examples use RTTI)
2. Perceptual cognition, first look at the case:
1. Official website case : http://docwiki.embarcadero.com/CodeExamples/Rio/en/Rtti.TRttiType_(Delphi)
2. Official installation package case : C:\Users\Public\Documents\Embarcadero\Studio\20.0\Samples\Object Pascal\RTL\AttributesAndRTTI\rtti_browser\rtti_browser.dproj
3. The case I wrote to you is to query the definition of any class and any low-level object in the system (methods, attributes (read and write methods to generate attribute information), fields (usually private generics, help attribute write assignment and read Information)), applicable to VCL and Fmx, can be used across platforms:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants,
System.Classes, System.IOUtils,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
btn_getRttiTypeOfTComponent: TButton;
Panel1: TPanel;
Panel2: TPanel;
ComboBoxEx1: TComboBoxEx;
ComboBoxEx2: TComboBoxEx;
///<summary>Button click event btn_getRttiTypeOfTComponent:</summary>
procedure btn_getRttiTypeOfTComponentClick(Sender: TObject);
///<summary>ComboBoxEx1.Items gets the type name of the TComponent of the list requested by RTTIType</summary>
procedure ComboBoxEx1DropDown(Sender : TObject);
///<summary>Generate file and its path:</summary>
procedure FormCreate(Sender: TObject);
private
LFilePath:string;
LFileHandle:THandle;
///<summary>Rtti gets all the objects running in the delphi system Class name and meta-information array at time: </summary>
///<summary>1, TRttiContext context runtime environment obtains the runtime type TRTTIType</summary>
///<summary>2 of a certain TComponent The class corresponding to the class name</summary>
///<summary>3, and get the ClassName and QualifiedName of a certain class object RTTIType</summary>
///<summary>4, and in the order of ClassName, Methods, Properties, Fields of the type ClassType</summary>
///<summary>: i.e. TRttiType.AsInstance.MetaClassType.ClassName, TRttiMethod, TRttiProperty, TRttiField order </summary>
///<summary>: Get their meta information array:</summary>
///<summary>:TRttiType.GetMethods;TRttiType.GetProperties;TRttiType.GetFields:</summary>
///<summary >5. Write the above information into TreeView(btn_getRttiTypeOfTComponentClick) through button events</summary>
procedure getRttiTypeOfTComponent(const AComponentName:string);
{Private declarations}
public
{Public declarations}
end;
var
Form1: TForm1;
implementation
uses
RTTI;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LFilePath:=ExtractFilePath(ParamStr(0))+'VclTWinControlCategories.text';
if not System.SysUtils.FileExists(LFilePath) then
begin
LFileHandle:=System.SysUtils.FileCreate(LFilePath,fmShareDenyNone);
if System.SysUtils.FileExists(LFilePath) then
System.SysUtils.FileClose(LFileHandle);
//System.SysUtils.FileCreate System.IOUtils
end;
end;
procedure TForm1.ComboBoxEx1DropDown(Sender: TObject);
//:ComboBoxEx1.Items gets the type name of the TComponent of the list requested by RTTIType
var
LFileLines:TArray<system.string>;
LTComponentClassName:string;
LListedAClassName:string;
LIfExistListedAClassName:Boolean;
begin
if (ComboBoxEx2.Items.Count<=1)
and (ComboBoxEx1.Items.Count<=1) then
begin
try
btn_getRttiTypeOfTComponentClick(sender);//: That is: getRttiTypeOfTComponent(trim(ComboBoxEx1.Text));
//: After execution Will automatically return to here and continue to execute
//: Because the execution is Rtti, (Sender:TObject) itself is also an object's Rtti
//:TNotifyEvent = procedure(Sender: TObject) of object;
//: The TObject column is not listed, it only depends on the definition of the method in FillVclClasses executed in it.
//: execute Rtti first, then execute sender
finally
end;
end;
if ComboBoxEx2.Items.Count>0 then
begin
LFileLines:=TFile. the ReadAllLines (LFilePath);
IF length (LFileLines)> 0 the then
the begin
LIfExistListedAClassName: = to false;
for LTComponentClassName LFileLines do in
the begin
for LListedAClassName in ComboBoxEx1.Items do
IF LListedAClassName = LTComponentClassName the then
the begin
LIfExistListedAClassName: = to true;
Break;
End;
IF = LIfExistListedAClassName false then
ComboBoxEx1.Items.Add(LTComponentClassName);
end;
end else exit;
end;
end;
procedure TForm1.btn_getRttiTypeOfTComponentClick(Sender: TObject);
begin
getRttiTypeOfTComponent(trim(ComboBoxEx1.Text));
end;
procedure TForm1. getRttiTypeOfTComponent (const AComponentName:string);
///<summary>returns whether it is successful, callbacks the TRttiType instances of all components and the class name of the metaclass of TRttiInstanceType:</summary>
function FillVclClasses(ATStrings:TStrings): Boolean;
var
ctx: TRttiContext;
typ: TRttiType;
list: TArray<TRttiType>;
begin
Result:=false;
ctx := TRttiContext.Create;
list := ctx.GetTypes;
for typ in list do
begin
if typ.IsInstance //: Yes The instantiated type
//and typ.IsManaged //: Cannot be used: The source code means it seems to refer to the basic data type or tkClass is referenced by the UI: TypeInfo^.Kind
//and typ.IsPublicType //: is a public type
and (
typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.StdCtrls')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.ComCtrls')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.ExtCtrls')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.ActnList')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.ImgList')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.Graphics')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.Menus')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.Controls')
or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('TObject')
//or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('System.Classes')
//or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl. Themes') //: Contains enumeration, collection, generic list and generic dictionary
//or typ.AsInstance.MetaClassType.QualifiedClassName.Contains('Vcl.Forms') //: Cannot get TRttiType
)
//and not Uppercase(typ.AsInstance.MetaClassType.ClassName).Contains('TCUSTOM')
//and not typ.AsInstance.MetaClassType.ClassParent.QualifiedClassName.Contains('Vcl.Controls')
then
begin
ATStrings.Add(typ.AsInstance.MetaClassType.ClassName+sLineBreak);
end
else
begin
continue;
end
;
end;
ctx.Free;
if ATStrings.Count=0 then Result:=false
else Result:=true;
end;
///<summary>Return the metaclass of the object based on the component name or object name</summary>
function FindAnyClass(const Name: string) : TClass;
var
ctx: TRttiContext;
typ: TRttiType;
list: TArray<TRttiType>;
begin
Result := nil;
ctx := TRttiContext.Create;
list := ctx.GetTypes;
for typ in list do
begin
if typ.IsInstance //: is the instantiated type
//and typ.IsManaged //: cannot be used: The source code seems to refer to the basic data type or tkClass referenced by the UI: TypeInfo^.Kind
//and typ.IsPublicType //: is the public type
and SameStr( Uppercase(Name), Uppercase(typ.Name))
and (Uppercase(Name)<>'TFRAME')//: Equivalent to: and (typ.ClassName<>TFRAME.ClassName)
then
begin
Result := typ.AsInstance.MetaClassType;
break;
end
else
begin
Result := nil;
continue;
end;
end;
ctx.Free;
end;
var
//ATComponent:TComponent;
LContext: TRttiContext;
LType: TRttiType;
LMethod: TRttiMethod;
LProperty: TRttiProperty;
LField: TRttiField;
LTreeNode1, LTreeNode2: TTreeNode;
LLTypeName:string;
LStringList:TStrings;
LTreeNodeCount:Integer;
LIfExistTreeNode:Boolean;
LTClass:TClass;
LTComponentsList:string;
LTComponentsListCount:Integer;
begin
ComboBoxEx2.Items.Clear;
ComboBoxEx2.Items.BeginUpdate;
LStringList:=TStringList.Create;
try
if FillVclClasses(LStringList)=true then
for LLTypeName in LStringList do
begin
ComboBoxEx2.Items.Add(LLTypeName);
end;
finally
LStringList.Free;
end;
ComboBoxEx2.Items.EndUpdate;
LTComponentsList:='';
for LTComponentsListCount :=0 to ComboBoxEx2.Items.Count-1 do
begin
LTComponentsList :=LTComponentsList
+ComboBoxEx2.Items[LTComponentsListCount];
//:ComboBoxEx2.Items itself contains a line break separator:
//: This requires a line break separator: for LTComponent in ComboBoxEx2.Items do
end;
TFile.WriteAllText(LFilePath,LTComponentsList);
// ATComponent :=FindGlobalComponent( AComponentName );
//: No way: LTClass :=ATComponent.ClassType;
LContext := TRttiContext.Create;
try
LTClass :=FindAnyClass( AComponentName );
//:不行:ATComponent.ClassType;
if LTClass=nil then exit;
LType := LContext.GetType( LTClass );//as TWinControl
LIfExistTreeNode:=false;
for LTreeNodeCount := 0 to TreeView1.Items.Count-1 do
if (TreeView1.Items[LTreeNodeCount].Text =LType.ToString) then
LIfExistTreeNode:=true; //:已经存在了
if LIfExistTreeNode=false then //: Don't repeat adding to TreeView1
begin
LTreeNode1 := TreeView1.Items.AddChild(nil, LType.ToString);
LTreeNode2 := TreeView1.Items.AddChild(LTreeNode1, 'Methods');
for LMethod in LType.GetMethods do
begin
TreeView1.Items.AddChild(LTreeNode2, LMethod.ToString);
end;
LTreeNode2 := TreeView1.Items.AddChild(LTreeNode1, 'Properties');
for LProperty in LType.GetProperties do
begin
TreeView1.Items.AddChild(LTreeNode2, LProperty.ToString);
end;
LTreeNode2 := TreeView1.Items.AddChild(LTreeNode1, 'Fields');
for LField in LType.GetFields do
begin
TreeView1.Items.AddChild(LTreeNode2, LField.ToString);
end;
end;
//TreeView1.FullExpand;
finally
LContext.Free;
end;
end;
end.
program gets the RttiType information of a TComponent or TObject class;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
running result:
Information generated by VclTWinControlCategories.text:
Three, the role in practical applications
Our usual simple code will be missing one layer, the object pool layer. After we get the information in the database from the server, the client must load it into the local data set (such as the memory table TFDMemTable), and then read the field type from the memory table, and compare it with the user's input in the UI interface Yes, to achieve the purpose of whether the type of user input meets the requirements.
This is very lagging, very cumbersome, low computational time and efficiency, heavy database server load, objects in the system cannot meet the requirements of serialization and Json data exchange, and it is not easy to manage, and the code is not easy to interface and expand , It is not easy to reuse, and ultimately your model is difficult to truly realize Restful.
1. Validation of the input data, type and length
After obtaining the object information from the server , the input information of the client UI interface matches the design requirements, and the client does not directly contact the database at this time .
2. Login information matches to verify user registration information
The method is the same as the above, except that it is compared with the relevant information in the user registration information window.
3. Personalization and interface of user interface configuration information (ie skin)
According to the user's preferences, the system can quickly change styles, colors, fonts, etc., and all of this does not need to modify part of the UI code, just switch the parameters and call the interface unit directly.
4. When the field attributes are calculated fields, search fields, aggregate fields, and internal calculation fields, the value is differentiated in different scenarios and the algorithm is differentiated
FieldKind attributes