Как привести объект к списку универсального типа в F # - PullRequest
6 голосов
/ 26 января 2010

В следующем фрагменте я собираюсь преобразовать объект System.Object (который может быть FSharpList) в список любого универсального типа, который он содержит.

    match o with
    | :? list<_>              -> addChildList(o :?> list<_>)
    | _                       -> addChild(o)

К сожалению, только list<obj> соответствует списку. Я бы хотел, чтобы list<Foo> также соответствовало списку.

Для некоторого контекста я пытаюсь обойти структуру объекта путем отражения, чтобы построить TreeView класса и его дочерних элементов. Рассмотрим следующий класс:

type Entity = {
    Transform   : Matrix
    Components  : obj list
    Children    : Entity list
}

Я хотел бы построить дерево, которое показывает мне все классы, содержащиеся в сущности. С помощью отражения я могу получить все свойства объекта, а также их значения (значение важно, поскольку я хочу отображать различные элементы в списке со свойством Name элемента, если оно есть):

        let o = propertyInfo.GetValue(obj, null)

Это значение может быть списком некоторого типа, но возвращаемое значение является просто System.Object Я сталкиваюсь с проблемами при попытке преобразовать этот объект в список. Я вынужден сделать следующее:

        match o with
        | :? list<obj>              -> addChildList(o :?> list<obj>)
        | :? list<Entity>           -> addChildList(o :?> list<Entity>)
        | _                         -> addChild(o)

Здесь я должен указать именно тот тип, в который я пытаюсь конвертировать.
Я действительно хотел бы написать это:

        match o with
        | :? list<_>              -> addChildList(o :?> list<_>)
        | _                       -> addChild(o)

К сожалению, это только когда-либо совпадало на list< obj >

Ответы [ 2 ]

5 голосов
/ 26 января 2010

К сожалению, нет простого способа сделать то, что вы хотите. Тесты типов могут использоваться только с определенными типами, и даже если тест типа пройден, оператор преобразования :?> также работает только для приведения выражений к определенным типам, поэтому правая часть вашего соответствия в любом случае не будет выполнять то, что вы хотите. Вы можете частично обойти эту проблему, используя активный шаблон:

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

Этот активный шаблон можно использовать следующим образом:

match o.GetType() with
| GenericType <@ typedefof<list<_>> @> [|t|] -> addChildListUntyped(t,o)
| _                                          -> addChild(o)

, где вы создали вариант addChildList, который принимает тип t и объект o (с типом времени выполнения list<t>) вместо общего списка.

Это немного неуклюже, но я не могу придумать более чистого решения.

1 голос
/ 31 марта 2010

Оказывается, что list<'a> или array<'a> могут быть сопоставлены как seq<obj>

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

Мне действительно все равно, что это список. Пока я могу это повторить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...