f# 模式匹配序列 :? seq<_> (IEnumerable)

Dav*_*peš 1 generics f# types pattern-matching

我想将结果发送到将内容输出到控制台/日志的单个方法

我曾希望检测结果是否包含 IEnumerable 并遍历该集合以获取结果。

这无法识别 seq 并简单地将其标识为Other Object.

对不起,冗长。

let rec LogResultGeneric (logInfo: string -> unit, logError: string -> unit) (result: Result<_, _>) =
    let innerSelect (item: _) =
        match item |> box with
        | :? Result<_, _> as res ->
            "RESULT" |> logInfo
            res |> LogResultGeneric(logInfo, logError)
        | _ ->
            "VALUE" |> logInfo
            item |> LogValueGeneric logInfo

    "DISPLAY OUTCOME : " + result.ToString() |> logInfo 
    match result with
    | Error msg ->
        "ERROR RESULT" |> logError
        match msg |> box with
        | :? string as msg -> msg |> logError
        | _ -> msg.ToString() |> logError

    | Ok payload ->
        "OK RESULT" |> logInfo

        match payload |> box with
        | :? seq<obj> as s ->
            "IENUMERABLE" |> logInfo
            s
            |> Seq.iter innerSelect
        | _ ->
            "VALUE" |> logInfo
            payload |> LogValueGeneric logInfo
        |> ignore
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 5

将值与泛型类型匹配的模式很棘手 - 因为编译器不会静态地知道'ainseq<'a>应该是什么。模式匹配seq<obj>也不起作用,因为 egseq<int>没有实现seq<obj>.

但是,对于集合,您很幸运,因为泛型IEnumerable<'T>继承自非泛型IEnumerable,因此您可以使用它:

let print payload = 
  match box payload with 
  | :? System.Collections.IEnumerable as ie ->
      let en = ie.GetEnumerator()
      while en.MoveNext() do
        printfn "ITEM: %A" en.Current
  | v -> 
      printfn "VALUE: %A" v

print [1;2;3]
print "ABC"
Run Code Online (Sandbox Code Playgroud)

让这样的东西适用于没有非泛型基类型的泛型类型option<'T>更难,通常需要反射。

一个技巧是拥有一个通用的帮助类并使用反射将其特化为正确的类型,然后调用该方法(但通常,这非常难看):

type OptionPrinter<'T> = 
  static member Print(o:option<'T>) = 
    match o with 
    | None -> printfn "Nothing"
    | Some v -> printfn "Something: %A" v 

let print payload = 
  match box payload with 
  | :? System.Collections.IEnumerable as ie ->
      let en = ie.GetEnumerator()
      while en.MoveNext() do
        printfn "ITEM: %A" en.Current
  | null -> 
      printfn "NULL (or None)"
  | v when v.GetType().IsGenericType && 
          v.GetType().GetGenericTypeDefinition() = typedefof<option<_>> ->
      let tya = v.GetType().GetGenericArguments()
      let ty = typedefof<OptionPrinter<_>>.MakeGenericType(tya)
      ty.GetMethod("Print").Invoke(null, [| v |]) |> ignore
  | v -> 
      printfn "VALUE: %A" v

print [1;2;3]
print "ABC"
print (Some 10)
print None
Run Code Online (Sandbox Code Playgroud)