Skip to main content

[Lua] 簡易模擬 C# Delegate Type 行為

因為工作需求,準備開始學習 C#,這兩天研究一些公開的 Unity 原始碼發現 C# 有個 Delegate Type 很有意思,它可以利用 + 與 - 將需要搭配執行的 function 指派到委任者然後一起執行。如:

//C#, d1 and d2 are functions
allMethodsDelegate += d1;
allMethodsDelegate += d2;
allMethodsDelegate(); // d1(), d2()
allMethodsDelegate -= d2;
allMethodsDelegate(); // d1()

參考資料:使用委派 (C# 程式設計手冊)

Edit: 03, 08, 2015, 感謝 fb@shanyungyang 的提醒可以使用 Lua 的 metamethod 實作,為了達到屬性能往下傳,本來是打算採用 * / 但是又怕太混亂,最後就是由另外一個 meta2 來達到效果囉! ^^

FuncDelegate2.lua
-------------------------------------
-- function delegate part 2
-- Author: Erin Lin
-- www.erinylin.com
-- 簡易模擬 C# delegate
-- licensed under the MIT license.
-- 2015, 03
-- 參考:https://gist.github.com/anonymous/3f0e5b046bfdcfa9864a
-------------------------------------
local next = next
local unpack = unpack
local t = {
remove = table.remove,
insert = table.insert,
indexOf = function(list, value)
local n = 0
for k, v in next, list do
if v==value then return k end
end
return -1
end
}
local meta = {
__add = function(x, y)
if type(y) == "function" then
t.insert(x, y)
return x
elseif type(y) == "table" then
for i=1,#y do
if type(y[i]) == "function" then t.insert( x, y[i] ) end
end
return x
end
end,
__sub = function(x, y)
if type(y) == "function" then
local index = t.indexOf( x, y )
if index > -1 then t.remove( x, index ) end
return x
elseif type(y) == "table" then
for i=#y,1,-1 do
local index = t.indexOf( x, y[i] )
if index > -1 then t.remove( x, index ) end
end
return x
end
end,
__call = function(d, ...)
for i,v in next, d do
v(...)
end
end
}
local meta2 = {
__add = meta.__add,
__sub = meta.__sub,
__call = function( d, ... )
local result = arg or {}
for i,v in next, d do
result = { v( unpack(result) ) }
end
return unpack(result)
end
}
local function newDelegate( bool )
local d = {}
--bool==false, use same arguments for every function
--bool==true, will pass retrun result to next function
setmetatable(d, bool and meta2 or meta)
return d
end
-------------------------------------
--
-------------------------------------
local function step1( n )
print("Step1", n, n + 10 )
return n + 10
end
local function step2( n )
print("Step2", n, n + 20 )
return n + 20
end
local function step3( n )
print("Step3", n, n + 30 )
return n + 30
end
local d = newDelegate()
local d2 = newDelegate( true )
d = d + { step3, step2 }
d2 = d2 + { step1, step2, step3 }
print("d(0):")
d( 0 )
d = d - step2
print("d = d - step2 ; d(0)")
d( 0 )
print("d2( 100 ):")
d2( 100 )
print("-----------------")
d2 = d2 - step2
print("d2 = d2 - step2 ; d2(100)")
d2( 100 )


這樣拿來組合執行函式實在很方便,所以為了可以在 CoronaSDK 內使用,利用 Lua table 簡單實作類似行為。(當然是沒有辦法寫 + and -,如果不像的話,就是我只實作自己想要的功能啊...XD)
FuncDelegate.lua
-------------------------------------
-- function delegate
-- Author: Erin Lin
-- www.erinylin.com
-- 簡易模擬 C# delegate
-- licensed under the MIT license.
-- 2015, 02
-------------------------------------
--[[
Usage1:
local function step1()
print("Step1")
end
local function step2()
print("Step2")
end
local function step3()
print("Step3")
end
local delegate = require("FuncDelegate").new()
delegate:insert( step1 ):insert( step2 ):insert( step3 )
-- Output:
-- Step1
-- Step2
-- Step3
Usage 2:
local function step1( n )
print("Step1", n )
return n + 10
end
local function step2( n )
print("Step2", n )
return n + 20
end
local function step3( n )
print("Step3", n )
return n + 30
end
local delegate = require("FuncDelegate").new()
delegate:insert( step1 ):insert( step2 ):insert( step3 )
print("delegate:run( 0 ) =", delegate:run( 0 ) )
print("delegate:runWithSameArgs( 0 ) =", delegate:runWithSameArgs( 0 ) )
-- Output:
-- Step1 0
-- Step2 10
-- Step3 30
-- delegate:run( 0 ) = 60
-- Step1 0
-- Step2 0
-- Step3 0
-- delegate:runWithSameArgs( 0 ) = 10
--]]
local M = {
_pool = {}
}
local M_mt = {__index=M}
local unpack = unpack
local t = {
remove = table.remove,
insert = table.insert,
indexOf = function(list, value)
local n = 0
for k, v in next, list do
if v==value then return k end
end
return -1
end
}
function M:insert( element )
assert( type(element)=="function", "argument must be a type of function")
local index = t.indexOf( self._pool, element )
if index<0 then
t.insert( self._pool, element )
end
return self
end
function M:remove( element )
local index = t.indexOf( self._pool, element )
if index > -1 then
t.remove( self._pool, index )
end
return self
end
-- will pass retrun result to next function
-- 會將前一個函式的回傳值傳到下一個函式,但是如果都是無回傳,用 run or runWithSame 都無所謂
function M:run( ... )
local result = arg or {}
for i=1,#self._pool do
result = { self._pool[i](unpack( result )) }
end
return unpack( result )
end
-- use same arguments for every function
-- 全部函式都用一開始的傳入值
function M:runWithSameArgs( ... )
local params = arg or {}
local result = {}
for i=1, #self._pool do
result = { self._pool[i](unpack( params )) }
end
-- only return last function's result
return unpack(result)
end
function M:clear()
for i=#self._pool,1,-1 do
self._pool[i] = nil
end
end
function M.new()
local obj = {}
setmetatable( obj, M_mt )
obj._pool = {}
return obj
end
return M

Comments

Popular posts from this blog

[Flex] PureMVC standard with Spring extensions

由於上次稍微玩了一下 Robotlegs 依賴注入(DI) 主導的 MVC 框架,而著名也使用依賴注入的 Java / Java EE 的 Spring framework 出了 for ActionScript 的版本,剛好在最近 Spring ActionScript 1.0 正式 release 了(想了解 Spring 是啥咪東東的話請自行找 google 大神),這個版本除了基本框架外,也包含了 Cairngorm 與 PureMVC 的外掛...想當然耳,就拿來測試一下用在 PureMVC 內的感覺囉!! 參考了 官方範例 中 PureMVC 唯二的範例原始檔,以下使用的是「設定檔依賴注入 facade 透過 addConfigSource() 的方式來 init 」:(其實除了 embed 外,都是外部載入) Online Demo with source code 工作環境:FlashBuilder, Flex SDK4 請下載 PureMVC Standard 版本 再下載 Spring ActionScript 最新版本後,除了 spring-actionscript-cairngorm 不需要外,都放到 /src 下(記得只需要 org 開始...),也別忘了lib 內的 swc 檔 copy 到 /libs 下 Spring 的 injection 並不像 Robotlegs 直接來個 [Inject] metadata 的自動化那樣方便,但是其冷血度(檔案的鬆偶程度)更勝後者!如果你要使用設定檔(applicationContext.xml) 來做注入的話,準備工作就挺多的...XD 依照 applicationContext.xml 內設定的方式分別寫入 constructor 或者是 setter 依賴注入(本範例統一使用 setter injection) 為了跟大家都沒關係所以都使用 interface 來處理,所以你會在範例中發現大家都有介面...(並沒有真的研究過 Spring,也許還有其他作法) 準備 compiler 時候要用的 classe。由於在 setter, getter 的寫法上都使用 interface,所以真正用到的 class 需要預先在輸出階段就打包到程式內。 基本上 PureMVC 類 class...

[AIR] JoSi FXGtoLayout

JoSi FXGtoLayout v0.3.0, Adobe AIR 3 runtime 這個又是一個 "就是" 系列懶人小工具,主要是針對 Adobe fxg 格式做分析轉成 Mobile 開發用的視圖程式碼,加速畫面配置使用。 為什麼會製作這個工具,原因主要是本人在使用的 Corona SDK 與 Titanium SDK 都沒有好用的視覺化編輯工具。一般設計師產出 layout 檔會使用 PhotoShop 來製作,在不多花錢的原則下,畫面對齊的基準就是其輸出的 fxg 資料做對應,如果要一筆一筆將資料鍵入,做久也是會膩的,所以花了點時間將這個工具做出來...