匹配列表类型的 FSharp 模式
FSharp pattern that matches List type
我想匹配类型为"obj"的对象obj1,根据其实际类型匹配。
问题是列表类型的类型检查模式(下例中的第二个)与 F# 列表不匹配。
let obj1 = [1;2;3] :> obj
match obj1 with
| :? System.Array as a -> printfn "it's an array: %A" a
| :? List<_> as l -> printfn "It's a list: %A" l
| _ -> printfn "other type"
输出 "other type",而我希望它是 "It's a list: [1;2;3]"
如何正确检查列表类型?
我不会将 List<_> 称为 F# 列表 - 它是 .NET 框架类型。您可以将它与 (int list) 类型匹配——它是 F# list:
match obj1 with | :? (int list) as l -> 1 | _ -> 0
obj : [any type]
您需要做的是区分您的 obj 是否为通用类型。通过 .GetType()
获取它的类型,在那里你会找到合适的 属性.
obj : [some type of the form T<'T1, 'T2, ..., 'Tn> for concrete types T1, T2, ..., Tn]
如果是,那么您可以通过对通过 .GetType()
获得的类型使用适当的方法来获得泛型类型定义
obj : [some type of the form T<'T1, 'T2, ..., 'Tn> with T1, T2, ..., Tn not bound yet]
并且您现在可以将此类型与 typedefof<_ list>
进行比较(_ list
被推断为 obj list
,但是 typedefof
与 typeof
不同已经为您获取了通用类型定义)。
最终代码看起来像这样(伪代码)
let ty = obj.GetType()
match obj with
| unknownType when not ty.IsGenericType -> "some non-generic type, probably a concrete collection"
| list when ty.GetGenericTypeDefinition() = typedefof<_ list> -> "list"
| array when ty.GetGenericTypeDefinition() = typedefof<_ array> -> "array"
| _ -> "something else, i.e. a generic type that we don't know"
Daniel Fabian 已经在他的回答中解释了这个问题。
实现他的解决方案的一种方法是使用 Active Pattern:
let (|IsList|_|) (candidate : obj) =
let t = candidate.GetType()
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<list<_>>
then Some (candidate :?> System.Collections.IEnumerable)
else None
您现在可以更改匹配以使用此活动模式:
let obj1 = [1;2;3] :> obj
match obj1 with
| :? System.Array as a -> printfn "it's an array: %A" a
| IsList l -> printfn "It's a list: %A" l
| _ -> printfn "other type"
这会打印:
> It's a list: [1; 2; 3]
我想匹配类型为"obj"的对象obj1,根据其实际类型匹配。 问题是列表类型的类型检查模式(下例中的第二个)与 F# 列表不匹配。
let obj1 = [1;2;3] :> obj
match obj1 with
| :? System.Array as a -> printfn "it's an array: %A" a
| :? List<_> as l -> printfn "It's a list: %A" l
| _ -> printfn "other type"
输出 "other type",而我希望它是 "It's a list: [1;2;3]"
如何正确检查列表类型?
我不会将 List<_> 称为 F# 列表 - 它是 .NET 框架类型。您可以将它与 (int list) 类型匹配——它是 F# list:
match obj1 with | :? (int list) as l -> 1 | _ -> 0
obj : [any type]
您需要做的是区分您的 obj 是否为通用类型。通过 .GetType()
获取它的类型,在那里你会找到合适的 属性.
obj : [some type of the form T<'T1, 'T2, ..., 'Tn> for concrete types T1, T2, ..., Tn]
如果是,那么您可以通过对通过 .GetType()
obj : [some type of the form T<'T1, 'T2, ..., 'Tn> with T1, T2, ..., Tn not bound yet]
并且您现在可以将此类型与 typedefof<_ list>
进行比较(_ list
被推断为 obj list
,但是 typedefof
与 typeof
不同已经为您获取了通用类型定义)。
最终代码看起来像这样(伪代码)
let ty = obj.GetType()
match obj with
| unknownType when not ty.IsGenericType -> "some non-generic type, probably a concrete collection"
| list when ty.GetGenericTypeDefinition() = typedefof<_ list> -> "list"
| array when ty.GetGenericTypeDefinition() = typedefof<_ array> -> "array"
| _ -> "something else, i.e. a generic type that we don't know"
Daniel Fabian 已经在他的回答中解释了这个问题。
实现他的解决方案的一种方法是使用 Active Pattern:
let (|IsList|_|) (candidate : obj) =
let t = candidate.GetType()
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<list<_>>
then Some (candidate :?> System.Collections.IEnumerable)
else None
您现在可以更改匹配以使用此活动模式:
let obj1 = [1;2;3] :> obj
match obj1 with
| :? System.Array as a -> printfn "it's an array: %A" a
| IsList l -> printfn "It's a list: %A" l
| _ -> printfn "other type"
这会打印:
> It's a list: [1; 2; 3]