开发者

Understanding Iterator Blocks and Dispose Method

I am watching Jon Skeet's Cop开发者_运维知识库enhagen C# talk videos and I ended up with this code.

QUESTION:

What is happening after the code prints Finished. I mean why is iterator.MoveNext() failing?

CODE:

 class IteratorBlocks
    {
        public static IEnumerable<string> GetStringsForever()
        {
            string current = "";
            char nextChar = 'a';
            try
            {
                while (true)
                {
                    current += nextChar;
                    nextChar++;

                    if (nextChar > 'z')
                    {
                        nextChar = 'a';
                    }

                    yield return current;
                }
            }
            finally
            {
                Console.WriteLine("Finished");
            }
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            IEnumerable<string> strings = IteratorBlocks.GetStringsForever();

            IEnumerator<string> iterator = strings.GetEnumerator();

            for (int i = 0; i < 10; i++)
            {
                iterator.MoveNext();
                Console.WriteLine(iterator.Current);
            }

            /* 
               I am not able to get what the code is doing beyond this line?
             */

            iterator.Dispose();

            for (int i = 0; i < 10; i++)
            {
                iterator.MoveNext();
                Console.WriteLine(iterator.Current);
            }

        }
    }

OUTPUT:

a
ab
abc
abcd
abcde
abcdef
abcdefg
abcdefgh
abcdefghi
abcdefghij
Finished
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij


Calling MoveNext() is just going to be returning false without doing anything else, as you've disposed of the iterator. The state machine built by the C# compiler will go into the "after" state, and stay there. See section 10.14.4.2 of the C# 3 spec for details.

The Current property will continue to return the last value it returned - the behaviour in this situation is explicitly undefined in MSDN. (I could have sworn it was meant to throw an exception, but apparently not.)

Does that make sense? Dispose doesn't "reset" the iterator (and the Reset method itself isn't supported by C# iterator blocks). If you want to iterate again, you need to call GetEnumerator again.

Now, I can't remember exactly what I said in the Copenhagen talk, so apologies if any of this appears to go against what the video shows :)


When you use .NET's iterator pattern with yield return you are getting a little state machine built for you. Calling dispose moves that state machine to it's final state. In that final state the iterator will no longer move forward but it will remember it's last state (Current). If you look at the generated IL (or maybe through Reflector, haven't tried) it becomes pretty clear.


Strange things happen if you keep on working with a disposed object!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