A case of using delphiXE RTTI to dynamically obtain information at runtime and obtain RttiType information of a TComponent class or TObject class

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

You can also take a look

Code example

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

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/pulledup/article/details/104769345