小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

DELPHI RTTI淺析

 Jason(徐子) 2010-09-06

目錄
===============================================================================
⊙ RTTI 簡(jiǎn)介
⊙ 類(lèi)(class) 和 VMT 的關(guān)系
⊙ 類(lèi)(class)、類(lèi)的類(lèi)(class of class)、類(lèi)變量(class variable) 的關(guān)系
⊙ TObject.ClassType 和 TObject.ClassInfo
⊙ is 和 as 運(yùn)算符的原理
⊙ TTypeInfo – RTTI 信息的結(jié)構(gòu)
⊙ 獲取類(lèi)(class)的屬性(property)信息
⊙ 獲取方法(method)的類(lèi)型信息
⊙ 獲取有序類(lèi)型(ordinal)、集合(set)類(lèi)型的 RTTI 信息
⊙ 獲取其它數(shù)據(jù)類(lèi)型的 RTTI 信息
===============================================================================

本文排版格式為:
    正文由窗口自動(dòng)換行;所有代碼以 80 字符為邊界;中英文字符以空格符分隔。

(作者保留對(duì)本文的所有權(quán)利,未經(jīng)作者同意請(qǐng)勿在在任何公共媒體轉(zhuǎn)載。)


正文
===============================================================================
⊙ RTTI 簡(jiǎn)介
===============================================================================

RTTI(Run-Time Type Information) 翻譯過(guò)來(lái)的名稱(chēng)是“運(yùn)行期類(lèi)型信息”,也就是說(shuō)可以在運(yùn)行期獲得數(shù)據(jù)類(lèi)型或類(lèi)(class)的信息。這個(gè) RTTI 到底有什么用處,我現(xiàn)在也說(shuō)不清楚。我是在閱讀 Delphi 持續(xù)機(jī)制的代碼中發(fā)現(xiàn)了很多 RTTI 的運(yùn)用,只好先把 RTTI 學(xué)習(xí)一遍。下面是我的學(xué)習(xí)筆記。如果你發(fā)現(xiàn)了錯(cuò)誤請(qǐng)告訴我。謝謝!

Delphi 的 RTTI 主要分為類(lèi)(class)的 RTTI 和一般數(shù)據(jù)類(lèi)型的 RTTI,下面從類(lèi)(class)開(kāi)始。

===============================================================================
⊙ 類(lèi)(class) 和 VMT 的關(guān)系
===============================================================================

一個(gè)類(lèi)(class),從編譯器的角度來(lái)看就是一個(gè)指向 VMT 的指針(在后文用 VMTptr 表示)。在類(lèi)的 VMTptr 的負(fù)地址方向存儲(chǔ)了一些類(lèi)信息的指針,這些指針的值和指針?biāo)傅膬?nèi)容在編譯后就確定了。比如 VMTptr - 44 的內(nèi)容是指向類(lèi)名稱(chēng)(ClassName)的指針。不過(guò)一般不使用數(shù)值來(lái)訪(fǎng)問(wèn)這些類(lèi)信息,而是通過(guò) System.pas 中定義的以 vmt 開(kāi)頭的常量,如 vtmClassName、vmtParent 等來(lái)訪(fǎng)問(wèn)。

類(lèi)的方法有兩種:對(duì)象級(jí)別的方法和類(lèi)級(jí)別的方法。兩者的 Self 指針意義是不同的。在對(duì)象級(jí)別的方法中 Self 指向?qū)ο蟮刂房臻g,因此可以用它來(lái)訪(fǎng)問(wèn)對(duì)象的成員函數(shù);在類(lèi)級(jí)別的方法中 Self 指向類(lèi)的 VMT,因此只能用它來(lái)訪(fǎng)問(wèn) VMT 信息,而不能訪(fǎng)問(wèn)對(duì)象的成員字段。

===============================================================================
⊙ 類(lèi)(class)、類(lèi)的類(lèi)(class of class)、類(lèi)變量(class variable) 的關(guān)系
===============================================================================

