23

Enum.GetNameF#の識別された共用体メンバーと同等のものを取得したいと思います。呼び出すToString()とTypeName+MemberNameが表示されますが、これは私が望んでいるものではありません。もちろん、部分文字列にすることもできますが、安全ですか?それとももっと良い方法がありますか?

4

4 に答える 4

33

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"|]
于 2009-08-11T10:17:23.100 に答える
2

@DanielAsherの答えは機能しますが、よりエレガントにするために(そして、メソッドの1つに反映がないために高速になります)、次のようにします。

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

(これこれに触発されました。)

于 2016-12-17T14:21:58.153 に答える
1

もっと簡潔なものを提案したいと思います。

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"

ただし、組合の場合がより巧妙な場合は、違いがあります。

> 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;}"
于 2017-09-05T15:49:15.987 に答える
1

この回答は、上位の回答に追加情報と解決策を提供します。

たった今、一番上の答えがうまくいかない場合がありました。問題は、値がインターフェイスの背後にあることでした。その後、ケース名(CoffeeまたはTea)を取得することがありましたが、ほとんどの場合、タイプ名(Beverage)のみを取得していました。理由がわかりません。私は.NET5.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
于 2021-01-29T20:12:04.807 に答える