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略有改動.
--test.lua
do
--事件原型對象, 所有事件由此原型產生
Event = {}
function Event:New()
local event = {}
setmetatable(event, self)
--覆蓋__index邏輯
self.__index = self
--覆蓋__call邏輯
self.__call = self.Call
return event
end
--事件註冊, 通過此方法將回應程式法註冊到事件上.
--@source:回應程式法的所屬對象
--@func:回應程式法
function Event:Add(source, func)
table.insert(self, {source, func})
end
--內部方法, 重寫了預設__call邏輯, 當event被觸發調用時, 迴圈執行event中註冊的回應程式法
--@table:對象產生調用時將本身傳入
--@:調用參數
function Event.Call(table, )
for _, item in ipairs(table) do
--item[1]就是source, item[2]就是func回應程式法
--lua 5.1中無需使用unpack(arg), 直接使用即可
item[2](item[1], unpack(arg))
end
end
------------------以下為測試案例-----------------------
--建立一個window對象, 註冊按鈕的點擊事件
Window = {
Name = "Simonw's Window",
}
function Window:Init()
--註冊事件, self即Window, 對象來源.
Button.ClickEvent:Add(self, self.Button_OnClick)
end
--響應事件方法, sender即是傳來的Button對象
function Window:Button_OnClick(sender)
print(sender.Name.." Click On "..self.Name)
end
--建立一個button對象, 擁有ClickEvent這樣的事件
Button = {
Name = "A Button",
--建立事件
ClickEvent = Event:New(),
}
--執行點擊按鈕的動作
function Button:Click()
print('Click begin')
--觸發事件, self即sender參數
self.ClickEvent(self)
print('Click end')
end
--從這裡執行
Window:Init()
Button:Click()
--[[
執行結果:
> dofile 'test.lua'
Click begin
A Button Click On Simonw's Window
Click end
]]
end