Consider the following code, the first demonstrates that the "cleanup" executes when we're finished iterating over the IEnumerable of strings. The second pass is what is causing me grief. I need to be able abandon the IEnumerable before reaching the end, and then have the clean up code execute. But if you run this you'll see that in the second pass the clean up never fires.
What is the preferred way of abandoning an IEnumerable like this?
static void Main(string[] args)
{
// first pass
foreach (String color in readColors())
Console.WriteLine(color);
// second pass
IEnumerator<string> reader = readColors().GetEnumerator();
if (reader.MoveNext())
{
Console.WriteLine(reader.Current);
reader.Dispose();
}
}
static IEnumerable<string> readColors()
{
string[] colors = { "red", "green", "blue" };
for (int i = 0; i < colors.Length; i++)
yield return colors[i];
Console.WriteLine("Cleanup goes here");
}
You need to put the main part of your iterator method into a try..finally, with the cleanup code in the finally:
public IEnumerable<string> readColors()
{
try
{
string[] colors = { "red", "green", "blue" };
for (int i = 0; i < colors.Length; i++)
yield return colors[i];
}
finally
{
Console.WriteLine("Cleanup goes here");
}
}
Remember that under the hood an iterator method causes a seperate class to be created, that implements IEnumerable
and IEnumerator
. By putting your cleanup in the finally block, it ends up in the generated class' Dispose
method.
[Edit: (as pointed out in other answers) prefer a using
statement over your approach of calling Dispose
manually. I was assuming you'd done it like this just to highlight the issue under discussion, but it is worth pointing out anyway]
That's one way to abandon it. The reason that you are not seeing
Cleanup goes here
printed on the console is because the loop for (int i = 0; i < colors.Length; i++)
never runs to completion. See below for how to force the cleanup code to execute.
Here's another way. This is the preferred pattern for using IDisposable
objects in C#. This is preferred because this will cause IEnumerator.Dispose
to be called even if an exception occurs.
using (IEnumerator<string> reader = readColors().GetEnumerator()) {
reader.MoveNext();
Console.WriteLine(reader.Current);
}
As for forcing the cleanup code you have to execute you can do the following:
static IEnumerable<string> readColors() {
string[] colors = { "red", "green", "blue" };
try {
for (int i = 0; i < colors.Length; i++) {
yield return colors[i];
}
}
finally {
Console.WriteLine("Cleanup goes here");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With