Определение домена передачи сообщений с очень большим количеством типов сообщений - PullRequest
0 голосов
/ 02 января 2019

Большинство примеров передачи сообщений F #, которые я видел до сих пор, работают с 2-4 типами сообщений и могут использовать сопоставление с шаблоном, чтобы направить каждое сообщение к его надлежащей функции-обработчику.

Для моего приложения мне нужны сотни уникальных типов сообщений из-за разной природы их обработки и требуемых параметров. До сих пор каждый тип сообщения - это свой собственный тип записи с присоединенным интерфейсом маркера, потому что включение сотен типов в один дискриминируемый союз не будет очень симпатичным - и не будет совпадения шаблонов с ними. В результате в настоящее время я использую рефлексию, чтобы найти правильные функции-обработчики сообщений.

Есть ли лучший и более функциональный способ сделать это? Возможно, даже более умный способ определить такой домен? Я хотел бы обеспечить как можно большую корректность во время компиляции, но в настоящее время я нахожу функции-обработчики на основе пользовательского атрибута, а также проверяю их сигнатуру во время выполнения.

Насколько я знаю, я не могу применить сигнатуру функции с помощью пользовательского атрибута .NET, и, поскольку существует слишком много типов для реалистичного сопоставления с образцом, я не могу (насколько мне известно) использовать одну общую функцию-обработчик сообщений. или. Я попытался использовать универсальную функцию-обертку в качестве «интерфейса» для всех обработчиков и только прикрепить к ней пользовательский атрибут, но это не предоставило обернутым функциям атрибут и не сделало их видимыми через отражение на основе этого атрибута (I ' я очень плохо знаком с .NET).

Я думал о возможности присоединения функций-обработчиков к их соответствующему типу записи в качестве члена, что позволило бы обойтись без отражения и обеспечить некоторую дополнительную корректность во время компиляции. Однако не имеет смысла, чтобы все эти функции присутствовали на стороне клиента.

1 Ответ

0 голосов
/ 03 января 2019

Вопрос немного широкий, но я попробую.

Давайте начнем с типов. Наше сообщение будет иметь тип и содержание. Вы можете добавить больше полей, таких как 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;}"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...