Delphi2009初体验 语言篇 反射单元ObjAuto的加强

摘要:
当我兴奋地在Delphi2009中介绍ObjAuto文件时,我惊讶地发现ObjAuto只提供了GetMethods方法,而不是类似于Java中getParameterTypes方法的GetParams方法。VMT可以理解为堆中存储的Delphi对象的对应类的组成的描述。它是类的结构,不包含对象的数据。

一、提出问题

在将json-rpc中JSONObject翻译成Delphi代码的时候,我碰到以下语句:

复制代码
 1 Method[] methods = klass.getMethods();
 2 
 3 Method method = methods[i];
 4 
 5 if (key.length() > 0 &&
 6     Character.isUpperCase(key.charAt(0)) &&
 7     method.getParameterTypes().length == 0) {
 8 
 9 }
10 
复制代码

很明显,这里是通过反射得到类中包含的函数的信息及函数所包含的参数信息。当我在Delphi2009中兴奋的引入ObjAuto文件时,我沮丧的发现,ObjAuto中只提供了GetMethods方法,没有提供类似于Java中getParameterTypes方法的GetParams方法。没关系,Delphi的SDK不提供,我们就根据VMT表,自己写一个GetParams函数出来!

二、分析问题

我们知道,在Delphi中对象是在堆中存放的。而对象在堆中存放的前四个字节组成一个地址,这个地址指向的是此对象所对应的VMT所在堆中的地址。VMT可以理解为Delphi对象所对应的类在堆中存放的组成形式的描述,它是类的结构,不包含对象的数据。有关VMT的更多信息,请百度一下、Google一下,或查看以下两篇文章:

1、    Delphi中类的运行期TypeInfo信息结构说明
2、    DELPHI的原子世界

类中的函数及函数的参数信息在VMT中也有存放,我们只要知道这些信息是如何存放的,所有事情都变得简单了。下面我画出在VMT中表示函数信息的那一块结构:

Delphi2009初体验 语言篇 反射单元ObjAuto的加强第3张

 从上图我们可以看到,在VMT中每个函数结构都包含了一个TMethodInfoHeader头,一个TReturnInfo返回值结构,若干个TParamInfo参数结构。参数的个数我们是没有办法直接获取的,但是我们可以通过指针往下遍历,直到指针的值大于TMethodInfoHeader.Len为止,累加参数的个数。

*1:为什么是SizeOf(TMethodInfoHeader) – 255 + Length(mi1.Name)字节呢?

首先我们来看TMethodInfoHeader结构体:

1   TMethodInfoHeader = record
2     Len: Word;
3     Addr: Pointer;
4     Name: ShortString;
5   end;

我们来分析一下,结构体TMethodInfoHeader所占的字节(SizeOf(TMethodInfoHeader))为SizeOf(Word) + SizeOf(Pointer) + SizeOf(ShortString) = 2 + 4 + 256 = 262。如果Name字段只占了3个字节,SizeOf(TMethodInfoHeader)仍然是262,不受Name字段长度的影响,但是下一个数据是紧挨着Name的3个字节存的,中间不会留空格。
所以,我们必须使用SizeOf(TMethodInfoHeader) – 256 + Length(Name)。另外,由于字符串第0个字节保存的是字符串的长度,我们-256把保存字符串长度的那一位也减掉了,所以得+1:
SizeOf(TMethodInfoHeader) – 256 + Length(Name) + 1 = SizeOf(TMethodInfoHeader) – 255 + Length(Name)

*2:mi1: TMethodInfoHeader的信息我们可以通过ObjAuto.GetMethodInfo方法获取,我们只要关注如何得到参数信息就可以了。

三、解决问题

通过以上问题的分析,我们可以很容易的写出两个函数

1、GetParams:获取方法所包含的参数信息集合

2、GetReturnInfo:获取方法的返回参数信息

 代码如下:

复制代码
uses
    SysUtils,
    StrUtils,
    TypInfo,
    ObjAuto;

type
    TParamInfoArray 
= array of PParamInfo;

function GetParams(aObj: TObject; aMethodName: string): TParamInfoArray;
function GetReturnInfo(aObj: TObject; aMethodName: string): PReturnInfo;

implementation

const
    SHORT_LEN 
= SizeOf(ShortString) - 1;

function GetReturnInfo(aObj: TObject; aMethodName: string): PReturnInfo;
var
    mi: PMethodInfoHeader;
begin
    
// 获取函数头指针并判断是否合法
    mi :
= ObjAuto.GetMethodInfo(aObj, ShortString(aMethodName));
    
if mi.Len <= SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN then
        Exit(
nil);

    Result :
= PReturnInfo(Integer(mi) + SizeOf(TMethodInfoHeader) +
        Length(mi.Name) 
- SHORT_LEN);
end;

function GetParams(aObj: TObject; aMethodName: string): TParamInfoArray;
var
    mi: PMethodInfoHeader;
    miEnd: Pointer;
    param: PParamInfo;
    count: Integer;
begin
    
// 初始化返回值
    SetLength(Result, 
0);

    
// 获取函数头指针并判断是否合法
    mi :
= ObjAuto.GetMethodInfo(aObj, ShortString(aMethodName));
    
if mi.Len <= SizeOf(TMethodInfoHeader) + Length(mi.Name) - SHORT_LEN then
        Exit;

    
