В следующем отрывке мое намерение состоит в том, чтобы преобразовать Систему. Объект (который мог быть 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)
Это значение могло быть списком некоторого типа, но возвратом значения является просто Система. Возразите, что я сталкиваюсь с проблемами при попытке преобразовать этот объект в список. Я вынужден сделать следующее:
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 >
Оказывается, что либо list<'a>
, либо array<'a>
могут быть сопоставлены как seq
match o with
| :? seq<obj> -> addChildCollection(o :?> seq<obj>)
| _ -> addChild(o)
Меня не очень волнует, что это список. До тех пор, пока я могу выполнять итерации над ним.
К сожалению, нет простого способа сделать то, что вы хотите. Тип тесты можно использовать только с конкретными типами, и даже если прошел тест типа, оператор преобразования :?>
также работает только для литых выражений для определенных типов, поэтому правая сторона вашего матча не будет Делайте то, что вы хотите все равно. Вы можете частично работать по этому вопросу, используя активную картину:
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
(с типом выполнения в списке
) вместо того, чтобы принимать универсальный список.
Это немного неуместно, но я не могу думать о чистом решении.