How can I drop IEnumerator without iterating to the end?

Consider the following code, the first shows that the "cleanup" is performed when we finish iterating over IEnumerable strings. The second pass is what causes me grief. I need to give up IEnumerable before reaching the goal, and then clean up the code. But if you run this, you will see that in the second pass, cleaning never works.

What is the preferred way to drop 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"); } 
+7
c #
source share
4 answers

You need to put the main part of your iterator method in try..finally, with the cleanup code in 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, the iterator method calls for the creation of a separate class that implements IEnumerable and IEnumerator . Having placed the cleanup in the finally block, it gets into the method of the generated Dispose class.

[ Edit: (as indicated in other answers) prefers the using statement for your approach to calling Dispose manually. I assumed that you did it this way to emphasize the issue under discussion, but it’s worth pointing out this anyway]

+7
source share

This is one way to abandon it. The reason you do not see

 Cleanup goes here 

printed on the console because the for loop for (int i = 0; i < colors.Length; i++) never terminates. The following describes how to enforce the cleanup code.

Here is another way. This is the preferred template for using IDisposable objects in C #. This is preferable because it will cause an IEnumerator.Dispose call, even if an exception is thrown.

 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"); } } 
+4
source share

I think the preferred way to clean up is to use IDisposable . In this case, it is better to implement your own IEnumerable<string> using a specific IEnumerator<string> and use the regular Dispose method. You get free help using foreach .

  class MyEnumerator : IEnumerator<string> { // ... #region IDisposable Members public void Dispose() { // do your cleanup here throw new NotImplementedException(); } #endregion // ... } 
+1
source share
 try { string[] colors = { "red", "green", "blue" }; for (int i = 0; i < colors.Length; i++) { if(condition == true) break; yield return colors[i]; } } finally { Console.WriteLine("Cleanup goes here"); } 
0
source share

All Articles