Причина, по которой вы получаете исключение, заключается в том, что при вызове 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"