
delphi的rtti机制浅探.doc
9页Delphi的RTTI机制浅探0 RTTI简介O类(class)和VMT的关系O 类(class) > 类的类(class of class) > 类变量(class variable) 的关系O TObject. ClassType 和 TObject. ClassinfoO is和as运算符的原理O TTypelnfo - RTTI信息的结构O获取类(class)的属性(property)信息O获取方法(method)的类型信息O获取有序类型(ordinal).集合(set)类型的RTTI信息O获取苴它数据类型的RTTI信息本文排版格式为:正文由窗口自动换行;所有代码以80字符为边界;中英文字符 以空格符分隔作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体 转载)正文O RTTI简介RTTI (Run-Time Type Information)翻译过来的名称是“运行期类型 信息”,也就是说可以在运行期获得数据类型或类(class)的信息 这个RTTI到底有什么用处,我现在也说不清楚我是在阅读Delphi 持续机制的代码中发现了很多RTTI的运用,只好先把RTTI学习一遍。
下面是我的学习笔记如果你发现了错误请告诉我谢谢!Delphi的RTTI主要分为类(class)的RTTI和一般数据类型的 RTTI,下面从类(class)开始O类(class)和VMT的关系一个类(class),从编译器的角度來看就是一个指向VMT的指针(在 后文用VMTptr表示)在类的VMTptr的负地址方向存储了一些类 信息的指针,这些指针的值和指针所指的内容在编译后就确定了比 如VMTptr-44的内容是指向类名称(ClassName)的指针不过一般 不使用数值来访问这些类信息,而是通过System, pas中定义的以 vmt开头的常量,如vtmClassName> vmtParent等来访问类的方法有两种:对象级别的方法和类级别的方法两者的Self指 针意义是不同的在对象级别的方法中Self指向对象地址空间,因 此可以用它來访问对象的成员函数;在类级别的方法中Self指向类 的VMT,因此只能用它來访问VMT信息,而不能访问对象的成员字 段0 类(class) > 类的类(class of class) 类变量(class variable) 的关系上面说到类(class)就是VMTptr。
在Delphi中还可以用class of 关键字定义类的类,并且可以使用类的类定义类变量从语法上理解 这三者的关键并不难,把类当成普通的数据类型來考虑就可以了在 编译器级别上表现如何呢?为了简化讨论,我们使用TObject、TClass和TMyClass来代表上 面说的三种类型:typeTClass 二 class of TObject;varTMyClass: TClass;MyObject: TObject; beginTMyClass :二 TObject;MyObject := TObject. Create;MyObject :二 TClass.Create;MyObject :二 TMyClass. Create; end;在上面的例子中,三个TObject对象都被成功地创建了编译器的 实现是:TObject是一个VMTPtr常量o TClass也是一个VMTptr常 量,它的值就是TObjecto TMyClass是一个VMTptr变量,它被赋 值为 TObjecto TObject. Create 与 TClass. Create 的汇编代码完全 相同但TClass不仅缺省代表一个类,而且还(主要)代表了类的类 型,可以用它來定义类变量,实现一些类级别的操作。
O TObject. ClassType 和 TObject. Classinfo function TObject.ClassType: TClass; beginPointer (Result) := PPointer(Self) end;TObject. ClassType是对象级别的方法,Self的值是指向对象内存 空间的指针,对象内存空间的前4个字节是类的VMTptc因此这个 函数的返回值就是类的VMTptcclass function TObject・Classinfo: Pointer;beginResult :二 PPointer(Integer(Self) + vmtTypelnfo); end;TObject. Classinfo使用class关键字定义,因此是一个类级别的 方法该方法中的Self指针就是VMTptc所以这个函数的返冋值 是VMTptr负方向的vmtTypelnfo的内容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; 对象实例的大小O is和as运算符的原理 我们知道可以在运行期使用is关键字判断一个对象是否属于某个 类,可以使用as关键字把某个对象安全地转换为某个类在编译器 的层次上,is和as的操作是由System, pas中两个函数完成的{ System・ pas }function _IsClass(Child: TObject; Parent: TClass): Boolean; beginResult :二(Child <> nil) and Child .InheritsFrom(Parent); end;_IsClass很简单,它使用TObject的InheritsForm函数判断该对 象是否是从某个类或它的父类中继承下來的。
每个类的VMT中都有 一项vmtParent指针,指向该类的父类的VMTTObject. InheritsFrom实际上是通过[递归]判断父类VMT指针是 否等于自己的VMT指针來判断是否是从该类继承的{ System・ pas }class function TObject・ InheritsFrom(AClass: TClass) : Boolean;varClassPtr: TClass;beginClassPtr :二 Self;while (ClassPtr <> nil) and (ClassPtr <> AClass) doClassPtr :二 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; beginResult :二 Child;if not (Child is Parent) thenError(reInvalidCast); // loses return addressend;O TTypelnfo - RTTI信息的结构TTypelnfo = recordKind: TTypeKind;Name: ShortString;{TypeData.: TTypeData} end;RTTI信息的结构定义在Typlnfo. pas中:// TTypelnfo是RTTI信息的结构// RTTI信息的数据类型//数据类型的名称// RTTI的内容TTypelnfo就是RTTI信息的结构o TObject. Classinfo返回指向存 放class TTypelnfo信息的指针Kind是枚举类型,它表示RTTI 结构中所包含数据类型Name是数据类型的名称注意,最后一个 字段TypeData被注释掉了,这说明该处的结构内容根据不同的数据 类型有所不同。
TTypeKind枚举定义了可以使用RTTI信息的数据类型,它儿乎包含 了所有的Delphi数据类型,其中包括tkClasSoTTypeKind 二(tkUnknown, tklnteger, tkChar, tkEnumeration, tkFloat,tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,tkVariant, tkArray, tkRecord, tklnterface, tklnt64, tkDynArray);TTypeData是个巨大的记录类型,在此不再列出,后文会根据需要列 出该记录的内容O获取类(class)的属性(property)信息这一段是RTTI中最复杂的部分,努力把本段吃透,后面的内容都是 非常简单的下面是…个获取类的属性的例了:procedure GetClassProperties(AClass: TClass; AStrings: TStrings);varPropCount, I: Smalllnt;PropList: PPropList;PropStr: string;beginPropCount :二 GetTypeData(AClass・ Classinfo). PropCount;GetPropList (AClass・ Classinfo, PropList); for I :二 0 to PropCount - 1 do begincase PropList[I].PropType八.Kind oftkClassPropStrtkMethodtkSettkEnumerationPropStrPropStrPropStr二’[Class];=[Method];二[Set];二[Enum]elsePropStr :二’[Field];end;PropStr :二 PropStr + PropList[I]"・Name;PropStr :二 PropStr + : + PropList[I].PtopType八.Name;AStrings・ Add(PropStr):end;FreeMem(PropList);end;你可以在表单上放置一个TListBox ,然后执行以下语句观察执行结 果:GetClassProperties(TForml, ListBox。
