===============================================================================
⊙ 擷取方法(method)的類型資訊
===============================================================================
所謂方法就是以
of
object
關鍵字聲明的函數指針,下面的函數可以顯示一個方法的類型資訊:
procedure
GetMethodTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);
type
PParamData = ^TParamData;
TParamData =
record
// 函數參數的資料結構
Flags: TParamFlags;
// 參數傳遞規則
ParamName:
ShortString
;
// 參數的名稱
TypeName:
ShortString
;
// 參數的類型名稱
end
;
function
GetParamFlagsName(AParamFlags: TParamFlags):
string
;
var
I:
Integer
;
begin
Result :=
''
;
for
I :=
Integer
(pfVar)
to
Integer
(pfOut)
do
begin
if
I =
Integer
(pfAddress)
then
Continue;
if
TParamFlag(I)
in
AParamFlags
then
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(
'Method Name: '
+ ATypeInfo^.Name);
AStrings
.
Add(
'Method Kind: '
+ GetEnumName(TypeInfo(TMethodKind),
Integer
(MethodTypeData^.MethodKind)));
AStrings
.
Add(
'Params Count: '
+ IntToStr(MethodTypeData^.ParamCount));
AStrings
.
Add(
'Params List:'
);
ParamData := PParamData(@MethodTypeData^.ParamList);
for
I :=
1
to
MethodTypeData^.ParamCount
do
begin
TypeStr :=
Pointer
(
Integer
(@ParamData^.ParamName) +
Length(ParamData^.ParamName) +
1
);
AStrings
.
Add(Format(
' [%s] %s: %s'
,[GetParamFlagsName(ParamData^.Flags),
ParamData^.ParamName, TypeStr^]));
ParamData := PParamData(
Integer
(ParamData) + SizeOf(TParamFlags) +
Length(ParamData^.ParamName) + Length(TypeStr^) +
2
);
end
;
if
MethodTypeData^.MethodKind = mkFunction
then
AStrings
.
Add(
'Result Value: '
+
PShortString
(ParamData)^);
end
;
作為實驗,在表單上放置一個 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
;
(如果枚舉元素沒有按預設的
基準定義,那麼将不能産生 RTTI 資訊,為什麼?)
===============================================================================
⊙ 擷取其它資料類型的 RTTI 資訊
===============================================================================
上面讨論了幾個典型的 RTTI 資訊的運作,其它的資料類型的 RTTI 資訊的擷取方法與上面類似。由于這些操作更加簡單,就不一一讨論。下面概述其它類型的 RTTI 資訊的情況:
LongString、
WideString
和 Variant 沒有 RTTI 資訊;
ShortString
隻有 MaxLength 資訊;
浮點數類型隻有 FloatType: TFloatType 資訊;
TFloatType = (ftSingle, ftDouble, ftExtended, ftComp, ftCurr);
Int64
隻有最大值和最小值資訊(也是
64
位整數表示);
Interface
和動态數組不太熟悉,就不作介紹了。