上面說(shuō)到類(lèi)(class) 就是 VMTptr。在 Delphi 中還可以用 class of 關(guān)鍵字定義類(lèi)的類(lèi),并且可以使用類(lèi)的類(lèi)定義類(lèi)變量。從語(yǔ)法上理解這三者的關(guān)鍵并不難,把類(lèi)當(dāng)成普通的數(shù)據(jù)類(lèi)型來(lái)考慮就可以了。在編譯器級(jí)別上表現(xiàn)如何呢?

為了簡(jiǎn)化討論,我們使用 TObject、TClass 和 TMyClass 來(lái)代表上面說(shuō)的三種類(lèi)型:

type
  TClass = class of TObject;
var
  TMyClass: TClass;
  MyObject: TObject;
begin
  TMyClass := TObject;
  MyObject := TObject.Create;
  MyObject := TClass.Create;
  MyObject := TMyClass.Create;
end;
 
在上面的例子中,三個(gè) TObject 對(duì)象都被成功地創(chuàng)建了。編譯器的實(shí)現(xiàn)是:TObject 是一個(gè) VMTPtr 常量。TClass 也是一個(gè) VMTptr 常量,它的值就是 TObject。TMyClass 是一個(gè) VMTptr 變量,它被賦值為 TObject。TObject.Create 與 TClass.Create 的匯編代碼完全相同。但 TClass 不僅缺省代表一個(gè)類(lèi),而且還(主要)代表了類(lèi)的類(lèi)型,可以用它來(lái)定義類(lèi)變量,實(shí)現(xiàn)一些類(lèi)級(jí)別的操作。

===============================================================================
⊙ TObject.ClassType 和 TObject.ClassInfo
===============================================================================

function TObject.ClassType: TClass;
begin
  Pointer(Result) := PPointer(Self)^;
end;

TObject.ClassType 是對(duì)象級(jí)別的方法,Self 的值是指向?qū)ο髢?nèi)存空間的指針,對(duì)象內(nèi)存空間的前 4 個(gè)字節(jié)是類(lèi)的 VMTptr。因此這個(gè)函數(shù)的返回值就是類(lèi)的 VMTptr。

class function TObject.ClassInfo: Pointer;
begin
  Result := PPointer(Integer(Self) + vmtTypeInfo)^;
end;

TObject.ClassInfo 使用 class 關(guān)鍵字定義,因此是一個(gè)類(lèi)級(jí)別的方法。該方法中的 Self 指針就是 VMTptr。所以這個(gè)函數(shù)的返回值是 VMTptr 負(fù)方向的 vmtTypeInfo 的內(nèi)容。

TObject.ClassInfo 返回的 Pointer 指針,實(shí)際上是指向類(lèi)的 RTTI 結(jié)構(gòu)的指針。但是不能訪(fǎng)問(wèn) TObject.ClassInfo 指向的內(nèi)容(TObject.ClassInfo 返回值是 0),因?yàn)?Delphi 只在 TPersistent 類(lèi)及 TPersistent 的后繼類(lèi)中產(chǎn)生 RTTI 信息。(從編譯器的角度來(lái)看,這是在 TPersistent 類(lèi)的聲明之前使用 {$M+} 指示字的結(jié)果。)

TObject 還定義了一些獲取類(lèi) RTTI 信息的函數(shù),列舉在下,就不一一分析了:

  TObject.ClassName: ShortString;   類(lèi)的名稱(chēng)
  TObject.ClassParent: TClass;      對(duì)象的父類(lèi)
  TObject.InheritsFrom: Boolean;    是否繼承自某類(lèi)
  TObject.InstanceSize: Longint;    對(duì)象實(shí)例的大小

===============================================================================
⊙ is 和 as 運(yùn)算符的原理
===============================================================================

