Как Reflection MakeUnion трехуровневого типа? - PullRequest
0 голосов
/ 26 июня 2019

Я хочу попытаться отразить все типы комбинаций,

Я использую рекурсивную функцию Работаем на двух уровнях Но на третьем уровне это не сработает.

open Microsoft.FSharp.Reflection

let rec getAll<'A> (c : UnionCaseInfo) : obj [] =
     match c.GetFields() |> List.ofSeq with
                | [ x ] when FSharpType.IsUnion x.PropertyType ->
                        FSharpType.GetUnionCases(x.PropertyType)
                        |> Array.map (fun uc ->
                            FSharpValue.MakeUnion(c, getAll(uc)))
                        |> Array.ofSeq
                | _ ->
                  [| FSharpValue.MakeUnion(c, Array.empty) |]

type C = | C1 | C2

//type B = | B1 | B2
type B = | B1 of C | B2

type A =
    | A1
    | A2toB of B 
    | A3
    static member GetAll =
        FSharpType.GetUnionCases(typeof<A>)
        |> Seq.collect getAll<A>
        |> Seq.cast<A>
        |> Array.ofSeq


(A2toB (B1 C1)).ToString()  |> printfn "%A"
A.GetAll |> Array.map (fun t -> t.ToString() |> printfn "%A")
"A2toB (B1 C1)"
Unhandled Exception: System.Reflection.TargetParameterCountException: Parameter count mismatch.

когда используется только два уровня type B = | B1 | B2

Правильный возврат

"A1"
"A2toB B1"
"A2toB B2"
"A3"

Ответы [ 3 ]

2 голосов
/ 26 июня 2019

Причина, по которой вы получаете исключение, заключается в том, что при вызове getAll в рекурсивном случае для B1 тип поля равен C, а C имеет два случая, C1 | C2, поэтому вы вернуть массив из двух элементов. Затем этот массив передается в вызов MakeUnion для B1, который ожидает только один элемент (один экземпляр C). Вызов завершается неудачно, поскольку в массиве передано неожиданное дополнительное значение C.

Вы можете сделать это для вашего примера, добавив что-то вроде Array.take 1 к вашему рекурсивному вызову getAll, но в общем случае это не сработает. Я не совсем уверен, чего вы пытаетесь достичь, поэтому предоставить общее решение в настоящее время немного сложно. Если вы сможете уточнить ваши требования, мы, вероятно, сможем предложить лучшее решение.

Вот версия, которая подходит для вашего конкретного примера (хотя, как я уже сказал, это не очень хорошее общее решение):

let rec getAll<'A> (c : UnionCaseInfo) : obj [] =
     match c.GetFields() |> List.ofSeq with
                | [ x ] when FSharpType.IsUnion x.PropertyType ->
                        FSharpType.GetUnionCases(x.PropertyType)
                        |> Array.map (fun uc ->
                            FSharpValue.MakeUnion(c, getAll(uc) |> Array.take 1))
                        |> Array.ofSeq
                | _ ->
                  [| FSharpValue.MakeUnion(c, Array.empty) |]

Вот вывод:

"A1"
"A2toB (B1 C1)"
"A2toB B2"
"A3"
1 голос
/ 27 июня 2019

спасибо Аарону М. Эшбаху за обнаружение моей рекурсивной ошибки, я исправляю свой код

let rec getAll<'A> (c: UnionCaseInfo): obj [] =
     match c.GetFields() |> List.ofSeq with
                | [ x ] when FSharpType.IsUnion x.PropertyType ->
                        FSharpType.GetUnionCases(x.PropertyType)
                        |> Array.map (fun uc ->
                          let t = uc.Name
                          getAll (uc) |> Array.map (fun a ->
                              FSharpValue.MakeUnion(c, [| a |]))
                              )
                        |> Array.concat
                        |> Array.ofSeq
                | _ ->
                  let t = c.Name
                  [| FSharpValue.MakeUnion(c, Array.empty) |]
0 голосов
/ 02 июля 2019

Я думаю ваш код можно упростить.Давайте снизим уровень вложенности на единицу;использовать выражение последовательности массива для генерации;а также, давайте вернемся к System.Type вместо громоздких UnionCaseInfo.

Параметр type, удаленный ниже, мог бы использоваться во время выполнения только для распаковки самого внешнего типа объединения.Тип других сгенерированных случаев обязательно obj, что также демонстрирует несколько ограниченную полезность динамически генерируемых случаев объединения.

let rec getCases t = [|
    for ucinfo in FSharpType.GetUnionCases t do
        match ucinfo.GetFields() with
        | [|pinfo|] when FSharpType.IsUnion pinfo.PropertyType ->
            for x in getCases pinfo.PropertyType ->
                 FSharpValue.MakeUnion(ucinfo, [|x|])
        | _ -> yield FSharpValue.MakeUnion(ucinfo, [||]) |]
// val getCases : t:System.Type -> obj []

type A = A1 | A2toB of B | A3
and B = B1 of C | B2
and C = C1 | C2

getCases typeof<A> 
// val it : obj [] = [|A1; A2toB (B1 C1); A2toB (B1 C2); A2toB B2; A3|]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...