作为实验,在表单上放置一个 TListBox,然后执行以下代码,观察执行结果:
type
TMyMethod = function(A: array of Char; var B:
TObject): Integer of object;
procedure TForm1.FormCreate(Sender: TObject);
begin
GetMethodTypeInfo(TypeInfo(TMyMethod),
ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TMouseEvent),
ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TKeyPressEvent),
ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TMouseWheelEvent),
ListBox1.Items);
end;
由于获取方法的类型信息比较复杂,我尽量压缩代码也还是有这么长,让我们看看它的实现原理。GetMethodTypeInfo
的第一个参数是 PTypeInfo
类型,表示方法的类型信息地址。第二个参数是一个字符串列表,可以使用任何实现 TStrings
操作的对象。我们可以使用 System.pas
中的 TypeInfo
函数获得任何类型的 RTTI
信息指针。TypeInfo
函数像 SizeOf
一样,是内置于编译器中的。
GetMethodTypeInfo
还用到了 TypInfo.pas
中的 GetEnumName
函数。这个函数通过枚举类型的整数值得到枚举类型的名称。
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer):
string;
与获取类(class)的属性信息类似,方法的类型信息也在
TTypeData
结构中
TTypeData = packed record
case
TTypeKind of
tkMethod: (
MethodKind:
TMethodKind;
//
方法指针的类型
ParamCount:
Byte;
//
参数数量
ParamList: array[0..1023] of
Char //
参数详细信息,见下行注释
{ParamList: array[1..ParamCount] of
record
Flags:
TParamFlags;
//
参数传递规则
ParamName:
ShortString;
//
参数的名称
TypeName:
ShortString;
//
参数的类型
end;
ResultType:
ShortString});
//
返回值的名称
end;
TMethodKind
是方法的类型,定义如下:
TMethodKind = (mkProcedure, mkFunction,
mkConstructor, mkDestructor,
mkClassProcedure, mkClassFunction,
{ Obsolete
}
mkSafeProcedure, mkSafeFunction);
TParamsFlags
是参数传递的规则,定义如下:
TParamFlag = (pfVar, pfConst, pfArray,
pfAddress, pfReference, pfOut);
TParamFlags = set of TParamFlag;
由于
ParamName
和 TypeName
是变长字符串,不能直接取用该字段的值,而应该使用指针步进的方法,取出参数信息,所以上面的代码显得比较长。
===============================================================================
⊙
获取有序类型(ordinal)、集合(set)类型的
RTTI
信息
===============================================================================
讨论完了属性和方法的
RTTI
信息之后再来看其它数据类型的 RTTI
就简单多了。所有获取 RTTI
的原理都是通过 GetTypeData
函数得到 TTypeData
的指针,再通过 TTypeInfo.TypeKind
来解析 TTypeData。任何数据类型的
TTypeInfo
指针可以通过 TypeInfo
函数获得。
有序类型的
TTypeData
定义如下:
TTypeData = packed record
tkInteger, tkChar, tkEnumeration, tkSet,
tkWChar: (
OrdType:
TOrdType;
//
有序数值类型
case
TTypeKind of
case TTypeKind of
tkInteger, tkChar, tkEnumeration, tkWChar: (
MinValue: Longint; //
类型的最小值
MaxValue: Longint; //
类型的最大值
case TTypeKind of
tkInteger, tkChar, tkWChar: ();
tkEnumeration: (
BaseType:
PPTypeInfo;
//
指针的指针,它指向枚举的 PTypeInfo
NameList:
ShortStringBase;
//
枚举的名称字符串(不能直接取用)
EnumUnitName: ShortStringBase)); //
所在的单元名称(不能直接取用)
tkSet: (
CompType:
PPTypeInfo));
//
指向集合基类 RTTI
指针的指针
end;
下面是一个获取有序类型和集合类型的
RTTI
信息的函数:
procedure GetOrdTypeInfo(ATypeInfo: PTypeInfo; AStrings:
TStrings);
var
OrdTypeData: PTypeData;
I: Integer;
begin
OrdTypeData := GetTypeData(ATypeInfo);
AStrings.Add('------------------------------------');
AStrings.Add('Type Name: ' +
ATypeInfo^.Name);
AStrings.Add('Type Kind: ' +
GetEnumName(TypeInfo(TTypeKind),
Integer(ATypeInfo^.Kind)));
AStrings.Add('Data Type: ' +
GetEnumName(TypeInfo(TOrdType),
Integer(OrdTypeData^.OrdType)));
if ATypeInfo^.Kind <> tkSet then
begin
AStrings.Add('Min Value: ' +
IntToStr(OrdTypeData^.MinValue));
AStrings.Add('Max Value: ' +
IntToStr(OrdTypeData^.MaxValue));
end;
if ATypeInfo^.Kind = tkSet then
GetOrdTypeInfo(OrdTypeData^.CompType^, AStrings);
if ATypeInfo^.Kind = tkEnumeration then
for I :=
OrdTypeData^.MinValue to OrdTypeData^.MaxValue do
AStrings.Add(Format(' Value %d: %s', [I,
GetEnumName(ATypeInfo, I)]));
end;
在表单上放置一个
TListBox,运行以下代码查看结果:
type TMyEnum = (EnumA, EnumB, EnumC);
procedure TForm1.FormCreate(Sender: TObject);
begin
GetOrdTypeInfo(TypeInfo(Char),
ListBox1.Items);
GetOrdTypeInfo(TypeInfo(Integer),
ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TFormBorderStyle),
ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TBorderIcons),
ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TMyEnum),
ListBox1.Items);
end;
(如果枚举元素没有按缺省的
0
基准定义,那么将不能产生 RTTI
信息,为什么?)
===============================================================================
⊙
获取其它数据类型的 RTTI
信息
===============================================================================
上面讨论了几个典型的
RTTI
信息的运行,其它的数据类型的 RTTI
信息的获取方法与上面类似。由于这些操作更加简单,就不一一讨论。下面概述其它类型的 RTTI
信息的情况:
LongString、WideString
和 Variant
没有 RTTI
信息;
ShortString
只有 MaxLength
信息;
浮点数类型只有
FloatType: TFloatType
信息;
TFloatType = (ftSingle, ftDouble, ftExtended,
ftComp, ftCurr);
Int64
只有最大值和最小值信息(也是
64
位整数表示);
Interface
和动态数组不太熟悉,就不作介绍了。