我們知道可以在運(yùn)行期使用 is 關(guān)鍵字判斷一個(gè)對(duì)象是否屬于某個(gè)類(lèi),可以使用 as 關(guān)鍵字把某個(gè)對(duì)象安全地轉(zhuǎn)換為某個(gè)類(lèi)。在編譯器的層次上,is 和 as 的操作是由 System.pas 中兩個(gè)函數(shù)完成的。

{ System.pas }
function _IsClass(Child: TObject; Parent: TClass): Boolean;
begin
  Result := (Child <> nil) and Child.InheritsFrom(Parent);
end;

_IsClass 很簡(jiǎn)單,它使用 TObject 的 InheritsForm 函數(shù)判斷該對(duì)象是否是從某個(gè)類(lèi)或它的父類(lèi)中繼承下來(lái)的。每個(gè)類(lèi)的 VMT 中都有一項(xiàng) vmtParent 指針,指向該類(lèi)的父類(lèi)的 VMT。TObject.InheritsFrom 實(shí)際上是通過(guò)[遞歸]判斷父類(lèi) VMT 指針是否等于自己的 VMT 指針來(lái)判斷是否是從該類(lèi)繼承的。

{ System.pas }
class function TObject.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 操作符實(shí)際上是由 System.pas 中的 _AsClass 函數(shù)完成的。它簡(jiǎn)單地調(diào)用 is 操作符判斷對(duì)象是否屬于某個(gè)類(lèi),如果不是就觸發(fā)異常。雖然 _AsClass 返回值為 TObject 類(lèi)型,但編譯器會(huì)自動(dòng)把返回的對(duì)象改變?yōu)?Parent 類(lèi),否則返回的對(duì)象沒(méi)有辦法使用 TObject 之外的方法和數(shù)據(jù)。

{ System.pas }
function _AsClass(Child: TObject; Parent: TClass): TObject;
begin
  Result := Child;
  if not (Child is Parent) then
    Error(reInvalidCast);   // loses return address
end;

===============================================================================
⊙ TTypeInfo – RTTI 信息的結(jié)構(gòu)
===============================================================================

RTTI 信息的結(jié)構(gòu)定義在 TypInfo.pas 中:

  TTypeInfo = record        // TTypeInfo 是 RTTI 信息的結(jié)構(gòu)
    Kind: TTypeKind;        // RTTI 信息的數(shù)據(jù)類(lèi)型
    Name: ShortString;      // 數(shù)據(jù)類(lèi)型的名稱(chēng)
   {TypeData: TTypeData}    // RTTI 的內(nèi)容
  end;

TTypeInfo 就是 RTTI 信息的結(jié)構(gòu)。TObject.ClassInfo 返回指向存放 class TTypeInfo 信息的指針。Kind 是枚舉類(lèi)型,它表示 RTTI 結(jié)構(gòu)中所包含數(shù)據(jù)類(lèi)型。Name 是數(shù)據(jù)類(lèi)型的名稱(chēng)。注意,最后一個(gè)字段 TypeData 被注釋掉了,這說(shuō)明該處的結(jié)構(gòu)內(nèi)容根據(jù)不同的數(shù)據(jù)類(lèi)型有所不同。

TTypeKind 枚舉定義了可以使用 RTTI 信息的數(shù)據(jù)類(lèi)型,它幾乎包含了所有的 Delphi 數(shù)據(jù)類(lèi)型,其中包括 tkClass。

  TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
    tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
    tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);

TTypeData 是個(gè)巨大的記錄類(lèi)型,在此不再列出,后文會(huì)根據(jù)需要列出該記錄的內(nèi)容。

===============================================================================
⊙ 獲取類(lèi)(class)的屬性(property)信息
===============================================================================

這一段是 RTTI 中最復(fù)雜的部分,努力把本段吃透,后面的內(nèi)容都是非常簡(jiǎn)單的。

下面是一個(gè)獲取類(lèi)的屬性的例子:

procedure GetClassProperties(AClass: TClass; AStrings: TStrings);
var
  PropCount, I: SmallInt;
  PropList: PPropList;
  PropStr: string;
