定义具有多种消息类型的消息传递域 [英] Defining a Message Passing domain with very many message types

查看:56
本文介绍了定义具有多种消息类型的消息传递域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我所看到的大多数F#消息传递示例都在处理2-4种消息类型,并且能够利用模式匹配将每条消息定向到其适当的处理函数.

Most F# Message Passing examples I've seen so far are working with 2-4 message types, and are able to utilize pattern matching to direct each message to its proper handler function.

对于我的应用程序,由于它们处理和所需参数的不同性质,我需要数百种独特的消息类型.到目前为止,每种消息类型都是其自己的记录类型,并附加了一个标记接口,因为在一个单独的联合中包含数百种类型并不是一件很漂亮的事,而这些类型的模式匹配也不会很漂亮.结果,我目前正在使用反射来查找消息的正确处理函数.

For my application, I need hundreds of unique message types due to the different nature of their handling and required parameters. So far, each message type is its own record type with a marker interface attached, because including hundreds of types in a single discriminated union would not be very pretty - and neither would the pattern matching of these be. As a result, I'm currently using reflection to find the correct handler functions of messages.

是否有更好,更实用的方法?也许甚至更聪明的方式来定义这样的域?我想在编译时强制执行尽可能多的正确性,但是目前,我正在基于自定义属性查找处理程序函数,并在运行时检查其签名.

Is there a better, and more functional way of doing this? Perhaps even a smarter way to define such a domain? I'd like to enforce as much correctness as possible at compile time, but currently I'm finding the handler functions based on a custom attribute, as well as checking their signature at run time.

据我所知,我无法使用.NET自定义属性来强制执行函数的签名,并且由于类型太多而无法实际进行模式匹配,因此(据我所知)我无法使用单个通用消息处理函数任何一个.我尝试将通用包装函数用作所有处理程序的接口",并且仅将自定义属性附加到此处理函数,但这并没有赋予包装的函数该属性,并使它们通过基于该属性的反射而可见(我是.NET的新手.)

As far as I know, I cannot enforce a function's signature with a .NET custom attribute, and since there are too many types to realistically pattern match, I can't (to my knowledge) use a single generic message handler function either. I tried using a generic wrapper function as an "interface" for all handlers, and only attaching the custom attribute to this one, but this didn't grant the wrapped functions the attribute and make them visible through reflection based on that attribute (I'm very new to .NET).

我考虑过将处理程序函数作为成员附加到它们各自的记录类型的可能性,这将避免反射的需要并在编译时强制执行其他一些正确性.但是,让所有这些功能都出现在客户端并没有多大意义.

I have thought about the possibility of attaching the handler functions to their respective record type as a member, which would circumvent the need for reflection and enforce some additional correctness at compile time. However, it doesn't make much sense to have all those functions present client side.

推荐答案

问题有点广泛,但我会尝试一下.

The question is a little broad but I will give it a try.

让我们从类型开始.我们的消息将具有类型和内容.您可以添加更多字段,例如 messageId sender receiver 等.

Lets start with the types. Our message will have a type and a content. You can add more fields like messageId, sender, receiver, etc.

type MessageType = MessageType of string
type Message<'T> = {
    messageType : MessageType
    message     : 'T
}

类似地,我们的处理程序类型将类型和处理程序函数配对.

Similarly our handler type will pair both type and handler function.

type HandlerResult      = Result<string, string>
type MessageHandler<'T> = {
    messageType : MessageType
    handlerF    : Message<'T> -> HandlerResult
}

我们想在某个地方注册所有处理程序及其类型.字典之所以理想,是因为它速度很快:

We want to have someplace to register all handlers with their types. A Dictionary is ideal because it is fast:

let Handlers = System.Collections.Generic.Dictionary<MessageType, MessageHandler<obj>>()

唯一的问题是字典不能具有泛型类型,因此此处的所有处理程序都将为 MessageHandler< obj> 类型.因此,我们需要能够将<'T> 消息和处理程序转换为< obj> 消息和处理程序并返回.我们在这里有一个辅助功能:

The only thing is the dictionary cannot have a generic type so all handlers here are going to be of type MessageHandler<obj>. Because of that we need to be able to convert <'T> messages and handlers to <obj> messages and handlers and back. Se here we have a helper function:

let ofMessageGen (msg: Message<obj>) : Message<_> = {
    messageType =       msg.messageType
    message     = unbox msg.message
}

以及将处理程序功能注册为< obj> 处理程序的函数:

and a function to register the handler function as an <obj> handler:

let registerHandler (handlerF:Message<'T> -> HandlerResult) = 
    let handler = {
        messageType = MessageType <| (typeof<'T>).FullName
        handlerF    = ofMessageGen >> handlerF
    }
    Handlers.Add(handler.messageType, handler )

有了它,我们可以注册任何类型的处理程序:

With that we can register handlers for any types:

registerHandler  (fun msg -> sprintf "String message: %s" msg.message |> Ok )
registerHandler  (fun msg -> sprintf "int    message: %d" msg.message |> Ok )
registerHandler  (fun msg -> sprintf "float  message: %f" msg.message |> Ok )

这是我们的通用消息处理程序:

and here is our generic message handler:

let genericHandler (msg:Message<obj>) : HandlerResult =
    match Handlers.TryGetValue msg.messageType with
    | false, _       -> Error <| sprintf "No Handler for message: %A" msg
    | true , handler -> handler.handlerF msg

创建一条消息:

let createMessage (m:'T) = {
    messageType = MessageType <| (typeof<'T>).FullName
    message     = box m
}

并像这样测试它:

createMessage "Hello" |> genericHandler |> printfn "%A" 
createMessage 123     |> genericHandler |> printfn "%A" 
createMessage 123.4   |> genericHandler |> printfn "%A" 
createMessage true    |> genericHandler |> printfn "%A" 

// Ok "String message: Hello"
// Ok "int    message: 123"
// Ok "float  message: 123.400000"    
// Error
//  "No Handler for message: {messageType = MessageType "System.Boolean";
// message = true;}"

这篇关于定义具有多种消息类型的消息传递域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