Delphi的RTTI机制浅探完整版_第1页
Delphi的RTTI机制浅探完整版_第2页
Delphi的RTTI机制浅探完整版_第3页
Delphi的RTTI机制浅探完整版_第4页
Delphi的RTTI机制浅探完整版_第5页
已阅读5页,还剩14页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

目录RTTI简介类CLASS和VMT的关系类CLASS、类的类CLASSOFCLASS、类变量CLASSVARIABLE的关系TOBJECTCLASSTYPE和TOBJECTCLASSINFOIS和AS运算符的原理TTYPEINFORTTI信息的结构获取类CLASS的属性PROPERTY信息获取方法METHOD的类型信息获取有序类型ORDINAL、集合SET类型的RTTI信息获取其它数据类型的RTTI信息本文排版格式为正文由窗口自动换行;所有代码以80字符为边界;中英文字符以空格符分隔。作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。正文RTTI简介RTTIRUNTIMETYPEINFORMATION翻译过来的名称是“运行期类型信息”,也就是说可以在运行期获得数据类型或类CLASS的信息。这个RTTI到底有什么用处,我现在也说不清楚。我是在阅读DELPHI持续机制的代码中发现了很多RTTI的运用,只好先把RTTI学习一遍。下面是我的学习笔记。如果你发现了错误请告诉我。谢谢DELPHI的RTTI主要分为类CLASS的RTTI和一般数据类型的RTTI,下面从类CLASS开始。类CLASS和VMT的关系一个类CLASS,从编译器的角度来看就是一个指向VMT的指针在后文用VMTPTR表示。在类的VMTPTR的负地址方向存储了一些类信息的指针,这些指针的值和指针所指的内容在编译后就确定了。比如VMTPTR44的内容是指向类名称CLASSNAME的指针。不过一般不使用数值来访问这些类信息,而是通过SYSTEMPAS中定义的以VMT开头的常量,如VTMCLASSNAME、VMTPARENT等来访问。类的方法有两种对象级别的方法和类级别的方法。两者的SELF指针意义是不同的。在对象级别的方法中SELF指向对象地址空间,因此可以用它来访问对象的成员函数;在类级别的方法中SELF指向类的VMT,因此只能用它来访问VMT信息,而不能访问对象的成员字段。类CLASS、类的类CLASSOFCLASS、类变量CLASSVARIABLE的关系上面说到类CLASS就是VMTPTR。在DELPHI中还可以用CLASSOF关键字定义类的类,并且可以使用类的类定义类变量。从语法上理解这三者的关键并不难,把类当成普通的数据类型来考虑就可以了。在编译器级别上表现如何呢为了简化讨论,我们使用TOBJECT、TCLASS和TMYCLASS来代表上面说的三种类型TYPETCLASSCLASSOFTOBJECTVARTMYCLASSTCLASSMYOBJECTTOBJECTBEGINTMYCLASSTOBJECTMYOBJECTTOBJECTCREATEMYOBJECTTCLASSCREATEMYOBJECTTMYCLASSCREATEEND在上面的例子中,三个TOBJECT对象都被成功地创建了。编译器的实现是TOBJECT是一个VMTPTR常量。TCLASS也是一个VMTPTR常量,它的值就是TOBJECT。TMYCLASS是一个VMTPTR变量,它被赋值为TOBJECT。TOBJECTCREATE与TCLASSCREATE的汇编代码完全相同。但TCLASS不仅缺省代表一个类,而且还主要代表了类的类型,可以用它来定义类变量,实现一些类级别的操作。TOBJECTCLASSTYPE和TOBJECTCLASSINFOFUNCTIONTOBJECTCLASSTYPETCLASSBEGINPOINTERRESULTPPOINTERSELFENDTOBJECTCLASSTYPE是对象级别的方法,SELF的值是指向对象内存空间的指针,对象内存空间的前4个字节是类的VMTPTR。因此这个函数的返回值就是类的VMTPTR。CLASSFUNCTIONTOBJECTCLASSINFOPOINTERBEGINRESULTPPOINTERINTEGERSELFVMTTYPEINFOENDTOBJECTCLASSINFO使用CLASS关键字定义,因此是一个类级别的方法。该方法中的SELF指针就是VMTPTR。所以这个函数的返回值是VMTPTR负方向的VMTTYPEINFO的内容。TOBJECTCLASSINFO返回的POINTER指针,实际上是指向类的RTTI结构的指针。但是不能访问TOBJECTCLASSINFO指向的内容TOBJECTCLASSINFO返回值是0,因为DELPHI只在TPERSISTENT类及TPERSISTENT的后继类中产生RTTI信息。从编译器的角度来看,这是在TPERSISTENT类的声明之前使用M指示字的结果。TOBJECT还定义了一些获取类RTTI信息的函数,列举在下,就不一一分析了TOBJECTCLASSNAMESHORTSTRING类的名称TOBJECTCLASSPARENTTCLASS对象的父类TOBJECTINHERITSFROMBOOLEAN是否继承自某类TOBJECTINSTANCESIZELONGINT对象实例的大小IS和AS运算符的原理我们知道可以在运行期使用IS关键字判断一个对象是否属于某个类,可以使用AS关键字把某个对象安全地转换为某个类。在编译器的层次上,IS和AS的操作是由SYSTEMPAS中两个函数完成的。SYSTEMPASFUNCTION_ISCLASSCHILDTOBJECTPARENTTCLASSBOOLEANBEGINRESULTCHILDNILANDCHILDINHERITSFROMPARENTEND_ISCLASS很简单,它使用TOBJECT的INHERITSFORM函数判断该对象是否是从某个类或它的父类中继承下来的。每个类的VMT中都有一项VMTPARENT指针,指向该类的父类的VMT。TOBJECTINHERITSFROM实际上是通过递归判断父类VMT指针是否等于自己的VMT指针来判断是否是从该类继承的。SYSTEMPASCLASSFUNCTIONTOBJECTINHERITSFROMACLASSTCLASSBOOLEANVARCLASSPTRTCLASSBEGINCLASSPTRSELFWHILECLASSPTRNILANDCLASSPTRACLASSDOCLASSPTRPPOINTERINTEGERCLASSPTRVMTPARENTRESULTCLASSPTRACLASSENDAS操作符实际上是由SYSTEMPAS中的_ASCLASS函数完成的。它简单地调用IS操作符判断对象是否属于某个类,如果不是就触发异常。虽然_ASCLASS返回值为TOBJECT类型,但编译器会自动把返回的对象改变为PARENT类,否则返回的对象没有办法使用TOBJECT之外的方法和数据。SYSTEMPASFUNCTION_ASCLASSCHILDTOBJECTPARENTTCLASSTOBJECTBEGINRESULTCHILDIFNOTCHILDISPARENTTHENERRORREINVALIDCAST/LOSESRETURNADDRESSENDTTYPEINFORTTI信息的结构RTTI信息的结构定义在TYPINFOPAS中TTYPEINFORECORD/TTYPEINFO是RTTI信息的结构KINDTTYPEKIND/RTTI信息的数据类型NAMESHORTSTRING/数据类型的名称TYPEDATATTYPEDATA/RTTI的内容ENDTTYPEINFO就是RTTI信息的结构。TOBJECTCLASSINFO返回指向存放CLASSTTYPEINFO信息的指针。KIND是枚举类型,它表示RTTI结构中所包含数据类型。NAME是数据类型的名称。注意,最后一个字段TYPEDATA被注释掉了,这说明该处的结构内容根据不同的数据类型有所不同。TTYPEKIND枚举定义了可以使用RTTI信息的数据类型,它几乎包含了所有的DELPHI数据类型,其中包括TKCLASS。TTYPEKINDTKUNKNOWN,TKINTEGER,TKCHAR,TKENUMERATION,TKFLOAT,TKSTRING,TKSET,TKCLASS,TKMETHOD,TKWCHAR,TKLSTRING,TKWSTRING,TKVARIANT,TKARRAY,TKRECORD,TKINTERFACE,TKINT64,TKDYNARRAYTTYPEDATA是个巨大的记录类型,在此不再列出,后文会根据需要列出该记录的内容。获取类CLASS的属性PROPERTY信息这一段是RTTI中最复杂的部分,努力把本段吃透,后面的内容都是非常简单的。下面是一个获取类的属性的例子PROCEDUREGETCLASSPROPERTIESACLASSTCLASSASTRINGSTSTRINGSVARPROPCOUNT,ISMALLINTPROPLISTPPROPLISTPROPSTRSTRINGBEGINPROPCOUNTGETTYPEDATAACLASSCLASSINFOPROPCOUNTGETPROPLISTACLASSCLASSINFO,PROPLISTFORI0TOPROPCOUNT1DOBEGINCASEPROPLISTIPROPTYPEKINDOFTKCLASSPROPSTRCLASSTKMETHODPROPSTRMETHODTKSETPROPSTRSETTKENUMERATIONPROPSTRENUMELSEPROPSTRFIELDENDPROPSTRPROPSTRPROPLISTINAMEPROPSTRPROPSTRPROPLISTIPROPTYPENAMEASTRINGSADDPROPSTRENDFREEMEMPROPLISTEND你可以在表单上放置一个TLISTBOX,然后执行以下语句观察执行结果GETCLASSPROPERTIESTFORM1,LISTBOX1ITEMS该函数先使用GETTYPEDATA函数获得类的属性数量。GETTYPEDATA是TYPINFOPAS中的一个函数,它的功能是返回TTYPEINFO的TYPEDATA数据的指针TYPINFOPASFUNCTIONGETTYPEDATATYPEINFOPTYPEINFOPTYPEDATAASSEMBLERCLASS的TTYPEDATA结构如下TTYPEDATAPACKEDRECORDCASETTYPEKINDOFTKCLASSCLASSTYPETCLASS/类VMTPTRPARENTINFOPPTYPEINFO/父类的RTTI指针PROPCOUNTSMALLINT/属性数量UNITNAMESHORTSTRINGBASE/单元的名称PROPDATATPROPDATA/属性的详细信息END其中的PROPDATA又是一个大小可变的字段。TPROPDATA的定义如下TPROPDATAPACKEDRECORDPROPCOUNTWORD/属性数量PROPLISTRECORDEND/占位符,真正的意义在下一行PROPLISTARRAY1PROPCOUNTOFTPROPINFOEND每个属性信息在内存中的结构就是TPROPINFO,它的定义如下PPROPINFOTPROPINFOTPROPINFOPACKEDRECORDPROPTYPEPPTYPEINFO/属性类型信息指针的指针GETPROCPOINTER/属性的GET方法指针SETPROCPOINTER/属性的SET方法指针STOREDPROCPOINTER/属性的STOREDPROC指针INDEXINTEGER/属性的INDEX值DEFAULTLONGINT/属性的DEFAULT值NAMEINDEXSMALLINT/属性的名称索引以0开始计数NAMESHORTSTRING/属性的名称END为了方便访问属性信息,TYPINFOPAS中还定义了指向TPROPINFO数组的指针PPROPLISTTPROPLISTTPROPLISTARRAY016379OFPPROPINFO我们可以使用GETPROPLIST获得所有属性信息的指针数组,数组用完以后要记得用FREEMEM把数组的内存清除。TYPINFOPASFUNCTIONGETPROPLISTTYPEINFOPTYPEINFOOUTPROPLISTPPROPLISTINTEGERGETPROPLIST传入类的TTYPEINFO指针和TPROPLIST的指针,它为PROPLIST分配一块内存后把该内存填充为指向TPROPINFO的指针数组,最后返回属性的数量。上面的例子演示了如何获得类的所有属性信息,也可以根据属性的名称单独获得属性信息TYPINFOPASFUNCTIONGETPROPINFOTYPEINFOPTYPEINFOCONSTPROPNAMESTRINGPPROPINFOGETPROPINFO根据类的RTTI指针和属性的名称字符串,返回属性的信息TPROPINFO的指针。如果没有找到该属性,则返回NIL。GETPROPINFO很容易使用,举个例子SHOWMESSAGEGETPROPINFOTFORM,NAMEPROPTYPENAME这句调用显示了TFORM类的NAME属性的类型名称TCOMPONENTNAME。获取方法METHOD的类型信息所谓方法就是以OFOBJECT关键字声明的函数指针,下面的函数可以显示一个方法的类型信息PROCEDUREGETMETHODTYPEINFOATYPEINFOPTYPEINFOASTRINGSTSTRINGSTYPEPPARAMDATATPARAMDATATPARAMDATARECORD/函数参数的数据结构FLAGSTPARAMFLAGS/参数传递规则PARAMNAMESHORTSTRING/参数的名称TYPENAMESHORTSTRING/参数的类型名称ENDFUNCTIONGETPARAMFLAGSNAMEAPARAMFLAGSTPARAMFLAGSSTRINGVARIINTEGERBEGINRESULTFORIINTEGERPFVARTOINTEGERPFOUTDOBEGINIFIINTEGERPFADDRESSTHENCONTINUEIFTPARAMFLAGIINAPARAMFLAGSTHENRESULTRESULTGETENUMNAMETYPEINFOTPARAMFLAG,IENDENDVARMETHODTYPEDATAPTYPEDATAPARAMDATAPPARAMDATATYPESTRPSHORTSTRINGIINTEGERBEGINMETHODTYPEDATAGETTYPEDATAATYPEINFOASTRINGSADDASTRINGSADDMETHODNAMEATYPEINFONAMEASTRINGSADDMETHODKINDGETENUMNAMETYPEINFOTMETHODKIND,INTEGERMETHODTYPEDATAMETHODKINDASTRINGSADDPARAMSCOUNTINTTOSTRMETHODTYPEDATAPARAMCOUNTASTRINGSADDPARAMSLISTPARAMDATAPPARAMDATAMETHODTYPEDATAPARAMLISTFORI1TOMETHODTYPEDATAPARAMCOUNTDOBEGINTYPESTRPOINTERINTEGERPARAMDATAPARAMNAMELENGTHPARAMDATAPARAMNAME1ASTRINGSADDFORMATSSS,GETPARAMFLAGSNAMEPARAMDATAFLAGS,PARAMDATAPARAMNAME,TYPESTRPARAMDATAPPARAMDATAINTEGERPARAMDATASIZEOFTPARAMFLAGSLENGTHPARAMDATAPARAMNAMELENGTHTYPESTR2ENDIFMETHODTYPEDATAMETHODKINDMKFUNCTIONTHENASTRINGSADDRESULTVALUEPSHORTSTRINGPARAMDATAEND作为实验,在表单上放置一个TLISTBOX,然后执行以下代码,观察执行结果TYPETMYMETHODFUNCTIONAARRAYOFCHARVARBTOBJECTINTEGEROFOBJECTPROCEDURETFORM1FORMCREATESENDERTOBJECTBEGINGETMETHODTYPEINFOTYPEINFOTMYMETHOD,LISTBOX1ITEMSGETMETHODTYPEINFOTYPEINFOTMOUSEEVENT,LISTBOX1ITEMSGETMETHODTYPEINFOTYPEINFOTKEYPRESSEVENT,LISTBOX1ITEMSGETMETHODTYPEINFOTYPEINFOTMOUSEWHEELEVENT,LISTBOX1ITEMSEND由于获取方法的类型信息比较复杂,我尽量压缩代码也还是有这么长,让我们看看它的实现原理。GETMETHODTYPEINFO的第一个参数是PTYPEINFO类型,表示方法的类型信息地址。第二个参数是一个字符串列表,可以使用任何实现TSTRINGS操作的对象。我们可以使用SYSTEMPAS中的TYPEINFO函数获得任何类型的RTTI信息指针。TYPEINFO函数像SIZEOF一样,是内置于编译器中的。GETMETHODTYPEINFO还用到了TYPINFOPAS中的GETENUMNAME函数。这个函数通过枚举类型的整数值得到枚举类型的名称。FUNCTIONGETENUMNAMETYPEINFOPTYPEINFOVALUEINTEGERSTRING与获取类CLASS的属性信息类似,方法的类型信息也在TTYPEDATA结构中TTYPEDATAPACKEDRECORDCASETTYPEKINDOFTKMETHODMETHODKINDTMETHODKIND/方法指针的类型PARAMCOUNTBYTE/参数数量PARAMLISTARRAY01023OFCHAR/参数详细信息,见下行注释PARAMLISTARRAY1PARAMCOUNTOFRECORDFLAGSTPARAMFLAGS/参数传递规则PARAMNAMESHORTSTRING/参数的名称TYPENAMESHORTSTRING/参数的类型ENDRESULTTYPESHORTSTRING/返回值的名称ENDTMETHODKIND是方法的类型,定义如下TMETHODKINDMKPROCEDURE,MKFUNCTION,MKCONSTRUCTOR,MKDESTRUCTOR,MKCLASSPROCEDURE,MKCLASSFUNCTION,OBSOLETEMKSAFEPROCEDURE,MKSAFEFUNCTIONTPARAMSFLAGS是参数传递的规则,定义如下TPARAMFLAGPFVAR,PFCONST,PFARRAY,PFADDRESS,PFREFERENCE,PFOUTTPARAMFLAGSSETOFTPARAMFLAG由于PARAMNAME和TYPENAME是变长字符串,不能直接取用该字段的值,而应该使用指针步进的方法,取出参数信息,所以上面的代码显得比较长。获取有序类型ORDINAL、集合SET类型的RTTI信息讨论完了属性和方法的RTTI信息之后再来看其它数据类型的RTTI就简单多了。所有获取RTTI的原理都是通过GETTYPEDATA函数得到TTYPEDATA的指针,再通过TTYPEINFOTYPEKIND来解析TTYPEDATA。任何数据类型的TTYPEINFO指针可以通过TYPEINFO函数获得。有序类型的TTYPEDATA定义如下TTYPEDATAPACKEDRECORDTKINTEGER,TKCHAR,TKENUMERATION,TKSET,TKWCHARORDTYPETORDTYPE/有序数值类型CASETTYPEKINDOFCASETTYPEKINDOFTKINTEGER,TKCHAR,TKENUMERATION,TKWCHARMINVALUELONGINT/类型的最小值MAXVALUELONGINT/类型的最大值CASETTYPEKINDOFTKINTEGER,TKCHAR,TKWCHARTKENUMERATIONBASETYPEPPTYPEINFO/指针的指针,它指向枚举的PTYPEINFONAMELISTSHORTSTRINGBASE/枚举的名称字符串不能直接取用ENUMUNITNAMESHORTSTRINGBASE/所在的单元名称不能直接取用TKSETCOMPTYPEPPTYPEINFO/指向集合基类RTTI指针的指针END下面是一个获取有序类型和集合类型的RTTI信息的函数PROCEDUREGETORDTYPEINFOATYPEINFOPTYPEINFOASTRINGSTSTRINGSVARORDTYPEDATAPTYPEDATAIINTEGERBEGINORDTYPEDATAGETTYPEDATAATYPEINFOASTRINGSADDASTRINGSADDTYPENAMEATYPEINFONAMEASTRINGSADDTYPEKINDGETENUMNAMETYPEINFOTTYPEKIND,INTEGERATYPEINFOKINDASTRINGSADDDATATYPEGETENUMNAMETYPEINFOTORDTYPE,INTEGERORDTYPEDATAORDTYPEIFATYPEINFOKINDTKSETTHENBEGINASTRINGSADDMINVALUEINTTOSTRORDTYPEDATAMINVALUEASTRINGSADDMAXVALUEINTTOSTRORDTYPEDATAMAXVALUEENDIFATYPEINFOKINDTKSETTHENGETORDTYPEINFOORDTYPEDATACOMPTYPE,ASTRINGSIFATYPEINFOKINDTKENUMERATIONTHENFORIORDTYPEDATAMINVALUETOORDTYPEDATAMAXVALUEDOASTRINGSADDFORMATVALUEDS,I,GETENUMNAMEATYPEINFO,IEND在表单上放置一个TLISTBOX,运行以下代码查看结果TYPETMYENUMENUMA,ENUMB,ENUMCPROCEDURETFORM1FORMCREATESENDERTOBJECTBEGINGETORDTYPEINFOTYPEINFOCHAR,LISTBOX1ITEMSGETORDTYPEINFOTYPEINFOINTEGER,LISTBOX1ITEMSGETORDTYPEINFOTYPEINFOTFORMBORDERSTYLE,LISTBOX1ITEMSGETORDTYPEINFOTYPEINFOTBORDERICONS,LISTBOX1ITEMSGETORDTYPEINFOTYPEINFOTMYENUM,LISTBOX1ITEMSEND如果枚举元素没有按缺省的0基准定义,那么将不能产生RTTI信息,为什么获取其它数据类型的RTTI信息上面讨论了几个典型的RTTI信息的运行,其它的数据类型的RTTI信息的获取方法与上面类似。由于这些操作更加简单,就不一一讨论。下面概述其它类型的RTTI信息的情况LONGSTRING、WIDESTRING和VARIANT没有RTTI信息;SHORTSTRING只有MAXLENGTH信息;浮点数类型只有FLOATTYPETFLOATTYPE信息;TFLOATTYPEFTSINGLE,FTDOUBLE,FTEXTENDED,FTCOMP,FTCURRINT64只有最大值和最小值信息也是64位整数表示;INTERFACE和动态数组不太熟悉,就不作介绍了。结束200733102840查看评语200733102946本文上篇基本上是RTTI入门介绍,续篇介绍了所有TYPINFOPAS中的函数,附加了CLASSESPAS、GRAPHICSPAS、CONTROLSPAS中的几个RTTI相关函数。对于关键函数的代码提供汇编注释。希望本文覆盖了DELPHI中80的RTTI函数。时间仓促,错误难免,敬请批评指正。本文排版格式为正文由窗口自动换行;所有代码以80字符为边界;中英文字符以空格符分隔。作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。目录GETTYPEDATA函数GETPROPINFO函数FINDPROPINFO函数GETPROPINFOS函数SORTPROPLIST函数GETPROPLIST函数GETOBJECTPROPCLASS函数PROPTYPE/PROPISTYPE函数ISPUBLISHEDPROP函数ISSTOREDPROP函数FREEANDNILPROPERTIES函数SETTOSTRING/STRINGTOSET函数GETENUMNAME/GETENUMVALUE/GETENUMNAMEVALUE函数GETORDPROP函数详解SETORDPROP函数GETENUMPROP/SETENUMPROP函数GETSETPROP/SETSETPROP函数GETOBJECTPROP/SETOBJECTPROP函数GETSTRPROP/SETSTRPROP函数GETFLOATPROP/SETFLOATPROP函数GETPROPVALUE/SETPROPVALUE函数TPUBLISHABL

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论