begin
  PropCount := GetTypeData(AClass.ClassInfo).PropCount;
  GetPropList(AClass.ClassInfo, PropList);
  for I := 0 to PropCount - 1 do
  begin
    case PropList[I]^.PropType^.Kind of
      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;

你可以在表單上放置一個(gè) TListBox ,然后執(zhí)行以下語(yǔ)句觀(guān)察執(zhí)行結(jié)果:

  GetClassProperties(TForm1, ListBox1.Items);

該函數(shù)先使用 GetTypeData 函數(shù)獲得類(lèi)的屬性數(shù)量。GetTypeData 是 TypInfo.pas 中的一個(gè)函數(shù),它的功能是返回 TTypeInfo 的 TypeData 數(shù)據(jù)的指針:

{ TypInfo.pas }
function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler;

class 的 TTypeData 結(jié)構(gòu)如下:

  TTypeData = packed record
    case TTypeKind of
      tkClass: (
        ClassType: TClass;         // 類(lèi) (VMTptr)
        ParentInfo: PPTypeInfo;    // 父類(lèi)的 RTTI 指針
        PropCount: SmallInt;       // 屬性數(shù)量
        UnitName: ShortStringBase; // 單元的名稱(chēng)
       {PropData: TPropData});     // 屬性的詳細(xì)信息
  end;

其中的 PropData 又是一個(gè)大小可變的字段。TPropData 的定義如下:

  TPropData = packed record
    PropCount: Word;       // 屬性數(shù)量
    PropList: record end;  // 占位符,真正的意義在下一行
    {PropList: array[1..PropCount] of TPropInfo}
  end;

每個(gè)屬性信息在內(nèi)存中的結(jié)構(gòu)就是 TPropInfo,它的定義如下:

  PPropInfo = ^TPropInfo;
  TPropInfo = packed record
    PropType: PPTypeInfo;    // 屬性類(lèi)型信息指針的指針
    GetProc: Pointer;        // 屬性的 Get 方法指針
    SetProc: Pointer;        // 屬性的 Set 方法指針
    StoredProc: Pointer;     // 屬性的 StoredProc 指針
    Index: Integer;          // 屬性的 Index 值
    Default: Longint;        // 屬性的 Default 值
    NameIndex: SmallInt;     // 屬性的名稱(chēng)索引(以 0 開(kāi)始計(jì)數(shù))
    Name: ShortString;       // 屬性的名稱(chēng)
  end;

為了方便訪(fǎng)問(wèn)屬性信息,TypInfo.pas 中還定義了指向 TPropInfo 數(shù)組的指針:

  PPropList = ^TPropList;
  TPropList = array[0..16379] of PPropInfo;

我們可以使用 GetPropList 獲得所有屬性信息的指針數(shù)組,數(shù)組用完以后要記得用 FreeMem 把數(shù)組的內(nèi)存清除。

{ TypInfo.pas }
function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): Integer;

GetPropList 傳入類(lèi)的 TTypeInfo 指針和 TPropList 的指針,它為 PropList 分配一塊內(nèi)存后把該內(nèi)存填充為指向 TPropInfo 的指針數(shù)組,最后返回屬性的數(shù)量。

上面的例子演示了如何獲得類(lèi)的所有屬性信息,也可以根據(jù)屬性的名稱(chēng)單獨(dú)獲得屬性信息:

{ TypInfo.pas }
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;

GetPropInfo 根據(jù)類(lèi)的 RTTI 指針和屬性的名稱(chēng)字符串,返回屬性的信息 TPropInfo 的指針。如果沒(méi)有找到該屬性,則返回 nil。GetPropInfo 很容易使用,舉個(gè)例子:

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

這句調(diào)用顯示了 TForm 類(lèi)的 Name 屬性的類(lèi)型名稱(chēng):TComponentName。

