http://blog.csdn.net/qustdong/article/details/7267258

字符串驱动技术—— MethodAddress , MethodName , ObjectInvoke

2012-02-17 11:46 1139人阅读 评论(0) 收藏 举报
 分类:
Delphi(24) 
首先看一段Delphi帮助中的介绍(After Delphi 6 ):

Returns the address of a published method.

class function MethodAddress(const Name: ShortString): Pointer;

Description

MethodAddress is used internally by the streaming system. When an event property is read from a stream, MethodAddress converts a method name, specified by Name, to a pointer containing the method address. There should be no need to call MethodAddress directly.

If Name does not specify a published method for the object, MethodAddress returns nil.

翻译如下:

返回一个published部分的方法的地址

描述:

MethodAddress方法主要是被流(streaming)系统内部所使用的。当一个事件属性被一个流(streaming)所读取到的时候,MethodAddress将参数Name获得的方法的名称转换成为一个指向方法地址的指针。在使用的过程中不要直接使用MethodAddress 该方法。如果参数Name所指示的不是对象published部分的方法的话,MethodAddress返回nil。

示例代码:

unit Unit2;

interface

type
  TMyFun = function (AIn: Integer): String of object;
{$M+}
  TMyObj = class(TObject)
  published
    function MyFun(AIn: Integer): String;
  end;
{$M-}
procedure GetMyFun(var AFunResult, AFunName: String);

implementation

uses SysUtils;

procedure GetMyFun(var AFunResult, AFunName: String);
var
  FMyObj: TMyObj;
  FMyFun: TMyFun;
begin
  FMyObj:= TMyObj.Create;
  try
    TMethod(FMyFun).Code:= FMyObj.MethodAddress('MyFun');
    TMethod(FMyFun).Data:= FMyObj;
    //执行FMyFun获得返回值
    AFunResult:= FMyFun(123);
    //传入FMyFun函数地址,获得函数声明的名称
    AFunName:= FMyObj.MethodName(TMethod(FMyFun).Code);
  finally
    FMyObj.Free;
  end;
end;

{ TMyObj }

function TMyObj.MyFun(AIn: Integer): String;
begin
  Result:= IntToStr(AIn);
end;

end.

在上面这段示例代码中,有以下几个关键地方:

对象声明的时候需要有编译开关$M。
编译开关$M控制的是在编译对象的时候是否要加入运行时信息RTTI。当一个类在声明的时候加入了编译开关$M+,或者该类的父类已经加入编译开关$M+ 的话,则编译器会将在published部分声明的对象变量field,方法method,属性property加入到运行时信息中。如果类声明的时候使用的编译开关是$M-,或者该类的父类没有使用$M+,则不能够访问该类published部分的RTTI。特别注意的是在对于前向声明forward declared的时候,一定要在第一个出现类声明的地方使用$M编译开关。
例如:
{$M+}
TMyObj = class; //forward declared
{$M-}
TMyObj = class(TObject)
published
  function MyFun(AIn: Integer): String;
end;

函数一定要声明在published部分。
只有声明在published部分的类成员,包括方法method,对象成员field,属性property才能够编译进RTTI。
根据名称调用方法的时候一定要使用结构体TMethod赋值。
结构体TMethod的声明如下:
TMethod = record
  Code, Data: Pointer;
end;
第一个参数Code接受的是方法的地址,即方法处在代码段中的地址,第二个参数Data接受的是调用对象的实例地址。函数的调用过程是,首先在代码段中索引到该函数的代码,然后将代码取出在栈上展开执行。当调用的是一个对象方法的时候,由于对象方法内部会有调用该对象其他成员的代码,往往在调用对象成员的时候会有一个隐式的参数Self,该参数的值就是实例的地址,而对象方法内部中所有调用Self值得来源就是TMethod.Data。如果在通过 MethodAddress方法获得函数地址的时候未能使用TMethod结构体给TMethod.Data赋值,后果就是该函数体内部所有Self都得不到正确实例地址,所以通过Self来调用类成员的时候,就会有异常。这点上的差异就决定形如T*** = function (***): ***;和T*** = function (***): *** of object;类型上的差异,对于of object的函数声明,无论何种形式的调用,都需要或显式或隐式的传递实例地址。

获得函数声明名称的方法是MethodName。
MethodName传入的是函数的地址指针,获得该函数声明的名称。要求传入的一定是什么在该类published部分的函数,且函数地址获得的方式形如TMethod(***).Code。当然该类在声明的时候一定要加上编译开关$M+。
应用实例:

