什么是F#union成员的Enum.GetName等价物?

时间:2009-08-11 08:20:54

标签: f# discriminated-union

我想为F#区别的联盟成员获得等效的Enum.GetName。调用ToString()给我TypeName + MemberName,这不是我想要的。当然,我可以将它归入其中,但是它安全吗?或许还有更好的方法?

4 个答案:

答案 0 :(得分:28)

您需要使用Microsoft.FSharp.Reflection命名空间中的类,以便:

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

答案 1 :(得分:2)

@ DanielAsher的答案有效,但为了使它更优雅(而且速度快?因为其中一种方法缺乏反射),我会这样做:

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

(灵感来自thisthis。)

答案 2 :(得分:1)

此答案为最佳答案提供了附加信息和解决方案。

我刚刚遇到了一个最佳答案不起作用的情况。问题是值在接口后面,然后我有时会得到案例名称(Coffee 或 Tea),但大多数情况下只有类型名称(Beverage)。我不明白为什么。我使用的是 .NET 5.0。

我将函数更改为此,然后它在我的接口 DU 上按预期工作,总是给我案例名称。

open FSharp.Reflection

let GetUnionCaseName (x: obj) =
    match FSharpValue.GetUnionFields(x, x.GetType()) with
    | case, _ -> case.Name

我知道这与此处的其他答案相似,但这不是成员函数,因此我想应该适用于任何 DU,无论是否在接口后面。我还没有测试过如果在非 DU 类型上使用会发生什么。

type IMessage = interface end

type Beverage = Coffee | Tea

type Car =
    | Tesla of model:string
    | Ford
    interface IMessage
    
type MySingleCase = MySingleCase of string
type SingleCase2 = SingleCase2 of string interface IMessage

let m1: Beverage = Coffee
let m2: IMessage = (Tesla "Model 3") :> IMessage
let m3 = MySingleCase "x"
let m4 = SingleCase2 "x" :> IMessage

printfn "%s" (GetUnionCaseName m1) // Coffee
printfn "%s" (GetUnionCaseName m2) // Tesla
printfn "%s" (GetUnionCaseName m3) // MySingleCase
printfn "%s" (GetUnionCaseName m4) // SingleCase2

答案 3 :(得分:0)

我想提出一些更简洁的方法:

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  

当工会案例很简单时,GetName()可能与ToString()相同:

> let tea = Tea
val tea : Beverage = Tea

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

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

然而,如果工会案例更加美好,那将会有所不同:

> 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;}"