枚举器和F#处理

时间:2012-11-22 12:13:39

标签: f# ienumerable 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
  • 枚举器“en”是否被独立处理? (我想在this msdn doc on ressources旁边有什么可以说/材料可以阅读这种行为)

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

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

1 个答案:

答案 0 :(得分: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)

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

如果使用序列表达式,那么None的含义会发生变化,它会在到达序列结束后自动处理枚举器(而不是在返回延迟序列时)。所以使用序列表达式可能是一个更好的选择,因为努力工作是为你完成的:

use

编辑为什么它适用于您的第一个示例? F#list类型返回的枚举器只是忽略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 () } 并继续工作,而如果在Dispose返回的枚举器上调用Dispose,则枚举器不能再次使用。 (这可以说是F#列表类型有点奇怪的行为。)