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 isiterator.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!
精彩评论