4

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

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

编辑:这只是一个说明行为的玩具示例,请勿遵循。直接操作枚举数的充分理由很少。

4

1 回答 1

8

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(然后返回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 () }

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

于 2012-11-22T12:35:05.673 回答