Delphi 7事件的多处理机制
Allen Tao
2007-08-19

  首先解释一下这个题目。在我使用Delphi 7的过程中发现,一个对象的事件只能被一个过程处理。如果多次给这个对象的事件赋给处理事件的过程,最后真正处理事件的将是最后赋值的那个过程。例如,有类TMyClass中定义了一个事件OnSomeFired,在类TClientClass中该类被实例化,它的事件被处理。如下所示:
constructor TClientClass.Create;
var
  myObj: TMyClass;
begin
  //…
  myObj:= TMyClass.Create;
  myObj.OnSomeFired:= SomeFired1;
  myObj.OnSomeFired:= SomeFired2;
  这里的SomeFired1与SomeFired2都是TClientClass中定义的处理过程。其最终的结果是当OnSomeFired事件发生时,只有SomeFired2被调用。
  但在编程的实际中,往往需要一个事件被多个方法所处理。为此,我参考了一些对这个问题的解决办法,总结得出了一个自己的方法,称为事件的多处理机制。

原理
  Delphi 7中的事件本质上是一种过程指针。但事件类型在定义时要比一般过程指针在最后多一个“of object”,如常用的TNotifyEvent的定义是:
TNotifyEvent = procedure(Sender: TObject) of object;
  因此,给一个事件属性赋值,也就是给一个类的过程指针类型的成员变量赋值,当然是最后一次的赋值才有效。要想多次赋值有效就必须有一个数据结构把每次赋值赋给的过程指针变量都记录下来,最合适的数据结构当然是列表TList。但如果在每一个有事件的类中都加一个记录事件赋值的列表对象,自然是不方便的,而且使用这个列表的代码在不同类中也差不多,应该抽取出来形成一个类。这个类就是事件多处理机制的核心内容。

做法
  要记录事件处理过程,就需要将过程指针变量放入列表对象中。但列表对象只能添加指针类型对象(也就是引用类型),而过程指针变量是指类型变量,不能直接添加。这就需要有一个类对过程指针变量进行包装,转化为指针类型对象。于是,先要定义包装类TCallMethod,如下所示:
TCallMethod = class
  private
    _callback: TMethod;
  public
    property Callback: TMethod read _callback write _callback;
  end;
  这里的TMethod是Delphi预定义的记录类型,任何过程指针变量都可以强制转化为这种类型。之后,再定义记录处理过程的类,如下所示:
  TEventObject = class
  private
    callList: TList;
    function GetItem(i: Integer): TMethod;
    function GetCount: Integer;
  public
    constructor Create;

procedure Add(p: TMethod);
    procedure Remove(p: TMethod);

property Count: Integer read GetCount;
    property Items[i: Integer]: TMethod read GetItem;  default;
  end;
  下面是实现部分:
constructor TEventObject.Create;
begin
  callList:= TList.Create;
end;

procedure TEventObject.Add(p: TMethod);
var
  a: TCallMethod;
begin
  a:= TCallMethod.Create;
  a.Callback:= p;
  callList.Add(a);
end;

procedure TEventObject.Remove(p: TMethod);
var
  i: Integer;
begin
  for i:= 0 to GetCount - 1 do
    if (TCallMethod(callList[i]).Callback.Code = p.Code) and
      (TCallMethod(callList[i]).Callback.Data = p.Data) then
      callList.Delete(i);
end;

function TEventObject.GetCount: Integer;
begin
  Result:= callList.Count;
end;

function TEventObject.GetItem(i: Integer): TMethod;
var
  a: TCallMethod;
begin
  a:= TCallMethod(callList[i]);
  Result:= a.Callback;
end;
  从上面的代码可以看到,TEventObject的目的是包装TList对象,屏蔽了TList类的大多数方法,只对外暴露了2个过程、1个属性与1个索引,分别用于添加、删除处理事件过程、获得过程个数及通过序号检索记录的过程变量。

使用
  使用时,在需要使用事件的类中将事件的类型由过程指针改为TEventObject,即可使该事件拥有多处理的能力。如下面代码所示:
  TMyClass=class
  private
    someFired: TEventObject;
  public
    constructor Create;
    procedure DoSth;
    property OnSomeFired: TEventObject read someFired write someFired; // 多处理事件
  end;
  以下是实现代码:
constructor TMyClass.Create;
begin
  Self.someFired:= TEventObject.Create;
end;

procedure TMyClass.DoSth;
var
  i: Integer;
  method: TNotifyEvent;
begin
  if someFired.Count > 0 then
    for i:= 0  to someFired.Count - 1 do
    begin
      method:= TNotifyEvent(someFired[i]); // 在该类中事件的真正类型是TNotifyEvent,因此在触发事件时先要转成这种类型的过程指针后再进行调用
      method(Self);
    end;
end;
  定义了一个包含多处理事件的类后,再看看这种类如果在其客户类中被调用。如以下代码:
{类TClientClass中}
var
  myObj: TMyClass;
  //…
  myObj:= TMyClass.Create;
  myObj.OnSomeFired.Add(SomeFired1);
  myObj.OnSomeFired.Add(SomeFired2);
  当客户类代码中调用TMyClass的DoSth方法时,事件将被触发,而2个处理过程SomeFired1与SomeFired2则会依次调用,实现多处理的目的。

再发展一步
  上面的TEventObject类可以实现事件多处理的目的,但它对加入其中的过程指针类型没有检查,这是一个隐患。因此可以针对每一种事件要求的过程指针类型从TEventObject继承一个类,实现类型检查。如要求事件的类型是TNotifyEvent,就可以继承一个TNotifyEventObject类,如下面代码:
  TNotifyEventObject = class(TEventObject)
  public
    procedure Add(p: TNotifyEvent); overload;
    procedure Remove(p: TNotifyEvent); overload;
  end;
  以下是实现部分:
