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