zoukankan      html  css  js  c++  java
  • 字符串 映射相应的 函数 字符串驱动技术—— MethodAddress , MethodName , ObjectInvoke

     

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

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

    标签: delphiintegerfunctionobjectsoapclass
     分类:
    首先看一段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结构的开发。
     
     
    delphi lazarus opengl 网页操作自动化, 图像分析破解,游戏开发
  • 相关阅读:
    Verilog非阻塞赋值的仿真/综合问题 (Nonblocking Assignments in Verilog Synthesis)上
    异步FIFO结构及FPGA设计 跨时钟域设计
    FPGA管脚分配需要考虑的因素
    An Introduction to Delta Sigma Converters (DeltaSigma转换器 上篇)
    An Introduction to Delta Sigma Converters (DeltaSigma转换器 下篇)
    中国通信简史 (下)
    谈谈德国大学的电子专业
    中国通信简史 (上)
    Verilog学习笔记
    Verilog非阻塞赋值的仿真/综合问题(Nonblocking Assignments in Verilog Synthesis) 下
  • 原文地址:https://www.cnblogs.com/delphi-xe5/p/5123026.html
Copyright © 2011-2022 走看看