VCL持久化。
在整个VCL框架中,持久化机制占有举足轻重的地位。因为在Delphi设计之初,就赋予了这门语言一个重要特性——PME(property method event),且有IDE支持,所以持久化机制必不可少。在VCL框架设计中,TPersistent是被用来作为可持久化对象的基类的,在 TObject中提供了对对象VMT、RTTI的访问方法,在TPersistent类定义中增加了编译开关$M+用于编译生成RTTI,且增加了一些持久化机制中需要使用到的方法,声明成虚方法让子类扩充。所以,所有从TPersistent继承的类中都自动拥有RTTI信息,而无需使用编译开关$M+。
MethodAddress、MethodName在中最重要的应用就是在持久化上。VCL的持久化机制中最重要的两个类是TReader和 TWriter,这两个类的作用分别是从持久化流中读取出数据加载到对象上,和从对象中读取出需要持久化的数据写入到流中间。在对象属性中间有一种类型为 tkMethod,即Event。对于Event来说,属性中存储的是方法的地址,默认情况下,这些Event的方法都是声明在持久化根对象root instance的published部分的方法,所谓根对象就是作为参数传入持久化readcomponent、writecomponent的对象,对于窗体设计器中的对象来说就是TForm。而方法MethodAddress、MethodName就是在持久化的时候,在方法地址和方法名称之间做转换,应用于写入流或从流中读出。
自定义持久化。
从以上关于VCL持久化的简介中可以看出有一个关键点,就是,对于Event持久化时是根对象的VMT中根据名称寻找方法地址或根据方法地址寻找名称,但是对于一些自定义的对象中,往往Event的函数是指向另外一个函数集合对象,而这个函数集合对象又不一定会依赖于根对象,即它有可能是一个公共对象,封装了一组公共方法,所以对于Event指向的不是根对象的方法的时候,标准的VCL持久化类就不能够正常工作了,需要我们自己重新定义持久化类。在自定义的持久化类中,当面对Event的时候,持久化字符串中需要包含函数集合对象的引用路径和函数名称,在将函数地址转换成持久化字符串的时候,通过 TMethod.Data获得对象的地址,然后根据自定义的框架获得对象的应用路径,加上函数名即可获得Event完整的持久化字符串。
动态调用类方法。
在很多使用Commond pattern设计的框架中,经常会使用消息机制来解耦系统的各部分,如果是一个分布式系统的话,消息往往会被封装成格式化字符串。在消息中包含需要调用的对象和函数名称,然后使用函数名获得函数地址,声明一个函数变量,例如上例中的FMyFun: TMyFun用来接收获得函数地址。再有一个通用的调用方式,如下例所示:
{$M+}{$METHODINFO ON}
TMyObj = class(TPersistent)
published
  function MyFun(AIn1, AIn2: Integer): String;
end;
{$M-}{$METHODINFO OFF}
  ……

procedure TForm1.Button1Click(Sender: TObject);
var
  FMyObj: TMyObj;
  PInf: PMethodInfoHeader;
  FResult: String;
begin
  FMyObj:= TMyObj.Create;
  try
    PInf:= GetMethodInfo(FMyObj, 'MyFun');
    FResult:= ObjectInvoke(FMyObj, PInf, [1,2], [3,2]);
    ShowMessage(FResult);
  finally
    FMyObj.Free;
  end;
end;
几点关键地方:
3.1  引用单元ObjAuto.pas
3.2  编译开关$METHODINFO仅仅作用在已经使用编译开关$TYPEINFO或$M打开编译进RTTI时的效果,该编译指令控制在编译的时候往RTTI中间加入方法method的更多细节,包括方法的参数名称、列表、类型、传参类型,当然加入该编译开关以后会使得RTTI所占空间变大。 Delphi中增加该编译开关本是为了在编译器中添加对接口RTTI支持,从而更好的支持Delphi对网络开发提供特性,例如支持SOAP等。
3.3  参数说明:function ObjectInvoke(Instance: TObject; MethodHeader: PMethodInfoHeader;
  const ParamIndexes: array of Integer;
  const Params: array of Variant): Variant;

Instance: 实例;
MethodHeader: 通过GetMethodInfo获得的函数RTTI信息;
ParamIndexes: Params中参数值对应参数列表中的位置,取值为1,2,3……如果ParamIndexes为空,则Params应该以倒序填参;
Params: 函数的参数值;
Result: 函数的返回值,如果是procedure的话,返回值为nil。
3.4  该方法可能从Delphi 7开始的版本才加入的特性。

动态调用接口方法。
为接口加入RTTI信息是Delphi 7开始的版本才加入的特性,用于对SOAP的支持,使得Delphi更好的适应于BS结构的开发。

 
 

