技术开发 频道

C#Actor消息执行:C# Actor的尴尬

    从上述代码中可以看出,由于没有Erlang的模式匹配,我们必须使用if…else…的方式来判断消息的Tag,接下来还必须使用麻烦而危险的cast操作来获取参数。更令人尴尬的是,与Erlang相比,在C#中使用Tag Message没有获得任何好处。同样是弱类型,同样得不到静态检查。那么好处在哪里?至少我的确看不出来。

    有朋友可能会说,C#既然是一门强类型的语言,为什么要学Erlang的Tag Message?为什么不把Ping定义为Actor<PingMessage>,同时把Pong定义为Actor<PingMessage>呢?

    呃……我承认,在这里使用Tag Message的确有种“画虎不成反类犬”的味道。不过,事情也不是您想象的那么简单。因为在实际情况中,一个Actor可能与各种外部服务打交道,它会接受到各式各样的消息。例如,它先向Service Locator发送一个请求,用于查询数据服务的位置,这样它会接受到一个ServiceLocatorResponse消息。然后,它会向数据服务发送一个请求,再接受到一个DataAccessResponse消息。也就是说,很可能我们必须把每个Actor都定义为Actor<object>,然后对消息进行类型判断,转换,再加以处理。

    诚然,这种方法相对于Tag Message拥有了一定的强类型优势(如静态检查)。但是如果您选择这么做,就必须为各种消息定义不同的类型,在这方面会带来额外的开发成本。要知道,消息的数量并不等于Actor类型的数量,即使是如Ping这样简单的Actor,都会发送两种不同的消息(Ping和Finished),而且每种消息拥有各自的参数。一般来说,某个Actor会接受2-3种消息都是比较正常的状况。在面对消息类型的汪洋时,您可能就会怀念Tag Message这种做法了。到时候您可能就会发牢骚说:

    “弱类型就弱类型吧,Erlang不也用的好好的么……”

F#中的模式匹配

    提到模式匹配,熟悉F#的同学们可能会欢喜不已。模式匹配是F#中的重要特性,它将F#中静态类型系统的灵活性体现地淋漓尽致。而且——它还很能节省代码(这点在老赵以前的文章中也有所提及)。那么我们再来看一次F#在乒乓测试中的表现。

    首先还是定义PingMsg和PongMsg:

type PingMsg =
| Ping of PongMsg Actor
| Finished
and PongMsg =
| Pong of PingMsg Actor

    这里体现了F#类型系统中的Discriminated Unions。简单地说,它的作用是把一种类型定义为多种表现形式,这个特性在Haskell等编程语言中非常常见。Discriminated Unions非常适合模式匹配,现在的ping对象和pong对象便可定义如下(在这里还是使用了ActorLite,而不是F#标准库中的MailboxProcessor来实现Actor模型):

let (<<) (a:_ Actor) msg = a.Post msg

let ping =
let count = ref 5
{ new PongMsg Actor() with
override self.Receive(message) =
match message with
| Pong(pong) ->
printfn "Ping received pong"
count := !count - 1
if (!count > 0) then
pong << Ping(self)
else
pong << Finished
self.Exit() }

let pong =
{ new PingMsg Actor() with
override self.Receive(message) =
match message with
| Ping(ping) ->
printfn "Pong received ping"
ping << Pong(self)
| Finished ->
printf "Fininshed"
self.Exit() }
 

    例如在pong对象的实现中,我们使用模式匹配,减少了不必要的类型转换和赋值,让代码变得简洁易读。还有一点值得顺带一提,我们在F#中可以灵活的定义一个操作符的作用,在这里我们便把“<<”定义为“发送”操作,避免Post方法的显式调用。这种做法往往可以简化代码,从语义上增强了代码的可读性。例如,我们可以这样启动乒乓测试:

ping << Pong(pong)
   至于结果则与C#的例子一模一样,就不再重复了。

F#中的弱类型消息

    可是,F#的世界就真的如此美好吗?试想,我们该如何实现一个需要接受多种不同消息的Actor对象呢?我们只能这样做:

 

 

    由于我们必须使用object作为Actor接受到的消息类型,因此我们在对它作模式匹配时,只能进行参数判断。如果您要更进一步地“挖掘”其中的数据,则很可能需要进行再一次的模式匹配(如PingMsg或PongMsg)或赋值(如string * int元组)。一旦出现这种情况,在我看来也变得不是那么理想了,我们既没有节省代码,也没有让代码变得更为易读。与C#相比,唯一的优势可能就是F#中相对灵活的类型系统吧。

 

let another =
{ new obj Actor() with
override self.Receive(message) =
match message with

| :? PingMsg as pingMsg ->
// sub matching
match pingMsg with
| Ping(pong) -> null |> ignore
| Finished -> null |> ignore

| :? PongMsg as pongMsg ->
// sub matching
match pongMsg with
| Pong(ping) -> null |> ignore

| :? (string * int) as m ->
// sub binding
let (s, i) = m
null |> ignore

| _ -> failwith "Unrecognized message" }
 

 原文地址:http://www.cnblogs.com/jeffreyzhao/archive/2009/07/13/message-execution-model-for-c-sharp-actor-2-embarrassing-c-sharp-actor.html

0
相关文章