How can I cancel IEnumerable?

In the method returning IEnumerable<> , I open and iterate through the resource (for example, a database row reader). At the end of the cycle, the resource closes again.

However, it may happen that the caller decides not to end the transfer. This leaves the resource open.

Example:

 IEnumerable<Foo> Bar () { using (var r = OpenResource()) { while (r.Read ()) { yield return r; } } } // OK - this closes the resource again foreach (var foo in Bar()) { Console.WriteLine (foo); } // Not OK - resource stays open! Console.WriteLine (Bar().First()); 

How can i solve this? Can I easily cancel the listing, i.e. Tell him to skip the rest of the loop or delete it (put the cleanup code in Dispose )?

I thought about returning Func<Result, bool> so that the user could return it false if iterated. In the same way, you can use some kind of undo marker. But both approaches seem cumbersome to me.

+5
source share
1 answer

Usually this IEnumerator<> implements IDisposable , and if you look at the definition of IEnumerator<> , you will see that:

 public interface IEnumerator<out T> : IDisposable, IEnumerator 

The foreach correctly Dispose() IEnumerator<> , which gets from IEnumerable<> , so that:

 IEnumerable<SomeClass> res = SomeQuery(); foreach (SomeClass sc in res) { if (something) break; } 

when you exit foreach any way ( break , exception, naturally ending with res ), you should call Dispose() IEnumerator<> . See https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx for an example implementation of foreach (a try ... finally ... with Dispose() inside finally )

Note that C # will generate the β€œcorrect” code for using , used inside the yield function. See here: http://goo.gl/Igzmiz

 public IEnumerable<Foo> Bar() { using (var r = OpenResource()) { while (r.Read ()) { yield return new Foo(); } } } 

transforms into something that

 void IDisposable.Dispose() { int num = this.<>1__state; if (num == -3 || num == 1) { try { } finally { this.<>m__Finally1(); } } } 

The Dispose() method of IEnumerator<> will call the m__Finally1 method, which will be (IDisposable)this.<r>5__1.Dispose(); (where 5__1 is the r returned from OpenResource() ). m__Finally is called even if the code just β€œexits” from while (r.Read ()) :

 if (!this.<r>5__1.Read()) { this.<>m__Finally1(); 

and / or if there is an exception.

  catch { this.System.IDisposable.Dispose(); 
+12
source

Source: https://habr.com/ru/post/1244432/


All Articles