===============================================================================
⊙ 獲取方法(method)的類(lèi)型信息
===============================================================================

所謂方法就是以 of object 關(guān)鍵字聲明的函數(shù)指針,下面的函數(shù)可以顯示一個(gè)方法的類(lèi)型信息:

procedure GetMethodTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);
type
  PParamData = ^TParamData;
  TParamData = record       // 函數(shù)參數(shù)的數(shù)據(jù)結(jié)構(gòu)
    Flags: TParamFlags;     // 參數(shù)傳遞規(guī)則
    ParamName: ShortString; // 參數(shù)的名稱(chēng)
    TypeName: ShortString;  // 參數(shù)的類(lèi)型名稱(chēng)
  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;

作為實(shí)驗(yàn),在表單上放置一個(gè) TListBox,然后執(zhí)行以下代碼,觀(guān)察執(zhí)行結(jié)果:

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;

由于獲取方法的類(lèi)型信息比較復(fù)雜,我盡量壓縮代碼也還是有這么長(zhǎng),讓我們看看它的實(shí)現(xiàn)原理。GetMethodTypeInfo 的第一個(gè)參數(shù)是 PTypeInfo 類(lèi)型,表示方法的類(lèi)型信息地址。第二個(gè)參數(shù)是一個(gè)字符串列表,可以使用任何實(shí)現(xiàn) TStrings 操作的對(duì)象。我們可以使用 System.pas 中的 TypeInfo 函數(shù)獲得任何類(lèi)型的 RTTI 信息指針。TypeInfo 函數(shù)像 SizeOf 一樣,是內(nèi)置于編譯器中的。

GetMethodTypeInfo 還用到了 TypInfo.pas 中的 GetEnumName 函數(shù)。這個(gè)函數(shù)通過(guò)枚舉類(lèi)型的整數(shù)值得到枚舉類(lèi)型的名稱(chēng)。

function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;

與獲取類(lèi)(class)的屬性信息類(lèi)似,方法的類(lèi)型信息也在 TTypeData 結(jié)構(gòu)中

  TTypeData = packed record
    case TTypeKind of
      tkMethod: (
        MethodKind: TMethodKind;            // 方法指針的類(lèi)型
        ParamCount: Byte;                   // 參數(shù)數(shù)量
        ParamList: array[0..1023] of Char   // 參數(shù)詳細(xì)信息,見(jiàn)下行注釋
       {ParamList: array[1..ParamCount] of
          record
            Flags: TParamFlags;             // 參數(shù)傳遞規(guī)則
            ParamName: ShortString;         // 參數(shù)的名稱(chēng)
            TypeName: ShortString;          // 參數(shù)的類(lèi)型
          end;
        ResultType: ShortString});          // 返回值的名稱(chēng)
  end;

TMethodKind 是方法的類(lèi)型,定義如下:

  TMethodKind = (mkProcedure, mkFunction, mkConstructor, mkDestructor,
    mkClassProcedure, mkClassFunction,
    { Obsolete }
    mkSafeProcedure, mkSafeFunction);

TParamsFlags 是參數(shù)傳遞的規(guī)則,定義如下:

  TParamFlag = (pfVar, pfConst, pfArray, pfAddress, pfReference, pfOut);
  TParamFlags = set of TParamFlag;

由于 ParamName 和 TypeName 是變長(zhǎng)字符串,不能直接取用該字段的值,而應(yīng)該使用指針步進(jìn)的方法,取出參數(shù)信息,所以上面的代碼顯得比較長(zhǎng)。

===============================================================================
⊙ 獲取有序類(lèi)型(ordinal)、集合(set)類(lèi)型的 RTTI 信息
===============================================================================

討論完了屬性和方法的 RTTI 信息之后再來(lái)看其它數(shù)據(jù)類(lèi)型的 RTTI 就簡(jiǎn)單多了。所有獲取 RTTI 的原理都是通過(guò) GetTypeData 函數(shù)得到 TTypeData 的指針,再通過(guò) TTypeInfo.TypeKind 來(lái)解析 TTypeData。任何數(shù)據(jù)類(lèi)型的 TTypeInfo 指針可以通過(guò) TypeInfo 函數(shù)獲得。

