I tested this block of code and find that the GetInts method does not exit the method and print "GetInts disconnected" as i would expect, traditionally. I want to write a scroll control that incrementally downloads datarows from the database with yield return, but I am unsure of the correct method.
On the other hand, wrapping the yield return block with using block will guarantee the call on dispose(), but should I go that way?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IteratorPattern
{
class Program
{
static IEnumerator<int> _mIter;
static void Main(string[] args)
{
// get an enumerator to enumerate values in the database, at a later time
_mIter = GetInts(100).GetEnumerator();
// simulate some scrolling, which will add values to my scroll box
Scroll(10);
// suppose this control is now redundant,
// but this does not disconnect the data source
_mIter.Dispose();
// program ends will connection still open?
Console.WriteLine("Program End");
}
// iterate and cache (not implemented) values
private static void Scroll(int units)
{
Console.WriteLine("Scroll()");
while(units-- != 0 && _mIter.MoveNext())
{
Console.WriteLine(_mIter.Current);
}
Console.WriteLine("Scroll() completed");
}
// connect to database, yield-return each datarow, and disconnect (hopefully)
static IEnumerable<int> GetInts(int i)
{
Console.WriteLine("GetInts connected");
using (var ds = new DataSourceWrapper())
{
while (i-- != 0)
{
Console.WriteLine("yield {0}", i);
yield return i;
}
}
// not called!
Console.WriteLine("GetInts disconnected");
}
}
// try using a datasource wrapper to ensure Dispose() is called to disconnect the connection.
public class DataSourceWrapper : IDisposable
{
public void Dispose()
{
Console.WriteLine("DataSource Disconnected");
}
}
}
When I run it, it does disconnect:
Note that it would disconnect itself if you were reading to the end of the data; however, you are asking for 100 ints, and scrolling through 10 of them; as fas as the iterator is concerned you've left it hanging 10% in. If you exhaust an iterator block, it cleans up any
usingetc; if you don't exhaust it, it needs to be explicitly disposed. You can illustrate this by changing it toGetInts(5)(leaving all other code the same):The reason it doesn't show "GetInts disconnected" otherwise is that ... it never gets there unless you exhaust it! That is what your code says: "print connected; yield
iitems; print disconnected" - it won't print disconnected unless it has first done all the yields. If you use afinallythough (using theGetInts(100)again), then this changes:Then it works:
Basically,
finallyis mapped into theDispose()of the iterator.Additionally,
_mIterisIEnumerator<T>; it explicitly implementsIDisposable, and you are responsible for callingDispose()at some point. Do that, and it will all work. Even withIEnumerator(non-generic, not explicitlyIDisposable) you should follow the same approach as the compiler, and check if the enumerator isIDisposable, and callDispose()accordingly.Since you aren't consuming the data in one chunk, you can't use
using, but you must still dispose it. This might mean making your type implementIDisposable, and passing along the call. Also, you might want to explicitly dispose and release the enumerator when you reach the end. I can't really change your code to illustrate this, as it doesn't make sense onstaticdata, but I would hope that an enumerator would not bestaticon real code anyway.