I am trying to draw lessons on the following behaviour from an example I simplified :
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
-
Is the enumerator “en” disposed of independently of it being captured ? (I guess it is but is there anything to say / materials to read on this behaviour beside this msdn doc on ressources)
-
Why is it working if the sequence is transformed to a list first ?
Edit : this is just a toy example to illustrate a behaviour, not to be followed.
There are very few good reasons to manipulate enumerators directly.
The
usingfunction disposes the enumerator as soon as the lambda function returns. However, the lambda function creates a lazy sequence usingSeq.unfoldand the lazy sequence accesses the enumerator after the sequence is returned fromgroupedEnum.You could either fully evaluate the whole sequence inside
using(by addingList.ofSeqthere) or you need to callDisposewhen the end of the generated sequence is reached:Exception handling becomes quite difficult in this case, but I guess that one way to do it would be to wrap the body in
try .. withand callDisposeif an exception happens (and then returnNone).If you use sequence expressions instead, then the meaning of
usechanges and it automatically disposes the enumerator after the end of sequence is reached (not when the lazy sequence is returned). So using sequence expressions might be a better choice because the hard work is done for you:EDIT And why does it work in your first example? The enumerator returned by F# list type simply ignores
Disposeand continues working, while if you callDisposeon an enumerator returned by astring, the enumerator cannot be used again. (This is arguably a bit odd behaviour of the F# list type.)