如何列举F#中的歧视联盟?

mic*_*lpm 31 f# discriminated-union

如何通过F#中歧视联盟的可能"价值"进行枚举?

我想知道是否有类似于Enum.GetValues(Type)受歧视的工会的事情,我很难确定我会列举什么样的数据.我想为每个选项生成一个具有区别联合的列表或数组,其中包含一个项目.

Rob*_*ert 37

是的,F#在.NET的反射之上构建了自己的反射层,以帮助您理解特定于F#的类型,例如区分联合.这是允许您枚举联合案例的代码:

open Microsoft.FSharp.Reflection

type MyDU =
    | One
    | Two
    | Three

let cases = FSharpType.GetUnionCases typeof<MyDU>

for case in cases do printfn "%s" case.Name
Run Code Online (Sandbox Code Playgroud)

  • 如果你想获得不受访问限制的案例,不要忘记传递`BF.Public ||| BF.NonPublic`其中`BF = System.Reflection.BindingFlags`为`GetUnionCases`. (5认同)

Tom*_*cek 9

稍微扩展Robert的示例 - 即使您没有受歧视联合的实例,也可以使用F#反射来获取有关类型的信息(例如各个案例的参数类型).以下扩展了Robert的示例,它还打印了参数的类型:

open Microsoft.FSharp.Reflection

let ty = typeof<option<int>>
let cases = FSharpType.GetUnionCases ty

printfn "type %s =" ty.FullName
for case in cases do 
  printf "| %s" case.Name 
  let fields = case.GetFields()
  if fields.Length > 0 then
    printf " of"
  for fld in fields do
    printf " %s " fld.PropertyType.FullName
  printfn ""
Run Code Online (Sandbox Code Playgroud)

例如,对于option<int>type,你会得到(我稍微简化了输出):

type Microsoft.FSharp.Core.FSharpOption`1[System.Int32] =
  | None
  | Some of System.Int32
Run Code Online (Sandbox Code Playgroud)

这些信息有许多有趣的用途 - 例如,您可以从F#联合生成DB模式,或者创建将XML解析为区别联合(描述结构)的函数.我在今年早些时候的GOTO会议上谈到了XML处理示例.


smo*_*per 7

如果你的歧视联盟只是由普通标识符组成(没有案例存储任何数据,这可能就是你需要的:gist

open Microsoft.FSharp.Reflection

module SimpleUnionCaseInfoReflection =

  // will crash if 'T contains members which aren't only tags
  let Construct<'T> (caseInfo: UnionCaseInfo)                   = FSharpValue.MakeUnion(caseInfo, [||]) :?> 'T

  let GetUnionCaseInfoAndInstance<'T> (caseInfo: UnionCaseInfo) = (caseInfo, Construct<'T> caseInfo)

  let AllCases<'T> = 
    FSharpType.GetUnionCases(typeof<'T>)
    |> Seq.map GetUnionCaseInfoAndInstance<'T>
Run Code Online (Sandbox Code Playgroud)
#load "SimpleUnionCaseInfoReflection.fs"

type Foos = Foo | Bar | Baz
SimpleUnionCaseInfoReflection.AllCases<Foos> |> Seq.iter (fun (caseInfo, instance) ->printfn "name: %s instance: %O is Bar? : %b" caseInfo.Name instance (instance.Equals(Foos.Bar)))

(*
> name: Foo instance: FSI_0055+Foos is Bar? : false
> name: Bar instance: FSI_0055+Foos is Bar? : true
> name: Baz instance: FSI_0055+Foos is Bar? : false
*)
Run Code Online (Sandbox Code Playgroud)