• Delphi 的RTTI机制浅探-2


    作为实验,在表单上放置一个 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
    和动态数组不太熟悉,就不作介绍了。

     

  • 相关阅读:
    【NodeJS】---express配置ejs mongoose route等
    【CSS3】---层模型position之fixed固定定位、absolute绝对定位和relative相对定位
    【CSS3】---:before :after生成内容
    px转rem的填坑之路
    markdown编写文件目录结构
    js reduce数组转对象
    处理Promise.reject()
    js事件循环
    为什么[] == false 为true
    为什么不建议用var
  • 原文地址:https://www.cnblogs.com/luckForever/p/7254562.html
Copyright © 2020-2023  润新知