Сопоставление с конструктором прецедентов объединения без соответствия шаблону

#f#

Вопрос:

У меня есть такой тип союза CustomerEvent , как этот:

 type CustomerRegisteredEvent = { CompanyName: string }
type CustomerDeletedEvent = { DeletedOn: DateTimeOffset }

type CustomerEvent =
  | CustomerRegistered of CustomerRegisteredEvent
  | CustomerDeleted of CustomerDeletedEvent
 

У меня также есть такая map функция, как эта:

 let map (input: Events.IEvent): CustomerEvent =
  match input with
  | :? Events.IEvent<CustomerRegisteredEvent> as event ->
      CustomerRegistered(event.Data)
  | :? Events.IEvent<CustomerDeletedEvent> as event ->
      CustomerDeleted(event.Data)
 

Как видно, оба пути почти одинаковы, за исключением конструктора случая объединения.

Может ли это быть написано более общим способом — может быть, даже без необходимости использовать сопоставление шаблонов?

Комментарии:

1. Вероятно, вы могли бы сделать это с помощью размышления. Такой подход вы бы рассмотрели?

2. Да, отражение было бы в порядке

Ответ №1:

Это не очень красиво, но я думаю, что это делает то, что ты хочешь:

 open FSharp.Reflection

let getCaseMap<'t> () =
    FSharpType.GetUnionCases(typeof<'t>)
        |> Seq.map (fun unionCase ->
            let typ =
                let property =
                    unionCase.GetFields() |> Seq.exactlyOne
                property.PropertyType
            let create data =
                FSharpValue.MakeUnion(unionCase, [| data |])
                    :?> 't
            typ.Name, create)
        |> Map

let caseMap = getCaseMap<CustomerEvent> ()

let map (input: Events.IEvent) =
    let typ =
        input.GetType().GenericTypeArguments
            |> Seq.exactlyOne
    let data =
        let property =
            input.GetType().GetProperty("Data")
        property.GetValue(input)
    caseMap.[typ.Name] data
 

Это использует отражение, чтобы избежать сопоставления с образцом, вместо этого выполняя поиск по карте, чтобы найти правильный случай объединения непосредственно из типа данного события. Предполагается, что DU следует жесткому шаблону, поэтому обработка ошибок невозможна.

Комментарии:

1. Заменил код из вопроса, и все по — прежнему работает. Спасибо!