爲了理解發生了什麼,我查了一下Event.map,Event.merge和Choice的源代碼。
type Choice<'T1,'T2> =
| Choice1Of2 of 'T1
| Choice2Of2 of 'T2
[<CompiledName("Map")>]
let map f (w: IEvent<'Delegate,'T>) =
let ev = new Event<_>()
w.Add(fun x -> ev.Trigger(f x));
ev.Publish
[<CompiledName("Merge")>]
let merge (w1: IEvent<'Del1,'T>) (w2: IEvent<'Del2,'T>) =
let ev = new Event<_>()
w1.Add(fun x -> ev.Trigger(x));
w2.Add(fun x -> ev.Trigger(x));
ev.Publish
這意味着我們的解決方案正在創建3個新事件。
async {
let merged = Event.merge
(f.KeyDown |> Event.map Choice1Of2)
(f.MouseMove |> Event.map Choice2Of2)
let! move = Async.AwaitEvent merged
}
通過製作此庫代碼的緊密耦合版本,我們可以將其減少爲一個事件。
type EventChoice<'T1, 'T2> =
| EventChoice1Of2 of 'T1
| EventChoice2Of2 of 'T2
with
static member CreateChoice (w1: IEvent<_,'T1>) (w2: IEvent<_,'T2>) =
let ev = new Event<_>()
w1.Add(fun x -> ev.Trigger(EventChoice1Of2 x))
w2.Add(fun x -> ev.Trigger(EventChoice2Of2 x))
ev.Publish
這裏是我們的新代碼。
async {
let merged = EventChoice.CreateChoice form.MouseMove form.KeyDown
let! move = Async.AwaitEvent merged
}
小心!當你將使用'Event.xyz'組合子創建的事件與'AwaitEvent'和'let!'一起使用時,你可以創建一個內存泄漏(當你在循環中等待時)。如果你想把組合器和異步工作流結合起來,你應該總是**使用'Observable'模塊來代替'Event'。查看我的答案以獲取更多詳細信息... – 2010-09-13 21:56:15