procedure TNotifyEventObject.Add(p: TNotifyEvent);
begin
  inherited Add(TMethod(p));
end;

procedure TNotifyEventObject.Remove(p: TNotifyEvent);
begin
  inherited Remove(TMethod(p));
end;
  在这个类中重载了添加与移除方法,可以有效地对过程指针类型进行检查。

  以上是我对Delphi 7中事件多处理问题的一个解决办法。如有兴趣,请光临我的博客http://allentao430.spaces.live.com赐教。

参考:http://blog.csdn.net/iseekcode/article/details/5352709

Delphi 7事件的多处理机制的更多相关文章

  1. 通俗理解Android事件分发与消费机制

    深入:Android Touch事件传递机制全面解析(从WMS到View树) 通俗理解Android事件分发与消费机制 说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与Li ...

  2. delphi之事件

    delphi的事件如上图所示: 图中oncloseup代表的是日期选择下拉框关闭时触发的事件. //事件定义 procedure Ondatechange(Sender: TObject); //事件 ...

  3. Javascript事件模型系列(二)事件的捕获-冒泡机制及事件委托机制

    一.事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监 ...

  4. android中的事件传递和处理机制

    一直以来,都被android中的事件传递和处理机制深深的困扰!今天特意来好好的探讨一下.现在的感觉是,只要你理解到位,其实事件的 传递和处理机制并没有想象中的那么难.总之,不要自己打击自己,要相信自己 ...

  5. Delphi动态事件深入分析(对象方法在调用的时候会传递一个隐含的Self指针,而该指针的值在EAX中。即左边第一个参数)

    Delphi动态事件深入分析 2009-2-7 作者:不得闲核心提示:本实验证明了在类中方法的调用时候,所有的方法都隐含了一个Self参数,并且该参数作为对象方法的第一个参数传递... 首先做一个空窗 ...

  6. WM_SYSCOMMAND包括很多功能,比如:拖动左边框、拖动标题栏、滚动条滚动、点击最小化、双击标题栏——Delphi 通过事件代替了大部分常用的消息,所以Delphi 简单、易用、高效

    procedure TForm1.WMSysCommand(var Message: TWMSysCommand); var str: string; begin case Message.CmdTy ...

  7. 左右JAVA示例代码事件分发和监督机制来实现-绝对原创有用

    文章标题:左右JAVA示例代码事件分发和监督机制来实现 文章地址: http://blog.csdn.net/5iasp/article/details/37054171 作者: javaboy201 ...

  8. 深入理解Spring的容器内事件发布监听机制

    目录 1. 什么是事件监听机制 2. JDK中对事件监听机制的支持 2.1 基于JDK实现对任务执行结果的监听 3.Spring容器对事件监听机制的支持 3.1 基于Spring实现对任务执行结果的监 ...

  9. 【spring源码学习】spring的事件发布监听机制源码解析

    [一]相关源代码类 (1)spring的事件发布监听机制的核心管理类:org.springframework.context.event.SimpleApplicationEventMulticast ...

随机推荐

  1. 跨域名设置cookie或获取cookie

    可以使用jquery里面的ajax中的jsonp的方式来访问就可以了.代码如下: $.ajax({ url: 'your url', data: {'xx' : 'xx', 'xx2' : 'xx2' ...

  2. apache+tomcat整合

    一 .Apache与Tomcat的比较 apache支持静态页面,tomcat支持动态的,比如servlet等. 一般使用apache+tomcat的话,apache只是作为一个转发,对jsp的处理是 ...

  3. openerp - asterisk connector(转载)

    原文:http://www.akretion.com/open-source-contributions/openerp-asterisk-voip-connector OpenERP - Aster ...

  4. java_Thread生产者与消费者 Demo

    package com.bjsxt.Thread.Demo; public class ProducerConsumer { /** * 生产者与消费者 * @param args */ public ...

  5. 【python】为什么用python

    python胶水语言.脚本语言之王,C/C++可以写python的module,标准库里就有用C/C++写的东西,这个跟java的JNI类似. 性能好,易调试: since 91年

  6. 使用PHP计算上一个月的今天

    一日,遇到一个问题,求上一个月的今天. 最开始我们使用 strtotime(“-1 month”) 函数求值,发现有一个问题,月长度不一样的月份的计算结果有误. 比如:2011-03-31,得到的结果 ...

  7. POJ 3801 有上下界最小流

    1: /** 2: POJ 3801 有上下界的最小流 3: 4: 1.对supersrc到supersink 求一次最大流,记为f1.(在有源汇的情况下,先使整个网络趋向必须边尽量满足的情况) 5: ...

  8. Spring4新特性简述

    Spring是一个java世界中极其流行 的开源框架.Spring的初衷是降低企业级开发的复杂性,并试图通过POJO对象实现之前EJB这类重型框架才能实现的功能.Spring不仅仅对服务 端开发有用, ...

  9. iOS中 @synthesize 和 @dynamic

    今天写点过时的东西,我记得  这个是xcode 4 那个年代的事情了,没想到有时候大家还会被问到.可能目的就是看看你是从是么时候才开始接触iOS的. 在声明property属性后,有2种实现选择 @s ...

  10. 2437: [Noi2011]兔兔与蛋蛋 - BZOJ

    Description Input 输入的第一行包含两个正整数 n.m.接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母" ...