F# 联合成员的 Enum.GetName 等效项是什么?

发布于 2024-08-01 16:37:14 字数 157 浏览 2 评论 0原文

我想要为 F# 受歧视的联合成员获取相当于 Enum.GetName 的内容。 调用 ToString() 会得到 TypeName+MemberName,这并不是我想要的。 当然,我可以对它进行子串化,但是它安全吗? 或者也许有更好的方法?

I want to get the equivalent of Enum.GetName for an F# discriminated union member. Calling ToString() gives me TypeName+MemberName, which isn't exactly what I want. I could substring it, of course, but is it safe? Or perhaps there's a better way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野鹿林 2024-08-08 16:37:14

您需要使用 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"|]

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"|]
药祭#氼 2024-08-08 16:37:14

@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

受到 这个这个。)

@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.)

心是晴朗的。 2024-08-08 16:37:14

该答案为最佳答案提供了更多信息和解决方案。

我刚刚遇到了一个案例,最佳答案不起作用。 问题是该值位于接口后面,然后我有时会得到案例名称(咖啡或茶),但大多数情况下只有类型名称(饮料)。 我不明白为什么。 我使用的是.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

This answer supplies additional information and solutions to the top answer.

I just now had a case where the top answer did not work. The problem was that the value was behind an interface, and then I would sometimes get the case name (Coffee or Tea), but mostly only the type name (Beverage). I don't understand why. I'm on .NET 5.0.

I changed the function to this, and then it worked as expected on my interfaced DU, always giving me the case name.

open FSharp.Reflection

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

I am aware that this is similar to other answers here, but this is not a member function, and so I guess should work on any DU, whether behind interfaces or not. I haven't tested what happens if used on a non-DU type.

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
遇见了你 2024-08-08 16:37:14

我想提出一些更简洁的建议:

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

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;}"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文