使用“灵活”与泛型类型进行模式匹配类型参数

发布于 2024-09-24 13:10:39 字数 184 浏览 5 评论 0原文

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"
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"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夜清冷一曲。 2024-10-01 13:10:39

正如已经指出的,没有办法直接做到这一点(模式匹配只能绑定值,但不能绑定新类型变量)。除了 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 以及类型参数是否是 SomeType 的子类型。在这种情况下,我们得到了一些派生类型的列表,因此我们可以将其转换为 SomeType 值的序列(这与使用派生类型的值列表略有不同,但它应该对于实际目的来说并不重要)。

As already pointed out, there is no way to do this directly (pattern matching can only bind values, but it cannot bind new type variables). In addition to the (more general) workaround by kvb you can use the fact that all collections implement non-generic IEnumerable, so you can check for this type:

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"

The code tests whether the value is a non-generic IEnumerable and whether the type parameter is subtype of SomeType. In that case, we got a list of some derived type, so we can cast it to a sequence of SomeType values (this is slightly different than working with list of values of the derived types, but it shouldn't matter for practical purposes).

旧人 2024-10-01 13:10:39

不,不幸的是不可能做这样的事情 - CLR 没有提供任何有效的方法来进行此类类型测试。请参阅如何转换对象F# 中的泛型类型列表F# 和在实现接口的非泛型方法中对泛型进行模式匹配,用于一些(相当丑陋的)解决方案。

No, it's unfortunately not possible to do something like this - the CLR doesn't provide any efficient way of doing that kind of type test. See How to cast an object to a list of generic type in F# and F# and pattern matching on generics in a non-generic method implementing an interface for a few (rather ugly) solutions.

杀お生予夺 2024-10-01 13:10:39

后来我需要类似的东西来匹配惰性实例。这是我的解决方案,以防有人觉得有帮助。

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>"

I later needed something similar for matching Lazy instances. Here's my solution, in case anyone finds it helpful.

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

Usage:

match value with
| Lazy typ when typeof<SomeType>.IsAssignableFrom(typ) -> (value :?> Lazy<_>).Value
| _ -> failwith "not an instance of Lazy<#SomeType>"
倦话 2024-10-01 13:10:39

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

According to the F# 2.0 specification, par. 14.5.2 (Solving Subtype Constraints), it will not work, because: "F# generic types do not support covariance or contravariance."

笑红尘 2024-10-01 13:10:39

不是最干净的,但很有效:

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())

Not the cleanest, but effective:

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())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文