Finally, the block may not be called when listing using the ledge method.

I found a situation where the final block is not called.

To the moment:

using System; using System.Collections.Generic; using System.Threading; using System.ComponentModel; class MainClass{ static IEnumerable<int> SomeYieldingMethod(){ try{ yield return 1; yield return 2; yield return 3; }finally{ Console.WriteLine("Finally block!"); } } static void Main(){ Example(7); Example(2); Example(3); Example(4); } static void Example(int iterations){ Console.WriteLine("\n Example with {0} iterations.", iterations); var e = SomeYieldingMethod().GetEnumerator(); for (int i = 0; i< iterations; ++i) { Console.WriteLine(e.Current); e.MoveNext(); } } } 

And the result:

 Example with 7 iterations. 0 1 2 3 Finally block! 3 3 3 Example with 2 iterations. 0 1 Example with 3 iterations. 0 1 2 Example with 4 iterations. 0 1 2 3 Finally block! 

So it looks like someone is using my yielding method and working with it manually using counters (not with foreach), then my last block can never be called.

Is there a way to ensure that my method completes it? And is there a mistake in the “syntactic sugar yield” or does it work the way it should be?

+6
source share
2 answers

This is because they did not use the iterator correctly. This code should be:

 using(var e = SomeYieldingMethod().GetEnumerator()) { for (int i = 0; i< iterations; ++i) { Console.WriteLine(e.Current); e.MoveNext(); } } 

Using is important here. . This is what is displayed in finally , etc. (Assuming the iterator is not finished yet).

In particular, foreach explicitly calls Dispose() on the iterator if the iterator implements IDisposable . Since IEnumerator<T> : IDisposable , this includes almost all iterators (caveat: foreach does not require IEnumerator[<T>] ).

+3
source

It works as intended, finally it starts when an error occurs or when you get to a block, if they never list to the end, you will never reach the end of this block, so finally it should not be done.

The output creates a class to represent your method as a method + member state + place to resume, so each time you “resume” the method, you will never reach the final place if your finally be placed at the end of the enumeration, and you will never list to end. If you want the finally block to be executed each time, you will need to do a reliable evaluation, but you cannot say something like "I want it to be streamed, and finally called at the end, but the compiler automatically detects in the client code, if it never reaches Stream to the end, then finally call. " There is no “right” time for the final call, because all that you know, it can transfer the enumerator to another project and continue to list it later, what will happen if it continues the enumeration, and the compiler is illegally called permanently?

+1
source

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


All Articles