首页 > 代码库 > DELPHI RTTI

DELPHI RTTI

运行期类型信息(RTTI)是一种语言特征,能使应用程序在运行时得到关于对象的信息。

 

    运行期类型信息(RTTI)是一种语言特征,能使应用程序在运行时得到关于对象的信息。RTTI是Delphi的组件能够融合到IDE中的关键。它在IDE中不仅仅是一个纯学术的过程。
    由于对象都是从TObject继承下来的,因此,对象都包含一个指向它们的
RTTI的指针以及几个内建的方法。下面的表列出了TObject的一些方法,用这些方法能获得某个对象实例的信息。

函数   返回类型 返回值
ClassName( )   string 对象的类名
ClassType()  boolean 对象的类型
InheritsFrom  boolean     判断对象是否继承于一个指定的类
ClassParent()  TClass 对象的祖先类型
Instancesize()  word  对象实例的长度(字节数)
ClassInfo() Pointer  指向RTTI的指针

 第一部分:关于as 和 is

    Object Pascal提供了两个运算符as和is,用它们通过RTTI能对对象进行比较和强制类型转换。
    关键字as是类型转换的一种新的形式。它能把一个基层的对象强制类型转换成它的派生类,如果转换不合法就产生一个异常。假定有一个过程,想让它能够传递任何类型的对象,它应该这样定义:
    Procedure Foo(AnObject :Tobject);
    在这个过程如果要对AnObject进行操作,要把它转换为一个派生对象。假定把AnObject看成是一个TEdit派生类型,并想要改变它所包含的文本,用下列代码:  (AnObject as Tedit).text := ‘wudi_1982‘;
    能用比较运算符来判断两个对象是否是相兼容的类型,用is运算符把一个未知的对象和一个已知类型或实例进行比较,确定这个未知对象的属性和行为。例如,在对(AnObject 进行强制类型转换前,确定(AnObject 和TEdit是否指针兼容: 

    if (AnObject is Tedit) then
     Tedit(AnObjject).text := ‘wudi_1982‘;
    注意在这个例子中不要再使用as进行强制类型转换,这是因为它要大量使用RTTI,另外还因为,在第一行已经判断Foo就是TEdit,可以通过在第2行进行指针转换来优化。

    这两个操作符最典型的应用我想应该是在程序需要的部分清空窗体上所有edit的text属性

技术分享procedure TForm1.ClearEdit(Acontrl: TWinControl);
技术分享var
技术分享i : integer;
技术分享begin
技术分享   for i := 0 to Acontrl.ControlCount-1 do
技术分享   begin
技术分享      if Acontrl.Controls[i] is TEdit then
技术分享        ((Acontrl.Controls[i]) as TEdit).Text := ‘‘;
技术分享      if Acontrl.Controls[i] is TCustomControl then
技术分享       ClearEdit( (Acontrl.Controls[i] as TCustomControl))
技术分享   end;
技术分享end;

 

第二部分:RTTI

   上文中已经多次提到了RTTI,但好像并没有看到RTTI出现。那么RTTI是如何表现自己的呢?你将发现, RTTI至少在两个地方对你有用。第一个地方是DELPHI的IDE,这在前面已提到过。通过RTTI,IDE就会知道你正在使用的对象和组件的任何事情。实际上,不只是RTTI,但为了这个讨论,我们只谈RTTI方面。其实上面的as,is操作都间接的使用了RTTI。
    还是用个例子来演示吧。在观看此例子之时,建议你看看typinfo.pas中的
内容(DELPHI安装目录下/source/rtl/common/TypInfo.pas);
    下面的例子主要分为两部分,界面上半部分,主要演示通过rtti来显示用户
选择类型的信息。(有3个TListBox)。
    下面的部分主要通过RTTI来完成通过配置信息对控件进行属性的赋值操作,
这里将演示文本类型和事件类型的赋值。
     窗体文件如下:
代码如下:

技术分享object Form1: TForm1
技术分享  Left = 150
技术分享  Top = 161
技术分享  Width = 639
技术分享  Height = 372
技术分享  Caption = Form1
技术分享  Color = clBtnFace
技术分享  Font.Charset = DEFAULT_CHARSET
技术分享  Font.Color = clWindowText
技术分享  Font.Height = -11
技术分享  Font.Name = Tahoma
技术分享  Font.Style = []
技术分享  OldCreateOrder = False
技术分享  OnCreate = FormCreate
技术分享  PixelsPerInch = 96
技术分享  TextHeight = 13
技术分享  object Panel1: TPanel
技术分享    Left = 0
技术分享    Top = 0
技术分享    Width = 631
技术分享    Height = 185
技术分享    Align = alTop
技术分享    TabOrder = 0
技术分享    object GroupBox1: TGroupBox
技术分享      Left = 1
技术分享      Top = 1
技术分享      Width = 185
技术分享      Height = 183
技术分享      Align = alLeft
技术分享      Caption = 在这里选择要查看类型的信息
技术分享      TabOrder = 0
技术分享      object ListBox1: TListBox
技术分享        Left = 2
技术分享        Top = 15
技术分享        Width = 181
技术分享        Height = 166
技术分享        Align = alClient
技术分享        ItemHeight = 13
技术分享        TabOrder = 0
技术分享        OnClick = ListBox1Click
技术分享      end
技术分享    end
技术分享    object GroupBox2: TGroupBox
技术分享      Left = 368
技术分享      Top = 1
技术分享      Width = 262
技术分享      Height = 183
技术分享      Align = alRight
技术分享      Caption = 属性信息
技术分享      TabOrder = 1
技术分享      object ListBox3: TListBox
技术分享        Left = 2
技术分享        Top = 15
技术分享        Width = 258
技术分享        Height = 166
技术分享        Align = alClient
技术分享        ItemHeight = 13
技术分享        TabOrder = 0      end    end    object GroupBox3: TGroupBox      Left = 186      Top = 1      Width = 182      Height = 183      Align = alClient      Caption = ‘基本信息‘      TabOrder = 2      object ListBox2: TListBox        Left = 2        Top = 15        Width = 178        Height = 166        Align = alClient        ItemHeight = 13        TabOrder = 0      end    end  end  object TPanel    Left = 0    Top = 185    Width = 631    Height = 157    Align = alClient    TabOrder = 1    object Panel2: TPanel      Left = 1      Top = 1      Width = 230      Height = 155      Align = alLeft      TabOrder = 0      object Label2: TLabel        Left = 10        Top = 8        Width = 84        Height = 13        Caption = ‘要修改的控件名‘      end      object Label3: TLabel        Left = 8        Top = 32        Width = 72        Height = 13        Caption = ‘修改的属性名‘      end      object Label4: TLabel        Left = 8        Top = 64        Width = 72        Height = 13        Caption = ‘将属性修改为‘      end      object edComName: TEdit        Left = 104        Top = 5        Width = 78        Height = 21        TabOrder = 0        Text = ‘label1‘      end      object edPproName: TEdit        Left = 104        Top = 32        Width = 81        Height = 21        TabOrder = 1        Text = ‘caption‘      end      object edValue: TEdit        Left = 104        Top = 56        Width = 81        Height = 21        TabOrder = 2        Text = ‘12345‘      end      object btnInit: TButton        Left = 8        Top = 104        Width = 75        Height = 25        Caption = ‘初始化‘        TabOrder = 3        OnClick = btnInitClick      end      object btnModify: TButton        Left = 104        Top = 104        Width = 75        Height = 25        Caption = ‘修改‘        TabOrder = 4        OnClick = btnModifyClick      end    end    object Panel3: TPanel      Left = 231      Top = 1      Width = 399      Height = 155      Align = alClient      TabOrder = 1      object GroupBox4: TGroupBox        Left = 1        Top = 1        Width = 397        Height = 153        Align = alClient        Caption = ‘被修改的控件‘        TabOrder = 0        object Label1: TLabel          Left = 16          Top = 32          Width = 28          Height = 13          Caption = ‘label1‘        end        object BitBtn1: TBitBtn          Left = 8          Top = 64          Width = 75          Height = 25          Caption = ‘BitBtn1‘          TabOrder = 0        end      end    end  endend

技术分享unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,

Forms,
  Dialogs,typinfo, StdCtrls, ExtCtrls, Buttons;

type
  InsertCom = record
    Name : string; //要修改属性的组件名
    PproName : string;//要修改控件的属性名
    MethodName :string;//要修改or添加给控件的事件名
    text : string; //属性值,这里修改的是string类型的数值
  end;
  TForm1 = class(TForm)
    Panel1: TPanel;
    GroupBox1: TGroupBox;
    ListBox1: TListBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    ListBox2: TListBox;
    ListBox3: TListBox;
    Panel2: TPanel;
    edComName: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    edPproName: TEdit;
    Label4: TLabel;
    edValue: TEdit;
    Panel3: TPanel;
    btnInit: TButton;
    btnModify: TButton;
    GroupBox4: TGroupBox;
    Label1: TLabel;
    BitBtn1: TBitBtn;

    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure btnInitClick(Sender: TObject);
    procedure btnModifyClick(Sender: TObject);
  private
    TestCom : InsertCom;
    procedure MyClick(Sender : TObject); //给控件添加onclick事件
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function CreateClass(const AClassName : string):TObject;//根据名字生成
var
  tm : TObject;
  t : TFormClass;
begin
   t := TFormClass(FindClass(AClassName));
   tm := t.Create(nil);
   Result := tm;
end;

procedure GetBaseClassInfo(AClass : TObject;AStrings : TStrings); //获

得类型的基本信息
var
  classTypeInfo : PTypeInfo;
  ClassDataInfo : PTypeData;
begin
   classTypeInfo := AClass.ClassInfo;
   ClassDataInfo := GetTypeData(classTypeInfo);
   with AStrings do
   begin
     Add(Format(‘name is :%s‘,[classTypeInfo.Name]));
     Add(format(‘type kind is :%s‘,[GetEnumName(TypeInfo

(TTypeKind),integer(classTypeInfo.Kind))]));
     Add(Format(‘in : %s‘,[ClassDataInfo.UnitName]));
   end;
end;

procedure GetBaseClassPro(AClass : TObject;Astrings : TStrings); //获

得属性信息
var
  NumPro : integer; //用来记录事件属性的个数
  Pplst : PPropList; //存放属性列表
  Classtypeinfo : PTypeInfo;
  classDataInfo: PTypeData;
  i : integer;
begin
  Classtypeinfo := AClass.ClassInfo;
  classDataInfo := GetTypeData(Classtypeinfo);
  if classDataInfo.PropCount <> 0 then
  begin
    //分配空间
    GetMem(Pplst,sizeof(PpropInfo)*classDataInfo.PropCount);
    try
      //获得属性信息到pplst
      GetPropInfos(AClass.ClassInfo,Pplst);
      for I := 0 to classDataInfo.PropCount - 1 do
        begin
          if Pplst[i]^.PropType^.Kind <> tkMethod then
          //这里过滤掉了事件属性
            Astrings.Add(Format(‘%s:%s‘,[Pplst[i]^.Name,Pplst[i]

^.PropType^.Name]));
        end;
        //获得事件属性
        NumPro := GetPropList(AClass.ClassInfo,[tkMethod],Pplst);
        if NumPro <> 0 then
        begin
          //给列表添加一些标志
          Astrings.Add(‘‘);
          Astrings.Add(‘-----------EVENT-----------‘);
          Astrings.Add(‘‘);
          for i := 0 to NumPro - 1 do //获得事件属性的列表
            Astrings.Add(Format(‘%s:%s‘,[Pplst[i]^.Name,Pplst[i]

^.PropType^.Name]));
        end;
    finally
       FreeMem(Pplst,sizeof(PpropInfo)*classDataInfo.PropCount);
    end;
  end;
end;


procedure TForm1.btnInitClick(Sender: TObject);
begin
   //修改label1的caption属性为12345
   TestCom.Name := edComName.Text;
   TestCom.PproName := edPproName.Text;
   TestCom.text := edValue.Text;
   TestCom.MethodName := ‘OnClick‘;
   btnModify.Enabled := true;
end;

procedure TForm1.btnModifyClick(Sender: TObject);
var
  pp : PPropInfo;
  obj : TComponent;
  a : TMethod;
  tm : TNotifyEvent;
begin
  obj := FindComponent(TestCom.Name);//通过名字查找此控件
  if not Assigned(obj) then exit; //如果没有则退出
  //通过getPropInfo获得指定控件的属性信息,注意,这里只能获得那些公开

了的属性
  pp := GetPropInfo(obj.ClassInfo,TestCom.PproName);
  if Assigned(pp) then
  begin
     //根据kind判断类型是否为string类型
     case pp^.PropType^.Kind  of
       //这里使用setStrProp来为string类型的属性赋值,对起来类型的赋值

,请参考TypInfo.pas
       tkString,tkLString,tkWString : SetStrProp

(obj,TestCom.PproName,TestCom.text);
     end;
     //给要修改的控件添加onClick事件,
     pp := GetPropInfo(obj.ClassInfo,TestCom.MethodName);
     if Assigned(pp) then
     begin
       if pp^.PropType^.Kind = tkMethod then
       begin
         tm := MyClick;
         //Tmethod的code为函数地址,你也可以通过MethodAddress方法获得
         a.Code := @tm;
         a.Data := Self;
         //对时间赋值
         SetMethodProp(obj,TestCom.MethodName,a);
       end;
     end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   btnModify.Enabled := false;
   //给listbox1添加一些类型的类名
   with ListBox1.Items do
   begin
     Add(‘TApplication‘);
     Add(‘TEdit‘);
     Add(‘TButton‘);
     Add(‘Tmemo‘);
     Add(‘TForm‘);
   end;

end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  t : TObject;
begin
   //当在类型列表中选择一个类型并用鼠标单击后,分别得到它的属性信息和

基本信息
    ListBox2.Clear;
    ListBox3.Clear;
    t := CreateClass(ListBox1.Items[ListBox1.ItemIndex]);
    try
      GetBaseClassInfo(t,ListBox2.Items);
      GetBaseClassPro(t,ListBox3.Items);
    finally
       t.Free;
    end;
end;

procedure TForm1.MyClick(Sender: TObject);
begin
   //给指定控件添加的一个方法
   ShowMessage(‘wudi_1982‘);
end;

initialization
   //初始化的时候注册
   RegisterClasses([TApplication,TButton,TEdit,TMemo,TForm]);

end.

      注:示例程序在winxp+D7以及turbo delphi+winxp下测试通过。Borland文档中不包含将来也许会有版本变化的功能。当使用如RTTI等无文档说明的功能时,就不能保证你的程序可以完全移植到Delphi的未来版本。转载请注明出处!

程序效果图如下:

技术分享

        编译、运行程序,你可以通过点击左上角列表框中的类型,获得他们的信息。而在窗体的下部,主要演示了通过读取配置信息来对控件的属性赋值(例程中的配置信息是通过edit输入的,可以在实际运用中改成从配置文件读取)。当使用下半部分功能时,在默认情况下,点击初始化按钮,然后点击修改,你会发现label1的caption变成了12345,并在在鼠标点击的时候会弹出一个对话框,你可以尝试把第一个edit的内容改成bitbtn1试试。

 

转载:http://www.xuebuyuan.com/2082610.html

DELPHI RTTI