在下面的代码片段中,我的目的是将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)
此值可以是某种类型的列表,但值return只是一个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 >
答案 0 :(得分:5)
不幸的是,没有简单的方法可以做你想做的事。类型测试只能用于特定类型,即使传递了类型测试,转换运算符:?>
也只能将表达式转换为特定类型,因此匹配的右侧不会执行您想要的操作无论如何。您可以使用活动模式部分解决此问题:
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 :(得分:1)
事实证明,list<'a>
或array<'a>
可以匹配为seq<obj>
match o with
| :? seq<obj> -> addChildCollection(o :?> seq<obj>)
| _ -> addChild(o)
我真的不在乎它是一个列表。只要我可以迭代它。