How do I get a discriminated union case from a string?

南楼画角 提交于 2020-02-22 06:11:46

问题


I have a discriminated union and I want to select a case based on a string (which is read from a JSON file). This is easy to do:

type MyDU = A | B
let str = "B"
let myDU : MyDU =
    match str with
    | "A" -> MyDU.A
    | "B" -> MyDU.B
    | _ -> failwith "whatever"
// val myDU : MyDU = B

However, sometimes there are many cases, which would require a lot of typing.

The Microsoft.FSharp.Reflection library allows me to get a UnionCaseInfo object:

open Microsoft.FSharp.Reflection
let myDUInfo : UnionCaseInfo =
    FSharpType.GetUnionCases(typeof<MyDU>)
    |> Array.find (fun x -> x.Name = str)
// val myDUInfo : UnionCaseInfo = MyDU.B

I would like to convert myDUInfo into a union case so as to get the same result as the code above relying on match, but without having to type the strings corresponding to all the cases.

Is this possible?


回答1:


To instantiate a union case, use the FSharpValue.MakeUnion method. Here is a function that will instantiate a union case given its name:

let instantiate<'t> name =
    Reflection.FSharpType.GetUnionCases( typeof<'t> )
    |> Seq.tryFind (fun uc -> uc.Name = name)
    |> Option.map (fun uc -> Reflection.FSharpValue.MakeUnion( uc, [||] ) :?> 't)

Usage:

> type T = A | B | C    
> instantiate<T> "A"

val it : T option = Some A

NOTE: this function assumes, but does not make sure, that the union case has no arguments. If you give it a case with arguments, it will crash:

> type T = A of int | B | C
> instantiate<T> "A"

System.Reflection.TargetParameterCountException: Parameter count mismatch.
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at FSI_0002.instantiate@5.Invoke(UnionCaseInfo uc) in c:\o\a.fsx:line 5
   at Microsoft.FSharp.Core.OptionModule.Map[T,TResult](FSharpFunc`2 mapping, FSharpOption`1 option)
   at <StartupCode$FSI_0006>.$FSI_0006.main@()
Stopped due to error

I leave resolution of this problem as an exercise for the reader (hint: use the UnionCase.GetFields method).



来源:https://stackoverflow.com/questions/46334181/how-do-i-get-a-discriminated-union-case-from-a-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!