2009-12-09 6 views
2

Je regarde les vidéos de Jon Skeet Copenhagen C# talk et j'ai fini avec ce code.
QUESTION:
Que se passe-t-il après l'impression du code Fini. Je veux dire pourquoi iterator.MoveNext() échoue?Comprendre les blocs d'itérateur et la méthode d'élimination

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 

Répondre

7

Appel MoveNext() va juste être de retour false sans faire quoi que ce soit d'autre, comme vous avez disposé du iterator. La machine d'état construite par le compilateur C# passera dans l'état "après", et restera là. Voir la section 10.14.4.2 de la spécification C# 3 pour plus de détails. La propriété Current continuera à renvoyer la dernière valeur renvoyée - le comportement dans cette situation est explicitly undefined in MSDN. (J'aurais pu jurer qu'il s'agissait d'une exception, mais apparemment pas.)

Est-ce que cela a du sens? Dispose ne "réinitialise" pas l'itérateur (et la méthode Reset elle-même n'est pas supportée par les blocs d'itération C#). Si vous voulez recommencer, vous devez appeler à nouveau le GetEnumerator.

Maintenant, je ne me souviens pas exactement ce que je disais dans le discours de Copenhague, donc des excuses si tout cela semble aller contre ce que la vidéo montre :)

+0

@Jon Ne vous inquiétez pas Jon vous n'avez rien dit de mal dans la conversation :-). En fait, j'ai accidentellement copié-collé la seconde pour la boucle et ensuite couru dans le problème. Merci (+ 1A). –

+0

@Jon 'Dispose ne "réinitialise" pas l'itérateur mais que fait-il alors dans ce cas? –

+0

@david: Il le fait juste s'arrêter - il le met dans l'état "après", à quel point d'autres appels à MoveNext() retourneront juste faux sans rien faire d'autre. –

1

Lorsque vous utilisez le modèle itérateur de .NET avec retour de rendement vous obtenez une petite machine d'état construite pour vous. Appeler la disposition déplace cette machine d'état dans son état final. Dans cet état final, l'itérateur n'ira plus en avant mais il se souviendra de son dernier état (Actuel). Si vous regardez l'IL généré (ou peut-être à travers Reflector, n'ont pas essayé), il devient assez clair.

0

Des choses étranges se produisent si vous continuez à travailler avec un objet disposé!

+0

Cela dépend de l'objet. Dispose() * peut * être utilisé pour fournir un comportement de "réinitialisation". Dans le cas de MemoryStream, vous pouvez toujours accéder aux données en utilisant ToArray après la mise au rebut, ce qui peut être utile. –

+0

Est-ce une fonctionnalité de langue accidentelle? Ou est-ce quelque chose qui va continuer à travailler à travers les futures versions de .net? –