Enum.GetName
F#の識別された共用体メンバーと同等のものを取得したいと思います。呼び出すToString()
とTypeName+MemberNameが表示されますが、これは私が望んでいるものではありません。もちろん、部分文字列にすることもできますが、安全ですか?それとももっと良い方法がありますか?
4 に答える
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"|]
@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
もっと簡潔なものを提案したいと思います。
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;}"
この回答は、上位の回答に追加情報と解決策を提供します。
たった今、一番上の答えがうまくいかない場合がありました。問題は、値がインターフェイスの背後にあることでした。その後、ケース名(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