【问题标题】:What is the Enum.GetName equivalent for F# union member?F# 联合成员的 Enum.GetName 等效项是什么?
【发布时间】:2010-11-18 13:05:07
【问题描述】:

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

【问题讨论】:

  • 当心!相同的表达式 x.ToString() 在不同的程序运行时有时会给出 AssemblyName+TypeName,有时会给出 AssemblyName+TypeName+MemberName。其他地方相同类型的另一个相同表达式将始终给我 AssemblyName+TypeName+MemberName。 x.GetType().Name 也有同样的问题。接受的答案很好。

标签: f# discriminated-union


【解决方案1】:

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

【讨论】:

  • 我来到这里是为了 GetUnionFields 语法 - 谢谢。当我在这里时,我想我会指出 GetUnionCaseName 可以更简洁地写成:let GetUnionCaseName (e:'a) = ( FSharpValue.GetUnionFields(e, typeof&lt;'a&gt;) |&gt; fst ).Name
  • 请注意,这是一种非常缓慢的方法,您肯定希望缓存结果以从中获得一些性能指标
【解决方案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。)

【讨论】:

    【解决方案3】:

    我想提出更简洁的建议:

    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"
    

    但是,如果 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;}"
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-10
        • 1970-01-01
        • 2011-01-15
        • 2018-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        相关资源
        最近更新 更多