Unfortunately, there’s no easy way to do what you want. Type tests can only be used with specific types, and even if the type test passed, the conversion operator :?>
also only works to cast expressions to specific types so the right hand side of your match wouldn’t do what you want anyway. You can partially work around this issue using an active pattern:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let ( |GenericType|_| ) =
(* methodinfo for typedefof<_> *)
let tdo =
let (Call(None,t,[])) = <@ typedefof<_> @>
t.GetGenericMethodDefinition()
(* match type t against generic def g *)
let rec tymatch t (g:Type) =
if t = typeof<obj> then None
elif g.IsInterface then
let ints = if t.IsInterface then [|t|] else t.GetInterfaces()
ints |> Seq.tryPick (fun t -> if (t.GetGenericTypeDefinition() = g) then Some(t.GetGenericArguments()) else None)
elif t.IsGenericType && t.GetGenericTypeDefinition() = g then
Some(t.GetGenericArguments())
else
tymatch (t.BaseType) g
fun (e:Expr<Type>) (t:Type) ->
match e with
| Call(None,mi,[]) ->
if (mi.GetGenericMethodDefinition() = tdo) then
let [|ty|] = mi.GetGenericArguments()
if ty.IsGenericType then
let tydef = ty.GetGenericTypeDefinition()
tymatch t tydef
else None
else
None
| _ -> None
This active pattern can be used as follows:
match o.GetType() with
| GenericType <@ typedefof<list<_>> @> [|t|] -> addChildListUntyped(t,o)
| _ -> addChild(o)
where you’ve created a variation of addChildList
which takes a type t
and an object o
(with runtime type list<t>
) instead of taking a generic list.
This is a bit clunky, but I can’t think of a cleaner solution.