2009-08-11 7 views
16

Ich möchte das Äquivalent von Enum.GetName für ein F # diskriminiertes Union-Mitglied erhalten. Aufruf ToString() gibt mir TypeName + MemberName, was nicht genau das ist, was ich will. Ich könnte es natürlich teilen, aber ist es sicher? Oder vielleicht gibt es einen besseren Weg?Was ist das Enum.GetName-Äquivalent für F # -Unionsmitglied?

+1

Vorsicht! Derselbe Ausdruck, x.ToString(), wird mir bei verschiedenen Programmläufen manchmal AssemblyName + TypeName und manchmal AssemblyName + TypeName + MemberName geben. Ein anderer identischer Ausdruck desselben Typs an anderer Stelle gibt mir immer AssemblyName + TypeName + MemberName. Gleiches Problem mit x.GetType(). Name. Die angenommene Antwort ist gut. –

Antwort

26

Sie müssen die Klassen im Microsoft.FSharp.Reflection Namespace verwenden, so: (? Und schnell, weil der Mangel an Reflexion für eine der Methoden)

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

Ich kam hierher wegen der '' 'GetUnionFields''' Syntax - danke dafür. Während ich hier bin, dachte ich, dass '' '' GetUnionCaseName''' könnte etwas prägnanter geschrieben werden als: '' 'lassen GetUnionCaseName (e: 'a) = (FSharpValue.GetUnionFields (e, typeof <'a>) |> fst) .Name''' – philsquared

+0

Beachten Sie, dass dies ein sehr langsamer Ansatz ist. Sie möchten die Ergebnisse auf jeden Fall zwischenspeichern, um einen gewissen Performance-Effekt zu erzielen –

2

@ DanielAsher Antwort funktioniert, aber es eleganter zu machen , würde ich es auf diese Weise tun: (. Inspiriert von this und 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

Ich möchte etwas vorzuschlagen noch prägnanter:

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 

Wenn Vereinigung Fall einfach ist, GetName() die gleiche wie ToString() bringen kann:

> let tea = Tea 
val tea : Beverage = Tea 

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

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

Wenn jedoch Vereinigung Fall ist schicker, es wird ein Unterschied:.

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