Как получить тип каждого случая объединения для данного типа объединения в F #

В приведенном ниже коде F # мне интересно, как получить тип, связанный с каждым случаем объединения, через отражение

type AccountCreatedArgs = {
    Owner: string
    AccountId: Guid
    CreatedAt: DateTimeOffset
    StartingBalance: decimal
}

type Transaction = {
    To: Guid
    From: Guid
    Description: string
    Time: DateTimeOffset
    Amount: decimal
}

type AccountEvents =
    | AccountCreated of AccountCreatedArgs
    | AccountCredited of Transaction
    | AccountDebited of Transaction

Я пробовал использовать FSharpType.GetUnionCases(typeof<AccountEvents>), но UnionCaseInfo не предоставляет никакой информации о типе дела (только декларирующий тип, известный как AccountEvents, поэтому в моем случае не очень полезен) = /


Ответ glennsl действительно помог мне https://stackoverflow.com/a/56351231/4636721

Что мне действительно пригодилось в моем случае, так это:

let getUnionCasesTypes<'T> =
    Reflection.FSharpType.GetUnionCases(typeof<'T>)
    |> Seq.map (fun x -> x.GetFields().[0].DeclaringType)

person Natalie Perret    schedule 28.05.2019    source источник
comment
Вы уже получили верный ответ, но, возможно, вы все же можете объяснить, почему вы хотите это сделать? Сочетание F # и отражения - путь, по которому следует идти с некоторой осторожностью. (Но прекрасно, если вы знаете, что делаете и почему, естественно)   -  person Guran    schedule 29.05.2019
comment
@Guran Я использую Marten с F #, и в некоторых случаях мне нужно предоставить Marten типы событий.   -  person Natalie Perret    schedule 29.05.2019
comment
Я понимаю. Я бы (вероятно) предпочел явную функцию и выдержал бы рутинную работу по обновлению, когда мои типы меняются, а не постоянное беспокойство о том, что мое отражение испорчено, но это ваш выбор и совершенно не связанный с этим вопрос.   -  person Guran    schedule 29.05.2019
comment
@Guran, вы сделали разумное замечание, и в процессе производства это, вероятно, будет иметь место, однако мне также было любопытно, как получить эти типы в случае дискриминируемых объединений.   -  person Natalie Perret    schedule 29.05.2019
comment
Любопытство само по себе является уважительной причиной. Промежуточный путь может заключаться в использовании поставщика типов или даже написании собственного. docs.microsoft.com/ en-us / dotnet / fsharp / tutorials / type-sizes /   -  person Guran    schedule 29.05.2019
comment
@ Гуран, да, типичный провайдер для этого? Хм, не могли бы вы немного уточнить? Вы выбрали мое любопытство. Не совсем уверен, как это может применяться здесь   -  person Natalie Perret    schedule 29.05.2019
comment
Позвольте нам продолжить это обсуждение в чате.   -  person Guran    schedule 29.05.2019


Ответы (1)


UnionCaseInfo имеет метод GetFields, который возвращает массив PropertyInfo, которые описывают каждое поле / аргумент случая объединения. Например:

FSharpType.GetUnionCases(typeof<AccountEvents>)
    |> Array.map(fun c -> (c.Name, c.GetFields()))
    |> printfn "%A"

напечатает

[|("AccountCreated", [|AccountCreatedArgs Item|]);
  ("AccountCredited", [|Transaction Item|]);
  ("AccountDebited", [|Transaction Item|])|]

Имя, присвоенное одному случаю объединения полей, - "Item", а если множественное - "Item1", "Item2" и т. Д. Сам тип поля можно получить из свойства PropertyType PropertyInfo, поэтому:

FSharpType.GetUnionCases(typeof<AccountEvents>)
    |> Array.map(fun c -> (c.Name, c.GetFields() |> Array.map(fun p -> p.PropertyType.Name)))
    |> printfn "%A"

таким образом напечатает

[|("AccountCreated", [|"AccountCreatedArgs"|]);
  ("AccountCredited", [|"Transaction"|]);
  ("AccountDebited", [|"Transaction"|])|]
person glennsl    schedule 28.05.2019