Lua中实现类似C#的事件机制

Lua的语法非常灵活, 使用他的metatable及metamethod可以模拟出很多语言的特性.

C#中我们这样使用事件:

xxx.Click += new System.EventHandler(xxx_Click);

private void xxx_Click(object sender, EventArgs e)

{

/**/

}

在Lua中要达到同样的效果, 并且支持事件多播机制, 其关键在于重写metamethod __call, 从而使得不光function才能被调用, table也能够被调用.

主要思想就是, 通过一个table来保存注册事件的若干响应函数, 然后拿table当function一样来调用, 重写__call后, 实现调用table时遍历执行table中的注册方法.

需要在lua5.0 或 lua.net上执行, lua 5.1略有改动.

1 --test.lua

   2 do

   3

   4 –事件原型对象, 所有事件由此原型生成

   5 Event = {}

   6

   7 function Event:New()

   8         local event = {}

   9         setmetatable(event, self)

  10         –覆盖__index逻辑

  11         self.__index = self

  12         –覆盖__call逻辑

  13         self.__call = self.Call

  14         return event

  15 end

  16

  17 –事件注册, 通过此方法将响应方法注册到事件上.

  18 –@source:响应方法的所属对象

  19 –@func:响应方法

  20 function Event:Add(source, func)

  21         table.insert(self, {source, func})     

  22 end

  23

  24 –内部方法, 重写了默认__call逻辑, 当event被触发调用时, 循环执行event中注册的响应方法

  25 –@table:对象产生调用时将本身传入

  26 –@…:调用参数

  27 function Event.Call(table, …)

  28         for _, item in ipairs(table) do

  29                 –item[1]就是source, item[2]就是func响应方法

  30                 –lua 5.1中无需使用unpack(arg), 直接使用…即可

  31                 item[2](item[1], unpack(arg))

  32         end

  33 end

  34

  35 ——————以下为测试用例———————–

  36

  37 –创建一个window对象, 注册按钮的点击事件

  38 Window = {

  39         Name = “Simonw’s Window”,      

  40 }

  41

  42 function Window:Init()

  43         –注册事件, self即Window, 对象来源.

  44         Button.ClickEvent:Add(self, self.Button_OnClick)       

  45 end

  46

  47 –响应事件方法, sender即是传来的Button对象

  48 function Window:Button_OnClick(sender) 

  49         print(sender.Name.." Click On “..self.Name)

  50 end

  51

  52 –创建一个button对象, 拥有ClickEvent这样的事件

  53 Button = {

  54         Name = “A Button”,

  55         –创建事件

  56         ClickEvent = Event:New(),

  57 }

  58

  59 –执行点击按钮的动作

  60 function Button:Click()

  61         print(‘Click begin’)

  62         –触发事件, self即sender参数

  63         self.ClickEvent(self)

  64         print(‘Click end’)

  65 end

  66

  67 –从这里执行

  68 Window:Init()

  69 Button:Click()

  70 –[[

  71 执行结果:

  72 > dofile ’test.lua’

  73 Click begin

  74 A Button Click On Simonw’s Window

  75 Click end

  76 ]]

  77

  78 end