DELPHI RTTI 探索.docx

上传人:b****7 文档编号:23749161 上传时间:2023-05-20 格式:DOCX 页数:18 大小:23.69KB
下载 相关 举报
DELPHI RTTI 探索.docx_第1页
第1页 / 共18页
DELPHI RTTI 探索.docx_第2页
第2页 / 共18页
DELPHI RTTI 探索.docx_第3页
第3页 / 共18页
DELPHI RTTI 探索.docx_第4页
第4页 / 共18页
DELPHI RTTI 探索.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

DELPHI RTTI 探索.docx

《DELPHI RTTI 探索.docx》由会员分享,可在线阅读,更多相关《DELPHI RTTI 探索.docx(18页珍藏版)》请在冰豆网上搜索。

DELPHI RTTI 探索.docx

DELPHIRTTI探索

问题:

Delphi的RTTI机制浅探(积分:

100,回复:

21,阅读:

352)

分类:

ObjectPascal(版主:

menxin,cAkk)

来自:

savetime,时间:

2004-1-210:

48:

00,ID:

2420610

[显示:

小字体|大字体]

Delphi的RTTI机制浅探

savetime2k@ 2004.1.20

目录

===============================================================================

⊙RTTI简介

⊙类(class)和VMT的关系

⊙类(class)、类的类(classofclass)、类变量(classvariable)的关系

⊙TObject.ClassType和TObject.ClassInfo

⊙is和as运算符的原理

⊙TTypeInfo–RTTI信息的结构

⊙获取类(class)的属性(property)信息

⊙获取方法(method)的类型信息

⊙获取有序类型(ordinal)、集合(set)类型的RTTI信息

⊙获取其它数据类型的RTTI信息

===============================================================================

本文排版格式为:

  正文由窗口自动换行;所有代码以80字符为边界;中英文字符以空格符分隔。

