Как перечислить дискриминационный союз в F #?
Как я могу перечислить возможные "значения" дискриминационного объединения в F #?
Я хочу знать, есть ли что-то вроде Enum.GetValues(Type)
для дискриминационных союзов, я не уверен, какие данные я бы перечислил. Я хотел бы создать список или массив дискриминационного объединения с одним элементом для каждой опции.
Ответы
Ответ 1
Да, у 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
Ответ 2
Чтобы немного расширить пример Роберта - даже если у вас нет экземпляра разграниченного объединения, вы можете использовать отражение F #, чтобы получить информацию о типе (например, типы аргументов отдельных случаев). Следующее расширяет образец Robert ans, а также выводит типы аргументов:
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 ""
Например, для типа option<int>
вы получите (я немного упростил вывод):
type Microsoft.FSharp.Core.FSharpOption`1[System.Int32] =
| None
| Some of System.Int32
Существует много интересных применений для этой информации - например, вы можете генерировать схему БД из профсоюзов F # или создавать функции, которые будут анализировать XML в дискриминационный союз (который описывает структуру). Я говорил о примере обработки XML на конференции GOTO в начале этого года.
Ответ 3
Если ваш дискриминационный союз состоит только из простых идентификаторов (без хранения каких-либо данных, это может быть то, что вам нужно: 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>
#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
*)
Ответ 4
Трудно понять, как это могло бы работать, не имея экземпляра, поскольку союзы дискриминации могут переносить значения.
Если у вас был такой тип, как это, например:
type Status = Success of string | Error of System.Exception | Timeout
Что бы вы, кроме своего массива, могли содержать для успеха или ошибки в этом случае?