枚举器和F#处理

nic*_*las 3 ienumerable f# idisposable

我试图从我简化的示例中吸取以下行为的教训:

let groupedEnum  (input: 'a seq) =
   using (input.GetEnumerator()) (fun en ->
   Seq.unfold(fun _ -> 
                  if en.MoveNext() then 
                     Some(en.Current, ())
                  else None) ()
   )


//WORKS    
let c = groupedEnum    ("11111122334569999"   |>  List.ofSeq ) |>  List.ofSeq 

//BOOM !!  System.NullReferenceException
let c = groupedEnum    ("11111122334569999"                  ) |>  List.ofSeq
Run Code Online (Sandbox Code Playgroud)
  • 调查员"en"是否被独立处理?(我想这是关于ressources这个msdn doc旁边有什么可以说的/材料来阅读这个行为)

  • 如果序列首先转换为列表,为什么它可以工作?

编辑:这只是一个玩具示例来说明行为,而不是被遵循.直接操纵枚举器的理由很少.

Tom*_*cek 7

using一旦lambda函数返回,该函数就会处理枚举器.但是,lambda函数创建一个惰性序列,Seq.unfold并且延迟序列返回序列访问枚举器groupedEnum.

您可以完全评估内部的整个序列using(通过添加List.ofSeq那里),或者您需要在Dispose到达生成的序列的末尾时调用:

let groupedEnum  (input: 'a seq) =
   let en = input.GetEnumerator()
   Seq.unfold(fun _ -> 
       if en.MoveNext() then 
           Some(en.Current, ())
       else 
           en.Dispose()
           None)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,异常处理变得非常困难,但我想这样做的一种方法是将主体包装并在异常发生时try .. with调用Dispose(然后返回None).

如果您使用序列表达式,那么use更改的含义,它会在到达序列结束后自动处理枚举器(而不是在返回延迟序列时).所以使用序列表达式可能是更好的选择,因为为您完成了艰苦的工作:

let groupedEnum  (input: 'a seq) = seq {
   use en = input.GetEnumerator()
   let rec loop () = seq {
      if en.MoveNext() then 
         yield en.Current
         yield! loop () }
   yield! loop () }
Run Code Online (Sandbox Code Playgroud)

编辑为什么它在你的第一个例子中起作用?F#list类型返回的枚举器只是忽略Dispose并继续工作,而如果调用Disposea返回string的枚举器,则枚举器不能再次使用.(这可以说是F#列表类型有点奇怪的行为.)