(作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。

正文

===============================================================================

⊙RTTI简介

===============================================================================

RTTI(Run-TimeTypeInformation)翻译过来的名称是“运行期类型信息”,也就是说可以在运行期获得数据类型或类(class)的信息。

这个RTTI到底有什么用处,我现在也说不清楚。

我是在阅读Delphi持续机制的代码中发现了很多RTTI的运用,只好先把RTTI学习一遍。

下面是我的学习笔记。

如果你发现了错误请告诉我。

谢谢!

Delphi的RTTI主要分为类(class)的RTTI和一般数据类型的RTTI,下面从类(class)开始。

===============================================================================

⊙类(class)和VMT的关系

===============================================================================

一个类(class),从编译器的角度来看就是一个指向VMT的指针(在后文用VMTptr表示)。

在类的VMTptr的负地址方向存储了一些类信息的指针,这些指针的值和指针所指的内容在编译后就确定了。

比如VMTptr-44的内容是指向类名称(ClassName)的指针。

不过一般不使用数值来访问这些类信息,而是通过System.pas中定义的以vmt开头的常量,如vtmClassName、vmtParent等来访问。

类的方法有两种:

对象级别的方法和类级别的方法。

两者的Self指针意义是不同的。

在对象级别的方法中Self指向对象地址空间,因此可以用它来访问对象的成员函数;在类级别的方法中Self指向类的VMT,因此只能用它来访问VMT信息,而不能访问对象的成员字段。

===============================================================================

⊙类(class)、类的类(classofclass)、类变量(classvariable)的关系

===============================================================================

上面说到类(class)就是VMTptr。

在Delphi中还可以用classof关键字定义类的类,并且可以使用类的类定义类变量。

从语法上理解这三者的关键并不难,把类当成普通的数据类型来考虑就可以了。

在编译器级别上表现如何呢?

为了简化讨论,我们使用TObject、TClass和TMyClass来代表上面说的三种类型:

type

 TClass=classofTObject;

var

 TMyClass:

TClass;

 MyObject:

TObject;

begin

 TMyClass:

=TObject;

 MyObject:

=TObject.Create;

 MyObject:

=TClass.Create;

 MyObject:

=TMyClass.Create;

end;

 

在上面的例子中,三个TObject对象都被成功地创建了。

编译器的实现是:

TObject是一个VMTPtr常量。

TClass也是一个VMTptr常量,它的值就是TObject。

TMyClass是一个VMTptr变量,它被赋值为TObject。

TObject.Create与TClass.Create的汇编代码完全相同。

但TClass不仅缺省代表一个类,而且还(主要)代表了类的类型,可以用它来定义类变量,实现一些类级别的操作。

===============================================================================

⊙TObject.ClassType和TObject.ClassInfo

===============================================================================

functionTObject.ClassType:

TClass;

begin

 Pointer(Result):

=PPointer(Self)^;

end;

TObject.ClassType是对象级别的方法,Self的值是指向对象内存空间的指针,对象内存空间的前4个字节是类的VMTptr。

因此这个函数的返回值就是类的VMTptr。

classfunctionTObject.ClassInfo:

Pointer;

begin

 Result:

=PPointer(Integer(Self)+vmtTypeInfo)^;

end;

TObject.ClassInfo使用class关键字定义,因此是一个类级别的方法。

该方法中的Self指针就是VMTptr。

所以这个函数的返回值是VMTptr负方向的vmtTypeInfo的内容。

TObject.ClassInfo返回的Pointer指针,实际上是指向类的RTTI结构的指针。

但是不能访问TObject.ClassInfo指向的内容(TObject.ClassInfo返回值是0),因为Delphi只在TPersistent类及TPersistent的后继类中产生RTTI信息。

(从编译器的角度来看,这是在TPersistent类的声明之前使用{$M+}指示字的结果。

TObject还定义了一些获取类RTTI信息的函数,列举在下,就不一一分析了:

 TObject.ClassName:

ShortString; 类的名称

 TObject.ClassParent:

TClass;   对象的父类

 TObject.InheritsFrom:

Boolean;  是否继承自某类

 TObject.InstanceSize:

Longint;  对象实例的大小

===============================================================================

⊙is和as运算符的原理

===============================================================================

我们知道可以在运行期使用is关键字判断一个对象是否属于某个类,可以使用as关键字把某个对象安全地转换为某个类。

在编译器的层次上,is和as的操作是由System.pas中两个函数完成的。

{System.pas}

function_IsClass(Child:

TObject;Parent:

TClass):

Boolean;

begin

 Result:

=(Child<>nil)andChild.InheritsFrom(Parent);

end;

_IsClass很简单,它使用TObject的InheritsForm函数判断该对象是否是从某个类或它的父类中继承下来的。

每个类的VMT中都有一项vmtParent指针,指向该类的父类的VMT。

TObject.InheritsFrom实际上是通过[递归]判断父类VMT指针是否等于自己的VMT指针来判断是否是从该类继承的。

{System.pas}

classfunctionTObject.InheritsFrom(AClass:

TClass):

Boolean;

var

 ClassPtr:

TClass;

begin

 ClassPtr:

=Self;

 while(ClassPtr<>nil)and(ClassPtr<>AClass)do

  ClassPtr:

=PPointer(Integer(ClassPtr)+vmtParent)^;

 Result:

=ClassPtr=AClass;

end;

as操作符实际上是由System.pas中的_AsClass函数完成的。

它简单地调用is操作符判断对象是否属于某个类,如果不是就触发异常。

虽然_AsClass返回值为TObject类型,但编译器会自动把返回的对象改变为Parent类,否则返回的对象没有办法使用TObject之外的方法和数据。

{System.pas}

function_AsClass(Child:

TObject;Parent:

TClass):

TObject;

begin

 Result:

=Child;

 ifnot(ChildisParent)then

  Error(reInvalidCast); //losesreturnaddress

end;

===============================================================================

⊙TTypeInfo–RTTI信息的结构

===============================================================================

RTTI信息的结构定义在TypInfo.pas中:

 TTypeInfo=record    //TTypeInfo是RTTI信息的结构

  Kind:

TTypeKind;    //RTTI信息的数据类型

  Name:

ShortString;   //数据类型的名称

 {TypeData:

TTypeData}  //RTTI的内容

 end;

TTypeInfo就是RTTI信息的结构。

TObject.ClassInfo返回指向存放classTTypeInfo信息的指针。

Kind是枚举类型,它表示RTTI结构中所包含数据类型。

Name是数据类型的名称。

注意,最后一个字段TypeData被注释掉了,这说明该处的结构内容根据不同的数据类型有所不同。

TTypeKind枚举定义了可以使用RTTI信息的数据类型,它几乎包含了所有的Delphi数据类型,其中包括tkClass。

 TTypeKind=(tkUnknown,tkInteger,tkChar,tkEnumeration,tkFloat,

  tkString,tkSet,tkClass,tkMethod,tkWChar,tkLString,tkWString,

  tkVariant,tkArray,tkRecord,tkInterface,tkInt64,tkDynArray);

TTypeData是个巨大的记录类型,在此不再列出,后文会根据需要列出该记录的内容。

===============================================================================

⊙获取类(class)的属性(property)信息

===============================================================================

这一段是RTTI中最复杂的部分,努力把本段吃透,后面的内容都是非常简单的。

下面是一个获取类的属性的例子:

procedureGetClassProperties(AClass:

TClass;AStrings:

TStrings);

var

 PropCount,I:

SmallInt;

 PropList:

PPropList;

 PropStr:

string;

begin

 PropCount:

=GetTypeData(AClass.ClassInfo).PropCount;

 GetPropList(AClass.ClassInfo,PropList);

 forI:

=0toPropCount-1do

 begin

  casePropList[I]^.PropType^.Kindof

   tkClass   :

PropStr:

='[Class]';

   tkMethod  :

PropStr:

='[Method]';

   tkSet    :

PropStr:

='[Set] ';

   tkEnumeration:

PropStr:

='[Enum] ';

  else

   PropStr:

='[Field]';

  end;

  PropStr:

=PropStr+PropList[I]^.Name;

  PropStr:

=PropStr+':

'+PropList[I]^.PropType^.Name;

  AStrings.Add(PropStr);

 end;

 FreeMem(PropList);

end;

你可以在表单上放置一个TListBox,然后执行以下语句观察执行结果:

 GetClassProperties(TForm1,ListBox1.Items);

该函数先使用GetTypeData函数获得类的属性数量。

GetTypeData是TypInfo.pas中的一个函数,它的功能是返回TTypeInfo的TypeData数据的指针:

{TypInfo.pas}

functionGetTypeData(TypeInfo:

PTypeInfo):

PTypeData;assembler;

class的TTypeData结构如下:

 TTypeData=packedrecord

  caseTTypeKindof

   tkClass:

    ClassType:

TClass;    //类(VMTptr)

    ParentInfo:

PPTypeInfo;  //父类的RTTI指针

    PropCount:

SmallInt;   //属性数量

    UnitName:

ShortStringBase;//单元的名称

   {PropData:

TPropData});  //属性的详细信息

 end;

其中的PropData又是一个大小可变的字段。

TPropData的定义如下:

 TPropData=packedrecord

  PropCount:

Word;   //属性数量

  PropList:

recordend; //占位符,真正的意义在下一行

  {PropList:

array[1..PropCount]ofTPropInfo}

 end;

每个属性信息在内存中的结构就是TPropInfo,它的定义如下:

 PPropInfo=^TPropInfo;

 TPropInfo=packedrecord

  PropType:

PPTypeInfo;  //属性类型信息指针的指针

  GetProc:

Pointer;    //属性的Get方法指针

  SetProc:

Pointer;    //属性的Set方法指针

  StoredProc:

Pointer;  //属性的StoredProc指针

  Index:

Integer;     //属性的Index值

  Default:

Longint;    //属性的Default值

  NameIndex:

SmallInt;  //属性的名称索引(以0开始计数)

  Name:

ShortString;   //属性的名称

 end;

为了方便访问属性信息,TypInfo.pas中还定义了指向TPropInfo数组的指针:

 PPropList=^TPropList;

 TPropList=array[0..16379]ofPPropInfo;

我们可以使用GetPropList获得所有属性信息的指针数组,数组用完以后要记得用FreeMem把数组的内存清除。

{TypInfo.pas}

functionGetPropList(TypeInfo:

PTypeInfo;outPropList:

PPropList):

Integer;

GetPropList传入类的TTypeInfo指针和TPropList的指针,它为PropList分配一块内存后把该内存填充为指向TPropInfo的指针数组,最后返回属性的数量。

上面的例子演示了如何获得类的所有属性信息,也可以根据属性的名称单独获得属性信息:

{TypInfo.pas}

functionGetPropInfo(TypeInfo:

PTypeInfo;constPropName:

string):

PPropInfo;

GetPropInfo根据类的RTTI指针和属性的名称字符串,返回属性的信息TPropInfo的指针。

如果没有找到该属性,则返回nil。

GetPropInfo很容易使用,举个例子:

 ShowMessage(GetPropInfo(TForm,'Name')^.PropType^.Name);

这句调用显示了TForm类的Name属性的类型名称:

TComponentName。

===============================================================================

⊙获取方法(method)的类型信息

===============================================================================

所谓方法就是以ofobject关键字声明的函数指针,下面的函数可以显示一个方法的类型信息:

procedureGetMethodTypeInfo(ATypeInfo:

PTypeInfo;AStrings:

TStrings);

type

 PParamData=^TParamData;

 TParamData=record   //函数参数的数据结构

  Flags:

TParamFlags;  //参数传递规则

  ParamName:

ShortString;//参数的名称

  TypeName:

ShortString; //参数的类型名称

 end;

 functionGetParamFlagsName(AParamFlags:

TParamFlags):

string;

 var

  I:

Integer;

 begin

  Result:

='';

  forI:

=Integer(pfVar)toInteger(pfOut)dobegin

   ifI=Integer(pfAddress)thenContinue;

   ifTParamFlag(I)inAParamFlagsthen

    Result:

=Result+''+GetEnumName(TypeInfo(TParamFlag),I);

  end;

 end;

var

 MethodTypeData:

PTypeData;

 ParamData:

PParamData;

 TypeStr:

PShortString;

 I:

Integer;

begin

 MethodTypeData:

=GetTypeData(ATypeInfo);

 AStrings.Add('---------------------------------');

 AStrings.Add('MethodName:

'+ATypeInfo^.Name);

 AStrings.Add('MethodKind:

'+GetEnumName(TypeInfo(TM

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 化学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1