使用联合类型F#的所有情况

时间:2010-12-17 12:18:12

标签: f#

这与How to enumerate an enum/type in F#中的问题非常相关。我定义了一个联合类型,然后我需要在静态方法中使用该类型的所有可能情况。例如:

type Interests =
| Music 
| Books
| Movies
    with 
        static member GetValue( this) = match this with 
                                         | Music  -> 0
                                         | Books -> 5
                                         | Movies -> 0
        static member GetSeqValues() = allCases|>Seq.map(GetValue)

我如何获得allCases?

非常感谢

2 个答案:

答案 0 :(得分:12)

您可以使用FSharpType.GetUnionCases()中的Microsoft.FSharp.Reflection来获取所有受歧视联盟的案例。在您的示例中,它看起来像这样:

type Interests = 
   | Music  
   | Books 
   | Movies
   static member GetValue(this) = (...)
   static member GetSeqValues() = 
     // Get all cases of the union
     let cases = FSharpType.GetUnionCases(typeof<Interests>)
     [ for c in cases do 
         // Create value for each case (assuming it has no arguments)
         let interest = FSharpValue.MakeUnion(c, [| |]) :?> Interests
         yield GetValue(interest) ]

然而,问题是您可能无法创建要传递给GetValue成员的实例,因为某些情况可能有参数(在调用MakeUnion时您必须传递一个数组参数和我只用了一个空数组。例如,如果你有:

type Sample =
 | A of int
 | B of bool

答案 1 :(得分:0)

我让an expansion of Tomas' work处理任何类型的FSharp Union(处理union case的属性) 只要你提供了如何处理非工会子女的逻辑。

let rec getAllDUCases fNonUnionArg t : obj list =
    let getAllDUCases = getAllDUCases fNonUnionArg
    // taken from http://stackoverflow.com/questions/6497058/lazy-cartesian-product-of-multiple-sequences-sequence-of-sequences
    let cartesian_product2 sequences = 
        let step acc sequence = seq {
            for x in acc do
            for y in sequence do
            yield seq { yield! x; yield y}}
        Seq.fold step (Seq.singleton Seq.empty) sequences

    let makeCaseTypes (fUnion:Type-> obj list) (fNonUnionArg:Type -> obj) (uc: UnionCaseInfo) : UnionCaseInfo*(obj list list) =
        let constructorArgs = 
            uc.GetFields() 
            |> Seq.map (fun f -> 
                if FSharpType.IsUnion f.PropertyType then 
                    let childTypes = fUnion f.PropertyType 
                    if 
                        childTypes
                        |> Seq.exists (fun ct -> FSharpType.IsUnion (ct.GetType()) |> not) then
                            failwithf "fUnion returned a bad type in list %A" childTypes
                    childTypes
                else [ fNonUnionArg f.PropertyType] )
            |> List.ofSeq
        let allCombinationsOfFieldPossibles = 
            cartesian_product2 constructorArgs
            |> Seq.map List.ofSeq
            |> List.ofSeq
        uc, allCombinationsOfFieldPossibles
    // with help from http://stackoverflow.com/a/4470670/57883
    let result =
        FSharpType.GetUnionCases t
        |> Seq.map (makeCaseTypes getAllDUCases fNonUnionArg)
        |> List.ofSeq
    let result = 
        result
        |> Seq.map (fun (uc,allFieldComboCases) -> allFieldComboCases |> Seq.map (fun args-> FSharpValue.MakeUnion(uc,args |> Array.ofList)))
        |> Seq.collect id
        |> Seq.map box
        |> List.ofSeq
    result