字符串 映射相应的 函数 字符串驱动技术—— MethodAddress , MethodName , ObjectInvoke的更多相关文章

  1. 字符串驱动技术—— MethodAddress , MethodName , ObjectInvoke

    首先看一段Delphi帮助中的介绍(After Delphi 6 ): Returns the address of a published method. class function Method ...

  2. bitMap算法实现以及ckHash函数类,将字符串映射成数字,同时可以将数字映射成字符串

    ckHash函数类,将字符串映射成数字,同时可以将数字映射成字符串 说明 1.所谓的BitMap就是用一个bit位来标记某个元素所对应的value,而key即是该元素,由于BitMap使用了bit位来 ...

  3. c语言字符串操作,及常用函数

    一,字符串操作 1 . strcpy : 拷贝 char *stpcpy(char *destin, char *source); 2 . strcat :  拼接 char *strcat(char ...

  4. Python第二天 变量 运算符与表达式 input()与raw_input()区别 字符编码 python转义符 字符串格式化 format函数字符串格式化 帮助

    Python第二天  变量  运算符与表达式  input()与raw_input()区别  字符编码  python转义符  字符串格式化  format函数字符串格式化  帮助 目录 Pychar ...

  5. javascript函数一共可分为五类: ·常规函数 ·数组函数 ·日期函数 ·数学函数 ·字符串函数

    javascript函数一共可分为五类:    ·常规函数    ·数组函数    ·日期函数    ·数学函数    ·字符串函数    1.常规函数    javascript常规函数包括以下9个 ...

  6. SQL SERVER 将表中字符串转换为数字的函数 (详询请加qq:2085920154)

    在SQL SERVER 2005中,将表中字符串转换为数字的函数共2个:1. convert(int,字段名)   例如:select convert(int,'3')2. cast(字段名 as i ...

  7. SQL server 创建 修改表格 及表格基本增删改查 及 高级查询 及 (数学、字符串、日期时间)函数[转]

    SQL server 创建 修改表格 及表格基本增删改查 及 高级查询 及 (数学.字符串.日期时间)函数   --创建表格 create table aa ( UserName varchar(50 ...

  8. python 数字、字符串、列表常用函数

    一.数字的标准类型: cmp():比较两个数的大小:返回值(-1,0,1). str():数字转化成字符串. type():返回数字类型. 转换工厂函数: int(obj,base=10) long( ...

  9. ***用php的strpos() 函数判断字符串中是否包含某字符串的方法

    判断某字符串中是否包含某字符串的方法 if(strpos('www.idc-gz.com','idc-gz') !== false){ echo '包含'; }else{ echo '不包含'; } ...

随机推荐

  1. POI读取文件的最佳实践

    POI是 Apache 旗下一款读写微软家文档声名显赫的类库.应该很多人在做报表的导出,或者创建 word 文档以及读取之类的都是用过 POI.POI 也的确对于这些操作带来很大的便利性.我最近做的一 ...

  2. Vue框架前言

    Vue框架 Vue 框架: 官网 vue框架:渐进式JavaScript框架 vue一个环境:可以只控制页面中一个标签.可以控制一组标签.可以控制整个页面.可以控制整个项目 vue可以根据实际需求,选 ...

  3. Bootstrap前端框架快速入门专题

    1.Bootstrap简介 Bootstrap,出自自 Twitter,是目前最受欢迎的前端框架. Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的前端框架,它简洁灵活,使得 W ...

  4. 软件包管理(rpm、yum、dpkg)

    一. 大多数Linux应用软件包的命名也有一定的规律,它遵循:名称-版本-修正版-类型,通常Linux应用软件的安装包有三种: 1) tar包,如software-1.2.3-1.tar.gz.以源码 ...

  5. [ZJOI2006]物流运输(动态规划,最短路)

    [ZJOI2006]物流运输 题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个 ...

  6. Qt中添加自定义信号和槽带来的一些问题

    背景: 自己定义了一个类,并在类中添加了槽函数 class XImage : public QWidget { public: XImage(QWidget *p = 0); //重载绘制方法 upd ...

  7. 前端每日实战:16# 视频演示如何用纯 CSS 创作一个渐变色动画边框

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/odpRKX 可交互视频教程 此视频 ...

  8. React Autocomplete(自动完成输入)示例教程

    React Autocomplete示例教程是今天的主题.在现代Web开发中,使用React改善用户体验是很容易.自动完成的概念很简单.它是基于用户输入的建议列表.然后,用户可以按Enter键以完成短 ...

  9. 前端之CSS:CSS选择器

    前端之css样式(选择器)... 一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器, ...

  10. RouterOS基础安装配置

    安装 光盘安装 载入光盘,按"A"全选按"I"进行安装,然后提示不否用旧的配置,按"N"全新安装,警告时按"Y"确定 安 ...