使用'flexible'类型参数对泛型类型进行模式匹配

时间:2010-09-21 22:07:40

标签: f# pattern-matching

match value with
| :? list<#SomeType> as l -> l //Is it possible to match any list of a type derived from SomeType?
| _ -> failwith "doesn't match"

5 个答案:

答案 0 :(得分:8)

正如已经指出的那样,没有办法直接这样做(模式匹配只能绑定值,但它不能绑定新的类型变量)。除了 kvb 的(更一般)解决方法之外,您可以使用所有集合实现非通用IEnumerable的事实,因此您可以检查以下类型:

match box value with 
| :? System.Collections.IEnumerable as l when 
     // assumes that the actual type of 'l' is 'List<T>' or some other type
     // with single generic type parameter (this is not fully correct, because
     // it could be other type too, but we can ignore this for now)
     typedefof<SomeType>.IsAssignableFrom
       (value.GetType().GetGenericArguments().[0]) -> 
   l |> Seq.cast<SomeType>
| _ -> failwith "doesn't match"

代码测试该值是否为非泛型IEnumerable以及type参数是否为SomeType的子类型。在这种情况下,我们得到了一些派生类型的列表,因此我们可以将它转换为SomeType值的序列(这与使用派生类型的值列表略有不同,但它应该无关紧要用于实际目的)。

答案 1 :(得分:3)

不,遗憾的是不可能做到这样的事情 - CLR没有提供任何有效的方式来进行这种类型的测试。有关一些(相当难看的)解决方案,请参阅How to cast an object to a list of generic type in F#F# and pattern matching on generics in a non-generic method implementing an interface

答案 2 :(得分:2)

我后来需要类似的东西来匹配Lazy实例。这是我的解决方案,万一有人发现它有用。

let (|Lazy|_|) (value : obj) =
    if box value <> null then
        let typ = value.GetType()
        if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<Lazy<_>> then
            Some(typ.GetGenericArguments().[0])
        else None
    else None

用法:

match value with
| Lazy typ when typeof<SomeType>.IsAssignableFrom(typ) -> (value :?> Lazy<_>).Value
| _ -> failwith "not an instance of Lazy<#SomeType>"

答案 3 :(得分:1)

不是最干净但最有效的:

let matchType<'T> () =
    try
        let o = Activator.CreateInstance<'T> ()
        match box o with
        | :? Type1 -> printfn "Type1"
        | :? Type2 -> printfn "Type2"
        | _ -> failwith "unknown type"
    with
    | ex -> failwith "%s" (ex.ToString())

答案 4 :(得分:0)

根据F# 2.0 specification,par。 14.5.2(解决子类型约束),它不起作用,因为:“F#泛型类型不支持协方差或逆变。”