Вопрос немного широкий, но я попробую.
Давайте начнем с типов. Наше сообщение будет иметь тип и содержание. Вы можете добавить больше полей, таких как messageId
, sender
, receiver
и т. Д.
type MessageType = MessageType of string
type Message<'T> = {
messageType : MessageType
message : 'T
}
Точно так же наш тип обработчика будет сопрягать и функцию типа и обработчика.
type HandlerResult = Result<string, string>
type MessageHandler<'T> = {
messageType : MessageType
handlerF : Message<'T> -> HandlerResult
}
Мы хотим где-нибудь зарегистрировать все обработчики с их типами. Словарь идеален, потому что он быстр:
let Handlers = System.Collections.Generic.Dictionary<MessageType, MessageHandler<obj>>()
Единственное, что словарь не может иметь универсальный тип, поэтому все обработчики здесь будут иметь тип MessageHandler<obj>
. Из-за этого нам нужно иметь возможность конвертировать <'T>
сообщений и обработчиков в <obj>
сообщений и обработчиков и обратно. Здесь у нас есть вспомогательная функция:
let ofMessageGen (msg: Message<obj>) : Message<_> = {
messageType = msg.messageType
message = unbox msg.message
}
и функция для регистрации функции обработчика в качестве обработчика <obj>
:
let registerHandler (handlerF:Message<'T> -> HandlerResult) =
let handler = {
messageType = MessageType <| (typeof<'T>).FullName
handlerF = ofMessageGen >> handlerF
}
Handlers.Add(handler.messageType, handler )
С этим мы можем зарегистрировать обработчики для любых типов:
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 )
и вот наш общий обработчик сообщений:
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;}"