16.4.4 封装邮箱处理器
为了封装处理器邮箱,需要把表示处理器的全局值,改成在通常的对象类型内的本地字段。还要添加发送消息到私有邮箱的方法,这也有益于封装:我们不必要公开邮箱可以响应的所有消息,如果有一些仅供内部使用的话。
此更改不需要对消息处理代码做任何修改。在清单 16.17 中,可以看到具体对象类型的声明,处理代码本身则省略了,因为它没有改变。
Listing 16.17 Encapsulating mailbox processor into a type (F#)
type DrawingState() =
let mbox = MailboxProcessor.Start(fun mbox –>
let rec loop(clr, rects) = async {
let! msg = mbox.Receive()
// Message processing code as before
}
loop(Color.Black, []) )
member x.SetColor(clr) =
mbox.Post(SetColor(clr))
member x.AddRectangle(rc) =
mbox.Post(AddRectangle(rc))
member x.AsyncGetRectangles() =
mbox.PostAndAsyncReply(GetRectangles))
member x.AsyncGetColor() =
mbox.PostAndAsyncReply(GetColor))
member x.GetRectangles() =
mbox.PostAndReply(GetRectangles)
member x.GetColor() =
mbox.PostAndReply(GetColor)
let state = new DrawingState()
为了在类声明中创建本地邮箱处理器,我们使用了一个本地 let 绑定,这成为类构造函数的一部分,这意味着,邮箱将在创建实例时启动。使用本地 let 绑定声明的值,都变成了字段,这样,就可以从类中的任何地方访问它们。
类型的成员通常是样板代码。这些成员使用 Post 方法发送消息,更新邮箱处理器的状态,并不等待任何返回的值。第二组成员异步读取状态,使用 PostAndAsyncReply 方法。注意,我们在命名这些成员时,使用了 Async 前缀,在整个 F# 库中,表示只能从异步工作流访问的成员,这是标准的表示法。最后的两个方法简单地阻塞了方法,它们使用了同步的 PostAndReply 方法。
把邮箱处理器封装到类中,还需要修改访问它的其余代码。不需要明确发送一条消息,只需调用其中的一个方法。例如,在异步工作流中,获取和设置所选颜色的调用,就变成了这样:
let! clr = state.AsyncGetColor()
state.SetColor(clr)
现在,我们已经在类中封装了邮箱处理器,就能把它编译成 F# 库,作为一个可重用的组件发布。不幸的是,很难从 C# 中,使用这些返回异步工作流对象(Async<'T>)的方法,所以,如果希望能够从 C# 中使用这个组件,还应提供一些方法,这需要一个委托作为回调,并异步操作完成时运行它。
下一节中,我们会添加更多的功能到我们的应用程序中,展示事件处理的另一个方面,在异步工作流中使用 AwaitObservable 基元。