23

我想获得Enum.GetNameF# 歧视工会成员的等价物。调用ToString()给了我 TypeName+MemberName,这不是我想要的。当然,我可以对它进行子串化,但它安全吗?或者也许有更好的方法?

4

4 回答 4

33

您需要使用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"|]
于 2009-08-11T10:17:23.100 回答
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

(受此和启发。)

于 2016-12-17T14:21:58.153 回答
1

我想提出更简洁的建议:

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  

当 union case 很简单时,GetName()可能会带来相同的结果ToString()

> let tea = Tea
val tea : Beverage = Tea

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

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

但是,如果 union case 更高级,就会有所不同:。

> 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;}"
于 2017-09-05T15:49:15.987 回答
1

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

我刚才有一个案例,上面的答案不起作用。问题是值在接口后面,然后我有时会得到案例名称(咖啡或茶),但主要是类型名称(饮料)。我不明白为什么。我在.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
于 2021-01-29T20:12:04.807 回答