2009-08-11 7 views
16

Deseo obtener el equivalente a Enum.GetName para un miembro de la unión discriminado F #. Llamar al ToString() me da TypeName + MemberName, que no es exactamente lo que quiero. Podría subscribirlo, por supuesto, pero ¿es seguro? ¿O tal vez hay una mejor manera?¿Cuál es el equivalente Enum.GetName para el miembro de la unión F #?

+1

¡Cuidado! La misma expresión, x.ToString(), en diferentes ejecuciones de programas a veces me dan AssemblyName + TypeName y algunas veces AssemblyName + TypeName + MemberName. Otra expresión idéntica en el mismo tipo en cualquier otro lugar siempre me dará AssemblyName + TypeName + MemberName. Mismo problema con x.GetType(). Nombre. La respuesta aceptada es buena. –

Respuesta

26

Es necesario utilizar las clases en el Microsoft.FSharp.Reflection espacio de nombres por lo que: (¿y rápido debido a la falta de reflexión de uno de los métodos)

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

Vine aquí por la sintaxis '' 'GetUnionFields''', gracias por eso. Mientras estoy aquí, pensé que señalaría que '' 'GetUnionCaseName''' podría escribirse de manera más sucinta como: ' '' let GetUnionCaseName (e: 'a) = (FSharpValue.GetUnionFields (e, typeof) <'a>) |> fst) .Name''' – philsquared

+0

Tenga en cuenta que este es un enfoque muy lento. Definitivamente, desea almacenar en caché los resultados para obtener un rendimiento de kilometraje de este –

2

@ obras respuesta de DanielAsher, pero para que sea más elegante , me gustaría hacerlo de esta manera: (. Inspirado por this y 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

me gustaría proponer algo aún más concisa:

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 

Cuando caso la unión es simple, GetName() podrá someter el mismo que ToString():

> let tea = Tea 
val tea : Beverage = Tea 

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

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

Sin embargo, si el caso unión es más elegante, no habrá una diferencia:.

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