【问题标题】:Pattern matching against generic type using 'flexible' type parameter使用“灵活”类型参数对泛型类型进行模式匹配
【发布时间】:2010-09-21 22:07:40
【问题描述】:
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"

【问题讨论】:

  • 我认为这并不重要,但为什么需要匹配某种类型的列表?如果列表是同质的,那么您可以简单地逐个处理列表元素,这很好。如果列表是异构的,那么无论如何您都不能将列表视为逻辑单元。您想在这里解决什么问题?
  • 我想我应该使用我的实际代码。为了简单起见,我只是使用 list 。问题是如何对类型参数进行灵活匹配。

标签: f# pattern-matching


【解决方案1】:

正如已经指出的,没有办法直接做到这一点(模式匹配只能绑定值,但不能绑定新的类型变量)。除了 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 值的序列(这与使用派生类型的值列表略有不同,但实际上并不重要目的)。

【讨论】:

    【解决方案2】:

    不,很遗憾,这样的事情是不可能的——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 了解一些(相当丑陋的)解决方案。

    【讨论】:

      【解决方案3】:

      我后来需要类似的东西来匹配 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>"
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          不是最干净,但有效:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-25
            • 2016-10-29
            • 2013-04-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-11-30
            • 1970-01-01
            相关资源
            最近更新 更多