有序類(lèi)型的 TTypeData 定義如下:

TTypeData = packed record
  tkInteger, tkChar, tkEnumeration, tkSet, tkWChar: (
    OrdType: TOrdType;         // 有序數(shù)值類(lèi)型
    case TTypeKind of
      case TTypeKind of
        tkInteger, tkChar, tkEnumeration, tkWChar: (
          MinValue: Longint;   // 類(lèi)型的最小值
          MaxValue: Longint;   // 類(lèi)型的最大值
          case TTypeKind of
            tkInteger, tkChar, tkWChar: ();
            tkEnumeration: (
              BaseType: PPTypeInfo;      // 指針的指針,它指向枚舉的 PTypeInfo
              NameList: ShortStringBase;     // 枚舉的名稱(chēng)字符串(不能直接取用)
              EnumUnitName: ShortStringBase)); // 所在的單元名稱(chēng)(不能直接取用)
          tkSet: (
            CompType: PPTypeInfo));            // 指向集合基類(lèi) RTTI 指針的指針
end;

下面是一個(gè)獲取有序類(lèi)型和集合類(lèi)型的 RTTI 信息的函數(shù):

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;

在表單上放置一個(gè) TListBox,運(yùn)行以下代碼查看結(jié)果:

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;

(如果枚舉元素沒(méi)有按缺省的 0 基準(zhǔn)定義,那么將不能產(chǎn)生 RTTI 信息,為什么?)

===============================================================================
⊙ 獲取其它數(shù)據(jù)類(lèi)型的 RTTI 信息
===============================================================================

上面討論了幾個(gè)典型的 RTTI 信息的運(yùn)行,其它的數(shù)據(jù)類(lèi)型的 RTTI 信息的獲取方法與上面類(lèi)似。由于這些操作更加簡(jiǎn)單,就不一一討論。下面概述其它類(lèi)型的 RTTI 信息的情況:

LongString、WideString 和 Variant 沒(méi)有 RTTI 信息;
ShortString 只有 MaxLength 信息;
浮點(diǎn)數(shù)類(lèi)型只有 FloatType: TFloatType 信息;
  TFloatType = (ftSingle, ftDouble, ftExtended, ftComp, ftCurr);
Int64 只有最大值和最小值信息(也是 64 位整數(shù)表示);
Interface 和動(dòng)態(tài)數(shù)組不太熟悉,就不作介紹了。

===============================================================================
⊙ 結(jié)束
===============================================================================
 

2007-3-31 0:28:40   

 2007-3-31 0:29:46   


本文上篇基本上是 RTTI 入門(mén)介紹,續(xù)篇介紹了所有 TypInfo.pas 中的函數(shù),附加了 Classes.pas、Graphics.pas、Controls.pas 中的幾個(gè) RTTI 相關(guān)函數(shù)。對(duì)于關(guān)鍵函數(shù)的代碼提供匯編注釋。希望本文覆蓋了 Delphi 中 80% 的 RTTI 函數(shù)。時(shí)間倉(cāng)促,錯(cuò)誤難免,敬請(qǐng)批評(píng)指正。


本文排版格式為:
    正文由窗口自動(dòng)換行;所有代碼以 80 字符為邊界;中英文字符以空格符分隔。

(作者保留對(duì)本文的所有權(quán)利,未經(jīng)作者同意請(qǐng)勿在在任何公共媒體轉(zhuǎn)載。)


