What is the Enum.GetName equivalent for F# union member?

后端 未结 3 1106
难免孤独
难免孤独 2020-12-30 00:50

I want to get the equivalent of Enum.GetName for an F# discriminated union member. Calling ToString() gives me TypeName+MemberName, which isn\'t ex

相关标签:
3条回答
  • 2020-12-30 01:37

    You need to use the classes in the Microsoft.FSharp.Reflection namespace so:

    open Microsoft.FSharp.Reflection
    
    ///Returns the case name of the object with union type 'ty.
    let GetUnionCaseName (x:'a) = 
        match FSharpValue.GetUnionFields(x, typeof<'a>) with
        | case, _ -> case.Name  
    
    ///Returns the case names of union type 'ty.
    let GetUnionCaseNames <'ty> () = 
        FSharpType.GetUnionCases(typeof<'ty>) |> Array.map (fun info -> info.Name)
    
    // Example
    type Beverage =
        | Coffee
        | Tea
    
    let t = Tea
    > val t : Beverage = Tea
    
    GetUnionCaseName(t)
    > val it : string = "Tea"
    
    GetUnionCaseNames<Beverage>()
    > val it : string array = [|"Coffee"; "Tea"|]
    
    0 讨论(0)
  • 2020-12-30 01:38

    @DanielAsher's answer works, but to make it more elegant (and fast? because of the lack of reflection for one of the methods), I would do it this way:

    type Beverage =
        | Coffee
        | Tea
        static member ToStrings() =
            Microsoft.FSharp.Reflection.FSharpType.GetUnionCases(typeof<Beverage>)
                |> Array.map (fun info -> info.Name)
        override self.ToString() =
            sprintf "%A" self
    

    (Inspired by this and this.)

    0 讨论(0)
  • 2020-12-30 01:45

    I would like to propose something even more concise:

    open Microsoft.FSharp.Reflection
    
    type Coffee = { Country: string; Intensity: int }
    
    type Beverage =
        | Tea
        | Coffee of Coffee
    
        member x.GetName() = 
            match FSharpValue.GetUnionFields(x, x.GetType()) with
            | (case, _) -> case.Name  
    

    When union case is simple, GetName() may bring the same as ToString():

    > let tea = Tea
    val tea : Beverage = Tea
    
    > tea.GetName()
    val it : string = "Tea"
    
    > tea.ToString()
    val it : string = "Tea"
    

    However, if union case is fancier, there will be a difference:.

    > let coffee = Coffee ({ Country = "Kenya"; Intensity = 42 })
    val coffee : Beverage = Coffee {Country = "Kenya"; Intensity = 42;}
    
    > coffee.GetName()
    val it : string = "Coffee"
    
    > coffee.ToString()
    val it : string = "Coffee {Country = "Kenya";        Intensity = 42;}"
    
    0 讨论(0)
提交回复
热议问题