映射到没有模式匹配的 Union Case 构造函数

Map to Union Case constructor without pattern match

我有一个这样的联合类型 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)

可以看出,除了union case的构造函数不同,两条路径几乎相同

是否可以用更通用的方式编写 - 甚至不必使用模式匹配?

它不是很漂亮,但我认为这可以满足您的要求:

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 遵循严格的模式,因此没有错误处理。