简体   繁体   English

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

[英]Using all cases of union type F#

This is very linked to the question here How to enumerate an enum/type in F# . 这与此处的问题非常相关如何枚举F#中的枚举/类型 I define a union type and then I need to use all the possible cases of the type in static method. 我定义了一个联合类型,然后我需要在静态方法中使用该类型的所有可能情况。 For example: 例如:

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)

How do I get allCases ? 我如何获得allCases?

Thanks a lot 非常感谢

You can use FSharpType.GetUnionCases() from Microsoft.FSharp.Reflection to get all cases of a discriminated union. 您可以使用Microsoft.FSharp.Reflection FSharpType.GetUnionCases()来获取区分联合的所有情况。 In your example, it would look like this: 在您的示例中,它看起来像这样:

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) ]

However, the problem is that you may not be able to create instances to pass to your GetValue member, because some cases may have arguments (when calling MakeUnion you have to pass it an array of arguments and I used just an empty array). 但是,问题是您可能无法创建传递给GetValue成员的实例,因为某些情况可能有参数(调用MakeUnion您必须传递一个参数数组,而我只使用一个空数组)。 For example if you had: 例如,如果你有:

type Sample =
 | A of int
 | B of bool

I made an expansion of Tomas' work that handles making any type of FSharp Union (handles properties of the union case) as long as you provide it the logic for what to do with non-union children. 扩展了Tomas的工作 ,只要你提供了与非联合子项有关的逻辑,就可以处理任何类型的FSharp Union(处理联合案例的属性)。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM