2009-08-11 18 views
16

Desidero ottenere l'equivalente di Enum.GetName per un membro del sindacato discriminato F #. Chiamare ToString() mi dà TypeName + MemberName, che non è esattamente quello che voglio. Potrei sottostrarla, ovviamente, ma è sicuro? O forse c'è un modo migliore?Qual è l'equivalente Enum.GetName per il membro del sindacato F #?

+1

Attenzione! La stessa stessa espressione, x.ToString(), a diverse esecuzioni del programma a volte mi dà AssemblyName + TypeName e talvolta AssemblyName + TypeName + MemberName. Un'altra espressione identica sullo stesso tipo altrove mi darà sempre AssemblyName + TypeName + MemberName. Lo stesso problema con x.GetType(). Nome. La risposta accettata è buona. –

risposta

26

È necessario utilizzare le classi nel Microsoft.FSharp.Reflection namespace così: (? E veloce a causa della mancanza di riflessione per uno dei metodi)

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

Sono venuto qui per la sintassi '' 'GetUnionFields''' - grazie per questo. Mentre sono qui ho pensato di far notare che '' 'GetUnionCaseName''' potrebbe essere scritto in modo un po 'più succinto come: ' '' lascia GetUnionCaseName (e:' a) = (FSharpValue.GetUnionFields (e, typeof <'a>) |> fst) .Name''' – philsquared

+0

Si noti che questo è un approccio molto lento, si vorrebbe sicuramente memorizzare nella cache i risultati al fine di ottenere un certo milleo di prestazioni da questo –

2

@ opere risposta di DanielAsher, ma per renderlo più elegante , lo farei in questo modo: (. Ispirato this e this)

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 

0

Vorrei proporre qualcosa di ancora più conciso:

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 

Quando caso l'unione è semplice, GetName() può portare lo stesso di ToString():

> let tea = Tea 
val tea : Beverage = Tea 

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

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

Tuttavia, se caso unione è più elaborato, ci sarà una differenza:.

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