目 錄
===============================================================================
⊙ GetTypeData 函數(shù)
⊙ GetPropInfo 函數(shù)
⊙ FindPropInfo 函數(shù)
⊙ GetPropInfos 函數(shù)
⊙ SortPropList 函數(shù)
⊙ GetPropList 函數(shù)
------------------------------------------------------
⊙ GetObjectPropClass 函數(shù)
⊙ PropType / PropIsType 函數(shù)
⊙ IsPublishedProp 函數(shù)
⊙ IsStoredProp 函數(shù)
⊙ FreeAndNilProperties 函數(shù)
⊙ SetToString / StringToSet 函數(shù)
⊙ GetEnumName / GetEnumValue / GetEnumNameValue 函數(shù)
------------------------------------------------------
⊙ GetOrdProp 函數(shù)詳解
⊙ SetOrdProp 函數(shù)
⊙ GetEnumProp / SetEnumProp 函數(shù)
⊙ GetSetProp / SetSetProp 函數(shù)
⊙ GetObjectProp / SetObjectProp 函數(shù)
⊙ GetStrProp / SetStrProp 函數(shù)
⊙ GetFloatProp / SetFloatProp 函數(shù)
⊙ GetPropValue / SetPropValue 函數(shù)
⊙ TPublishableVariantType class
------------------------------------------------------
⊙ RegisterClass / FindClass 系列函數(shù) (Classes.pas)
⊙ IdentToInt / IntToIdent 系列函數(shù) (Classes.pas)
===============================================================================


正 文
===============================================================================
⊙ GetTypeData 函數(shù)
===============================================================================
GetTypeData 函數(shù)根據(jù) TTypeInfo 指針獲得 TTypeData 的地址。

function GetTypeData(TypeInfo: PTypeInfo): PTypeData;
asm
        XOR     EDX,EDX                           ; EDX 清零
        MOV     DL,[EAX].TTypeInfo.Name.Byte[0]   ; 獲得 Name 字符串長(zhǎng)度
        LEA     EAX,[EAX].TTypeInfo.Name[EDX+1]   ; 獲得 TTypeData 的地址
end;

===============================================================================
⊙ GetPropInfo 函數(shù)
===============================================================================
GetPropInfo 函數(shù)用于獲得屬性的 RTTI 指針 PPropInfo。它有四種重載形式,后面三種重載的實(shí)現(xiàn)都是調(diào)用第一種形式。AKinds 參數(shù)用于限制屬性的類(lèi)型,如果得到的 PPropInfo 不屬于指定的類(lèi)型,則返回 nil。

  function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;

  function GetPropInfo(Instance: TObject; const PropName: string;
    AKinds: TTypeKinds = []): PPropInfo;
  function GetPropInfo(AClass: TClass; const PropName: string;
    AKinds: TTypeKinds = []): PPropInfo;
  function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string;
    AKinds: TTypeKinds): PPropInfo;

===============================================================================
⊙ FindPropInfo 函數(shù)
===============================================================================
FindPropInfo 函數(shù)根據(jù)屬性名稱(chēng)獲得屬性的 RTTI 指針,它只是在 GetPropInfo 函數(shù)的基礎(chǔ)上加上了錯(cuò)誤檢查功能,如果沒(méi)有屬性 RTTI 信息,則觸發(fā) EPropertyError 異常。

function FindPropInfo(Instance: TObject; const PropName: string): PPropInfo;
function FindPropInfo(AClass: TClass; const PropName: string): PPropInfo;

===============================================================================
⊙ GetPropInfos 函數(shù)
===============================================================================
GetPropInfos 函數(shù)的功能是把一個(gè)類(lèi)(class)所有屬性 RTTI 指針 PPropInfo 填充至傳入的參數(shù) PPropList 數(shù)組中。

注意:這個(gè)函數(shù)不負(fù)責(zé)分配該數(shù)組的內(nèi)容,使用前必須根據(jù)屬性的數(shù)量分配足夠的空間。該數(shù)組結(jié)束后必須清除分配的內(nèi)容。

  procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList);

注:使用 GetPropList 實(shí)現(xiàn)相同的功能更方便。

===============================================================================
⊙ SortPropList 函數(shù)
==========================================

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多