// 获取函数尾地址用于遍历
    miEnd :
= Pointer(Integer(mi) + mi.Len);

    
// 第一个参数的地址根据以下算法得来
    param :
= PParamInfo(Integer(mi) + SizeOf(TMethodInfoHeader) +
        Length(mi.Name) 
- SHORT_LEN + SizeOf(TReturnInfo));
    count :
= 0;

    
// 判断遍历是否超过了函数尾地址
    
while Integer(param) < Integer(miEnd) do
    
begin
        Inc(count);
        SetLength(Result, count);
        Result[count 
- 1] := param;

        
// 往后的参数地址算法由来
        param :
= PParamInfo(Integer(param) + SizeOf(TParamInfo) +
             Length(param.Name) 
- SHORT_LEN);
    
end;
end;
复制代码

 

以下是测试代码:

复制代码
 1Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张program TestChar;
 2Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
 3Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张{$APPTYPE CONSOLE}
 4Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
 5Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张uses
 6Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张  SysUtils,
 7Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张  ObjAuto,
 8Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张  TypInfo,
 9Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张  AutoPtr in '..\..\Djson\common\AutoPtr.pas',
10Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张  Utils in '..\..\Djson\common\Utils.pas';
11Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
12Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张type
13Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张{$METHODINFO ON}
14Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    TTestClass = class
15Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    public
16Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        function Test3: Integer;
17Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        procedure Test2(a: string);
18Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        function Test1(a: string; b: Single): Single;
19Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    end;
20Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张{$METHODINFO OFF}
21Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
22Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张var
23Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    t: TTestClass;
24Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
25Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张{ TTestClass }
26Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
27Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张function TTestClass.Test1(a: string; b: Single): Single;
28Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张begin
29Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
30Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张end;
31Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
32Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张procedure TTestClass.Test2(a: string);
33Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张begin
34Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
35Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张end;
36Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
37Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张function TTestClass.Test3: Integer;
38Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张begin
39Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
40Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张end;
41Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
42Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张procedure TestIt;
43Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张var
44Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    miArr: TMethodInfoArray;
45Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    mi: PMethodInfoHeader;
46Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    t: TTestClass;
47Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    retInfo: PReturnInfo;
48Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    piArr: TParamInfoArray;
49Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    pi: PParamInfo;
50Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    i: Integer;
51Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张begin
52Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    t := TTestClass.Create;
53Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
54Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    miArr := GetMethods(TTestClass);
55Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    for mi in miArr do
56Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    begin
57Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        Writeln('Method: ' + mi.Name);
58Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
59Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        retInfo := GetReturnInfo(t, mi.Name);
60Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        if retInfo.ReturnType <> nil then
61Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        begin
62Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张            Writeln('ReturnType: ' + retInfo.ReturnType^.Name);
63Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        end;
64Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
65Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        piArr := GetParams(t, mi.Name);
66Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        if piArr <> nil then
67Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        begin
68Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张            for pi in piArr do
69Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张                Writeln('Param Name: ' + pi.Name + ' Param Type: ' + pi.ParamType^.Name);
70Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张        end;
71Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    end;
72Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
73Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    t.Free;
74Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张end;
75Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张
76Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张begin
77Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    TestIt;
78Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张    Readln;
79Delphi2009初体验 语言篇 反射单元ObjAuto的加强第7张end.
复制代码

 

 代码运行结果:

Delphi2009初体验 语言篇 反射单元ObjAuto的加强第87张 

免责声明:文章转载自《Delphi2009初体验 语言篇 反射单元ObjAuto的加强》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇docker “no space left on device”问题定位解决CAN数据格式-ASC下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于malloc和sizeof的用法

问题1: 1.L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));2.newbase = (ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType)); 其中L是已经定义的线性表,LIST_INIT_SIZ...

python爬虫之短信报警

1 importsmtplib 2 importemail.mime.multipart 3 importemail.mime.text 4 5 def send_email(content=''): 6 """ 7 发送邮件 8 :param SMTP_host: smtp.163.com 9 :param from_addr:...

Delphi dbgrideh颜色设置

dbgrideh颜色设置 (2010-06-30 10:30:59)转载 标签:杂谈 (1)分行不同颜色设置;在DBGridEh1DrawColumnCell中写;     if ADOQuery1.RecNo mod 2=0 then    begin        DBGridEh1.Canvas.Font.Color := clRed...

微信app支付,完整流程,完整代码 (转)

微信app支付流程 需要的配置参数 private function wechat($body,$indent_id,$cou,$user_id,$total_fee,$ip,$domain,$nonce_str){ //微信配置信息和初始逻辑 $appid= WxPayConfig::APPID; //appid (微信开放平台的应用appid) $bo...

四、Delphi 2009 泛型容器单元(Generics.Collections)[3]: TStack&amp;lt;T&amp;gt;

TQueue 和 TStack, 一个是队列列表, 一个是堆栈列表; 一个是先进先出, 一个是先进后出.TStack 主要有三个方法、一个属性:Push(压栈)、Pop(出栈)、Peek(查看下一个要出栈的元素);Count(元素总数). pasunit Unit1;interfaceuses  Windows, Messages, SysUtils,...

CAS原子操作实现无锁及性能分析

  CAS原子操作实现无锁及性能分析 Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn.net/chen19870707 Date:Nov 13th, 2014 近期在研究nginx的自旋锁的时候,又见到了GCC CAS原子操作,于是决定动手分析下CAS实现的无锁究竟性能...