How to cast an object to a list of generic type in F#

后端 未结 2 1821
走了就别回头了
走了就别回头了 2020-11-29 09:47

In the following snippet my intention is to convert a System.Object (which could be an FSharpList) to a list of whatever generic type it is holding.

    matc         


        
相关标签:
2条回答
  • 2020-11-29 10:21

    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.

    0 讨论(0)
  • 2020-11-29 10:22

    It turns out that either list<'a> or array<'a> can be matched as seq<obj>

        match o with
        | :? seq<obj> -> addChildCollection(o :?> seq<obj>)
        | _           -> addChild(o)
    

    I don't really care that it is a list. As long as I can iterate over it.

    0 讨论(0)
提交回复
热议问题